groovy GMail API“尝试访问服务帐户的电子邮件/线程时,[用户]的委派被拒绝

iecba09b  于 2022-12-03  发布在  其他
关注(0)|答案(1)|浏览(270)

我有一个Katalon Studio代码库,它有一个成员转换脚本,这取决于是否能够从一个Gmail账户中提取注册链接进行测试。在那个Gmail账户(我们称之为KatalonAutoTesting@gmail.com)上,我设置了OAuth 2.0客户端ID和服务账户,并为服务账户下载了一个gmail-access-credentials.json
我的SMDEmailUtils类对于这一切,被定义为:

public final class SMDEmailUtils {

    private static Gmail _GmailInstance;

    public static String GetMainEmail() {
        if (!GeneralWebUIUtils.GlobalVariableExists('emailID'))
            return "dev@xxx-dev.com";
        return GlobalVariable.emailID.toString();
    }

    public static String CreateEmailFor(String firstName, String lastName) {
        final String[] mainEmailParts = this.GetMainEmail().split('@');

        return "${mainEmailParts[0]}+${firstName}${lastName}@${mainEmailParts[1]}"
                .replaceAll("\'", "");
    }

    public static String ExtractSignUpLink() {
        String link;

        int retryAttempts;

        ActionHandler.Handle({
            link = this.ProcessHTML(this.GetLatestMessageBody(30),
                    "//a[.//div[@class = 'sign-mail-btn-text']]/@href");
        }, { boolean success, ex -> 
            if (!success)
                sleep(1000 * 2**retryAttempts++);
        }, TimeUnit.MINUTES.toSeconds(15))

        return link;
    }

    /**
     * Application name.
     */
    private static final String AppName = "Gmail Message Accessor";
    /**
     * Global instance of the JSON factory.
     */
    private static final JsonFactory JSONFactory = GsonFactory.getDefaultInstance();
    /**
     * Directory to store authorization tokens for this application.
     */
    private static final String TokensDirectoryPath = "tokens";

    /**
     * Global instance of the Scopes required by this quickstart.
     * If modifying these Scopes, delete your previously saved tokens/ folder.
     */
    private static final List<String> Scopes = [GmailScopes.GMAIL_READONLY,];
    private static final String CredentialsFilePath = "./gmail-access-credentials.json";

    /**
     * Creates an authorized Credential object.
     *
     * @param httpTransport The network HTTP Transport.
     * @return An authorized Credential object.
     * @throws IOException If the credentials.json file cannot be found.
     */
    private static Credential getCredentials(final NetHttpTransport httpTransport)
    throws IOException {
        // Load client secrets.
        InputStream is = new FileInputStream(this.CredentialsFilePath);
        if (is == null) {
            throw new FileNotFoundException("Resource not found: " + this.CredentialsFilePath);
        }
        GoogleClientSecrets clientSecrets =
                GoogleClientSecrets.load(this.JSONFactory, new InputStreamReader(is));

        // Build flow and trigger user authorization request.
        GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
                httpTransport, this.JSONFactory, clientSecrets, this.Scopes)
                .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(this.TokensDirectoryPath)))
                .setAccessType("offline")
                .build();
        LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();
        return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
    }

    public static Gmail GetGmailInstance() {
        if (this._GmailInstance == null) {
            // Build a new authorized API client service.
            final NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
            this._GmailInstance = new Gmail.Builder(httpTransport, this.JSONFactory, getCredentials(httpTransport))
                    .setApplicationName(this.AppName)
                    .build();
        }
        return this._GmailInstance;
    }

    public static String GetLatestMessageBody(int timeOut) {
        return this.getContent(this.GetLatestMessage(timeOut));
    }

    public static Message GetLatestMessage(int timeOut) {
        // get the latest thread list
        ListThreadsResponse response = this.HandleRequest({
            return this.GetGmailInstance()
                    .users()
                    .threads()
                    .list(this.GetMainEmail())
                    .setQ("is:unread newer_than:1d")
                    .setIncludeSpamTrash(true)
                    .execute();
        },
        { ListThreadsResponse res -> return !res.getThreads().isEmpty() },
        timeOut);

        return response.getThreads()
                .collect({ Thread thread ->
                    return this.GetGmailInstance()
                            .users()
                            .threads()
                            .get(this.GetMainEmail(), thread.getId())
                            .execute()
                }).max { Thread thread -> thread.getMessages().last().getInternalDate() }
                .getMessages()
                .last();
    }

    /**
     * Copied from https://stackoverflow.com/a/58286921
     * @param message
     * @return
     */
    private static String getContent(Message message) {
        StringBuilder stringBuilder = new StringBuilder();
        try {
            getPlainTextFromMessageParts(message.getPayload().getParts(), stringBuilder);
            // NOTE: updated by Mike Warren, this was adapted for message that contain URLs in its body
            return new String(Base64.getUrlDecoder().decode(stringBuilder.toString()),
                    StandardCharsets.UTF_8);
        } catch (UnsupportedEncodingException e) {
            // NOTE: updated by Mike Warren
            Logger.getGlobal().severe("UnsupportedEncoding: ${e.toString()}");
            return message.getSnippet();
        }
    }

    /**
     * Copied from https://stackoverflow.com/a/58286921
     * @param messageParts
     * @param stringBuilder
     */
    private static void getPlainTextFromMessageParts(List<MessagePart> messageParts, StringBuilder stringBuilder) {
        for (MessagePart messagePart : messageParts) {
            // NOTE: updated by Mike Warren
            if (messagePart.getMimeType().startsWith("text/")) {
                stringBuilder.append(messagePart.getBody().getData());
            }

            if (messagePart.getParts() != null) {
                getPlainTextFromMessageParts(messagePart.getParts(), stringBuilder);
            }
        }
    }

    public static GenericJson HandleRequest(Closure<GenericJson> onDoRequest, Closure<Boolean> onCheckResponse, int timeOut) {
        long startTime = System.currentTimeSeconds();

        int exponent = 0;

        while (System.currentTimeSeconds() < startTime + timeOut) {
            GenericJson response = onDoRequest();
            if (onCheckResponse(response))
                return response;

            // wait some time to try again, exponential backoff style
            sleep(1000 * 2**exponent++);
        }

        return null;
    }

    /**
     * **NOTE**: forked from https://stackoverflow.com/a/2269464/2027839 , and then refactored
     * 
     * Processes HTML, using XPath
     * 
     * @param html
     * @param xpath
     * @return the result 
     */
    public static String ProcessHTML(String html, String xpath) {

        final String properHTML = this.ToProperHTML(html);

        final Element document = DocumentBuilderFactory.newInstance()
                .newDocumentBuilder()
                .parse(new ByteArrayInputStream( properHTML.bytes ))
                .documentElement;
        return XPathFactory.newInstance()
                .newXPath()
                .evaluate( xpath, document );
    }

    private static String ToProperHTML(String html) {
        // SOURCE: https://stackoverflow.com/a/19125599/2027839
        String properHTML = html.replaceAll( "(&(?!amp;))", "&amp;" );

        if (properHTML.contains('<!DOCTYPE html'))
            return properHTML;

        return """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
    <head></head>
    <body>
        ${properHTML}
    </body>
</html>
""";
    }
}

我正在尝试点击KatalonAutoTesting@gmail.com收件箱的消息/线程。
然而,当我点击SMDEmailUtils.ExtractSignUpLink()时,在从OAuth同意屏幕登录后,我遇到了一些403错误,消息是:
Delegation denied for [my_email]
我已在OAuth同意屏面设置测试用户步骤中添加[my_email]作为测试用户。
当我用"me"代替this.GetMainEmail()时,它可以工作,但它访问的是 * 我的 * 电子邮件收件箱,而不是KatalonAutoTesting@gmail.com
我该怎么做才能补救这一点,并让它工作?

注意:这是工作,正如所写的,直到我遇到一些invalid_grant问题。我删除了令牌,试图重新创建它,我似乎面临着这个问题,它感觉像我什么也做不了...

zzlelutf

zzlelutf1#

事实证明,我尝试做的是所谓的委托,经过长时间的思考,没有必要(通过Katalon工作室GlobalVariable s(或类似的东西),最终用户可以设置自己的主电子邮件。这是故意的设计决策WAY回到当我第一次在这个工作).
这里的委派是一个YAGNI,只有for Google Workspace users才有意义。但是,我的主电子邮件和我的个人电子邮件只是他们自己的独立用户,而不是在任何工作区上。
因此,这里的解决方案是从tokens文件夹中删除StoredCredential,运行用户测试用例,这一次,通过主电子邮件的Chrome配置文件手动处理OAuth同意屏幕

相关问题