Add tracking of the kind of temporary access tokens, so we can display if a pull/push by token is for a build worker

This commit is contained in:
Joseph Schorr 2015-02-17 12:35:16 -05:00
parent 3e04e3cfd7
commit 83e05d2342
10 changed files with 88 additions and 14 deletions

View file

@ -333,6 +333,10 @@ class PermissionPrototype(BaseModel):
) )
class AccessTokenKind(BaseModel):
name = CharField(unique=True, index=True)
class AccessToken(BaseModel): class AccessToken(BaseModel):
friendly_name = CharField(null=True) friendly_name = CharField(null=True)
code = CharField(default=random_string_generator(length=64), unique=True, code = CharField(default=random_string_generator(length=64), unique=True,
@ -341,6 +345,7 @@ class AccessToken(BaseModel):
created = DateTimeField(default=datetime.now) created = DateTimeField(default=datetime.now)
role = ForeignKeyField(Role) role = ForeignKeyField(Role)
temporary = BooleanField(default=True) temporary = BooleanField(default=True)
kind = ForeignKeyField(AccessTokenKind, null=True)
class BuildTriggerService(BaseModel): class BuildTriggerService(BaseModel):
@ -600,4 +605,5 @@ all_models = [User, Repository, Image, AccessToken, Role, RepositoryPermission,
Notification, ImageStorageLocation, ImageStoragePlacement, Notification, ImageStorageLocation, ImageStoragePlacement,
ExternalNotificationEvent, ExternalNotificationMethod, RepositoryNotification, ExternalNotificationEvent, ExternalNotificationMethod, RepositoryNotification,
RepositoryAuthorizedEmail, ImageStorageTransformation, DerivedImageStorage, RepositoryAuthorizedEmail, ImageStorageTransformation, DerivedImageStorage,
TeamMemberInvite, ImageStorageSignature, ImageStorageSignatureKind] TeamMemberInvite, ImageStorageSignature, ImageStorageSignatureKind,
AccessTokenKind]

View file

@ -0,0 +1,44 @@
"""Add access token kinds type
Revision ID: 3e2d38b52a75
Revises: 1d2d86d09fcd
Create Date: 2015-02-17 12:03:26.422485
"""
# revision identifiers, used by Alembic.
revision = '3e2d38b52a75'
down_revision = '1d2d86d09fcd'
from alembic import op
import sqlalchemy as sa
def upgrade(tables):
### commands auto generated by Alembic - please adjust! ###
op.create_table('accesstokenkind',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.PrimaryKeyConstraint('id', name=op.f('pk_accesstokenkind'))
)
op.create_index('accesstokenkind_name', 'accesstokenkind', ['name'], unique=True)
op.add_column(u'accesstoken', sa.Column('kind_id', sa.Integer(), nullable=True))
op.create_index('accesstoken_kind_id', 'accesstoken', ['kind_id'], unique=False)
op.create_foreign_key(op.f('fk_accesstoken_kind_id_accesstokenkind'), 'accesstoken', 'accesstokenkind', ['kind_id'], ['id'])
### end Alembic commands ###
op.bulk_insert(tables.accesstokenkind,
[
{'id': 1, 'name':'build-worker'},
{'id': 2, 'name':'pushpull-token'},
])
def downgrade(tables):
### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(op.f('fk_accesstoken_kind_id_accesstokenkind'), 'accesstoken', type_='foreignkey')
op.drop_index('accesstoken_kind_id', table_name='accesstoken')
op.drop_column(u'accesstoken', 'kind_id')
op.drop_index('accesstokenkind_name', table_name='accesstokenkind')
op.drop_table('accesstokenkind')
### end Alembic commands ###

View file

@ -15,7 +15,8 @@ from data.database import (User, Repository, Image, AccessToken, Role, Repositor
RepositoryNotification, RepositoryAuthorizedEmail, TeamMemberInvite, RepositoryNotification, RepositoryAuthorizedEmail, TeamMemberInvite,
DerivedImageStorage, ImageStorageTransformation, random_string_generator, DerivedImageStorage, ImageStorageTransformation, random_string_generator,
db, BUILD_PHASE, QuayUserField, ImageStorageSignature, QueueItem, db, BUILD_PHASE, QuayUserField, ImageStorageSignature, QueueItem,
ImageStorageSignatureKind, validate_database_url, db_for_update) ImageStorageSignatureKind, validate_database_url, db_for_update,
AccessTokenKind)
from peewee import JOIN_LEFT_OUTER, fn from peewee import JOIN_LEFT_OUTER, fn
from util.validation import (validate_username, validate_email, validate_password, from util.validation import (validate_username, validate_email, validate_password,
INVALID_PASSWORD_MESSAGE) INVALID_PASSWORD_MESSAGE)
@ -1902,10 +1903,14 @@ def get_private_repo_count(username):
.count()) .count())
def create_access_token(repository, role): def create_access_token(repository, role, kind=None, friendly_name=None):
role = Role.get(Role.name == role) role = Role.get(Role.name == role)
kind_ref = None
if kind is not None:
kind_ref = AccessTokenKind.get(AccessTokenKind.name == kind)
new_token = AccessToken.create(repository=repository, temporary=True, new_token = AccessToken.create(repository=repository, temporary=True,
role=role) role=role, kind=kind_ref, friendly_name=friendly_name)
return new_token return new_token

View file

@ -215,7 +215,8 @@ def start_build(repository, dockerfile_id, tags, build_name, subdir, manual,
host = urlparse.urlparse(request.url).netloc host = urlparse.urlparse(request.url).netloc
repo_path = '%s/%s/%s' % (host, repository.namespace_user.username, repository.name) repo_path = '%s/%s/%s' % (host, repository.namespace_user.username, repository.name)
token = model.create_access_token(repository, 'write') token = model.create_access_token(repository, 'write', kind='build-worker',
friendly_name='Repository Build Token')
logger.debug('Creating build %s with repo %s tags %s and dockerfile_id %s', logger.debug('Creating build %s with repo %s tags %s and dockerfile_id %s',
build_name, repo_path, tags, dockerfile_id) build_name, repo_path, tags, dockerfile_id)

View file

@ -50,7 +50,7 @@ def generate_headers(role='read'):
if has_token_request: if has_token_request:
repo = model.get_repository(namespace, repository) repo = model.get_repository(namespace, repository)
if repo: if repo:
token = model.create_access_token(repo, role) token = model.create_access_token(repo, role, 'pushpull-token')
token_str = 'signature=%s' % token.code token_str = 'signature=%s' % token.code
response.headers['WWW-Authenticate'] = token_str response.headers['WWW-Authenticate'] = token_str
response.headers['X-Docker-Token'] = token_str response.headers['X-Docker-Token'] = token_str

View file

@ -34,6 +34,10 @@ def track_and_log(event_name, repo, **kwargs):
elif authenticated_token: elif authenticated_token:
metadata['token'] = authenticated_token.friendly_name metadata['token'] = authenticated_token.friendly_name
metadata['token_code'] = authenticated_token.code metadata['token_code'] = authenticated_token.code
if authenticated_token.kind:
metadata['token_type'] = authenticated_token.kind.name
analytics_id = 'token:' + authenticated_token.code analytics_id = 'token:' + authenticated_token.code
else: else:
metadata['public'] = True metadata['public'] = True

View file

@ -192,6 +192,9 @@ def initialize_database():
BuildTriggerService.create(name='github') BuildTriggerService.create(name='github')
AccessTokenKind.create(name='build-worker')
AccessTokenKind.create(name='pushpull-token')
LogEntryKind.create(name='account_change_plan') LogEntryKind.create(name='account_change_plan')
LogEntryKind.create(name='account_change_cc') LogEntryKind.create(name='account_change_cc')
LogEntryKind.create(name='account_change_password') LogEntryKind.create(name='account_change_password')
@ -393,7 +396,7 @@ def populate_database():
'Empty repository which is building.', 'Empty repository which is building.',
False, [], (0, [], None)) False, [], (0, [], None))
token = model.create_access_token(building, 'write') token = model.create_access_token(building, 'write', 'build-worker')
trigger = model.create_build_trigger(building, 'github', '123authtoken', trigger = model.create_build_trigger(building, 'github', '123authtoken',
new_user_1, pull_robot=dtrobot[0]) new_user_1, pull_robot=dtrobot[0])

View file

@ -56,7 +56,8 @@
<td> <td>
<span class="log-performer" ng-if="log.metadata.oauth_token_application"> <span class="log-performer" ng-if="log.metadata.oauth_token_application">
<div> <div>
<span class="application-reference" data-title="log.metadata.oauth_token_application" <span class="application-reference"
data-title="log.metadata.oauth_token_application"
client-id="log.metadata.oauth_token_application_id"></span> client-id="log.metadata.oauth_token_application_id"></span>
</div> </div>
<div style="text-align: center; font-size: 12px; color: #aaa; padding: 4px;">on behalf of</div> <div style="text-align: center; font-size: 12px; color: #aaa; padding: 4px;">on behalf of</div>

View file

@ -3314,7 +3314,11 @@ quayApp.directive('logsView', function () {
} }
if (metadata.token) { if (metadata.token) {
prefix += ' via token {token}'; if (metadata.token_type == 'build-worker') {
prefix += ' by <b>build worker</b>';
} else {
prefix += ' via token';
}
} else if (metadata.username) { } else if (metadata.username) {
prefix += ' by {username}'; prefix += ' by {username}';
} else { } else {
@ -3325,7 +3329,13 @@ quayApp.directive('logsView', function () {
}, },
'pull_repo': function(metadata) { 'pull_repo': function(metadata) {
if (metadata.token) { if (metadata.token) {
return 'Pull repository {repo} via token {token}'; var prefix = 'Pull of repository'
if (metadata.token_type == 'build-worker') {
prefix += ' by <b>build worker</b>';
} else {
prefix += ' via token';
}
return prefix;
} else if (metadata.username) { } else if (metadata.username) {
return 'Pull repository {repo} by {username}'; return 'Pull repository {repo} by {username}';
} else { } else {

Binary file not shown.