Merge branch 'master' into guardian
This commit is contained in:
commit
feb80049aa
|
@ -1,5 +1,5 @@
|
||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 0.6.9-beta
|
current_version = 0.6.11-beta
|
||||||
tag = True
|
tag = True
|
||||||
commit = True
|
commit = True
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)
|
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)
|
||||||
|
|
|
@ -96,7 +96,7 @@ build-passbook-server:
|
||||||
before_script:
|
before_script:
|
||||||
- echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
|
- echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
|
||||||
script:
|
script:
|
||||||
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.beryju.org/passbook/server:latest --destination docker.beryju.org/passbook/server:0.6.9-beta
|
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.beryju.org/passbook/server:latest --destination docker.beryju.org/passbook/server:0.6.11-beta
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
- /^version/.*$/
|
- /^version/.*$/
|
||||||
|
@ -108,7 +108,7 @@ build-passbook-static:
|
||||||
before_script:
|
before_script:
|
||||||
- echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
|
- echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
|
||||||
script:
|
script:
|
||||||
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/static.Dockerfile --destination docker.beryju.org/passbook/static:latest --destination docker.beryju.org/passbook/static:0.6.9-beta
|
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/static.Dockerfile --destination docker.beryju.org/passbook/static:latest --destination docker.beryju.org/passbook/static:0.6.11-beta
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
- /^version/.*$/
|
- /^version/.*$/
|
||||||
|
@ -145,8 +145,8 @@ notify-sentry:
|
||||||
before_script:
|
before_script:
|
||||||
- apk add curl
|
- apk add curl
|
||||||
script:
|
script:
|
||||||
- sentry-cli releases new passbook@0.6.9-beta
|
- sentry-cli releases new passbook@0.6.11-beta
|
||||||
- sentry-cli releases set-commits --auto passbook@0.6.9-beta
|
- sentry-cli releases set-commits --auto passbook@0.6.11-beta
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
- /^version/.*$/
|
- /^version/.*$/
|
||||||
|
|
|
@ -16,5 +16,8 @@ COPY --from=locker /app/requirements-dev.txt /app/
|
||||||
|
|
||||||
WORKDIR /app/
|
WORKDIR /app/
|
||||||
|
|
||||||
RUN pip install -r requirements.txt --no-cache-dir && \
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends postgresql-client-11 && \
|
||||||
|
rm -rf /var/lib/apt/ && \
|
||||||
|
pip install -r requirements.txt --no-cache-dir && \
|
||||||
adduser --system --no-create-home --uid 1000 --group --home /app passbook
|
adduser --system --no-create-home --uid 1000 --group --home /app passbook
|
||||||
|
|
|
@ -39,7 +39,7 @@ http {
|
||||||
gzip on;
|
gzip on;
|
||||||
gzip_types application/javascript image/* text/css;
|
gzip_types application/javascript image/* text/css;
|
||||||
gunzip on;
|
gunzip on;
|
||||||
add_header X-passbook-Version 0.6.9-beta;
|
add_header X-passbook-Version 0.6.11-beta;
|
||||||
add_header Vary X-passbook-Version;
|
add_header Vary X-passbook-Version;
|
||||||
root /data/;
|
root /data/;
|
||||||
|
|
||||||
|
|
|
@ -8,3 +8,4 @@ threads = 2
|
||||||
enable-threads = true
|
enable-threads = true
|
||||||
uid = passbook
|
uid = passbook
|
||||||
gid = passbook
|
gid = passbook
|
||||||
|
disable-logging=True
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
appVersion: "0.6.9-beta"
|
appVersion: "0.6.11-beta"
|
||||||
description: A Helm chart for passbook.
|
description: A Helm chart for passbook.
|
||||||
name: passbook
|
name: passbook
|
||||||
version: "0.6.9-beta"
|
version: "0.6.11-beta"
|
||||||
icon: https://git.beryju.org/uploads/-/system/project/avatar/108/logo.png
|
icon: https://git.beryju.org/uploads/-/system/project/avatar/108/logo.png
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
- name: postgresql
|
- name: postgresql
|
||||||
version: 4.2.2
|
version: 6.3.13
|
||||||
repository: https://kubernetes-charts.storage.googleapis.com/
|
repository: https://kubernetes-charts.storage.googleapis.com/
|
||||||
- name: redis
|
- name: redis
|
||||||
version: 9.2.1
|
version: 9.2.1
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# This is a YAML-formatted file.
|
# This is a YAML-formatted file.
|
||||||
# Declare variables to be passed into your templates.
|
# Declare variables to be passed into your templates.
|
||||||
image:
|
image:
|
||||||
tag: 0.6.9-beta
|
tag: 0.6.11-beta
|
||||||
|
|
||||||
nameOverride: ""
|
nameOverride: ""
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
"""passbook"""
|
"""passbook"""
|
||||||
__version__ = '0.6.9-beta'
|
__version__ = '0.6.11-beta'
|
||||||
|
|
|
@ -122,8 +122,9 @@ class PolicyTestView(LoginRequiredMixin, DetailView, PermissionRequiredMixin, Fo
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
policy = self.get_object()
|
policy = self.get_object()
|
||||||
user = form.cleaned_data.get('user')
|
user = form.cleaned_data.get('user')
|
||||||
policy_engine = PolicyEngine([policy])
|
policy_engine = PolicyEngine([policy], user, self.request)
|
||||||
policy_engine.for_user(user).with_request(self.request).build()
|
policy_engine.use_cache = False
|
||||||
|
policy_engine.build()
|
||||||
result = policy_engine.passing
|
result = policy_engine.passing
|
||||||
if result:
|
if result:
|
||||||
messages.success(self.request, _('User successfully passed policy.'))
|
messages.success(self.request, _('User successfully passed policy.'))
|
||||||
|
|
|
@ -17,8 +17,8 @@ def user_factors(context: RequestContext) -> List[UserSettings]:
|
||||||
matching_factors: List[UserSettings] = []
|
matching_factors: List[UserSettings] = []
|
||||||
for factor in _all_factors:
|
for factor in _all_factors:
|
||||||
user_settings = factor.user_settings()
|
user_settings = factor.user_settings()
|
||||||
policy_engine = PolicyEngine(factor.policies.all())
|
policy_engine = PolicyEngine(factor.policies.all(), user, context.get('request'))
|
||||||
policy_engine.for_user(user).with_request(context.get('request')).build()
|
policy_engine.build()
|
||||||
if policy_engine.passing and user_settings:
|
if policy_engine.passing and user_settings:
|
||||||
matching_factors.append(user_settings)
|
matching_factors.append(user_settings)
|
||||||
return matching_factors
|
return matching_factors
|
||||||
|
@ -31,8 +31,8 @@ def user_sources(context: RequestContext) -> List[UserSettings]:
|
||||||
matching_sources: List[UserSettings] = []
|
matching_sources: List[UserSettings] = []
|
||||||
for factor in _all_sources:
|
for factor in _all_sources:
|
||||||
user_settings = factor.user_settings()
|
user_settings = factor.user_settings()
|
||||||
policy_engine = PolicyEngine(factor.policies.all())
|
policy_engine = PolicyEngine(factor.policies.all(), user, context.get('request'))
|
||||||
policy_engine.for_user(user).with_request(context.get('request')).build()
|
policy_engine.build()
|
||||||
if policy_engine.passing and user_settings:
|
if policy_engine.passing and user_settings:
|
||||||
matching_sources.append(user_settings)
|
matching_sources.append(user_settings)
|
||||||
return matching_sources
|
return matching_sources
|
||||||
|
|
|
@ -31,6 +31,6 @@ class AccessMixin:
|
||||||
def user_has_access(self, application: Application, user: User) -> Tuple[bool, List[str]]:
|
def user_has_access(self, application: Application, user: User) -> Tuple[bool, List[str]]:
|
||||||
"""Check if user has access to application."""
|
"""Check if user has access to application."""
|
||||||
LOGGER.debug("Checking permissions", user=user, application=application)
|
LOGGER.debug("Checking permissions", user=user, application=application)
|
||||||
policy_engine = PolicyEngine(application.policies.all())
|
policy_engine = PolicyEngine(application.policies.all(), user, self.request)
|
||||||
policy_engine.for_user(user).with_request(self.request).build()
|
policy_engine.build()
|
||||||
return policy_engine.result
|
return policy_engine.result
|
||||||
|
|
|
@ -16,8 +16,7 @@ class OverviewView(LoginRequiredMixin, TemplateView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs['applications'] = []
|
kwargs['applications'] = []
|
||||||
for application in Application.objects.all():
|
for application in Application.objects.all():
|
||||||
engine = PolicyEngine(application.policies.all())
|
engine = PolicyEngine(application.policies.all(), self.request.user, self.request)
|
||||||
engine.for_user(self.request.user).with_request(self.request)
|
|
||||||
engine.build()
|
engine.build()
|
||||||
if engine.passing:
|
if engine.passing:
|
||||||
kwargs['applications'].append(application)
|
kwargs['applications'].append(application)
|
||||||
|
|
|
@ -13,8 +13,8 @@ def password_policy_checker(sender, password, **_):
|
||||||
setattr(sender, '__password__', password)
|
setattr(sender, '__password__', password)
|
||||||
_all_factors = PasswordFactor.objects.filter(enabled=True).order_by('order')
|
_all_factors = PasswordFactor.objects.filter(enabled=True).order_by('order')
|
||||||
for factor in _all_factors:
|
for factor in _all_factors:
|
||||||
policy_engine = PolicyEngine(factor.password_policies.all().select_subclasses())
|
policy_engine = PolicyEngine(factor.password_policies.all().select_subclasses(), sender)
|
||||||
policy_engine.for_user(sender).build()
|
policy_engine.build()
|
||||||
passing, messages = policy_engine.result
|
passing, messages = policy_engine.result
|
||||||
if not passing:
|
if not passing:
|
||||||
raise PasswordPolicyInvalid(*messages)
|
raise PasswordPolicyInvalid(*messages)
|
||||||
|
|
|
@ -67,8 +67,8 @@ class AuthenticationView(UserPassesTestMixin, View):
|
||||||
for factor in _all_factors:
|
for factor in _all_factors:
|
||||||
LOGGER.debug("Checking if factor applies to user",
|
LOGGER.debug("Checking if factor applies to user",
|
||||||
factor=factor, user=self.pending_user)
|
factor=factor, user=self.pending_user)
|
||||||
policy_engine = PolicyEngine(factor.policies.all())
|
policy_engine = PolicyEngine(factor.policies.all(), self.pending_user, self.request)
|
||||||
policy_engine.for_user(self.pending_user).with_request(self.request).build()
|
policy_engine.build()
|
||||||
if policy_engine.passing:
|
if policy_engine.passing:
|
||||||
pending_factors.append((factor.uuid.hex, factor.type))
|
pending_factors.append((factor.uuid.hex, factor.type))
|
||||||
LOGGER.debug("Factor applies", factor=factor, user=self.pending_user)
|
LOGGER.debug("Factor applies", factor=factor, user=self.pending_user)
|
||||||
|
|
|
@ -31,6 +31,7 @@ class PolicyProcessInfo:
|
||||||
class PolicyEngine:
|
class PolicyEngine:
|
||||||
"""Orchestrate policy checking, launch tasks and return result"""
|
"""Orchestrate policy checking, launch tasks and return result"""
|
||||||
|
|
||||||
|
use_cache: bool = True
|
||||||
policies: List[Policy] = []
|
policies: List[Policy] = []
|
||||||
__request: HttpRequest
|
__request: HttpRequest
|
||||||
__user: User
|
__user: User
|
||||||
|
@ -43,16 +44,6 @@ class PolicyEngine:
|
||||||
self.__user = user
|
self.__user = user
|
||||||
self.__processes = []
|
self.__processes = []
|
||||||
|
|
||||||
def for_user(self, user: User) -> 'PolicyEngine':
|
|
||||||
"""Check policies for user"""
|
|
||||||
self.__user = user
|
|
||||||
return self
|
|
||||||
|
|
||||||
def with_request(self, request: HttpRequest) -> 'PolicyEngine':
|
|
||||||
"""Set request"""
|
|
||||||
self.__request = request
|
|
||||||
return self
|
|
||||||
|
|
||||||
def _select_subclasses(self) -> List[Policy]:
|
def _select_subclasses(self) -> List[Policy]:
|
||||||
"""Make sure all Policies are their respective classes"""
|
"""Make sure all Policies are their respective classes"""
|
||||||
return Policy.objects \
|
return Policy.objects \
|
||||||
|
@ -69,14 +60,14 @@ class PolicyEngine:
|
||||||
request.http_request = self.__request
|
request.http_request = self.__request
|
||||||
for policy in self._select_subclasses():
|
for policy in self._select_subclasses():
|
||||||
cached_policy = cache.get(cache_key(policy, self.__user), None)
|
cached_policy = cache.get(cache_key(policy, self.__user), None)
|
||||||
if cached_policy:
|
if cached_policy and self.use_cache:
|
||||||
LOGGER.debug("Taking result from cache", policy=policy)
|
LOGGER.debug("Taking result from cache", policy=policy)
|
||||||
cached_policies.append(cached_policy)
|
cached_policies.append(cached_policy)
|
||||||
else:
|
else:
|
||||||
LOGGER.debug("Evaluating policy", policy=policy)
|
LOGGER.debug("Evaluating policy", policy=policy)
|
||||||
our_end, task_end = Pipe(False)
|
our_end, task_end = Pipe(False)
|
||||||
task = PolicyProcess(policy, request, task_end)
|
task = PolicyProcess(policy, request, task_end)
|
||||||
LOGGER.debug("Starting Process", for_policy=policy)
|
LOGGER.debug("Starting Process", policy=policy)
|
||||||
task.start()
|
task.start()
|
||||||
self.__processes.append(PolicyProcessInfo(process=task,
|
self.__processes.append(PolicyProcessInfo(process=task,
|
||||||
connection=our_end, policy=policy))
|
connection=our_end, policy=policy))
|
||||||
|
|
|
@ -42,7 +42,7 @@ class PolicyProcess(Process):
|
||||||
if self.policy.negate:
|
if self.policy.negate:
|
||||||
policy_result.passing = not policy_result.passing
|
policy_result.passing = not policy_result.passing
|
||||||
LOGGER.debug("Got result", policy=self.policy, result=policy_result,
|
LOGGER.debug("Got result", policy=self.policy, result=policy_result,
|
||||||
process="PolicyProcess")
|
process="PolicyProcess", passing=policy_result.passing, user=self.request.user)
|
||||||
key = cache_key(self.policy, self.request.user)
|
key = cache_key(self.policy, self.request.user)
|
||||||
cache.set(key, policy_result)
|
cache.set(key, policy_result)
|
||||||
LOGGER.debug("Cached policy evaluation", key=key)
|
LOGGER.debug("Cached policy evaluation", key=key)
|
||||||
|
|
|
@ -18,8 +18,8 @@ def check_permissions(request, user, client):
|
||||||
except Application.DoesNotExist:
|
except Application.DoesNotExist:
|
||||||
return redirect('passbook_providers_oauth:oauth2-permission-denied')
|
return redirect('passbook_providers_oauth:oauth2-permission-denied')
|
||||||
LOGGER.debug("Checking permissions for application", user=user, application=application)
|
LOGGER.debug("Checking permissions for application", user=user, application=application)
|
||||||
policy_engine = PolicyEngine(application.policies.all())
|
policy_engine = PolicyEngine(application.policies.all(), user, request)
|
||||||
policy_engine.for_user(user).with_request(request).build()
|
policy_engine.build()
|
||||||
|
|
||||||
# Check permissions
|
# Check permissions
|
||||||
passing, policy_messages = policy_engine.result
|
passing, policy_messages = policy_engine.result
|
||||||
|
|
|
@ -59,8 +59,9 @@ class AccessRequiredView(AccessMixin, View):
|
||||||
|
|
||||||
def _has_access(self):
|
def _has_access(self):
|
||||||
"""Check if user has access to application"""
|
"""Check if user has access to application"""
|
||||||
policy_engine = PolicyEngine(self.provider.application.policies.all())
|
policy_engine = PolicyEngine(self.provider.application.policies.all(),
|
||||||
policy_engine.for_user(self.request.user).with_request(self.request).build()
|
self.request.user, self.request)
|
||||||
|
policy_engine.build()
|
||||||
return policy_engine.passing
|
return policy_engine.passing
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
|
|
@ -292,7 +292,7 @@ with CONFIG.cd('log'):
|
||||||
'formatters': {
|
'formatters': {
|
||||||
"plain": {
|
"plain": {
|
||||||
"()": structlog.stdlib.ProcessorFormatter,
|
"()": structlog.stdlib.ProcessorFormatter,
|
||||||
"processor": structlog.processors.JSONRenderer(),
|
"processor": structlog.processors.JSONRenderer(sort_keys=True),
|
||||||
"foreign_pre_chain": LOG_PRE_CHAIN,
|
"foreign_pre_chain": LOG_PRE_CHAIN,
|
||||||
},
|
},
|
||||||
"colored": {
|
"colored": {
|
||||||
|
|
|
@ -14,14 +14,12 @@ from structlog import get_logger
|
||||||
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "passbook.root.settings")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "passbook.root.settings")
|
||||||
|
|
||||||
LOGGER = get_logger()
|
|
||||||
|
|
||||||
|
|
||||||
class WSGILogger:
|
class WSGILogger:
|
||||||
""" This is the generalized WSGI middleware for any style request logging. """
|
""" This is the generalized WSGI middleware for any style request logging. """
|
||||||
|
|
||||||
def __init__(self, application):
|
def __init__(self, application):
|
||||||
self.application = application
|
self.application = application
|
||||||
|
self.logger = get_logger('passbook.wsgi')
|
||||||
|
|
||||||
def __healthcheck(self, start_response):
|
def __healthcheck(self, start_response):
|
||||||
start_response('204 OK', [])
|
start_response('204 OK', [])
|
||||||
|
@ -64,7 +62,7 @@ class WSGILogger:
|
||||||
query_string = ''
|
query_string = ''
|
||||||
if environ.get('QUERY_STRING') != '':
|
if environ.get('QUERY_STRING') != '':
|
||||||
query_string = f"?{environ.get('QUERY_STRING')}"
|
query_string = f"?{environ.get('QUERY_STRING')}"
|
||||||
LOGGER.info(f"{environ.get('PATH_INFO', '')}{query_string}",
|
self.logger.info(f"{environ.get('PATH_INFO', '')}{query_string}",
|
||||||
host=host,
|
host=host,
|
||||||
method=environ.get('REQUEST_METHOD', ''),
|
method=environ.get('REQUEST_METHOD', ''),
|
||||||
protocol=environ.get('SERVER_PROTOCOL', ''),
|
protocol=environ.get('SERVER_PROTOCOL', ''),
|
||||||
|
|
Reference in New Issue