0

I have an application which integrates with a Google Apps for Work domain and requires to be migrated from oauth 1 to oauth 2.

The is a server application which requires simply requires to:

  1. list all groups in the domain.
  2. list users in a specified group.
  3. add members to a specified group.
  4. remove members from a specified group.

Given the above, I believe that this should be done using a service account. I have created this, downloaded the P12 token (what is difference between P12 and a JSON token?) and enabled the Admin SDK API via the Developers console. API Access is enabled in the Domain's control panel and I have enabled the scope https://www.googleapis.com/auth/admin.directory.group.member for the client ID associated with the service account.

I have tried some random operations around groups but get "insufficient permissions" response.

{
  "code" : 403,
  "errors" : [ {
    "domain" : "global",
    "message" : "Insufficient Permission",
    "reason" : "insufficientPermissions"
  } ],
  "message" : "Insufficient Permission"
}

Anyway, firstly, I am looking for some help with the code necessary to implement the above operations correctly and then will see if there remains a permissions issue:

import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;

import org.apache.commons.httpclient.HttpException;

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.admin.directory.Directory;
import com.google.api.services.admin.directory.model.Group;
import com.google.api.services.admin.directory.model.Groups;
import com.google.api.services.admin.directory.model.Users;

public class GoogleAppsService {

    HttpTransport httpTransport;
    JsonFactory jsonFactory;

    public GoogleAppsService() throws GeneralSecurityException, IOException {
        httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        jsonFactory = JacksonFactory.getDefaultInstance();
    }

    public GoogleCredential getCredentials() throws HttpException, IOException, GeneralSecurityException {

        GoogleCredential credential = new GoogleCredential.Builder()
                .setTransport(httpTransport)
                .setJsonFactory(jsonFactory)
                .setServiceAccountId("179997031769-pf4t5hifo7dmtbqul1dbl9rulneijl7o@developer.gserviceaccount.com")
                .setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/admin.directory.group.member"))
                .setServiceAccountPrivateKeyFromP12File(
                        new File(this.getClass().getResource("/google_apps/google-apps-key.p12").getPath())).build();

        return credential;
    }

    public void listGroups() throws Exception{
        GoogleCredential credentials = getCredentials();

        Directory directory = new Directory.Builder(
                httpTransport, jsonFactory, credentials)
                .setApplicationName("xyz")
                .build();

        //403 insufficient permissions thrown below is the above correct??
        Groups result = directory.groups().list().execute();
        System.out.println(result);

        //iterate and print id/alias of each group
    }

    public void listUsers(String groupName) throws Exception {
        GoogleCredential credentials = getCredentials();

        //iterate and print email of each member for specified group
    }

    public void addUser(String groupname, String emailAddress)throws Exception {
        GoogleCredential credentials = getCredentials();
    }

    public void removeUser(String groupName, String emailAddress)throws Exception {
        GoogleCredential credentials = getCredentials(); 
    }

    public static void main(String[] args) throws Exception {
        try {
            GoogleAppsService service = new GoogleAppsService();
            service.listGroups();
        } catch (HttpException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Alan Hay
  • 22,665
  • 4
  • 56
  • 110
  • In the above code, you used https://www.googleapis.com/auth/admin.directory.group.member. But this scope can be used only for Members resource not for groups. For groups you should use https://www.googleapis.com/auth/admin.directory.group . Also ,even though its not mentioned in the documentation, while listing the groups, using groups.list, you should give domain name too. – SGC Sep 02 '15 at 21:24
  • How does one specify the domain name? – Alan Hay Sep 03 '15 at 09:47
  • Additonally, using googleapis.com/auth/admin.directory.group results in a 'bad request' exception. – Alan Hay Sep 03 '15 at 09:50
  • While sending request to groups.list, you have a parameter called domain, there you should include the domain name. – SGC Sep 04 '15 at 15:47
  • Thanks. Managed to get it working as below. – Alan Hay Sep 05 '15 at 09:07

1 Answers1

0

Okay, finally go this working with full solution as below. The key was specifying the service account user (the email address of an admin user on the Google Apps account) and calling setCustomer("my_customer") when obtaining the list of groups.

public class GoogleAppsService {

    private static final Logger LOGGER = Logger.getLogger(GoogleAppsService.class);

    private static final String SERVICE_ACCOUNT_ID = "SERVICE_ACCOUNT_KEY";
    private static final String SERVICE_ACCOUNT_USER = "EMAIL_ADDRESS_OF_ADMIN_ACCOUNT_ON_GOOGLE_APPS";
    private static final String APPLICATION_NAME = "APP_NAME";

    private HttpTransport httpTransport;
    private JsonFactory jsonFactory;

    private String googleAppsAllEmailListName;
    private String googleAppsCommitteeEmailListName;

    public GoogleAppsService() throws GeneralSecurityException, IOException {
        httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        jsonFactory = JacksonFactory.getDefaultInstance();
    }

    protected Directory getDirectory() throws HttpException, IOException, GeneralSecurityException {

        InputStream in = this.getClass().getResourceAsStream("/google_apps/google-apps-key.p12");
        PrivateKey privateKey = SecurityUtils.loadPrivateKeyFromKeyStore(SecurityUtils.getPkcs12KeyStore(), in, "notasecret",
                "privatekey", "notasecret");

        GoogleCredential credentials = new GoogleCredential.Builder().setTransport(httpTransport).setJsonFactory(jsonFactory)
                .setServiceAccountId(SERVICE_ACCOUNT_ID)
                .setServiceAccountScopes(Arrays.asList(DirectoryScopes.ADMIN_DIRECTORY_GROUP))
                .setServiceAccountUser(SERVICE_ACCOUNT_USER).setServiceAccountPrivateKey(privateKey).build();

        Directory directory = new Directory.Builder(httpTransport, jsonFactory, credentials).setApplicationName(APPLICATION_NAME)
                .build();

        return directory;
    }

    protected Groups listGroups(Directory directory) throws Exception {
        //IF SPECIFYING THE SERVICE_ACCOUNT_USER WHEN CONNECTING YOU CAN USE setCustomer("my_customer")
        return directory.groups().list().setCustomer("my_customer").execute();
    }

    protected Group getGroup(Directory directory, String emailAddress) throws IOException {
        Group group = directory.groups().get(emailAddress).execute();

        LOGGER.debug("Returning Group: " + group != null ? group.getEmail() + "(" + group.getDirectMembersCount() + " members)"
                : "! no group loaded");

        return group;
    }

    protected Members listGroupMembers(Directory directory, Group group) throws Exception {
        return directory.members().list(group.getEmail()).execute();
    }

    protected boolean isMemberInGroup(Directory directory, Group group, String emailAddress) throws IOException {
        boolean exists = false;

        Members memberList = directory.members().list(group.getEmail()).execute();
        List<Member> members = memberList.getMembers();

        if (members != null) {
            for (Member member : members) {
                if (member.getEmail().equals(emailAddress)) {
                    exists = true;
                    break;
                }
            }
        }

        return exists;
    }

    protected void addMemberToGroup(Directory directory, Group group, String emailAddress) throws Exception {
        Member member = new Member();
        member.setEmail(emailAddress);

        LOGGER.debug("Attempting Insert of Member to Group: " + group != null ? group.getEmail() : "! no group loaded");

        directory.members().insert(group.getEmail(), member).execute();
    }

    protected void removeMemberFromGroup(Directory directory, Group group, String emailAddress) throws Exception {

        LOGGER.debug("Attempting Deletetion of Member to Group: " + group != null ? group.getEmail() : "! no group loaded");

        directory.members().delete(group.getEmail(), emailAddress).execute();
    }

    public void addMemberToMembersList(String emailAddress) throws MailingListException {
        addMemberToList(googleAppsAllEmailListName, emailAddress);
    }

    public void addMemberToCommitteeList(String emailAddress) throws MailingListException {
        addMemberToList(googleAppsCommitteeEmailListName, emailAddress);
    }

    protected void addMemberToList(String listAddress, String emailAddress) throws MailingListException {
        try {
            Directory directory = getDirectory();
            Group group = getGroup(directory, listAddress);

            if (!isMemberInGroup(directory, group, emailAddress)) {
                addMemberToGroup(directory, group, emailAddress);
            }

        } catch (Exception e) {
            LOGGER.error("Error adding member (" + emailAddress + ") to mailing list " + listAddress, e);
            throw new MailingListException(e);
        }
    }

    public void removeMemberFromMembersList(String emailAddress) throws MailingListException {
        removeMemberFromList(googleAppsAllEmailListName, emailAddress);
    }

    public void removeMemberFromCommitteeList(String emailAddress) throws MailingListException {
        removeMemberFromList(googleAppsCommitteeEmailListName, emailAddress);
    }

    protected void removeMemberFromList(String listAddress, String emailAddress) throws MailingListException {
        try {
            Directory directory = getDirectory();
            Group group = getGroup(directory, listAddress);

            if (isMemberInGroup(directory, group, emailAddress)) {
                removeMemberFromGroup(directory, group, emailAddress);
            }

        } catch (Exception e) {
            LOGGER.error("Error removing member (" + emailAddress + ") from mailing list " + listAddress, e);
            throw new MailingListException(e);
        }
    }

    public void setHttpTransport(HttpTransport httpTransport) {
        this.httpTransport = httpTransport;
    }

    public void setJsonFactory(JsonFactory jsonFactory) {
        this.jsonFactory = jsonFactory;
    }

    public void setGoogleAppsAllEmailListName(String googleAppsAllEmailListName) {
        this.googleAppsAllEmailListName = googleAppsAllEmailListName;
    }

    public void setGoogleAppsCommitteeEmailListName(String googleAppsCommitteeEmailListName) {
        this.googleAppsCommitteeEmailListName = googleAppsCommitteeEmailListName;
    }
}
Alan Hay
  • 22,665
  • 4
  • 56
  • 110