| Index: appengine/auth_service/delegation.py
|
| diff --git a/appengine/auth_service/delegation.py b/appengine/auth_service/delegation.py
|
| index c2cb4d3a76e64f459d3a9257c75fc2f4614e5517..98e061644f1ac774188c036866c712711558a6a1 100644
|
| --- a/appengine/auth_service/delegation.py
|
| +++ b/appengine/auth_service/delegation.py
|
| @@ -7,10 +7,13 @@
|
| import logging
|
| import webapp2
|
|
|
| +from google.appengine.ext import ndb
|
| +
|
| from components import auth
|
| from components import utils
|
|
|
| from components.auth import delegation
|
| +from components.auth import ipaddr
|
| from components.auth.proto import delegation_pb2
|
|
|
| from proto import config_pb2
|
| @@ -78,7 +81,7 @@ class CreateDelegationTokenHandler(auth.ApiHandler):
|
| raise auth.AuthorizationError(
|
| 'This API call must not be used with active delegation token')
|
|
|
| - # Convert request body to proto (with validation).
|
| + # Convert request body to proto (with validation). Verify IP format.
|
| try:
|
| subtoken = subtoken_from_jsonish(self.parse_body())
|
| except (TypeError, ValueError) as exc:
|
| @@ -98,7 +101,10 @@ class CreateDelegationTokenHandler(auth.ApiHandler):
|
| subtoken.services[:] = get_default_allowed_services(user_id)
|
|
|
| # Check ACL (raises auth.AuthorizationError on errors).
|
| - check_can_create_token(user_id, subtoken)
|
| + rule = check_can_create_token(user_id, subtoken)
|
| +
|
| + # Register the token in the datastore, generate its ID.
|
| + subtoken.subtoken_id = register_subtoken(subtoken, rule, auth.get_peer_ip())
|
|
|
| # Create and sign the token.
|
| try:
|
| @@ -183,6 +189,9 @@ def subtoken_from_jsonish(d):
|
| return msg
|
|
|
|
|
| +################################################################################
|
| +
|
| +
|
| # Fallback rule returned if nothing else matches.
|
| DEFAULT_RULE = config_pb2.DelegationConfig.Rule(
|
| user_id=['*'],
|
| @@ -243,6 +252,9 @@ def check_can_create_token(user_id, subtoken):
|
| user_id: identity string of a current caller.
|
| subtoken: instance of delegation_pb2.Subtoken describing root token.
|
|
|
| + Returns:
|
| + config_pb2.DelegationConfig.Rule that allows the operation.
|
| +
|
| Raises:
|
| auth.AuthorizationError if such token is not allowed for the caller.
|
| """
|
| @@ -255,13 +267,13 @@ def check_can_create_token(user_id, subtoken):
|
|
|
| # Just delegating one's own identity (not impersonating someone else)? Allow.
|
| if subtoken.issuer_id == user_id:
|
| - return
|
| + return rule
|
|
|
| # Verify it's OK to impersonate a given user.
|
| impersonated = auth.Identity.from_bytes(subtoken.issuer_id)
|
| for principal_set in rule.allowed_to_impersonate:
|
| if is_identity_in_principal_set(impersonated, principal_set):
|
| - return
|
| + return rule
|
|
|
| raise auth.AuthorizationError(
|
| '"%s" is not allowed to impersonate "%s" on %s' %
|
| @@ -277,3 +289,62 @@ def get_default_allowed_services(user_id):
|
| rule = get_delegation_rule(user_id, ['*'])
|
| return rule.target_service
|
|
|
| +
|
| +################################################################################
|
| +
|
| +
|
| +class AuthDelegationSubtoken(ndb.Model):
|
| + """Represents a delegation subtoken.
|
| +
|
| + Used to track what tokens are issued. Root entity. ID is autogenerated.
|
| + """
|
| + # Serialized delegation_pb2.Subtoken proto.
|
| + subtoken = ndb.BlobProperty()
|
| + # Serialized config_pb2.DelegationConfig.Rule that allowed this token.
|
| + rule = ndb.BlobProperty()
|
| + # IP address the minting request came from.
|
| + caller_ip = ndb.StringProperty()
|
| + # Version of the auth_service that created the subtoken.
|
| + auth_service_version = ndb.StringProperty()
|
| +
|
| + # Fields below are extracted from 'subtoken', for indexing purposes.
|
| +
|
| + # Whose authority the token conveys.
|
| + issuer_id = ndb.StringProperty()
|
| + # When the token was created.
|
| + creation_time = ndb.DateTimeProperty()
|
| + # List of services that accept the token (or ['*'] if all).
|
| + services = ndb.StringProperty(repeated=True)
|
| + # Who initiated the minting request if it is an impersonation token.
|
| + impersonator_id = ndb.StringProperty()
|
| +
|
| +
|
| +def register_subtoken(subtoken, rule, caller_ip):
|
| + """Creates new AuthDelegationSubtoken entity in the datastore, returns its ID.
|
| +
|
| + Args:
|
| + subtoken: delegation_pb2.Subtoken describing the token.
|
| + rule: config_pb2.DelegationConfig.Rule that allows the operation
|
| + caller_ip: ipaddr.IP of the caller.
|
| +
|
| + Returns:
|
| + int64 with ID of the new entity.
|
| + """
|
| + entity = AuthDelegationSubtoken(
|
| + subtoken=subtoken.SerializeToString(),
|
| + rule=rule.SerializeToString(),
|
| + caller_ip=ipaddr.ip_to_string(caller_ip),
|
| + auth_service_version=utils.get_app_version(),
|
| + issuer_id=subtoken.issuer_id,
|
| + creation_time=utils.timestamp_to_datetime(subtoken.creation_time*1e6),
|
| + services=list(subtoken.services or ['*']),
|
| + impersonator_id=subtoken.impersonator_id)
|
| + entity.put(use_cache=False, use_memcache=False)
|
| + subtoken_id = entity.key.integer_id()
|
| +
|
| + # Keep a logging entry (extractable via BigQuery) too.
|
| + logging.info(
|
| + 'subtoken: subtoken_id=%d caller_ip=%s issuer_id=%s impersonator_id=%s',
|
| + subtoken_id, entity.caller_ip, entity.issuer_id, entity.impersonator_id)
|
| +
|
| + return subtoken_id
|
|
|