Beispiel #1
0
def attempt_oauth_initialization(scope):
    """Attempts to perform GetOAuthUser RPC retrying deadlines.

  The result it cached in appengine.api.oauth guts. Never raises exceptions,
  just gives up letting subsequent oauth.* calls fail in a proper way.
  """
    # 4 attempts: ~20 sec (default RPC deadline is 5 sec).
    attempt = 0
    while attempt < 4:
        attempt += 1
        try:
            oauth.get_client_id(scope)
            return
        except apiproxy_errors.DeadlineExceededError as e:
            logging.warning('DeadlineExceededError: %s', e)
            continue
        except oauth.OAuthServiceFailureError as e:
            logging.warning('oauth.OAuthServiceFailureError (%s): %s',
                            e.__class__.__name__, e)
            # oauth library "caches" the error code in os.environ and retrying
            # oauth.get_client_id doesn't do anything. Clear this cache first, see
            # oauth_api.py, _maybe_call_get_oauth_user in GAE SDK.
            os.environ.pop('OAUTH_ERROR_CODE', None)
            continue
        except oauth.Error as e:
            # Next call to oauth.get_client_id() will trigger same error and it will
            # be handled for real.
            logging.warning('oauth.Error (%s): %s', e.__class__.__name__, e)
            return
Beispiel #2
0
def attempt_oauth_initialization(scope):
  """Attempts to perform GetOAuthUser RPC retrying deadlines.

  The result it cached in appengine.api.oauth guts. Never raises exceptions,
  just gives up letting subsequent oauth.* calls fail in a proper way.
  """
  # 4 attempts: ~20 sec (default RPC deadline is 5 sec).
  attempt = 0
  while attempt < 4:
    attempt += 1
    try:
      oauth.get_client_id(scope)
      return
    except apiproxy_errors.DeadlineExceededError as e:
      logging.warning('DeadlineExceededError: %s', e)
      continue
    except oauth.OAuthServiceFailureError as e:
      logging.warning(
          'oauth.OAuthServiceFailureError (%s): %s', e.__class__.__name__, e)
      # oauth library "caches" the error code in os.environ and retrying
      # oauth.get_client_id doesn't do anything. Clear this cache first, see
      # oauth_api.py, _maybe_call_get_oauth_user in GAE SDK.
      os.environ.pop('OAUTH_ERROR_CODE', None)
      continue
    except oauth.Error as e:
      # Next call to oauth.get_client_id() will trigger same error and it will
      # be handled for real.
      logging.warning('oauth.Error (%s): %s', e.__class__.__name__, e)
      return
Beispiel #3
0
  def testMaybeSetVarsWithActualRequestAccessToken(self):
    dummy_scope = 'scope'
    dummy_token = 'dummy_token'
    dummy_email = '*****@*****.**'
    dummy_client_id = self._SAMPLE_ALLOWED_CLIENT_IDS[0]

    @api_config.api('TestApi', 'v1',
                    allowed_client_ids=self._SAMPLE_ALLOWED_CLIENT_IDS,
                    scopes=[dummy_scope])
    class TestApiScopes(remote.Service):
      """Describes TestApiScopes."""

      # pylint: disable=g-bad-name
      @api_config.method(message_types.VoidMessage, message_types.VoidMessage)
      def method(self, request):
        return request

    # users_id_token._get_id_token_user and time.time don't need to be stubbed
    # because the scopes used will not be [EMAIL_SCOPE] hence _get_id_token_user
    # will never be attempted

    self.mox.StubOutWithMock(users_id_token, '_is_local_dev')
    users_id_token._is_local_dev().AndReturn(False)

    self.mox.StubOutWithMock(oauth, 'get_client_id')
    oauth.get_client_id(dummy_scope).AndReturn(dummy_client_id)

    self.mox.ReplayAll()

    api_instance = TestApiScopes()
    os.environ['HTTP_AUTHORIZATION'] = 'Bearer ' + dummy_token
    api_instance.method(message_types.VoidMessage())
    self.assertEqual(os.getenv('ENDPOINTS_USE_OAUTH_SCOPE'), dummy_scope)
    self.mox.VerifyAll()
    def testGetClientIdCache(self):
        self.users_stub.SetOAuthUser(scopes=['https://www.mynet.net/myscope'])
        client_id = oauth.get_client_id('https://www.mynet.net/myscope')
        self.assertEqual('123456789.apps.googleusercontent.com', client_id)

        self.users_stub.SetOAuthUser(client_id='another_client_id')
        client_id = oauth.get_client_id('https://www.mynet.net/myscope')
        self.assertEqual('123456789.apps.googleusercontent.com', client_id)
Beispiel #5
0
def DoOAuthAuth(is_admin=None, require_level=None):
  """Verify OAuth was used with a valid account.

  Args:
    is_admin: Boolean. When True, checks if the user is an admin.
    require_level: int, default None, when defined,
        requires that a session be at level x.
  Returns:
    users.User() object.
  Raises:
    NotAuthenticated: there is no authenticated user for this request.
    IsAdminMismatch: the current user is not an administrator.
  """
  # TODO(user): make use of require_level.
  try:
    user = oauth.get_current_user(OAUTH_SCOPE)
  except oauth.OAuthRequestError:
    raise NotAuthenticated('OAuthRequestError')

  try:
    if oauth.get_client_id(OAUTH_SCOPE) != settings.OAUTH_CLIENT_ID:
      raise NotAuthenticated('mismatched OAUTH_CLIENT_ID')
  except AttributeError:
    raise NotAuthenticated('OAUTH_CLIENT_ID not set')

  email = user.email()

  if is_admin is not None and not IsAdminUser(email):
    raise IsAdminMismatch

  if email in _GetGroupMembers('oauth_users'):
    return user

  logging.warning('OAuth user unknown: %s', email)
  raise NotAuthenticated('DoOAuthAuthUnknownUser')
Beispiel #6
0
def Authorize():
  try:
    email = utils.GetEmail()
  except oauth.OAuthRequestError:
    raise OAuthError

  if not email:
    raise NotLoggedInError

  try:
    if not email.endswith('.gserviceaccount.com'):
      # For non-service account, need to verify that the OAuth client ID
      # is in our whitelist.
      client_id = oauth.get_client_id(utils.OAUTH_SCOPES)
      if client_id not in OAUTH_CLIENT_ID_WHITELIST:
        logging.error('OAuth client id %s for user %s not in whitelist',
                      client_id, email)
        email = None
        raise OAuthError
  except oauth.OAuthRequestError:
    # Transient errors when checking the token result should result in HTTP 500,
    # so catch oauth.OAuthRequestError here, not oauth.Error (which would catch
    # both fatal and transient errors).
    raise OAuthError

  logging.info('OAuth user logged in as: %s', email)
  if utils.IsInternalUser():
    datastore_hooks.SetPrivilegedRequest()
Beispiel #7
0
def get_current_rietveld_oauth_user():
    """Gets the current OAuth 2.0 user associated with a request.

  This user must be intending to reach this application, so we check the token
  info to verify this is the case.

  Returns:
    A users.User object that was retrieved from the App Engine OAuth library if
        the token is valid, otherwise None.
  """
    # TODO(dhermes): Address local environ here as well.
    try:
        current_client_id = oauth.get_client_id(EMAIL_SCOPE)
    except oauth.Error:
        return

    accepted_client_id, _, additional_client_ids = SecretKey.get_config()
    if (accepted_client_id != current_client_id
            and current_client_id not in additional_client_ids):
        logging.debug('Client ID %r not intended for this application.',
                      current_client_id)
        return

    try:
        return oauth.get_current_user(EMAIL_SCOPE)
    except oauth.Error:
        logging.warning(
            'A Client ID was retrieved with no corresponsing user.')
Beispiel #8
0
def get_current_rietveld_oauth_user():
  """Gets the current OAuth 2.0 user associated with a request.

  This user must be intending to reach this application, so we check the token
  info to verify this is the case.

  Returns:
    A users.User object that was retrieved from the App Engine OAuth library if
        the token is valid, otherwise None.
  """
  # TODO(dhermes): Address local environ here as well.
  try:
    current_client_id = oauth.get_client_id(EMAIL_SCOPE)
  except oauth.Error:
    return

  accepted_client_id, _, additional_client_ids = SecretKey.get_config()
  if (accepted_client_id != current_client_id and
      current_client_id not in additional_client_ids):
    logging.debug('Client ID %r not intended for this application.',
                  current_client_id)
    return

  try:
    return oauth.get_current_user(EMAIL_SCOPE)
  except oauth.Error:
    logging.warning('A Client ID was retrieved with no corresponsing user.')
Beispiel #9
0
def get_client_id():
    try:
        if not config.get('DEV_SERVER'):
            return oauth.get_client_id(SCOPES)
    except oauth.Error as e:
        pass
    return None
Beispiel #10
0
def Authorize():
  try:
    user = oauth.get_current_user(OAUTH_SCOPES)
  except oauth.Error:
    raise NotLoggedInError

  if not user:
    raise NotLoggedInError

  try:
    if not user.email().endswith('.gserviceaccount.com'):
      # For non-service account, need to verify that the OAuth client ID
      # is in our whitelist.
      client_id = oauth.get_client_id(OAUTH_SCOPES)
      if client_id not in OAUTH_CLIENT_ID_WHITELIST:
        logging.info('OAuth client id %s for user %s not in whitelist',
                     client_id, user.email())
        user = None
        raise OAuthError
  except oauth.Error:
    raise OAuthError

  logging.info('OAuth user logged in as: %s', user.email())
  if utils.IsGroupMember(user.email(), 'chromeperf-access'):
    datastore_hooks.SetPrivilegedRequest()
Beispiel #11
0
def _set_bearer_user_vars(allowed_client_ids, scopes):
    """Validate the oauth bearer token and set endpoints auth user variables.

  If the bearer token is valid, this sets ENDPOINTS_USE_OAUTH_SCOPE.  This
  provides enough information that our endpoints.get_current_user() function
  can get the user.

  Args:
    allowed_client_ids: List of client IDs that are acceptable.
    scopes: List of acceptable scopes.
  """
    for scope in scopes:
        try:
            client_id = oauth.get_client_id(scope)
        except oauth.Error:

            continue

        if allowed_client_ids and client_id not in allowed_client_ids:
            logging.warning('Client ID is not allowed: %s', client_id)
            return

        os.environ[_ENV_USE_OAUTH_SCOPE] = scope
        logging.debug('Returning user from matched oauth_user.')
        return

    logging.warning('Oauth framework user didn\'t match oauth token user.')
    return None
Beispiel #12
0
def Authorize():
    try:
        user = oauth.get_current_user(OAUTH_SCOPES)
    except oauth.Error:
        raise NotLoggedInError

    if not user:
        raise NotLoggedInError

    try:
        if not user.email().endswith('.gserviceaccount.com'):
            # For non-service account, need to verify that the OAuth client ID
            # is in our whitelist.
            client_id = oauth.get_client_id(OAUTH_SCOPES)
            if client_id not in OAUTH_CLIENT_ID_WHITELIST:
                logging.info('OAuth client id %s for user %s not in whitelist',
                             client_id, user.email())
                user = None
                raise OAuthError
    except oauth.Error:
        raise OAuthError

    logging.info('OAuth user logged in as: %s', user.email())
    if utils.IsGroupMember(user.email(), 'chromeperf-access'):
        datastore_hooks.SetPrivilegedRequest()
def _set_bearer_user_vars(allowed_client_ids, scopes):
  """Validate the oauth bearer token and set endpoints auth user variables.

  If the bearer token is valid, this sets ENDPOINTS_USE_OAUTH_SCOPE.  This
  provides enough information that our endpoints.get_current_user() function
  can get the user.

  Args:
    allowed_client_ids: List of client IDs that are acceptable.
    scopes: List of acceptable scopes.
  """
  for scope in scopes:
    try:
      client_id = oauth.get_client_id(scope)
    except oauth.Error:

      continue




    if (list(allowed_client_ids) != SKIP_CLIENT_ID_CHECK and
        client_id not in allowed_client_ids):
      logging.warning('Client ID is not allowed: %s', client_id)
      return

    os.environ[_ENV_USE_OAUTH_SCOPE] = scope
    logging.debug('Returning user from matched oauth_user.')
    return

  logging.debug('Oauth framework user didn\'t match oauth token user.')
  return None
Beispiel #14
0
def _set_bearer_user_vars(allowed_client_ids, scopes):
    """Validate the oauth bearer token and set endpoints auth user variables.

  If the bearer token is valid, this sets ENDPOINTS_USE_OAUTH_SCOPE.  This
  provides enough information that our endpoints.get_current_user() function
  can get the user.

  Args:
    allowed_client_ids: List of client IDs that are acceptable.
    scopes: List of acceptable scopes.
  """
    for scope in scopes:
        try:
            client_id = oauth.get_client_id(scope)
        except oauth.Error:
            # This scope failed.  Try the next.
            continue

        # The client ID must be in allowed_client_ids.  If allowed_client_ids is
        # empty, don't allow any client ID.  If allowed_client_ids is set to
        # SKIP_CLIENT_ID_CHECK, all client IDs will be allowed.
        if (list(allowed_client_ids) != SKIP_CLIENT_ID_CHECK
                and client_id not in allowed_client_ids):
            logging.warning('Client ID is not allowed: %s', client_id)
            return

        os.environ[_ENV_USE_OAUTH_SCOPE] = scope
        logging.debug('Returning user from matched oauth_user.')
        return

    logging.debug('Oauth framework user didn\'t match oauth token user.')
    return None
Beispiel #15
0
def Authorize():
  try:
    email = utils.GetEmail()
  except oauth.OAuthRequestError:
    raise OAuthError

  if not email:
    raise NotLoggedInError

  try:
    # TODO(dberris): Migrate to using Cloud IAM and checking roles instead, to
    # allow for dynamic management of the accounts.
    if not email.endswith('.gserviceaccount.com'):
      # For non-service accounts, need to verify that the OAuth client ID
      # is in our allowlist.
      client_id = oauth.get_client_id(utils.OAUTH_SCOPES)
      if client_id not in OAUTH_CLIENT_ID_ALLOWLIST:
        logging.error('OAuth client id %s for user %s not in allowlist',
                      client_id, email)
        raise OAuthError
  except oauth.OAuthRequestError:
    # Transient errors when checking the token result should result in HTTP 500,
    # so catch oauth.OAuthRequestError here, not oauth.Error (which would catch
    # both fatal and transient errors).
    raise OAuthError

  logging.info('OAuth user logged in as: %s', email)
  if utils.IsInternalUser():
    datastore_hooks.SetPrivilegedRequest()
def _set_bearer_user_vars(allowed_client_ids, scopes):
  """Validate the oauth bearer token and set endpoints auth user variables.

  If the bearer token is valid, this sets ENDPOINTS_USE_OAUTH_SCOPE.  This
  provides enough information that our endpoints.get_current_user() function
  can get the user.

  Args:
    allowed_client_ids: List of client IDs that are acceptable.
    scopes: List of acceptable scopes.
  """
  all_scopes, sufficient_scopes = _process_scopes(scopes)
  try:
    authorized_scopes = oauth.get_authorized_scopes(sorted(all_scopes))
  except oauth.Error:
    _logger.debug('Unable to get authorized scopes.', exc_info=True)
    return
  if not _are_scopes_sufficient(authorized_scopes, sufficient_scopes):
    _logger.debug('Authorized scopes did not satisfy scope requirements.')
    return
  client_id = oauth.get_client_id(authorized_scopes)

  # The client ID must be in allowed_client_ids.  If allowed_client_ids is
  # empty, don't allow any client ID.  If allowed_client_ids is set to
  # SKIP_CLIENT_ID_CHECK, all client IDs will be allowed.
  if (list(allowed_client_ids) != SKIP_CLIENT_ID_CHECK and
      client_id not in allowed_client_ids):
    _logger.warning('Client ID is not allowed: %s', client_id)
    return

  os.environ[_ENV_USE_OAUTH_SCOPE] = ' '.join(authorized_scopes)
  _logger.debug('get_current_user() will return user from matched oauth_user.')
Beispiel #17
0
    def AssertWhitelistedOrXSRF(self, mc, metadata):
        """Raise an exception if we don't want to process this request."""
        # For local development, we accept any request.
        # TODO(jrobbins): make this more realistic by requiring a fake XSRF token.
        if settings.local_mode:
            return

        # Check if the user is whitelisted.
        auth_client_ids, auth_emails = (
            client_config_svc.GetClientConfigSvc().GetClientIDEmails())
        if mc.auth.user_pb.email in auth_emails:
            logging.info('User %r is whitelisted to use any client',
                         mc.auth.user_pb.email)
            return

        # Check if the client is whitelisted.
        client_id = None
        try:
            client_id = oauth.get_client_id(framework_constants.OAUTH_SCOPE)
            logging.info('Oauth client ID %s', client_id)
        except oauth.Error as ex:
            logging.info('oauth.Error: %s' % ex)

        if client_id in auth_client_ids:
            logging.info('Client %r is whitelisted for any user', client_id)
            return

        # Otherwise, require an XSRF token generated by our app UI.
        logging.info(
            'Neither email nor client ID is whitelisted, checking XSRF')
        token = metadata.get(XSRF_TOKEN_HEADER)
        xsrf.ValidateToken(token,
                           mc.auth.user_id,
                           xsrf.XHR_SERVLET_PATH,
                           timeout=self.xsrf_timeout)
Beispiel #18
0
def Authorize():
  try:
    email = utils.GetEmail()
  except oauth.OAuthRequestError:
    raise OAuthError

  if not email:
    raise NotLoggedInError

  try:
    if not email.endswith('.gserviceaccount.com'):
      # For non-service account, need to verify that the OAuth client ID
      # is in our whitelist.
      client_id = oauth.get_client_id(utils.OAUTH_SCOPES)
      if client_id not in OAUTH_CLIENT_ID_WHITELIST:
        logging.error('OAuth client id %s for user %s not in whitelist',
                      client_id, email)
        email = None
        raise OAuthError
  except oauth.OAuthRequestError:
    # Transient errors when checking the token result should result in HTTP 500,
    # so catch oauth.OAuthRequestError here, not oauth.Error (which would catch
    # both fatal and transient errors).
    raise OAuthError

  logging.info('OAuth user logged in as: %s', email)
  if utils.IsInternalUser():
    datastore_hooks.SetPrivilegedRequest()
Beispiel #19
0
    def test_oauth_works_as_expected_in_test(self):
        oauth_user = oauth.get_current_user(EMAIL_SCOPE)
        self.assertEqual(oauth_user.email(), TEST_EMAIL)
        self.assertEqual(oauth_user.auth_domain(), 'gmail.com')
        self.assertEqual(oauth_user.user_id(), '0')

        client_id = oauth.get_client_id(EMAIL_SCOPE)
        self.assertEqual(client_id, CLIENT_ID)
Beispiel #20
0
  def test_oauth_works_as_expected_in_test(self):
    oauth_user = oauth.get_current_user(EMAIL_SCOPE)
    self.assertEqual(oauth_user.email(), TEST_EMAIL)
    self.assertEqual(oauth_user.auth_domain(), 'gmail.com')
    self.assertEqual(oauth_user.user_id(), '0')

    client_id = oauth.get_client_id(EMAIL_SCOPE)
    self.assertEqual(client_id, CLIENT_ID)
Beispiel #21
0
def _get_client_id(tries=3):
    """Call oauth.get_client_id() and retry if it times out."""
    for attempt in xrange(tries):
        try:
            return oauth.get_client_id(EMAIL_SCOPE)
        except apiproxy_errors.DeadlineExceededError:
            logging.error('get_client_id() timed out on attempt %r', attempt)
            if attempt == tries - 1:
                raise
Beispiel #22
0
def _get_client_id(tries=3):
  """Call oauth.get_client_id() and retry if it times out."""
  for attempt in xrange(tries):
    try:
      return oauth.get_client_id(EMAIL_SCOPE)
    except apiproxy_errors.DeadlineExceededError:
      logging.error('get_client_id() timed out on attempt %r', attempt)
      if attempt == tries - 1:
        raise
Beispiel #23
0
def extract_oauth_caller_identity(extra_client_ids=None):
  """Extracts and validates Identity of a caller for current request.

  Uses client_id whitelist fetched from the datastore to validate OAuth client
  used to build access_token. Also recognizes various types of service accounts
  and verifies that their client_id is what it should be. Service account's
  client_id doesn't have to be in client_id whitelist.

  Used by webapp2 request handlers and Cloud Endpoints API methods.

  Args:
    extra_client_ids: optional list of allowed client_ids, in addition to
      the whitelist in the datastore.

  Returns:
    Identity of the caller in case the request was successfully validated.

  Raises:
    AuthenticationError in case access_token is missing or invalid.
    AuthorizationError in case client_id is forbidden.
  """
  # OAuth2 scope a token should have.
  oauth_scope = 'https://www.googleapis.com/auth/userinfo.email'

  # Fetch OAuth request state with retries. oauth.* calls use it internally.
  attempt_oauth_initialization(oauth_scope)

  # Extract client_id and email from access token. That also validates the token
  # and raises OAuthRequestError if token is revoked or otherwise not valid.
  try:
    client_id = oauth.get_client_id(oauth_scope)
  except oauth.OAuthRequestError:
    raise AuthenticationError('Invalid OAuth token')

  # This call just reads data cached by oauth.get_client_id, and thus should
  # never fail.
  email = oauth.get_current_user(oauth_scope).email()

  # Is client_id in the explicit whitelist? Used with three legged OAuth. Detect
  # Google service accounts. No need to whitelist client_ids for each of them,
  # since email address uniquely identifies credentials used.
  good = (
      email.endswith('.gserviceaccount.com') or
      get_request_auth_db().is_allowed_oauth_client_id(client_id) or
      client_id in (extra_client_ids or []))

  if not good:
    raise AuthorizationError(
        'Unrecognized combination of email (%s) and client_id (%s). '
        'Is client_id whitelisted? Is it unrecognized service account?' %
        (email, client_id))
  try:
    return model.Identity(model.IDENTITY_USER, email)
  except ValueError:
    raise AuthenticationError('Unsupported user email: %s' % email)
Beispiel #24
0
def attempt_oauth_initialization(scope):
  """Attempts to perform GetOAuthUser RPC retrying deadlines.

  The result it cached in appengine.api.oauth guts. Never raises exceptions,
  just gives up letting subsequent oauth.* calls fail in a proper way.
  """
  # 4 attempts: ~20 sec (default RPC deadline is 5 sec).
  attempt = 0
  while attempt < 4:
    attempt += 1
    try:
      oauth.get_client_id(scope)
      return
    except apiproxy_errors.DeadlineExceededError as e:
      logging.warning('DeadlineExceededError: %s', e)
      continue
    except oauth.Error as e:
      # Next call to oauth.get_client_id() will trigger same error and it will
      # be handled for real.
      logging.warning('oauth.Error: %s', e)
      return
Beispiel #25
0
  def AttemptOauth(self, client_id, allowed_client_ids=None):
    if allowed_client_ids is None:
      allowed_client_ids = self._SAMPLE_ALLOWED_CLIENT_IDS
    self.mox.StubOutWithMock(oauth, 'get_client_id')
    # We have four cases:
    #  * no client ID is specified, so we raise for every scope.
    #  * the given client ID is in the whitelist or there is no
    #    whitelist, so we'll only be called once.
    #  * we have a client ID not on the whitelist, so we need a
    #    mock call for every scope.
    if client_id is None:
      for scope in self._SAMPLE_OAUTH_SCOPES:
        oauth.get_client_id(scope).AndRaise(oauth.Error)
    elif (list(allowed_client_ids) == users_id_token.SKIP_CLIENT_ID_CHECK or
          client_id in allowed_client_ids):
      scope = self._SAMPLE_OAUTH_SCOPES[0]
      oauth.get_client_id(scope).AndReturn(client_id)
    else:
      for scope in self._SAMPLE_OAUTH_SCOPES:
        oauth.get_client_id(scope).AndReturn(client_id)

    self.mox.ReplayAll()
    users_id_token._set_bearer_user_vars(allowed_client_ids,
                                         self._SAMPLE_OAUTH_SCOPES)
    self.mox.VerifyAll()
    def testMultipleScopesSuccess(self):
        self.users_stub.SetOAuthUser(scopes=['scope1', 'scope2', 'scope3'])
        authorized_scopes = oauth.get_authorized_scopes(
            ('scope1', 'scope2', 'scope4'))
        client_id = oauth.get_client_id(('scope1', 'scope2', 'scope4'))
        user = oauth.get_current_user(['scope1', 'scope2', 'scope5'])
        self.assertCountEqual(['scope1', 'scope2'], authorized_scopes)
        self.assertEqual('123456789.apps.googleusercontent.com', client_id)
        self.assertEqual('*****@*****.**', user.email())
        self.assertEqual('0', user.user_id())
        self.assertEqual('gmail.com', user.auth_domain())
        self.assertFalse(oauth.is_current_user_admin(('scope1', 'scope2')))

        authorized_scopes = oauth.get_authorized_scopes(
            ['scope1', 'scope2', 'scope4'])
        client_id = oauth.get_client_id(['scope1', 'scope2', 'scope4'])
        user = oauth.get_current_user(['scope1', 'scope2', 'scope4'])
        self.assertCountEqual(['scope1', 'scope2'], authorized_scopes)
        self.assertEqual('123456789.apps.googleusercontent.com', client_id)
        self.assertEqual('*****@*****.**', user.email())
        self.assertEqual('0', user.user_id())
        self.assertEqual('gmail.com', user.auth_domain())
        self.assertFalse(oauth.is_current_user_admin(('scope1', 'scope2')))
Beispiel #27
0
def _GetApiUser():
    """Get the GAE `User` object for the currently authenticated user."""
    user = users.get_current_user()
    if user: return user

    # Note (http://goo.gl/PCgGNp): "On the local development server,
    # oauth.get_current_user() always returns a User object with email set
    # to "*****@*****.**" and user ID set to 0 regardless of whether or
    # not a valid OAuth request was made.  So test for oauth last.
    try:
        # Get the db.User that represents the user on whose behalf the
        # consumer is making this request.
        user = oauth.get_current_user(base_settings.OAUTH_SCOPE)
        client_id = oauth.get_client_id(base_settings.OAUTH_SCOPE)
        if user and client_id and base_settings.OAUTH_CLIENT_ID == client_id:
            return user
    except oauth.OAuthRequestError:
        pass

    raise AccessDeniedError('All authentication methods failed')
Beispiel #28
0
def authenticate_user():
    """Quite possible that endpoints.get_current_user() may perform the authentication being performed here
    Ref: https://cloud.google.com/appengine/docs/python/endpoints/auth -Adding a user check to methods- """

    scope = 'https://www.googleapis.com/auth/userinfo.email'
    logging.info('\noauth.get_current_user(%s)' % repr(scope))
    try:
        user = oauth.get_current_user(scope)
        allowed_clients = ['407408718192.apps.googleusercontent.com'] # list your client ids here
        token_audience = oauth.get_client_id(scope)
        if token_audience not in allowed_clients:
            raise oauth.OAuthRequestError('audience of token \'%s\' is not in allowed list (%s)'
                                          % (token_audience, allowed_clients))

        logging.info(' = %s\n' % user)
        logging.info('- auth_domain = %s\n' % user.auth_domain())
        logging.info('- email       = %s\n' % user.email())
        logging.info('- nickname    = %s\n' % user.nickname())
        logging.info('- user_id     = %s\n' % user.user_id())
    except oauth.OAuthRequestError, e:
        # # self.response.set_status(401)
        # # self.response.write(' -> %s %s\n' % (e.__class__.__name__, e.message))
        # logging.warn(traceback.format_exc())
        logging.error(e.message)
Beispiel #29
0
from google.appengine.api import oauth

scope = 'https://www.googleapis.com/auth/userinfo.email'
self.response.write('\noauth.get_current_user(%s)' % repr(scope))
try:
    user = oauth.get_current_user(scope)
    allowed clients = ['474832205158-5q0sqta9e932cmftub9aaihgeb2c6hko.apps.googleusercontent.com']
    token_audience = oauth.get_client_id(scope)
    if token_audience not in allowed_clients:
        raise oauth.OAuthRequestError('audience of token \'%s\' is not in allowed list (%s)'
                                      % (token_audience, allowed clients))
    
    self.response.write(' = %s\n' % user)
    self.response.write(' - auth_domain = %s \n' % user.auth_domain())
    self.response.write(' - email       = %s \n' % user.nickname())
    self.response.write(' - user_id     = %s \n' % user.user_id())
except oauth.OAuthRequestError, e:
    self.response.set_status(401)
    self.response.write(' -> %s %s \n' % (e.__class__.__name__, e.message))
    logging.warn(traceback.format_exc())
Beispiel #30
0
def GetOauthClientId(scope=_EMAIL_SCOPE):
    """Returns the client id of the oauth user."""
    try:
        return oauth.get_client_id(scope)
    except oauth.OAuthRequestError:
        return None  # Invalid oauth token.
Beispiel #31
0
def extract_oauth_caller_identity(extra_client_ids=None):
    """Extracts and validates Identity of a caller for current request.

  Uses client_id whitelist fetched from the datastore to validate OAuth client
  used to build access_token. Also recognizes various types of service accounts
  and verifies that their client_id is what it should be. Service account's
  client_id doesn't have to be in client_id whitelist.

  Used by webapp2 request handlers and Cloud Endpoints API methods.

  Args:
    extra_client_ids: optional list of allowed client_ids, in addition to
      the whitelist in the datastore.

  Returns:
    Identity of the caller in case the request was successfully validated.

  Raises:
    AuthenticationError in case access_token is missing or invalid.
    AuthorizationError in case client_id is forbidden.
  """
    # OAuth2 scope a token should have.
    oauth_scope = 'https://www.googleapis.com/auth/userinfo.email'

    # Extract client_id and email from access token. That also validates the token
    # and raises OAuthRequestError if token is revoked or otherwise not valid.
    try:
        client_id = oauth.get_client_id(oauth_scope)
    except oauth.OAuthRequestError:
        raise AuthenticationError('Invalid OAuth token')

    # This call just reads data cached by oauth.get_client_id, and thus should
    # never fail.
    email = oauth.get_current_user(oauth_scope).email()

    # Is client_id in the explicit whitelist? Used with three legged OAuth.
    good = (get_request_auth_db().is_allowed_oauth_client_id(client_id)
            or client_id in (extra_client_ids or []))

    # Detect various sorts of service accounts. They have a property: client_id
    # and email are related in some way. No need to whitelist client_ids for each
    # of them, since email address uniquely identifies credentials used.

    # GAE service account. Token via app_identity.get_access_token().
    if not good and email.endswith('@appspot.gserviceaccount.com'):
        good = (client_id == 'anonymous')

    # GCE service account. Token via GCE metadata server.
    if not good and email.endswith('@project.gserviceaccount.com'):
        project_id = email[:-len('@project.gserviceaccount.com')]
        good = (client_id == '%s.project.googleusercontent.com' % project_id)

    # Service account with *.p12 key. Token via SignedJwtAssertionCredentials.
    if not good and email.endswith('@developer.gserviceaccount.com'):
        prefix = email[:-len('@developer.gserviceaccount.com')]
        good = (client_id == '%s.apps.googleusercontent.com' % prefix)

    if not good:
        raise AuthorizationError('Invalid OAuth client_id: %s' % client_id)
    try:
        return model.Identity(model.IDENTITY_USER, email)
    except ValueError:
        raise AuthenticationError('Unsupported user email: %s' % email)
Beispiel #32
0
def api_base_checks(request, requester, services, cnxn,
                    auth_client_ids, auth_emails):
  """Base checks for API users.

  Args:
    request: The HTTP request from Cloud Endpoints.
    requester: The user who sends the request.
    services: Services object.
    cnxn: connection to the SQL database.
    auth_client_ids: authorized client ids.
    auth_emails: authorized emails when client is anonymous.

  Returns:
    Client ID and client email.

  Raises:
    endpoints.UnauthorizedException: If the requester is anonymous.
    user_svc.NoSuchUserException: If the requester does not exist in Monorail.
    project_svc.NoSuchProjectException: If the project does not exist in
        Monorail.
    permissions.BannedUserException: If the requester is banned.
    permissions.PermissionException: If the requester does not have
        permisssion to view.
  """
  valid_user = False
  auth_err = ''
  client_id = None

  try:
    client_id = oauth.get_client_id(framework_constants.OAUTH_SCOPE)
    logging.info('Oauth client ID %s', client_id)
  except oauth.Error as ex:
    auth_err = 'oauth.Error: %s' % ex

  if not requester:
    try:
      requester = oauth.get_current_user(framework_constants.OAUTH_SCOPE)
      logging.info('Oauth requester %s', requester.email())
    except oauth.Error as ex:
      auth_err = 'oauth.Error: %s' % ex

  if client_id and requester:
    if client_id != 'anonymous':
      if client_id in auth_client_ids:
        valid_user = True
      else:
        auth_err = 'Client ID %s is not whitelisted' % client_id
    # Some service accounts may have anonymous client ID
    else:
      if requester.email() in auth_emails:
        valid_user = True
      else:
        auth_err = 'Client email %s is not whitelisted' % requester.email()

  if not valid_user:
    raise endpoints.UnauthorizedException('Auth error: %s' % auth_err)
  else:
    logging.info('API request from user %s:%s', client_id, requester.email())

  project_name = None
  if hasattr(request, 'projectId'):
    project_name = request.projectId
  issue_local_id = None
  if hasattr(request, 'issueId'):
    issue_local_id = request.issueId
  # This could raise user_svc.NoSuchUserException
  requester_id = services.user.LookupUserID(cnxn, requester.email())
  requester_pb = services.user.GetUser(cnxn, requester_id)
  requester_view = framework_views.UserView(requester_pb)
  if permissions.IsBanned(requester_pb, requester_view):
    raise permissions.BannedUserException(
        'The user %s has been banned from using Monorail' %
        requester.email())
  if project_name:
    project = services.project.GetProjectByName(
        cnxn, project_name)
    if not project:
      raise project_svc.NoSuchProjectException(
          'Project %s does not exist' % project_name)
    if project.state != project_pb2.ProjectState.LIVE:
      raise permissions.PermissionException(
          'API may not access project %s because it is not live'
          % project_name)
    requester_effective_ids = services.usergroup.LookupMemberships(
        cnxn, requester_id)
    requester_effective_ids.add(requester_id)
    if not permissions.UserCanViewProject(
        requester_pb, requester_effective_ids, project):
      raise permissions.PermissionException(
          'The user %s has no permission for project %s' %
          (requester.email(), project_name))
    if issue_local_id:
      # This may raise a NoSuchIssueException.
      issue = services.issue.GetIssueByLocalID(
          cnxn, project.project_id, issue_local_id)
      perms = permissions.GetPermissions(
          requester_pb, requester_effective_ids, project)
      config = services.config.GetProjectConfig(cnxn, project.project_id)
      granted_perms = tracker_bizobj.GetGrantedPerms(
          issue, requester_effective_ids, config)
      if not permissions.CanViewIssue(
          requester_effective_ids, perms, project, issue,
          granted_perms=granted_perms):
        raise permissions.PermissionException(
            'User is not allowed to view this issue %s:%d' %
            (project_name, issue_local_id))

  return client_id, requester.email()
def get_client_id():
  try:
    return oauth.get_client_id(SCOPES)
  except oauth.Error as e:
    return None
Beispiel #34
0
def extract_oauth_caller_identity(extra_client_ids=None):
  """Extracts and validates Identity of a caller for current request.

  Uses client_id whitelist fetched from the datastore to validate OAuth client
  used to build access_token. Also recognizes various types of service accounts
  and verifies that their client_id is what it should be. Service account's
  client_id doesn't have to be in client_id whitelist.

  Used by webapp2 request handlers and Cloud Endpoints API methods.

  Args:
    extra_client_ids: optional list of allowed client_ids, in addition to
      the whitelist in the datastore.

  Returns:
    Identity of the caller in case the request was successfully validated.

  Raises:
    AuthenticationError in case access_token is missing or invalid.
    AuthorizationError in case client_id is forbidden.
  """
  # OAuth2 scope a token should have.
  oauth_scope = 'https://www.googleapis.com/auth/userinfo.email'

  # Extract client_id and email from access token. That also validates the token
  # and raises OAuthRequestError if token is revoked or otherwise not valid.
  try:
    client_id = oauth.get_client_id(oauth_scope)
  except oauth.OAuthRequestError:
    raise AuthenticationError('Invalid OAuth token')

  # This call just reads data cached by oauth.get_client_id, and thus should
  # never fail.
  email = oauth.get_current_user(oauth_scope).email()

  # Is client_id in the explicit whitelist? Used with three legged OAuth.
  good = (
      get_request_auth_db().is_allowed_oauth_client_id(client_id) or
      client_id in (extra_client_ids or []))

  # Detect various sorts of service accounts. They have a property: client_id
  # and email are related in some way. No need to whitelist client_ids for each
  # of them, since email address uniquely identifies credentials used.

  # GAE service account. Token via app_identity.get_access_token().
  if not good and email.endswith('@appspot.gserviceaccount.com'):
    good = (client_id == 'anonymous')

  # GCE service account. Token via GCE metadata server.
  if not good and email.endswith('@project.gserviceaccount.com'):
    project_id = email[:-len('@project.gserviceaccount.com')]
    good = (client_id == '%s.project.googleusercontent.com' % project_id)

  # Service account with *.p12 key. Token via SignedJwtAssertionCredentials.
  if not good and email.endswith('@developer.gserviceaccount.com'):
    prefix = email[:-len('@developer.gserviceaccount.com')]
    good = (client_id == '%s.apps.googleusercontent.com' % prefix)

  if not good:
    raise AuthorizationError('Invalid OAuth client_id: %s' % client_id)
  try:
    return model.Identity(model.IDENTITY_USER, email)
  except ValueError:
    raise AuthenticationError('Unsupported user email: %s' % email)