Using service account to access Google Admin Report SDK

1.2k views Asked by At

Is it possible to use a service account to access the Google Admin Report SDK?

I have a very basic example I am trying to run and I always get a 400 error returned. I have validated the key and service ID are correct and I have even delegated authority to this service account. Is this just not possible? Anyone have any ideas?

PrivateKey serviceAcountPrivateKey = null;
    try (InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("insta2.p12")) {
        serviceAcountPrivateKey = SecurityUtils.loadPrivateKeyFromKeyStore(
                SecurityUtils.getPkcs12KeyStore(), resourceAsStream, "notasecret",
                "privatekey", "notasecret");
    } catch (IOException | GeneralSecurityException e) {
        throw new RuntimeException("Error loading private key", e);
    }
    try {
        HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
        // Build service account credential.
        GoogleCredential credential = new GoogleCredential.Builder()
                .setTransport(httpTransport)
                .setJsonFactory(jsonFactory)
                .setServiceAccountId("[email protected]")
                .setServiceAccountPrivateKey(serviceAcountPrivateKey)
                .setServiceAccountScopes(
                        Arrays.asList(
                                ReportsScopes.ADMIN_REPORTS_USAGE_READONLY,
                                ReportsScopes.ADMIN_REPORTS_AUDIT_READONLY))
                .build();
        Reports service = new Reports.Builder(httpTransport, jsonFactory, credential)
                .setApplicationName("APP_NAME_BLOCKED")
                .build();

        service.activities().list("all", "admin").execute();

    } catch (GeneralSecurityException | IOException e) {
        throw new RuntimeException("Error init google", e);
    }

The error I get back is the following:

{ "code" : 401, "errors" : [ { "domain" : "global", "location" : "Authorization", "locationType" : "header", "message" : "Access denied. You are not authorized to read activity records.", "reason" : "authError" } ], "message" : "Access denied. You are not authorized to read activity records." }

2

There are 2 answers

0
Russell Spitler On BEST ANSWER

For all of those wondering, if you do not use the call

.setServiceAccountUser("admins email address")

on the GoogleCredential object then this will fail as above. It is a little confusing as the service account on it's own does not have permission to access the reports, but it does have the ability to assume the role of an account that does...

0
rgoel On

Yes, you need to pass the admin email id which are impersonating. Here is the working code in GO language:

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "time"

    "golang.org/x/net/context"
    "golang.org/x/oauth2/google"

    //admin "google.golang.org/api/admin/directory/v1"
    admin "google.golang.org/api/admin/reports/v1"
    "google.golang.org/api/option"
)

// Path to the Service Account's Private Key file
var ServiceAccountFilePath = “/path/to/keyfile.json"

// Build and returns an Admin SDK Directory service object authorized with
// the service accounts that act on behalf of the given user.
// Args:
//    user_email: The email of the user. Needs permissions to access the Admin APIs.
// Returns:
//    Admin SDK directory service object.
func CreateReportsService(userEmail string) (*admin.Service, error) {
    ctx := context.Background()

    jsonCredentials, err := ioutil.ReadFile(ServiceAccountFilePath)
    if err != nil {
        return nil, err
    }

    config, err := google.JWTConfigFromJSON(jsonCredentials, "https://www.googleapis.com/auth/admin.reports.audit.readonly")
    if err != nil {
        return nil, fmt.Errorf("JWTConfigFromJSON: %v", err)
    }
    config.Subject = userEmail

    ts := config.TokenSource(ctx)

    srv, err := admin.NewService(ctx, option.WithTokenSource(ts))
    if err != nil {
        return nil, fmt.Errorf("NewService: %v", err)
    }
    return srv, nil
}

func main() {
    srv, err := CreateReportsService(“<admin_user_email_id>") // Here please enter the admin user email id; it is the admin user who has the permission
    if err != nil {
        log.Fatalf("Unable to retrieve reports Client %v", err)
        return
    }

    var userKey = "all"
    //var appName = "admin"
    var appName = "login"
    //var appName = "token"
    r, err := srv.Activities.List(userKey, appName).MaxResults(10).Do()
    if err != nil {
        log.Fatalf("Unable to retrieve logins to domain: userKey=%s, appName=%s, error: %v", userKey, appName, err)
        return
    }

    if len(r.Items) == 0 {
        fmt.Println("No logins found.")
    } else {
        fmt.Println("Logins:")
        for _, a := range r.Items {
            t, err := time.Parse(time.RFC3339Nano, a.Id.Time)
            if err != nil {
                fmt.Println("Unable to parse login time.")
                // Set time to zero.
                t = time.Time{}
            }
            fmt.Printf("%s: %s %s\n", t.Format(time.RFC822), a.Actor.Email,
                a.Events[0].Name)
        }
    }
}