Integrate flask-principal in order to provide RBAC.
This commit is contained in:
parent
8e169b1026
commit
458b69953a
8 changed files with 237 additions and 103 deletions
5
app.py
5
app.py
|
@ -1,10 +1,13 @@
|
||||||
from flask import Flask, make_response, jsonify
|
from flask import Flask, make_response, jsonify
|
||||||
|
from flask.ext.principal import Principal
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
Principal(app, use_sessions=False)
|
||||||
|
|
||||||
@app.route('/_ping')
|
@app.route('/_ping')
|
||||||
@app.route('/v1/_ping')
|
@app.route('/v1/_ping')
|
||||||
def ping():
|
def ping():
|
||||||
response = make_response('true', 200);
|
response = make_response('true', 200)
|
||||||
response.headers['X-Docker-Registry-Version'] = '0.6.0'
|
response.headers['X-Docker-Registry-Version'] = '0.6.0'
|
||||||
return response
|
return response
|
32
auth.py
32
auth.py
|
@ -1,30 +1,28 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from werkzeug import LocalProxy
|
from flask import request, make_response, _request_ctx_stack, abort
|
||||||
from flask import request, make_response, _request_ctx_stack
|
from flask.ext.principal import identity_changed, Identity
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
|
|
||||||
import model
|
import model
|
||||||
|
from app import app
|
||||||
|
|
||||||
from util import parse_namespace_repository
|
from util import parse_namespace_repository
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _get_user():
|
def get_authenticated_user():
|
||||||
return getattr(_request_ctx_stack.top, 'authenticated_user', None)
|
return getattr(_request_ctx_stack.top, 'authenticated_user', None)
|
||||||
|
|
||||||
|
|
||||||
def _get_token():
|
def get_validated_token():
|
||||||
return getattr(_request_ctx_stack.top, 'validated_token', None)
|
return getattr(_request_ctx_stack.top, 'validated_token', None)
|
||||||
|
|
||||||
|
|
||||||
authenticated_user = LocalProxy(_get_user)
|
def process_basic_auth():
|
||||||
validated_token = LocalProxy(_get_token)
|
|
||||||
|
|
||||||
|
|
||||||
def check_basic_auth():
|
|
||||||
auth = request.headers.get('authorization', '')
|
auth = request.headers.get('authorization', '')
|
||||||
normalized = [part.strip() for part in auth.split(' ') if part]
|
normalized = [part.strip() for part in auth.split(' ') if part]
|
||||||
if normalized[0].lower() != 'basic' or len(normalized) != 2:
|
if normalized[0].lower() != 'basic' or len(normalized) != 2:
|
||||||
|
@ -43,13 +41,15 @@ def check_basic_auth():
|
||||||
ctx = _request_ctx_stack.top
|
ctx = _request_ctx_stack.top
|
||||||
ctx.authenticated_user = authenticated
|
ctx.authenticated_user = authenticated
|
||||||
|
|
||||||
|
identity_changed.send(app, identity=Identity(authenticated.username))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# We weren't able to authenticate via basic auth.
|
# We weren't able to authenticate via basic auth.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def check_token():
|
def process_token():
|
||||||
auth = request.headers.get('authorization', '')
|
auth = request.headers.get('authorization', '')
|
||||||
logger.debug('Validating auth token: %s' % auth)
|
logger.debug('Validating auth token: %s' % auth)
|
||||||
|
|
||||||
|
@ -74,14 +74,15 @@ def check_token():
|
||||||
unquoted = token_vals['repository'][1:-1]
|
unquoted = token_vals['repository'][1:-1]
|
||||||
namespace, repository = parse_namespace_repository(unquoted)
|
namespace, repository = parse_namespace_repository(unquoted)
|
||||||
logger.debug('Validing signature: %s' % token_vals['signature'])
|
logger.debug('Validing signature: %s' % token_vals['signature'])
|
||||||
validated = model.verify_token(namespace, repository,
|
validated = model.verify_token(token_vals['signature'])
|
||||||
token_vals['signature'])
|
|
||||||
|
|
||||||
if validated:
|
if validated:
|
||||||
logger.debug('Successfully validated token: %s' % validated.code)
|
logger.debug('Successfully validated token: %s' % validated.code)
|
||||||
ctx = _request_ctx_stack.top
|
ctx = _request_ctx_stack.top
|
||||||
ctx.validated_token = validated
|
ctx.validated_token = validated
|
||||||
|
|
||||||
|
identity_changed.send(app, identity=Identity(validated.code))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# WE weren't able to authenticate the token
|
# WE weren't able to authenticate the token
|
||||||
|
@ -89,11 +90,10 @@ def check_token():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def requires_auth(f):
|
def process_auth(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
if not check_basic_auth() and not check_token():
|
process_token()
|
||||||
return make_response('Invalid credentials.', 401)
|
process_basic_auth()
|
||||||
logger.debug('Successfully authenticated user or token.')
|
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
39
database.py
39
database.py
|
@ -15,16 +15,20 @@ class BaseModel(Model):
|
||||||
|
|
||||||
|
|
||||||
class User(BaseModel):
|
class User(BaseModel):
|
||||||
username = CharField(primary_key=True)
|
username = CharField(unique=True)
|
||||||
password_hash = CharField()
|
password_hash = CharField()
|
||||||
email = CharField()
|
email = CharField(unique=True)
|
||||||
verified = BooleanField(default=False)
|
verified = BooleanField(default=False)
|
||||||
|
|
||||||
|
|
||||||
|
class Visibility(BaseModel):
|
||||||
|
name = CharField()
|
||||||
|
|
||||||
|
|
||||||
class Repository(BaseModel):
|
class Repository(BaseModel):
|
||||||
repository_id = PrimaryKeyField()
|
|
||||||
namespace = CharField()
|
namespace = CharField()
|
||||||
name = CharField()
|
name = CharField()
|
||||||
|
visibility = ForeignKeyField(Visibility)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
database = db
|
database = db
|
||||||
|
@ -34,6 +38,16 @@ class Repository(BaseModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Role(BaseModel):
|
||||||
|
name = CharField()
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoryPermission(BaseModel):
|
||||||
|
user = ForeignKeyField(User)
|
||||||
|
repository = ForeignKeyField(Repository)
|
||||||
|
role = ForeignKeyField(Role)
|
||||||
|
|
||||||
|
|
||||||
def random_string_generator(length=16):
|
def random_string_generator(length=16):
|
||||||
def random_string():
|
def random_string():
|
||||||
random = SystemRandom()
|
random = SystemRandom()
|
||||||
|
@ -43,14 +57,14 @@ def random_string_generator(length=16):
|
||||||
|
|
||||||
|
|
||||||
class AccessToken(BaseModel):
|
class AccessToken(BaseModel):
|
||||||
token_id = PrimaryKeyField()
|
code = CharField(default=random_string_generator(), unique=True)
|
||||||
code = CharField(default=random_string_generator())
|
user = ForeignKeyField(User)
|
||||||
repository = ForeignKeyField(Repository)
|
repository = ForeignKeyField(Repository)
|
||||||
created = DateTimeField(default=datetime.now)
|
created = DateTimeField(default=datetime.now)
|
||||||
|
|
||||||
|
|
||||||
class Image(BaseModel):
|
class Image(BaseModel):
|
||||||
image_id = CharField(primary_key=True)
|
image_id = CharField(unique=True)
|
||||||
checksum = CharField(null=True)
|
checksum = CharField(null=True)
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,8 +81,15 @@ class RepositoryImage(BaseModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_tables():
|
def intiialize_db():
|
||||||
create_model_tables([User, Repository, Image, RepositoryImage, AccessToken])
|
create_model_tables([User, Repository, Image, RepositoryImage, AccessToken,
|
||||||
|
Role, RepositoryPermission, Visibility])
|
||||||
|
Role.create(name='admin')
|
||||||
|
Role.create(name='write')
|
||||||
|
Role.create(name='read')
|
||||||
|
Visibility.create(name='public')
|
||||||
|
Visibility.create(name='private')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
create_tables()
|
intiialize_db()
|
||||||
|
|
91
index.py
91
index.py
|
@ -3,12 +3,14 @@ import urllib
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from flask import request, make_response, jsonify
|
from flask import request, make_response, jsonify, abort
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from app import app
|
from app import app
|
||||||
from auth import requires_auth, authenticated_user, validated_token
|
from auth import process_auth, get_authenticated_user, get_validated_token
|
||||||
from util import parse_namespace_repository
|
from util import parse_namespace_repository
|
||||||
|
from permissions import (ModifyRepositoryPermission, ReadRepositoryPermission,
|
||||||
|
UserPermission)
|
||||||
|
|
||||||
import model
|
import model
|
||||||
|
|
||||||
|
@ -34,9 +36,11 @@ def generate_headers(access):
|
||||||
|
|
||||||
response.headers['X-Docker-Endpoints'] = REGISTRY_SERVER
|
response.headers['X-Docker-Endpoints'] = REGISTRY_SERVER
|
||||||
|
|
||||||
if request.headers.get('X-Docker-Token', ''):
|
has_token_request = request.headers.get('X-Docker-Token', '')
|
||||||
repo = model.create_or_fetch_repository(namespace, repository)
|
|
||||||
token = model.create_access_token(repo)
|
if has_token_request and get_authenticated_user():
|
||||||
|
repo = model.get_repository(namespace, repository)
|
||||||
|
token = model.create_access_token(get_authenticated_user(), repo)
|
||||||
token_str = ('Token signature=%s,repository="%s/%s",access=%s' %
|
token_str = ('Token signature=%s,repository="%s/%s",access=%s' %
|
||||||
(token.code, namespace, repository, access))
|
(token.code, namespace, repository, access))
|
||||||
response.headers['WWW-Authenticate'] = token_str
|
response.headers['WWW-Authenticate'] = token_str
|
||||||
|
@ -58,44 +62,69 @@ def create_user():
|
||||||
|
|
||||||
@app.route('/v1/users', methods=['GET'])
|
@app.route('/v1/users', methods=['GET'])
|
||||||
@app.route('/v1/users/', methods=['GET'])
|
@app.route('/v1/users/', methods=['GET'])
|
||||||
@requires_auth
|
@process_auth
|
||||||
def get_user():
|
def get_user():
|
||||||
|
if not get_authenticated_user():
|
||||||
|
abort(401)
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'username': authenticated_user.username,
|
'username': get_authenticated_user().username,
|
||||||
'email': authenticated_user.email,
|
'email': get_authenticated_user().email,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/users/<username>/', methods=['PUT'])
|
@app.route('/v1/users/<username>/', methods=['PUT'])
|
||||||
@requires_auth
|
@process_auth
|
||||||
def update_user(username):
|
def update_user(username):
|
||||||
if authenticated_user.username != username:
|
permission = UserPermission(username)
|
||||||
return make_response('Forbidden', 403)
|
|
||||||
|
|
||||||
|
if permission.can():
|
||||||
update_request = request.get_json()
|
update_request = request.get_json()
|
||||||
|
|
||||||
if update_request.has_key('password'):
|
if 'password' in update_request:
|
||||||
logger.debug('Updating user password.')
|
logger.debug('Updating user password.')
|
||||||
model.change_password(authenticated_user, update_request['password'])
|
model.change_password(get_authenticated_user(),
|
||||||
|
update_request['password'])
|
||||||
|
|
||||||
if update_request.has_key('email'):
|
if 'email' in update_request:
|
||||||
logger.debug('Updating user email')
|
logger.debug('Updating user email')
|
||||||
model.update_email(authenticated_user, update_request['email'])
|
model.update_email(get_authenticated_user(), update_request['email'])
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'username': authenticated_user.username,
|
'username': get_authenticated_user().username,
|
||||||
'email': authenticated_user.email,
|
'email': get_authenticated_user().email,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/repositories/<path:repository>', methods=['PUT'])
|
@app.route('/v1/repositories/<path:repository>', methods=['PUT'])
|
||||||
@requires_auth
|
@process_auth
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
@generate_headers(access='write')
|
@generate_headers(access='write')
|
||||||
def create_repository(namespace, repository):
|
def create_repository(namespace, repository):
|
||||||
|
# TODO check that the user is the same as indicated by the namespace
|
||||||
|
|
||||||
image_descriptions = json.loads(request.data)
|
image_descriptions = json.loads(request.data)
|
||||||
|
|
||||||
repo = model.create_or_fetch_repository(namespace, repository)
|
repo = model.get_repository(namespace, repository)
|
||||||
|
|
||||||
|
auth_fail_response = 403
|
||||||
|
if not get_validated_token() or get_authenticated_user():
|
||||||
|
auth_fail_response = 401
|
||||||
|
|
||||||
|
if repo:
|
||||||
|
permission = ModifyRepositoryPermission(namespace, repository)
|
||||||
|
if not permission.can():
|
||||||
|
abort(auth_fail_response)
|
||||||
|
else:
|
||||||
|
if not get_authenticated_user():
|
||||||
|
abort(auth_fail_response)
|
||||||
|
|
||||||
|
logger.debug('Creaing repository with owner: %s' %
|
||||||
|
get_authenticated_user().username)
|
||||||
|
repo = model.create_repository(namespace, repository,
|
||||||
|
get_authenticated_user())
|
||||||
|
|
||||||
new_repo_images = {desc['id']: desc for desc in image_descriptions}
|
new_repo_images = {desc['id']: desc for desc in image_descriptions}
|
||||||
added_images = dict(new_repo_images)
|
added_images = dict(new_repo_images)
|
||||||
|
@ -114,10 +143,13 @@ def create_repository(namespace, repository):
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/repositories/<path:repository>/images', methods=['PUT'])
|
@app.route('/v1/repositories/<path:repository>/images', methods=['PUT'])
|
||||||
@requires_auth
|
@process_auth
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
@generate_headers(access='write')
|
@generate_headers(access='write')
|
||||||
def update_images(namespace, repository):
|
def update_images(namespace, repository):
|
||||||
|
permission = ModifyRepositoryPermission(namespace, repository)
|
||||||
|
|
||||||
|
if permission.can():
|
||||||
image_with_checksums = json.loads(request.data)
|
image_with_checksums = json.loads(request.data)
|
||||||
|
|
||||||
for image in image_with_checksums:
|
for image in image_with_checksums:
|
||||||
|
@ -125,20 +157,19 @@ def update_images(namespace, repository):
|
||||||
|
|
||||||
return make_response('Updated', 204)
|
return make_response('Updated', 204)
|
||||||
|
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/repositories/<path:repository>/images', methods=['GET'])
|
@app.route('/v1/repositories/<path:repository>/images', methods=['GET'])
|
||||||
@requires_auth
|
@process_auth
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
@generate_headers(access='read')
|
@generate_headers(access='read')
|
||||||
def get_repository_images(namespace, repository):
|
def get_repository_images(namespace, repository):
|
||||||
if validated_token and (validated_token.repository.name != repository or
|
permission = ReadRepositoryPermission(namespace, repository)
|
||||||
validated_token.repository.namespace != namespace):
|
|
||||||
return make_response('Forbidden', 403)
|
|
||||||
|
|
||||||
#TODO check user has permission for repository
|
# TODO invalidate token?
|
||||||
|
|
||||||
#TODO invalidate token
|
|
||||||
|
|
||||||
|
if permission.can():
|
||||||
all_images = []
|
all_images = []
|
||||||
for image in model.get_repository_images(namespace, repository):
|
for image in model.get_repository_images(namespace, repository):
|
||||||
new_image_view = {
|
new_image_view = {
|
||||||
|
@ -151,13 +182,13 @@ def get_repository_images(namespace, repository):
|
||||||
resp = make_response(json.dumps(all_images), 200)
|
resp = make_response(json.dumps(all_images), 200)
|
||||||
resp.mimetype = 'application/json'
|
resp.mimetype = 'application/json'
|
||||||
|
|
||||||
repo = model.create_or_fetch_repository(namespace, repository)
|
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/repositories/<path:repository>/images', methods=['DELETE'])
|
@app.route('/v1/repositories/<path:repository>/images', methods=['DELETE'])
|
||||||
@requires_auth
|
@process_auth
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
@generate_headers(access='delete')
|
@generate_headers(access='delete')
|
||||||
def delete_repository_images(namespace, repository):
|
def delete_repository_images(namespace, repository):
|
||||||
|
|
57
model.py
57
model.py
|
@ -1,6 +1,11 @@
|
||||||
import bcrypt
|
import bcrypt
|
||||||
|
import logging
|
||||||
|
|
||||||
from database import User, Repository, Image, RepositoryImage, AccessToken
|
from database import (User, Repository, Image, RepositoryImage, AccessToken,
|
||||||
|
RepositoryPermission, Visibility, Role)
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def create_user(username, password, email):
|
def create_user(username, password, email):
|
||||||
|
@ -23,6 +28,13 @@ def verify_user(username, password):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def verify_token(code):
|
||||||
|
try:
|
||||||
|
return AccessToken.get(code=code)
|
||||||
|
except AccessToken.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def change_password(user, new_password):
|
def change_password(user, new_password):
|
||||||
pw_hash = bcrypt.hashpw(new_password, bcrypt.gensalt())
|
pw_hash = bcrypt.hashpw(new_password, bcrypt.gensalt())
|
||||||
user.password_hash = pw_hash
|
user.password_hash = pw_hash
|
||||||
|
@ -35,12 +47,27 @@ def update_email(user, new_email):
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
|
|
||||||
def create_or_fetch_repository(namespace, name):
|
def get_all_repo_permissions(user):
|
||||||
|
select = User.select(User, Repository, RepositoryPermission)
|
||||||
|
joined = select.join(RepositoryPermission).join(Repository)
|
||||||
|
return joined.where(User.username == user.username)
|
||||||
|
|
||||||
|
|
||||||
|
def get_repository(namespace, name):
|
||||||
try:
|
try:
|
||||||
repo = Repository.get(Repository.name == name and
|
return Repository.get(Repository.name == name and
|
||||||
Repository.namespace == namespace)
|
Repository.namespace == namespace)
|
||||||
except Repository.DoesNotExist:
|
except Repository.DoesNotExist:
|
||||||
repo = Repository.create(namespace=namespace, name=name)
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def create_repository(namespace, name, owner):
|
||||||
|
private = Visibility.get(name='private')
|
||||||
|
repo = Repository.create(namespace=namespace, name=name,
|
||||||
|
visibility=private)
|
||||||
|
admin = Role.get(name='admin')
|
||||||
|
permission = RepositoryPermission.create(user=owner, repository=repo,
|
||||||
|
role=admin)
|
||||||
return repo
|
return repo
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,22 +96,12 @@ def get_repository_images(namespace_name, repository_name):
|
||||||
Repository.namespace == namespace_name)
|
Repository.namespace == namespace_name)
|
||||||
|
|
||||||
|
|
||||||
def create_access_token(repository):
|
def create_access_token(repository, user):
|
||||||
new_token = AccessToken.create(repository=repository)
|
new_token = AccessToken.create(user=user, repository=repository)
|
||||||
return new_token
|
return new_token
|
||||||
|
|
||||||
|
|
||||||
def verify_token(namespace_name, repository_name, code):
|
def get_user_repo_permissions(user, repository):
|
||||||
try:
|
select = RepositoryPermission.select()
|
||||||
fetched_repo = Repository.get(Repository.namespace == namespace_name and
|
return select.where(RepositoryPermission.user == user and
|
||||||
Repository.name == repository_name)
|
RepositoryPermission.repository == repository)
|
||||||
except Repository.DoesNotExist:
|
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
|
||||||
fetched = AccessToken.get(AccessToken.code == code and
|
|
||||||
AccessToken.repository == fetched_repo)
|
|
||||||
except AccessToken.DoesNotExist:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return fetched
|
|
||||||
|
|
60
permissions.py
Normal file
60
permissions.py
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from flask.ext.principal import identity_loaded, UserNeed, Permission
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
import model
|
||||||
|
|
||||||
|
from app import app
|
||||||
|
from auth import get_authenticated_user, get_validated_token
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
_RepositoryNeed = namedtuple('repository', ['namespace', 'name', 'role'])
|
||||||
|
|
||||||
|
|
||||||
|
class ModifyRepositoryPermission(Permission):
|
||||||
|
def __init__(self, namespace, name):
|
||||||
|
admin_need = _RepositoryNeed(namespace, name, 'admin')
|
||||||
|
write_need = _RepositoryNeed(namespace, name, 'write')
|
||||||
|
super(ModifyRepositoryPermission, self).__init__(admin_need, write_need)
|
||||||
|
|
||||||
|
|
||||||
|
class ReadRepositoryPermission(Permission):
|
||||||
|
def __init__(self, namespace, name):
|
||||||
|
admin_need = _RepositoryNeed(namespace, name, 'admin')
|
||||||
|
write_need = _RepositoryNeed(namespace, name, 'write')
|
||||||
|
read_need = _RepositoryNeed(namespace, name, 'read')
|
||||||
|
super(ReadRepositoryPermission, self).__init__(admin_need, write_need)
|
||||||
|
|
||||||
|
|
||||||
|
class UserPermission(Permission):
|
||||||
|
def __init__(self, username):
|
||||||
|
user_need = UserNeed(username)
|
||||||
|
super(UserPermission, self).__init__(user_need)
|
||||||
|
|
||||||
|
|
||||||
|
@identity_loaded.connect_via(app)
|
||||||
|
def on_identity_loaded(sender, identity):
|
||||||
|
# We have verified an identity, load in all of the permissions
|
||||||
|
if get_authenticated_user():
|
||||||
|
identity.provides.add(UserNeed(get_authenticated_user().username))
|
||||||
|
|
||||||
|
for user in model.get_all_repo_permissions(get_authenticated_user()):
|
||||||
|
grant = _RepositoryNeed(user.repositorypermission.repository.namespace,
|
||||||
|
user.repositorypermission.repository.name,
|
||||||
|
user.repositorypermission.role.name)
|
||||||
|
logger.debug('User added permission: {0}'.format(grant))
|
||||||
|
identity.provides.add(grant)
|
||||||
|
|
||||||
|
if get_validated_token():
|
||||||
|
query = model.get_user_repo_permissions(get_validated_token().user,
|
||||||
|
get_validated_token().repository)
|
||||||
|
for permission in query:
|
||||||
|
t_grant = _RepositoryNeed(get_validated_token().repository.namespace,
|
||||||
|
get_validated_token().repository.name,
|
||||||
|
permission.role.name)
|
||||||
|
logger.debug('Token added permission: {0}'.format(t_grant))
|
||||||
|
identity.provides.add(t_grant)
|
|
@ -1,3 +1,4 @@
|
||||||
peewee
|
peewee
|
||||||
flask
|
flask
|
||||||
py-bcrypt
|
py-bcrypt
|
||||||
|
Flask-Principal
|
3
wsgi.py
3
wsgi.py
|
@ -5,7 +5,8 @@ from app import app
|
||||||
import index
|
import index
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
FORMAT = '%(asctime)-15s - %(levelname)s - %(pathname)s - %(funcName)s - %(message)s'
|
FORMAT = '%(asctime)-15s - %(levelname)s - %(pathname)s - ' + \
|
||||||
|
'%(funcName)s - %(message)s'
|
||||||
logging.basicConfig(format=FORMAT, level=logging.DEBUG)
|
logging.basicConfig(format=FORMAT, level=logging.DEBUG)
|
||||||
|
|
||||||
app.run(port=5002, debug=True)
|
app.run(port=5002, debug=True)
|
||||||
|
|
Reference in a new issue