Skip to content

Commit

Permalink
split service accounts to global and project + add static tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
rmb938 committed May 7, 2018
1 parent 67bf48b commit d0d2104
Show file tree
Hide file tree
Showing 21 changed files with 627 additions and 315 deletions.
33 changes: 0 additions & 33 deletions deli/counter/auth/driver.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import json
import logging
from abc import ABCMeta, abstractmethod
from typing import Dict

from cryptography.fernet import Fernet
from simple_settings import settings

from deli.counter.http.router import SandwichRouter
from deli.kubernetes.resources.v1alpha1.role.model import GlobalRole


class AuthDriver(object):
Expand All @@ -28,31 +23,3 @@ def auth_router(self) -> SandwichRouter:
@abstractmethod
def health(self):
return None

def generate_user_token(self, expires_at, username, global_role_names, project=None, project_role_ids=None):
fernet = Fernet(settings.AUTH_FERNET_KEYS[0])

global_role_ids = []

for role_name in global_role_names:
role = GlobalRole.get_by_name(role_name)
if role is not None:
global_role_ids.append(role.id)

token_data = {
'expires_at': expires_at,
'user': {
'name': username,
'driver': self.name
},
'roles': {
'global': global_role_ids,
'project': []
}
}

if project is not None:
token_data['project'] = project.id
token_data['roles']['project'] = project_role_ids if project_role_ids is not None else []

return fernet.encrypt(json.dumps(token_data).encode())
13 changes: 7 additions & 6 deletions deli/counter/auth/drivers/database/router.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import arrow
import cherrypy
from ingredients_http.request_methods import RequestMethods
from ingredients_http.route import Route

from deli.counter.auth.drivers.database.models.user import User, UserRole
from deli.counter.auth.token import Token
from deli.counter.auth.validation_models.database import RequestDatabaseLogin, RequestDatabaseCreateUser, \
ResponseDatabaseUser, ParamsDatabaseUser, ParamsListDatabaseUser, RequestDatabaseChangePassword, \
RequestDatabaseUserRole
Expand All @@ -28,13 +28,14 @@ def login(self):
if user is None or user.password != request.password:
raise cherrypy.HTTPError(403, "Invalid username or password")

expiry = arrow.now().shift(days=+1)
token = self.driver.generate_user_token(expiry, user.username, [role.role for role in user.roles])
session.commit()
token = Token()
token.driver_name = self.driver.name
token.username = user.username
token.set_global_roles([role.role for role in user.roles])

response = ResponseOAuthToken()
response.access_token = token
response.expiry = expiry
response.access_token = token.marshal(self.mount.fernet)
response.expiry = token.expires_at
return response

@Route(route='users', methods=[RequestMethods.POST])
Expand Down
13 changes: 7 additions & 6 deletions deli/counter/auth/drivers/github/router.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import arrow
import cherrypy
import github
import github.AuthenticatedUser
Expand All @@ -10,6 +9,7 @@
from simple_settings import settings
from sqlalchemy_utils.types.json import json

from deli.counter.auth.token import Token
from deli.counter.auth.validation_models.github import RequestGithubAuthorization, RequestGithubToken
from deli.counter.http.mounts.root.routes.v1.auth.validation_models.tokens import ResponseOAuthToken
from deli.counter.http.router import SandwichRouter
Expand All @@ -25,12 +25,13 @@ def generate_token(self, token_github_client):
if self.driver.check_in_org(github_user) is False:
raise cherrypy.HTTPError(403, "User not a member of GitHub organization: '" + settings.GITHUB_ORG + "'")

expiry = arrow.now().shift(days=+1)
token = self.driver.generate_user_token(expiry, github_user.login, self.driver.find_roles(github_user))

token = Token()
token.driver_name = self.driver.name
token.username = github_user.login
token.set_global_roles(self.driver.find_roles(github_user))
response = ResponseOAuthToken()
response.access_token = token
response.expiry = expiry
response.access_token = token.marshal(self.mount.fernet)
response.expiry = token.expires_at
return response

@Route(route='authorization', methods=[RequestMethods.POST])
Expand Down
83 changes: 24 additions & 59 deletions deli/counter/auth/manager.py
Original file line number Diff line number Diff line change
@@ -1,72 +1,37 @@
import importlib
import logging

import cherrypy
from kubernetes.client.rest import ApiException
from simple_settings import settings

from deli.counter.auth.driver import AuthDriver
from deli.kubernetes.resources.project import Project
from deli.kubernetes.resources.v1alpha1.role.model import GlobalRole, ProjectRole

DRIVERS = {}

class AuthManager(object):
def __init__(self):
self.logger = logging.getLogger("%s.%s" % (self.__module__, self.__class__.__name__))
self.drivers = {}

def load_drivers(self):
for driver_string in settings.AUTH_DRIVERS:
if ':' not in driver_string:
raise ValueError("AUTH_DRIVER does not contain a module and class. "
"Must be in the following format: 'my.module:MyClass'")
def load_drivers():
logger = logging.getLogger("%s.%s" % (load_drivers.__module__, load_drivers.__name__))
for driver_string in settings.AUTH_DRIVERS:
if ':' not in driver_string:
raise ValueError("AUTH_DRIVER does not contain a module and class. "
"Must be in the following format: 'my.module:MyClass'")

auth_module, auth_class, *_ = driver_string.split(":")
try:
auth_module = importlib.import_module(auth_module)
except ImportError:
self.logger.exception("Could not import auth driver's module: " + auth_module)
raise
try:
driver_klass = getattr(auth_module, auth_class)
except AttributeError:
self.logger.exception("Could not get driver's module class: " + auth_class)
raise
auth_module, auth_class, *_ = driver_string.split(":")
try:
auth_module = importlib.import_module(auth_module)
except ImportError:
logger.exception("Could not import auth driver's module: " + auth_module)
raise
try:
driver_klass = getattr(auth_module, auth_class)
except AttributeError:
logger.exception("Could not get driver's module class: " + auth_class)
raise

if not issubclass(driver_klass, AuthDriver):
raise ValueError("AUTH_DRIVER class is not a subclass of '" + AuthDriver.__module__ + ".AuthDriver'")
if not issubclass(driver_klass, AuthDriver):
raise ValueError("AUTH_DRIVER class is not a subclass of '" + AuthDriver.__module__ + ".AuthDriver'")

driver: AuthDriver = driver_klass()
self.drivers[driver.name] = driver
driver: AuthDriver = driver_klass()
DRIVERS[driver.name] = driver

if len(self.drivers) == 0:
raise ValueError("No auth drivers loaded")

def enforce_policy(self, policy_name, token_data: dict, project: Project):

# Check project first because it probably will have less things
# to loop through
if project is not None:
for role_id in token_data['roles']['project']:
try:
role: ProjectRole = ProjectRole.get(project, role_id)
if role is None:
continue
if policy_name in role.policies:
return
except ApiException as e:
if e.status != 404:
raise

for role_id in token_data['roles']['global']:
try:
role: GlobalRole = GlobalRole.get(role_id)
if role is None:
continue
if policy_name in role.policies:
return
except ApiException as e:
if e.status != 404:
raise

raise cherrypy.HTTPError(403, "Insufficient permissions to perform the requested action.")
if len(DRIVERS) == 0:
raise ValueError("No auth drivers loaded")
65 changes: 55 additions & 10 deletions deli/counter/auth/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,43 +461,88 @@

# Service Accounts
{
"name": "service_accounts:create",
"description": "Ability to create a service account",
"name": "service_accounts:global:create",
"description": "Ability to create a global service account"
},
{
"name": "service_accounts:global:get",
"description": "Ability to get a global service account"
},
{
"name": "service_accounts:global:list",
"description": "Ability to list global service accounts"
},
{
"name": "service_accounts:global:update",
"description": "Ability to update a global service account"
},

{
"name": "service_accounts:global:delete",
"description": "Ability to delete a global service account"
},
{
"name": "service_accounts:global:key:create",
"description": "Ability to create keys for global service accounts"
},
{
"name": "service_accounts:global:key:delete",
"description": "Ability to delete keys from global service accounts"
},
{
"name": "service_accounts:project:create",
"description": "Ability to create a project service account",
"tags": [
"project",
"default_project_member"
]
},
{
"name": "service_accounts:get",
"description": "Ability to get a service account",
"name": "service_accounts:project:get",
"description": "Ability to get a project service account",
"tags": [
"project",
"default_project_member",
"default_service_account"
]
},
{
"name": "service_accounts:list",
"description": "Ability to list service accounts",
"name": "service_accounts:project:list",
"description": "Ability to list project service accounts",
"tags": [
"project",
"default_project_member",
"default_service_account"
]
},
{
"name": "service_accounts:update",
"description": "Ability to update a service account",
"name": "service_accounts:project:update",
"description": "Ability to update a project service account",
"tags": [
"project",
"default_project_member"
]
},

{
"name": "service_accounts:delete",
"description": "Ability to delete a service account",
"name": "service_accounts:project:delete",
"description": "Ability to delete a project service account",
"tags": [
"project",
"default_project_member"
]
},
{
"name": "service_accounts:project:key:create",
"description": "Ability to create keys for project service accounts",
"tags": [
"project",
"default_project_member"
]
},
{
"name": "service_accounts:project:key:delete",
"description": "Ability to delete keys from project service accounts",
"tags": [
"project",
"default_project_member"
Expand Down
Loading

0 comments on commit d0d2104

Please sign in to comment.