Compare commits

..

7 Commits

16 changed files with 48 additions and 45 deletions

View File

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 0.9.0-pre6 current_version = 0.9.0-pre7
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>.*)

View File

@ -16,11 +16,11 @@ jobs:
- name: Building Docker Image - name: Building Docker Image
run: docker build run: docker build
--no-cache --no-cache
-t beryju/passbook:0.9.0-pre6 -t beryju/passbook:0.9.0-pre7
-t beryju/passbook:latest -t beryju/passbook:latest
-f Dockerfile . -f Dockerfile .
- name: Push Docker Container to Registry (versioned) - name: Push Docker Container to Registry (versioned)
run: docker push beryju/passbook:0.9.0-pre6 run: docker push beryju/passbook:0.9.0-pre7
- name: Push Docker Container to Registry (latest) - name: Push Docker Container to Registry (latest)
run: docker push beryju/passbook:latest run: docker push beryju/passbook:latest
build-gatekeeper: build-gatekeeper:
@ -37,11 +37,11 @@ jobs:
cd gatekeeper cd gatekeeper
docker build \ docker build \
--no-cache \ --no-cache \
-t beryju/passbook-gatekeeper:0.9.0-pre6 \ -t beryju/passbook-gatekeeper:0.9.0-pre7 \
-t beryju/passbook-gatekeeper:latest \ -t beryju/passbook-gatekeeper:latest \
-f Dockerfile . -f Dockerfile .
- name: Push Docker Container to Registry (versioned) - name: Push Docker Container to Registry (versioned)
run: docker push beryju/passbook-gatekeeper:0.9.0-pre6 run: docker push beryju/passbook-gatekeeper:0.9.0-pre7
- name: Push Docker Container to Registry (latest) - name: Push Docker Container to Registry (latest)
run: docker push beryju/passbook-gatekeeper:latest run: docker push beryju/passbook-gatekeeper:latest
build-static: build-static:
@ -66,11 +66,11 @@ jobs:
run: docker build run: docker build
--no-cache --no-cache
--network=$(docker network ls | grep github | awk '{print $1}') --network=$(docker network ls | grep github | awk '{print $1}')
-t beryju/passbook-static:0.9.0-pre6 -t beryju/passbook-static:0.9.0-pre7
-t beryju/passbook-static:latest -t beryju/passbook-static:latest
-f static.Dockerfile . -f static.Dockerfile .
- name: Push Docker Container to Registry (versioned) - name: Push Docker Container to Registry (versioned)
run: docker push beryju/passbook-static:0.9.0-pre6 run: docker push beryju/passbook-static:0.9.0-pre7
- name: Push Docker Container to Registry (latest) - name: Push Docker Container to Registry (latest)
run: docker push beryju/passbook-static:latest run: docker push beryju/passbook-static:latest
test-release: test-release:
@ -100,5 +100,5 @@ jobs:
SENTRY_PROJECT: passbook SENTRY_PROJECT: passbook
SENTRY_URL: https://sentry.beryju.org SENTRY_URL: https://sentry.beryju.org
with: with:
tagName: 0.9.0-pre6 tagName: 0.9.0-pre7
environment: production environment: production

View File

@ -27,6 +27,7 @@ config:
enabled: false enabled: false
server_url: "" server_url: ""
secret_token: "" secret_token: ""
verify_server_cert: true
# This Helm chart ships with built-in Prometheus ServiceMonitors and Rules. # This Helm chart ships with built-in Prometheus ServiceMonitors and Rules.
# This requires the CoreOS Prometheus Operator. # This requires the CoreOS Prometheus Operator.

View File

@ -1,6 +1,6 @@
apiVersion: v1 apiVersion: v1
appVersion: "0.9.0-pre6" appVersion: "0.9.0-pre7"
description: A Helm chart for passbook. description: A Helm chart for passbook.
name: passbook name: passbook
version: "0.9.0-pre6" version: "0.9.0-pre7"
icon: https://git.beryju.org/uploads/-/system/project/avatar/108/logo.png icon: https://git.beryju.org/uploads/-/system/project/avatar/108/logo.png

View File

@ -25,3 +25,4 @@ data:
enabled: {{ .Values.config.apm.enabled }} enabled: {{ .Values.config.apm.enabled }}
server_url: "{{ .Values.config.apm.server_url }}" server_url: "{{ .Values.config.apm.server_url }}"
secret_token: "{{ .Values.config.apm.server_token }}" secret_token: "{{ .Values.config.apm.server_token }}"
verify_server_cert: {{ .Values.config.apm.verify_server_cert }}

View File

@ -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.9.0-pre6 tag: 0.9.0-pre7
nameOverride: "" nameOverride: ""
@ -19,6 +19,7 @@ config:
enabled: false enabled: false
server_url: "" server_url: ""
secret_token: "" secret_token: ""
verify_server_cert: true
# This Helm chart ships with built-in Prometheus ServiceMonitors and Rules. # This Helm chart ships with built-in Prometheus ServiceMonitors and Rules.
# This requires the CoreOS Prometheus Operator. # This requires the CoreOS Prometheus Operator.

View File

@ -1,2 +1,2 @@
"""passbook""" """passbook"""
__version__ = "0.9.0-pre6" __version__ = "0.9.0-pre7"

View File

@ -4,6 +4,7 @@ from botocore.client import ClientError
from django.core.exceptions import DisallowedHost, ValidationError from django.core.exceptions import DisallowedHost, ValidationError
from django.db import InternalError, OperationalError, ProgrammingError from django.db import InternalError, OperationalError, ProgrammingError
from django_redis.exceptions import ConnectionInterrupted from django_redis.exceptions import ConnectionInterrupted
from elasticapm.transport.http import TransportException
from redis.exceptions import RedisError from redis.exceptions import RedisError
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from structlog import get_logger from structlog import get_logger
@ -33,6 +34,7 @@ def before_send(event, hint):
OSError, OSError,
RedisError, RedisError,
SentryIgnoredException, SentryIgnoredException,
TransportException,
) )
if "exc_info" in hint: if "exc_info" in hint:
_, exc_value, _ = hint["exc_info"] _, exc_value, _ = hint["exc_info"]

View File

@ -32,6 +32,7 @@ def update_score(request: HttpRequest, username: str, amount: int):
# pylint: disable=unused-argument # pylint: disable=unused-argument
def handle_failed_login(sender, request, credentials, **_): def handle_failed_login(sender, request, credentials, **_):
"""Lower Score for failed loging attempts""" """Lower Score for failed loging attempts"""
if "username" in credentials:
update_score(request, credentials.get("username"), -1) update_score(request, credentials.get("username"), -1)

View File

@ -281,7 +281,7 @@ if not DEBUG and _ERROR_REPORTING:
release="passbook@%s" % __version__, release="passbook@%s" % __version__,
) )
_APM_ENABLED = CONFIG.y("apm.enabled", True) _APM_ENABLED = CONFIG.y("apm.enabled", False)
if _APM_ENABLED: if _APM_ENABLED:
INSTALLED_APPS.append("elasticapm.contrib.django") INSTALLED_APPS.append("elasticapm.contrib.django")
ELASTIC_APM = { ELASTIC_APM = {
@ -290,7 +290,8 @@ if _APM_ENABLED:
"SERVICE_NAME": "passbook", "SERVICE_NAME": "passbook",
"SERVICE_VERSION": __version__, "SERVICE_VERSION": __version__,
"SECRET_TOKEN": CONFIG.y("apm.secret_token", ""), "SECRET_TOKEN": CONFIG.y("apm.secret_token", ""),
"SERVER_URL": CONFIG.y("apm.secret_token", "http://localhost:8200"), "SERVER_URL": CONFIG.y("apm.server_url", "http://localhost:8200"),
"VERIFY_SERVER_CERT": CONFIG.y_bool("apm.verify_server_cert", True),
} }

View File

@ -1,24 +1,23 @@
"""passbook oauth_client Authorization backend""" """passbook oauth_client Authorization backend"""
from django.contrib.auth.backends import ModelBackend from django.contrib.auth.backends import ModelBackend
from django.db.models import Q from django.db.models import Q
from django.http import HttpRequest
from passbook.core.models import User
from passbook.sources.oauth.models import OAuthSource, UserOAuthSourceConnection from passbook.sources.oauth.models import OAuthSource, UserOAuthSourceConnection
class AuthorizedServiceBackend(ModelBackend): class AuthorizedServiceBackend(ModelBackend):
"Authentication backend for users registered with remote OAuth provider." "Authentication backend for users registered with remote OAuth provider."
def authenticate(self, request, source=None, identifier=None): def authenticate(
self, request: HttpRequest, source: OAuthSource, identifier: str
) -> User:
"Fetch user for a given source by id." "Fetch user for a given source by id."
source_q = Q(source__name=source) source_q = Q(source__name=source)
if isinstance(source, OAuthSource): if isinstance(source, OAuthSource):
source_q = Q(source=source) source_q = Q(source=source)
try:
access = UserOAuthSourceConnection.objects.filter( access = UserOAuthSourceConnection.objects.filter(
source_q, identifier=identifier source_q, identifier=identifier
).select_related("user")[0] ).select_related("user")[0]
except IndexError:
return None
else:
return access.user return access.user

View File

@ -89,16 +89,16 @@ class GitHubOAuthSource(OAuthSource):
verbose_name_plural = _("GitHub OAuth Sources") verbose_name_plural = _("GitHub OAuth Sources")
class TwitterOAuthSource(OAuthSource): # class TwitterOAuthSource(OAuthSource):
"""Social Login using Twitter.com""" # """Social Login using Twitter.com"""
form = "passbook.sources.oauth.forms.TwitterOAuthSourceForm" # form = "passbook.sources.oauth.forms.TwitterOAuthSourceForm"
class Meta: # class Meta:
abstract = True # abstract = True
verbose_name = _("Twitter OAuth Source") # verbose_name = _("Twitter OAuth Source")
verbose_name_plural = _("Twitter OAuth Sources") # verbose_name_plural = _("Twitter OAuth Sources")
class FacebookOAuthSource(OAuthSource): class FacebookOAuthSource(OAuthSource):

View File

@ -1,9 +1,5 @@
"""Oauth2 Client Settings""" """Oauth2 Client Settings"""
AUTHENTICATION_BACKENDS = [
"passbook.sources.oauth.backends.AuthorizedServiceBackend",
]
PASSBOOK_SOURCES_OAUTH_TYPES = [ PASSBOOK_SOURCES_OAUTH_TYPES = [
"passbook.sources.oauth.types.discord", "passbook.sources.oauth.types.discord",
"passbook.sources.oauth.types.facebook", "passbook.sources.oauth.types.facebook",

View File

@ -50,9 +50,9 @@ class SourceTypeManager:
return self.__source_types[kind.value][source.provider_type] return self.__source_types[kind.value][source.provider_type]
LOGGER.warning("no matching type found, using default") LOGGER.warning("no matching type found, using default")
# Return defaults # Return defaults
if kind.value == RequestKind.callback: if kind == RequestKind.callback:
return OAuthCallback return OAuthCallback
if kind.value == RequestKind.redirect: if kind == RequestKind.redirect:
return OAuthRedirect return OAuthRedirect
raise KeyError( raise KeyError(
f"Provider Type {source.provider_type} (type {kind.value}) not found." f"Provider Type {source.provider_type} (type {kind.value}) not found."

View File

@ -1,10 +1,10 @@
"""Twitter OAuth Views""" """Twitter OAuth Views"""
from passbook.sources.oauth.types.manager import MANAGER, RequestKind # from passbook.sources.oauth.types.manager import MANAGER, RequestKind
from passbook.sources.oauth.utils import user_get_or_create from passbook.sources.oauth.utils import user_get_or_create
from passbook.sources.oauth.views.core import OAuthCallback from passbook.sources.oauth.views.core import OAuthCallback
@MANAGER.source(kind=RequestKind.callback, name="Twitter") # @MANAGER.source(kind=RequestKind.callback, name="Twitter")
class TwitterOAuthCallback(OAuthCallback): class TwitterOAuthCallback(OAuthCallback):
"""Twitter OAuth2 Callback""" """Twitter OAuth2 Callback"""

View File

@ -3,7 +3,6 @@ from typing import Any, Callable, Dict, Optional
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth import authenticate
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import Http404, HttpRequest, HttpResponse from django.http import Http404, HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
@ -22,6 +21,7 @@ from passbook.flows.planner import (
) )
from passbook.flows.views import SESSION_KEY_PLAN from passbook.flows.views import SESSION_KEY_PLAN
from passbook.lib.utils.urls import redirect_with_qs from passbook.lib.utils.urls import redirect_with_qs
from passbook.sources.oauth.auth import AuthorizedServiceBackend
from passbook.sources.oauth.clients import BaseOAuthClient, get_client from passbook.sources.oauth.clients import BaseOAuthClient, get_client
from passbook.sources.oauth.models import OAuthSource, UserOAuthSourceConnection from passbook.sources.oauth.models import OAuthSource, UserOAuthSourceConnection
from passbook.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND from passbook.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
@ -134,7 +134,7 @@ class OAuthCallback(OAuthClientMixin, View):
identifier=identifier, identifier=identifier,
access_token=token.get("access_token"), access_token=token.get("access_token"),
) )
user = authenticate( user = AuthorizedServiceBackend().authenticate(
source=self.source, identifier=identifier, request=request source=self.source, identifier=identifier, request=request
) )
if user is None: if user is None:
@ -181,7 +181,8 @@ class OAuthCallback(OAuthClientMixin, View):
self.request, self.request,
{ {
PLAN_CONTEXT_PENDING_USER: user, PLAN_CONTEXT_PENDING_USER: user,
PLAN_CONTEXT_AUTHENTICATION_BACKEND: user.backend, # Since we authenticate the user by their token, they have no backend set
PLAN_CONTEXT_AUTHENTICATION_BACKEND: "django.contrib.auth.backends.ModelBackend",
PLAN_CONTEXT_SSO: True, PLAN_CONTEXT_SSO: True,
}, },
) )
@ -206,7 +207,7 @@ class OAuthCallback(OAuthClientMixin, View):
% {"source": self.source.name} % {"source": self.source.name}
), ),
) )
user = authenticate( user = AuthorizedServiceBackend().authenticate(
source=access.source, identifier=access.identifier, request=self.request source=access.source, identifier=access.identifier, request=self.request
) )
return self.handle_login_flow(source.authentication_flow, user) return self.handle_login_flow(source.authentication_flow, user)
@ -249,7 +250,7 @@ class OAuthCallback(OAuthClientMixin, View):
) )
) )
# User was not authenticated, new user has been created # User was not authenticated, new user has been created
user = authenticate( user = AuthorizedServiceBackend().authenticate(
source=access.source, identifier=access.identifier, request=self.request source=access.source, identifier=access.identifier, request=self.request
) )
messages.success( messages.success(