Merge branch 'master' into guardian
# Conflicts: # Pipfile # Pipfile.lock # passbook/core/models.py
This commit is contained in:
commit
143a575369
|
@ -1,5 +1,5 @@
|
|||
[bumpversion]
|
||||
current_version = 0.6.3-beta
|
||||
current_version = 0.6.4-beta
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)
|
||||
|
|
|
@ -27,7 +27,7 @@ create-base-image:
|
|||
before_script:
|
||||
- echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
|
||||
script:
|
||||
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/base.Dockerfile --destination docker.beryju.org/passbook/base:latest --destination docker.beryju.org/passbook/base:0.6.3-beta
|
||||
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/base.Dockerfile --destination docker.beryju.org/passbook/base:latest
|
||||
stage: build-base-image
|
||||
only:
|
||||
refs:
|
||||
|
@ -41,7 +41,7 @@ build-dev-image:
|
|||
before_script:
|
||||
- echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
|
||||
script:
|
||||
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dev.Dockerfile --destination docker.beryju.org/passbook/dev:latest --destination docker.beryju.org/passbook/dev:0.6.3-beta
|
||||
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dev.Dockerfile --destination docker.beryju.org/passbook/dev:latest
|
||||
stage: build-dev-image
|
||||
only:
|
||||
refs:
|
||||
|
@ -70,13 +70,13 @@ migrations:
|
|||
# services:
|
||||
# - postgres:latest
|
||||
# - redis:latest
|
||||
# pylint:
|
||||
# script:
|
||||
# - pylint passbook
|
||||
# stage: test
|
||||
# services:
|
||||
# - postgres:latest
|
||||
# - redis:latest
|
||||
pylint:
|
||||
script:
|
||||
- pylint passbook
|
||||
stage: test
|
||||
services:
|
||||
- postgres:latest
|
||||
- redis:latest
|
||||
coverage:
|
||||
script:
|
||||
- coverage run manage.py test
|
||||
|
@ -95,7 +95,7 @@ build-passbook-server:
|
|||
before_script:
|
||||
- echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
|
||||
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.3-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.4-beta
|
||||
only:
|
||||
- tags
|
||||
- /^version/.*$/
|
||||
|
@ -107,7 +107,7 @@ build-passbook-static:
|
|||
before_script:
|
||||
- echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
|
||||
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.3-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.4-beta
|
||||
only:
|
||||
- tags
|
||||
- /^version/.*$/
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
FROM docker.beryju.org/passbook/base:latest
|
||||
|
||||
COPY --chown=passbook:passbook ./passbook/ /app/passbook
|
||||
COPY ./passbook/ /app/passbook
|
||||
COPY ./manage.py /app/
|
||||
COPY ./docker/uwsgi.ini /app/
|
||||
|
||||
|
|
6
Pipfile
6
Pipfile
|
@ -35,18 +35,18 @@ service_identity = "*"
|
|||
signxml = "*"
|
||||
urllib3 = {extras = ["secure"],version = "*"}
|
||||
structlog = "*"
|
||||
pyuwsgi = "*"
|
||||
django-guardian = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.7"
|
||||
|
||||
[dev-packages]
|
||||
astroid = "==2.2.5"
|
||||
coverage = "*"
|
||||
isort = "*"
|
||||
pylint = "==2.3.1"
|
||||
pylint-django = "==2.0.10"
|
||||
prospector = "==1.1.7"
|
||||
pylint-django = "*"
|
||||
prospector = "*"
|
||||
django-debug-toolbar = "*"
|
||||
bumpversion = "*"
|
||||
unittest-xml-reporting = "*"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "1636ead76bcc61736245f5255a6dfafbf261c3b37b1a2b2665db50919b4cb1ea"
|
||||
"sha256": "587f6d2958f73bf9ae1026c03d123b66937fa642ccd2877f86c4c9b453d15fae"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
|
@ -101,10 +101,10 @@
|
|||
},
|
||||
"cheroot": {
|
||||
"hashes": [
|
||||
"sha256:709259ea832932fb4e1c040b87836a260d386155098c7e138fc317937763e7ae",
|
||||
"sha256:abba64f5f0d09b3b8bddf98fa18df1176cd83728b220a995e061c1a766e20a45"
|
||||
"sha256:3ff64073efa35b39d5e107410f5c79664dc8c6c5990651e970740c80ab8878a8",
|
||||
"sha256:d523a1525258730026aa35b86c8c47c8d0e3892fb89f0f39157d4b32a50edf05"
|
||||
],
|
||||
"version": "==8.0.0"
|
||||
"version": "==8.1.0"
|
||||
},
|
||||
"cherrypy": {
|
||||
"hashes": [
|
||||
|
@ -572,6 +572,36 @@
|
|||
],
|
||||
"version": "==2019.3"
|
||||
},
|
||||
"pyuwsgi": {
|
||||
"hashes": [
|
||||
"sha256:15a4626740753b0d0dfeeac7d367f9b2e89ab6af16c195927e60f75359fc1bbc",
|
||||
"sha256:24c40c3b889eb9f283d43feffbc0f7c7fc024e914451425156ddb68af3df1e71",
|
||||
"sha256:393737bd43a7e38f0a4a1601a37a69c4bf893635b37665ff958170fdb604fdb7",
|
||||
"sha256:5a08308f87e639573c1efaa5966a6d04410cd45a73c4586a932fe3ee4b56369d",
|
||||
"sha256:5f4b36c0dbb9931c4da8008aa423158be596e3b4a23cec95a958631603a94e45",
|
||||
"sha256:7c31794f71bbd0ccf542cab6bddf38aa69e84e31ae0f9657a2e18ebdc150c01a",
|
||||
"sha256:802ec6dad4b6707b934370926ec1866603abe31ba03c472f56149001b3533ba1",
|
||||
"sha256:814d73d4569add69a6c19bb4a27cd5adb72b196e5e080caed17dbda740402072",
|
||||
"sha256:829299cd117cf8abe837796bf587e61ce6bfe18423a3a1c510c21e9825789c2c",
|
||||
"sha256:85f2210ceae5f48b7d8fad2240d831f4b890cac85cd98ca82683ac6aa481dfc8",
|
||||
"sha256:861c94442b28cd64af033e88e0f63c66dbd5609f67952dc18694098b47a43f3a",
|
||||
"sha256:957bc6316ffc8463795d56d9953d58e7f32aa5aad1c5ac80bc45c69f3299961e",
|
||||
"sha256:9760c3f56fb5f15852d163429096600906478e9ed2c189a52f2bb21d8a2a986c",
|
||||
"sha256:a4b24703ea818196d0be1dc64b3b57b79c67e8dee0cfa207a4216220912035a7",
|
||||
"sha256:ad7f4968c1ddbf139a306d9b075360d959cc554d994ba5e1f512af9a40e62357",
|
||||
"sha256:b1127d34b90f74faf1707718c57a4193ac028b9f4aec0238638983132297d456",
|
||||
"sha256:bcb04d6ec644b3e08d03c64851e06edd7110489261e50627a4bcadf66ff6920e",
|
||||
"sha256:bebfebb9ee83d7cf37668bf54275b677b7ae283e84a944f9f3ac6a4b66f95d4b",
|
||||
"sha256:c29892dafc65a8b6eb95823fa4bac7754ca3fd1c28ab8d2a973289531b340a27",
|
||||
"sha256:cb296b50b51ba022b0090b28d032ff1dd395a6db03672b65a39e83532edad527",
|
||||
"sha256:ce777ebdf49ce736fc04abf555b5c41ab3f130127543a689dcf8d4871cd18fe4",
|
||||
"sha256:d8b4bf930b6a19bc9ee982b9163d948c87501ad91b71516924e8ed25fe85d2ee",
|
||||
"sha256:e2a420f2c4d35f3ec0b7e752a80d7bd385e2c5a64f67c05f2d2d74230e3114b6",
|
||||
"sha256:fed899ce96f4f2b4d1b9f338dd145a4040ee1d8a5152213af0dd8d4a4d36e9fe"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.0.18.post0"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9",
|
||||
|
@ -745,7 +775,6 @@
|
|||
"sha256:6560e1e1749f68c64a4b5dee4e091fce798d2f0d84ebe638cf0e0585a343acf4",
|
||||
"sha256:b65db1bbaac9f9f4d190199bb8680af6f6f84fd3769a5ea883df8a91fe68b4c4"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.2.5"
|
||||
},
|
||||
"autopep8": {
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
FROM python:3.7-slim-stretch
|
||||
FROM python:3.7-slim-buster as locker
|
||||
|
||||
COPY ./Pipfile /app/
|
||||
COPY ./Pipfile.lock /app/
|
||||
|
||||
WORKDIR /app/
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends build-essential && \
|
||||
pip install pipenv uwsgi --no-cache-dir && \
|
||||
apt-get remove -y --purge build-essential && \
|
||||
apt-get autoremove -y --purge && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
RUN pip install pipenv && \
|
||||
pipenv lock -r > requirements.txt && \
|
||||
pipenv lock -rd > requirements-dev.txt
|
||||
|
||||
RUN pipenv lock -r > requirements.txt && \
|
||||
pipenv --rm && \
|
||||
pip install -r requirements.txt --no-cache-dir && \
|
||||
FROM python:3.7-slim-buster
|
||||
|
||||
COPY --from=locker /app/requirements.txt /app/
|
||||
|
||||
WORKDIR /app/
|
||||
|
||||
RUN pip install -r requirements.txt --no-cache-dir && \
|
||||
adduser --system --no-create-home --uid 1000 --group --home /app passbook
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
FROM docker.beryju.org/passbook/base:latest
|
||||
|
||||
RUN pipenv lock --dev -r > requirements-dev.txt && \
|
||||
pipenv --rm && \
|
||||
pip install -r /app/requirements-dev.txt --no-cache-dir
|
||||
RUN pip install -r /app/requirements-dev.txt --no-cache-dir
|
||||
|
|
|
@ -49,6 +49,7 @@ services:
|
|||
- -E
|
||||
- -B
|
||||
- -A=passbook.root.celery
|
||||
- -s=/tmp/celerybeat-schedule
|
||||
networks:
|
||||
- internal
|
||||
labels:
|
||||
|
|
|
@ -39,7 +39,7 @@ http {
|
|||
gzip on;
|
||||
gzip_types application/javascript image/* text/css;
|
||||
gunzip on;
|
||||
add_header X-passbook-Version 0.6.3-beta;
|
||||
add_header X-passbook-Version 0.6.4-beta;
|
||||
add_header Vary X-passbook-Version;
|
||||
root /data/;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
apiVersion: v1
|
||||
appVersion: "0.6.3-beta"
|
||||
appVersion: "0.6.4-beta"
|
||||
description: A Helm chart for passbook.
|
||||
name: passbook
|
||||
version: "0.6.3-beta"
|
||||
version: "0.6.4-beta"
|
||||
icon: https://git.beryju.org/uploads/-/system/project/avatar/108/logo.png
|
||||
|
|
|
@ -36,6 +36,7 @@ spec:
|
|||
- -E
|
||||
- -B
|
||||
- -A=passbook.root.celery
|
||||
- -s=/tmp/celerybeat-schedule
|
||||
volumeMounts:
|
||||
- mountPath: /etc/passbook
|
||||
name: config-volume
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
image:
|
||||
tag: 0.6.3-beta
|
||||
tag: 0.6.4-beta
|
||||
|
||||
nameOverride: ""
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
"""passbook"""
|
||||
__version__ = '0.6.3-beta'
|
||||
__version__ = '0.6.4-beta'
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.6 on 2019-10-10 11:48
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_core', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='nonce',
|
||||
name='description',
|
||||
field=models.TextField(blank=True, default=''),
|
||||
),
|
||||
]
|
|
@ -77,6 +77,7 @@ class Provider(models.Model):
|
|||
return getattr(self, 'name')
|
||||
return super().__str__()
|
||||
|
||||
|
||||
class PolicyModel(UUIDModel, CreatedUpdatedModel):
|
||||
"""Base model which can have policies applied to it"""
|
||||
|
||||
|
@ -262,21 +263,29 @@ class Invitation(UUIDModel):
|
|||
verbose_name = _('Invitation')
|
||||
verbose_name_plural = _('Invitations')
|
||||
|
||||
|
||||
class Nonce(UUIDModel):
|
||||
"""One-time link for password resets/sign-up-confirmations"""
|
||||
|
||||
expires = models.DateTimeField(default=default_nonce_duration)
|
||||
user = models.ForeignKey('User', on_delete=models.CASCADE)
|
||||
expiring = models.BooleanField(default=True)
|
||||
description = models.TextField(default='', blank=True)
|
||||
|
||||
@property
|
||||
def is_expired(self) -> bool:
|
||||
"""Check if nonce is expired yet."""
|
||||
return now() > self.expires
|
||||
|
||||
def __str__(self):
|
||||
return f"Nonce f{self.uuid.hex} (expires={self.expires})"
|
||||
return f"Nonce f{self.uuid.hex} {self.description} (expires={self.expires})"
|
||||
|
||||
class Meta:
|
||||
|
||||
verbose_name = _('Nonce')
|
||||
verbose_name_plural = _('Nonces')
|
||||
|
||||
|
||||
class PropertyMapping(UUIDModel):
|
||||
"""User-defined key -> x mapping which can be used by providers to expose extra data."""
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
"""passbook Recovery app config"""
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PassbookRecoveryConfig(AppConfig):
|
||||
"""passbook Recovery app config"""
|
||||
|
||||
name = 'passbook.recovery'
|
||||
label = 'passbook_recovery'
|
||||
verbose_name = 'passbook Recovery'
|
||||
mountpoint = 'recovery/'
|
|
@ -0,0 +1,46 @@
|
|||
"""passbook recovery createkey command"""
|
||||
from datetime import timedelta
|
||||
from getpass import getuser
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.urls import reverse
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext as _
|
||||
from structlog import get_logger
|
||||
|
||||
from passbook.core.models import Nonce, User
|
||||
from passbook.lib.config import CONFIG
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Create Nonce used to recover access"""
|
||||
|
||||
help = _('Create a Key which can be used to restore access to passbook.')
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('duration', default=1, action='store',
|
||||
help='How long the token is valid for (in years).')
|
||||
parser.add_argument('user', action='store',
|
||||
help='Which user the Token gives access to.')
|
||||
|
||||
def get_url(self, nonce: Nonce) -> str:
|
||||
"""Get full recovery link"""
|
||||
path = reverse('passbook_recovery:use-nonce', kwargs={'uuid': str(nonce.uuid)})
|
||||
return f"https://{CONFIG.y('domain')}{path}"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""Create Nonce used to recover access"""
|
||||
duration = int(options.get('duration', 1))
|
||||
delta = timedelta(days=duration * 365.2425)
|
||||
_now = now()
|
||||
expiry = _now + delta
|
||||
user = User.objects.get(username=options.get('user'))
|
||||
nonce = Nonce.objects.create(
|
||||
expires=expiry,
|
||||
user=user,
|
||||
description=f'Recovery Nonce generated by {getuser()} on {_now}')
|
||||
self.stdout.write((f"Store this link safely, as it will allow"
|
||||
f" anyone to access passbook as {user}."))
|
||||
self.stdout.write(self.get_url(nonce))
|
|
@ -0,0 +1,9 @@
|
|||
"""recovery views"""
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from passbook.recovery.views import UseNonceView
|
||||
|
||||
urlpatterns = [
|
||||
path('use-nonce/<uuid:uuid>/', UseNonceView.as_view(), name='use-nonce'),
|
||||
]
|
|
@ -0,0 +1,24 @@
|
|||
"""recovery views"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import login
|
||||
from django.http import Http404, HttpRequest, HttpResponse
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views import View
|
||||
|
||||
from passbook.core.models import Nonce
|
||||
|
||||
|
||||
class UseNonceView(View):
|
||||
"""Use nonce to login"""
|
||||
|
||||
def get(self, request: HttpRequest, uuid: str) -> HttpResponse:
|
||||
"""Check if nonce exists, log user in and delete nonce."""
|
||||
nonce: Nonce = get_object_or_404(Nonce, pk=uuid)
|
||||
if nonce.is_expired:
|
||||
nonce.delete()
|
||||
raise Http404
|
||||
login(request, nonce.user, backend='django.contrib.auth.backends.ModelBackend')
|
||||
nonce.delete()
|
||||
messages.warning(request, _("Used recovery-link to authenticate."))
|
||||
return redirect('passbook_core:overview')
|
|
@ -74,6 +74,7 @@ INSTALLED_APPS = [
|
|||
'passbook.api.apps.PassbookAPIConfig',
|
||||
'passbook.lib.apps.PassbookLibConfig',
|
||||
'passbook.audit.apps.PassbookAuditConfig',
|
||||
'passbook.recovery.apps.PassbookRecoveryConfig',
|
||||
|
||||
'passbook.sources.ldap.apps.PassbookSourceLDAPConfig',
|
||||
'passbook.sources.oauth.apps.PassbookSourceOAuthConfig',
|
||||
|
|
Reference in New Issue