Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1245)

Unified Diff: appengine/auth_service/delegation.py

Issue 2164733003: auth: Keep audit log of all generated delegation tokens. (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-py@master
Patch Set: auth: Keep audit log of all generates delegation tokens. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « appengine/auth_service/config_test.py ('k') | appengine/auth_service/delegation_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « appengine/auth_service/config_test.py ('k') | appengine/auth_service/delegation_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698