service keys: do all the right stuff

This commit is contained in:
Jimmy Zelinskie 2016-03-23 18:16:03 -04:00 committed by Jimmy Zelinskie
parent 6ecff950ab
commit 4079dba167
7 changed files with 149 additions and 41 deletions

View file

@ -871,18 +871,22 @@ class TorrentInfo(BaseModel):
_ServiceKeyApproverProxy = Proxy()
class ServiceKeyApproval(BaseModel):
approver = ForeignKeyField(_ServiceKeyApproverProxy)
approver = ForeignKeyField(_ServiceKeyApproverProxy, null=True)
approval_type = CharField(index=True)
approved_date = DateTimeField(default=datetime.now)
approved_date = DateTimeField(default=datetime.utcnow)
notes = TextField()
_ServiceKeyApproverProxy.initialize(User)
class ServiceKey(BaseModel):
name = CharField()
kid = CharField(unique=True, index=True)
service = CharField(index=True)
jwk = CharField(unique=True)
jwk = JSONField(unique=True)
metadata = JSONField(unique=True)
created_date = DateTimeField(default=datetime.utcnow)
expiration_date = DateTimeField(null=True)
approval = ForeignKeyField(ServiceKeyApproval, index=True)
approval = ForeignKeyField(ServiceKeyApproval, index=True, null=True)
is_model = lambda x: inspect.isclass(x) and issubclass(x, BaseModel) and x is not BaseModel

View file

@ -80,6 +80,10 @@ class ServiceKeyDoesNotExist(DataModelException):
pass
class ServiceKeyAlreadyApproved(DataModelException):
pass
class TooManyLoginAttemptsException(Exception):
def __init__(self, message, retry_after):
super(TooManyLoginAttemptsException, self).__init__(message)

View file

@ -1,25 +1,52 @@
from datetime import datetime
from datetime import datetime, timedelta
from app import app
from data.database import db_for_update, User, ServiceKey, ServiceKeyApproval
from data.model import ServiceKeyDoesNotExist, ServiceKeyAlreadyApproved, db_transaction
from data.model.notification import create_notification
UNAPPROVED_TTL = timedelta(seconds=app.config['UNAPPROVED_SERVICE_KEY_TTL_SEC'])
from data.model import ServiceKeyDoesNotExist, db_transaction
from data.database import db_for_update, ServiceKey, ServiceKeyApproval
def _gc_expired(service):
ServiceKey.delete().where(ServiceKey.service == service,
ServiceKey.expiration_date <= datetime.now).execute()
expired_keys = ((ServiceKey.service == service) &
(ServiceKey.expiration_date <= datetime.utcnow()))
stale_unapproved_keys = ((ServiceKey.service >> service) &
(ServiceKey.approval >> None) &
(ServiceKey.created_date + UNAPPROVED_TTL) >= datetime.utcnow())
ServiceKey.delete().where(expired_keys | stale_unapproved_keys).execute()
def upsert_service_key(kid, service, jwk, expiration_date):
# TODO ACTION_LOGS for keys
def upsert_service_key(name, kid, service, jwk, metadata, expiration_date):
_gc_expired(service)
try:
with db_transaction():
key = db_for_update(ServiceKey.select().where(ServiceKey.kid == kid)).get()
key.service = service
key.jwk = jwk
key.name = name
key.metadata.update(metadata)
key.expiration_date = expiration_date
key.save()
except ServiceKey.DoesNotExist:
ServiceKey.create(kid=kid, service=service, jwk=jwk, expiration_date=expiration_date)
sk = ServiceKey.create(name=name, kid=kid, service=service, jwk=jwk, metadata=metadata,
expiration_date=expiration_date)
superusers = User.select().where(User.username << app.config['SUPER_USERS'])
for superuser in superusers:
# TODO(jzelinskie): create notification type in the database migration
create_notification('service_key_submitted', superuser, {
'name': name,
'kid': kid,
'service': service,
'jwk': jwk,
'metadata': metadata,
'created_date': sk.created_date,
'expiration_date': expiration_date,
})
def get_service_keys(service, kid=None):
@ -45,9 +72,12 @@ def delete_service_key(service, kid):
def approve_service_key(service, kid, approver, approval_type):
try:
with db_transaction():
approval = ServiceKeyApproval.create(approver=approver, approval_type=approval_type)
key = db_for_update(ServiceKey.select().where(ServiceKey.service == service,
ServiceKey.kid == kid)).get()
if key.approval is not None:
raise ServiceKeyAlreadyApproved
approval = ServiceKeyApproval.create(approver=approver, approval_type=approval_type)
key.approval = approval
key.save()
except ServiceKey.DoesNotExist: