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 |