def main(argv):
    """A script to gather token data and revoke unapproved tokens issued.

   -In v1: only an 'client_id' blacklist and/or a 'scope' blacklist are
    accepted. Handling of blacklists is straightforward. This utility revokes
    any token issued to an client_id on the client_id black list or with
    a scope found on the scope blacklist.
   -In v2: an client_id white_list may be supplied.  But, the white_list
    may not be supplied if any blacklist is supplied.  It must be alone.
    In handling the white_list, any token to an client_id must also be
    present in the client_id white_list otherwise it is revoked.  As a
    special case, if an client_id white_list is supplied, but it is empty,
    ALL TOKENS WILL BE REVOKED.
   -In v3: combinations of blacklists and white_lists will be allowed.  This
    will allow for three unusual circumstances:
    1) If client_ids are discovered in both white and black lists, the
       utility will fail and take no action. This is considered an unexpected
       error and requires immediate attention.
    2) If an empty white_list is supplied along with non-empty black lists,
       the utility will fail and take no action. This is considered an
       unexpected error and requires immediate attention.
    3) As normal, if a token is found that matches a blacklist it is revoked,
       and tokens found that match the non-empty white_list will remain.  But,
       tokens found that are present neither in a blacklist or white_list
       will cause a WARNING LOG message to be emitted that may be observed.

  Args:
    argv: argument tokens in a list of strings.
  """
    flags = common_flags.ParseFlags(
        argv, 'Revoke unapproved tokens across a domain.', AddFlags)
    if not flags.client_blacklist_file and not flags.scope_blacklist_file:
        log_utils.LogError(
            'Either --client_blacklist_file or --scope_blacklist_file must '
            'be supplied.')
        sys.exit(1)

    log_border = 40 * '-'
    log_utils.LogInfo('revoke_unapproved_tokens starting...\n%s' % log_border)
    token_revoker = TokenRevoker(flags)
    if flags.client_blacklist_file:
        token_revoker.LoadClientBlacklist(flags.client_blacklist_file)
    if flags.scope_blacklist_file:
        token_revoker.LoadScopeBlacklist(flags.scope_blacklist_file)
    token_revoker.ExitIfBothBlackListsEmptys()

    if not flags.use_local_users_list:
        ForceGatherNewUsersList()

    # Should normally refresh the token stats - this is for the case where a
    # second pass is desired either for resuming or running a complicated set
    # of rules.
    if not flags.use_local_token_stats:
        RefreshTokenStats(flags)

    token_revoker.RevokeUnapprovedTokens()
    log_utils.LogInfo('revoke_unapproved_tokens done.\n%s' % log_border)
    print 'Revocation details logged to: %s.' % log_utils.GetLogFileName()
def main(argv):
    """A script to test Apps Security APIs: revoking oauth2 tokens."""
    flags = common_flags.ParseFlags(argv,
                                    'Revoke tokens issued for a client id.',
                                    AddFlags)
    log_border = (40 * '-')
    log_utils.LogInfo('revoke_tokens_for_domain_clientid starting...\n%s' %
                      log_border)

    if flags.use_local_token_stats:
        stats_user_list = token_report_utils.GetUsersInDomain(
            token_report_utils.GetTokenStats(), flags.client_id)
    else:
        stats_user_list = []  # List of users with a token for an issue domain

    http = auth_helper.GetAuthorizedHttp(flags)
    apps_security_api = tokens_api.TokensApiWrapper(http)

    # The user list holds a tuple for each user of: email, id, full_name
    # (e.g. '*****@*****.**', '000000000098938768732', 'Larry Summon').
    print 'Scanning domain users for %s...' % PREFIX
    for user in user_iterator.StartUserIterator(http, PREFIX, flags):
        user_email, _, _ = user
        # Skip revocation attempts if tokens not found in the latest report.
        if flags.use_local_token_stats and user_email not in stats_user_list:
            continue

        try:
            # NOTE: attempting to revoke a non-existent token causes no
            #       discernible output (no failure message or fail status).
            apps_security_api.DeleteToken(user_email, flags.client_id)
        except admin_api_tool_errors.AdminAPIToolTokenRequestError as e:
            # This suggests an unexpected response from the apps security api.
            # As much detail as possible is provided by the raiser.
            sys.stdout.write('%80s\r' % '')  # Clear the previous entry.
            sys.stdout.flush()
            log_utils.LogError(
                'Unable to revoke token for user %s and client_id %s.' %
                (user, flags.client_id), e)
            sys.exit(1)
        # If attempting to revoke tokens for the whole domain, do not print
        # confirmation because we're not sure which users actually had the tokens.
        if flags.use_local_token_stats:
            log_utils.LogInfo(
                'Successfully revoked token for user %s for client_id %s.' %
                (user_email, flags.client_id))
    log_utils.LogInfo('revoke_tokens_for_domain_clientid done.\n%s' %
                      log_border)
    print 'Revocation details logged to: %s.' % log_utils.GetLogFileName()
    if not flags.use_local_token_stats:
        print 'NOTE: To save time, revocation is attempted for all domain users '
        print '      without checking in advance if a token was granted.  Because '
        print '      revocation returns no indication of actual token revocation, '
        print '      the actual clients of tokens revoked are not logged. If it '
        print '      is required to log the actual client ids of revoked tokens, '
        print '      run the gather token stats command and use '
        print '      --use_local_token_stats with this command. '
Example #3
0
    def _IssueTokensRequestForUser(self, request):
        """Create and issue a tokens request authorized by the user.

    The request will reflect one of a get(), list() or delete() command. The
    operation can be inspected by looking at request.methodId for one of:
    u'directory.tokens.list', u'directory.tokens.get' or
    u'directory.tokens.delete'.

    Args:
      request: Google API service request object from a get(), list() or
               delete() invocation.

    Returns:
      A dictionary (called a json document in references) with a member 'items'
      which is a list of tokens.
    """
        backoff = http_utils.Backoff()
        while backoff.Loop():
            try:
                return request.execute()
            except apiclient_errors.HttpError as e:
                # 404 is returned from get when no tokens for client_id exist.
                # This is normal and should not be presented to the user as an error.
                if request.method != 'DELETE' and e.resp.status == 404:
                    return {}
                if e.resp.status not in http_utils.RETRY_RESPONSE_CODES:
                    raise admin_api_tool_errors.AdminAPIToolUserError(
                        '%s\nPlease check your domain spelling.' %
                        http_utils.ParseHttpResult(e.uri, e.resp, e.content))
                log_utils.LogInfo(
                    'Possible quota problem with %s tokens (%d).' %
                    (request.methodId, e.resp.status))
                backoff.Fail()
    def RevokeUnapprovedTokens(self):
        """Examine each token and match it against rules to determine revocation.

    The black lists (client and scope) are straightforward to handle: tokens
    issued that are matched to a blacklist are revoked.
    """
        self._IdentifyTokensToRevoke()
        if not self._tokens_to_revoke:
            log_utils.LogInfo('No tokens found to revoke')
            return
        log_utils.LogInfo('Tokens found to revoke.  Revoking now...')
        with log_utils.Timer('All _RevokeToken() calls',
                             hide_timing=self._flags.hide_timing):
            for client_id in sorted(self._tokens_to_revoke):
                for user_mail in sorted(self._tokens_to_revoke[client_id]):
                    self._RevokeToken(user_mail, client_id)
Example #5
0
  def GetDomainUser(self, user_mail):
    """Retrieve document for a user in an apps domain.

    A common reason to call this is to retrieve the user_id from an email name.

    Args:
      user_mail: username to check.

    Returns:
      The user document (available fields listed in _PrintOneUser()).
    """
    log_utils.LogDebug('GetDomainUser --plus_domains (%s).' % user_mail)
    request = self._users.get(userId=user_mail)
    backoff = http_utils.Backoff()
    while backoff.Loop():
      try:
        return request.execute()
      except apiclient_errors.HttpError as e:  # Missing user raises HttpError.
        if e.resp.status not in http_utils.RETRY_RESPONSE_CODES:
          error_text = http_utils.ParseHttpResult(e.uri, e.resp, e.content)
          if error_text.startswith('ERROR: status=404'):
            # User not found is reflected by 404 - resource not found.
            return None
          elif error_text.startswith('ERROR: status=403'):
            missing_user_message = (
                'message=The operation is not allowed because the requested '
                'people are not part of the domain.')
            if missing_user_message in error_text:
              return None
          raise admin_api_tool_errors.AdminAPIToolPlusDomainsError(
              'User %s not found: %s' % (user_mail, error_text))
        log_utils.LogInfo(
            'Possible quota problem retrieving user %s (%d). Retrying after '
            'a short wait.' % (user_mail, e.resp.status))
        backoff.Fail()
Example #6
0
def main(argv):
  """A script to test Admin SDK Directory APIs: delete."""
  flags = common_flags.ParseFlags(argv, 'Remove a domain user.', AddFlags)
  http = auth_helper.GetAuthorizedHttp(flags)
  api_wrapper = users_api.UsersApiWrapper(http)
  try:
    api_wrapper.DeleteDomainUser(flags.user_email)
  except admin_api_tool_errors.AdminAPIToolUserError as e:
    log_utils.LogError('Unable to rm user %s.' % flags.user_email, e)
    sys.exit(1)
  log_utils.LogInfo('User %s successfully removed.' % flags.user_email)
Example #7
0
def main(argv):
    """A script to test Admin SDK Directory APIs."""
    flags = common_flags.ParseFlags(argv, 'Add a domain user.', AddFlags)
    http = auth_helper.GetAuthorizedHttp(flags)
    api_wrapper = users_api.UsersApiWrapper(http)
    try:
        api_wrapper.AddDomainUser(flags.first_name, flags.last_name,
                                  flags.user_email, flags.password)
    except admin_api_tool_errors.AdminAPIToolUserError as e:
        # Could not add user. Details provided by api wrapper in the e string.
        log_utils.LogError('Unable to add user %s.' % flags.user_email, e)
        sys.exit(1)
    log_utils.LogInfo('User %s added.' % flags.user_email)
Example #8
0
    def AddDomainUser(self,
                      first_name,
                      last_name,
                      user_mail,
                      new_password,
                      verify=False):
        """Adds user to the domain.

    Checks if the user already exists.  Users are created by default
    as unsuspended, non-admin users.

    Args:
      first_name: Given first name.
      last_name: Family name.
      user_mail: user_mail to add.
      new_password: Password to set.
      verify: If True, verify user was created.
    """
        log_utils.LogDebug('AddDomainUser (%s).' % user_mail)
        if self.IsDomainUser(user_mail):
            raise admin_api_tool_errors.AdminAPIToolUserError(
                'User %s already exists.' % user_mail)
        body = {
            'primaryEmail': user_mail,
            'name': {
                'givenName': first_name,
                'familyName': last_name
            },
            'password': new_password
        }
        backoff = http_utils.Backoff()
        while backoff.Loop():
            try:
                self._users.insert(body=body).execute()
                if verify:
                    time.sleep(
                        2
                    )  # Seems to be needed for Verify to work consistently.
                    if not self.IsDomainUser(user_mail):
                        raise admin_api_tool_errors.AdminAPIToolUserError(
                            'Problem creating user: %s' % user_mail)
                return
            except apiclient_errors.HttpError as e:
                if e.resp.status not in http_utils.RETRY_RESPONSE_CODES:
                    raise admin_api_tool_errors.AdminAPIToolUserError(
                        http_utils.ParseHttpResult(e.uri, e.resp, e.content))
                log_utils.LogInfo(
                    'Possible quota problem adding user %s (%d). Retrying after '
                    'a short wait.' % (user_mail, e.resp.status))
                backoff.Fail()
    def _RevokeToken(self, user_mail, client_id):
        """Revoke a single token based on client_id and user.

    Failures are logged but execution continues.

    Args:
      user_mail: String email address of the user that authorized the token.
      client_id: String of the client domain issued the token.
                 May include spaces.
    """
        log_utils.LogInfo('Revoking: %s, %s.' % (user_mail, client_id))
        try:
            self._tokens_api.DeleteToken(user_mail, client_id)
        except admin_api_tool_errors.AdminAPIToolTokenRequestError as e:
            log_utils.LogError(
                'Unable to revoke token for user %s and client_id %s.' %
                (user_mail, client_id), e)
Example #10
0
    def DeleteDomainUser(self, user_mail, verify=False):
        """Deletes user from the domain.

    Checks if the user already exists before attempting delete.

    Args:
      user_mail: user_mail to add.
      verify: If True, verify user was deleted.

    Raises:
      AdminAPIToolUserError: Unable to delete user.
    """
        log_utils.LogDebug('DeleteDomainUser (%s).' % user_mail)
        if not self.IsDomainUser(user_mail):
            raise admin_api_tool_errors.AdminAPIToolUserError(
                'ERROR: user (%s) not a domain member. You may need to check "Enable '
                'provisioning API" in your Domain Settings->User Settings.' %
                (user_mail))
        backoff = http_utils.Backoff()
        while backoff.Loop():
            try:
                self._users.delete(userKey=user_mail).execute()
                if verify:
                    time.sleep(
                        2
                    )  # Seems to be needed for Verify to work consistently.
                    if self.IsDomainUser(user_mail):
                        raise admin_api_tool_errors.AdminAPIToolUserError(
                            'Problem deleting user %s.' % user_mail)
                return
            except apiclient_errors.HttpError as e:
                if e.resp.status not in http_utils.RETRY_RESPONSE_CODES:
                    raise admin_api_tool_errors.AdminAPIToolUserError(
                        http_utils.ParseHttpResult(e.uri, e.resp, e.content))
                log_utils.LogInfo(
                    'Possible quota problem deleting user %s (%d). Retrying after '
                    'a short wait.' % (user_mail, e.resp.status))
                backoff.Fail()
Example #11
0
def main(argv):
    """A script to revoke tokens issued to users."""
    flags = common_flags.ParseFlags(
        argv, 'Revoke token issued by a user for a client.', AddFlags)
    http = auth_helper.GetAuthorizedHttp(flags)
    user_api = users_api.UsersApiWrapper(http)
    if not user_api.IsDomainUser(flags.user_email):
        print 'User %s not found.' % flags.user_email
        sys.exit(1)

    apps_security_api = tokens_api.TokensApiWrapper(http)
    try:
        # NOTE: attempting to revoke a non-existent token causes no
        #       discernible output (no failure message or fail status).
        apps_security_api.DeleteToken(flags.user_email, flags.client_id)
    except admin_api_tool_errors.AdminAPIToolTokenRequestError as e:
        log_utils.LogError(
            'Unable to revoke token for user %s and client_id %s.' %
            (flags.user_email, flags.client_id), e)
        sys.exit(1)
    log_utils.LogInfo(
        'Successfully revoked token for user %s for client_id %s.' %
        (flags.user_email, flags.client_id))
Example #12
0
    def _ProcessUserListPage(self,
                             apps_domain,
                             max_page,
                             next_page_token=None,
                             query_filter=None):
        """Helper that handles exceptions retrieving pages of users.

    Args:
      apps_domain: Users apps domain e.g. mybiz.com.
      max_page: Used to optimize paging (1-500).
      next_page_token: Used for ongoing paging of users.
      query_filter: Optinally allow filtering based on many fields.
                    Obvious ones include orgName and orgUnitPath.

    Returns:
      List of users retrieved (one page).
    """
        request = self._users.list(domain=apps_domain,
                                   maxResults=max_page,
                                   pageToken=next_page_token,
                                   query=query_filter)
        backoff = http_utils.Backoff()
        while backoff.Loop():
            try:
                users_list = request.execute()
                return users_list
            except apiclient_errors.HttpError as e:
                if e.resp.status not in http_utils.RETRY_RESPONSE_CODES:
                    raise admin_api_tool_errors.AdminAPIToolUserError(
                        '%s\nPlease check your domain spelling (%s).' %
                        (http_utils.ParseHttpResult(e.uri, e.resp,
                                                    e.content), apps_domain))
                log_utils.LogInfo(
                    'Possible quota problem retrieving users (%d).' %
                    e.resp.status)
                backoff.Fail()