Compare commits

..

28 Commits

Author SHA1 Message Date
f124314eab new release: 0.7.12-beta 2020-01-02 20:22:44 +01:00
684e4ffdcf providers/app_gw: fix formatting 2020-01-02 20:22:36 +01:00
d9ff5c69c8 providers/app_gw: fix assignment of response_types 2020-01-02 20:20:10 +01:00
8142e3df45 providers/oidc: fix application property of wrong object being used 2020-01-02 20:19:53 +01:00
73920899de static: use current pixie image 2020-01-02 20:09:30 +01:00
13666965a7 actions: fix build over gatekeeper 2020-01-02 16:55:30 +01:00
86f16e2781 providers/oidc: fix incorrectly sorted imports 2020-01-02 16:42:52 +01:00
2ed8e72c62 new release: 0.7.11-beta 2020-01-02 16:38:11 +01:00
edeed18ae8 providers/oidc: fix error when using with app_gw 2020-01-02 16:38:01 +01:00
d24133d8a2 core: fix _redirect_with_qs appending an array to the URL 2020-01-02 16:14:56 +01:00
b9733e56aa providers/app_gw: fix passbook domain being empty 2020-01-02 16:09:17 +01:00
cd34413914 providers/app_gw: separate host field into external_ and internal_ 2020-01-02 16:09:04 +01:00
c3a4a76d43 providers/app_gw: fix Client's response_type not being set 2020-01-02 16:06:32 +01:00
a59a29b256 actions: also build gatekeeper on release 2020-01-02 15:55:39 +01:00
dce1edbe53 new release: 0.7.10-beta 2020-01-02 14:54:52 +01:00
264d43827a actions: create release based on version number, not tag name 2020-01-02 14:46:44 +01:00
6207226bdf new release: 0.7.9-beta 2020-01-02 14:09:58 +01:00
ebf33f39c9 actions: fix missing backslash for dockerbuild 2020-01-02 14:09:42 +01:00
696cd1f247 new release: 0.7.8-beta 2020-01-02 14:03:36 +01:00
b7b3abc462 actions: automatically create release when version/* tag is created, run tests before creating release 2020-01-02 13:49:24 +01:00
575739d07c ci: add bandit for static security checks 2020-01-02 13:41:49 +01:00
2d7e70eebf audit: fix import order 2020-01-02 13:20:41 +01:00
387f3c981f audit: fix error when trying to save models with UUID as PK 2020-01-02 13:12:23 +01:00
865435fb25 actions: fix path to helm chart 2020-01-02 11:38:54 +01:00
b10c5306b9 actions: ensure release gets only executed on release creation 2020-01-02 11:37:46 +01:00
7c706369cd new release: 0.7.7-beta 2020-01-02 11:22:08 +01:00
20dd6355c1 actions: run unittests in final docker images after build 2020-01-02 11:20:32 +01:00
ba8d5d6e27 actions: push both versioned and :latest tags 2020-01-02 11:19:55 +01:00
20 changed files with 236 additions and 64 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.7.6-beta
current_version = 0.7.12-beta
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)

View File

@ -9,7 +9,7 @@ env:
jobs:
# Linting
pylint:
runs-on: [ubuntu-latest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v1
@ -26,7 +26,7 @@ jobs:
- name: Lint with pylint
run: pipenv run pylint passbook
black:
runs-on: [ubuntu-latest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v1
@ -43,7 +43,7 @@ jobs:
- name: Lint with black
run: pipenv run black --check passbook
prospector:
runs-on: [ubuntu-latest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v1
@ -59,6 +59,23 @@ jobs:
run: pip install -U pip pipenv && pipenv install --dev
- name: Lint with prospector
run: pipenv run prospector
bandit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v1
with:
python-version: '3.7'
- uses: actions/cache@v1
with:
path: ~/.local/share/virtualenvs/
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
restore-keys: |
${{ runner.os }}-pipenv-
- name: Install dependencies
run: pip install -U pip pipenv && pipenv install --dev
- name: Lint with bandit
run: pipenv run bandit -r passbook
# Actual CI tests
migrations:
needs:
@ -78,7 +95,7 @@ jobs:
image: redis:latest
ports:
- 6379:6379
runs-on: [ubuntu-latest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v1
@ -112,7 +129,7 @@ jobs:
image: redis:latest
ports:
- 6379:6379
runs-on: [ubuntu-latest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v1

View File

@ -1,13 +1,11 @@
name: passbook-release
on:
release:
types: # This configuration does not affect the page_build event above
- created
release
jobs:
# Build
build-server:
runs-on: [ubuntu-latest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Docker Login Registry
@ -16,11 +14,38 @@ jobs:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
run: docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
- name: Building Docker Image
run: docker build --no-cache -t beryju/passbook:0.7.6-beta -f Dockerfile .
- name: Push Docker Container to Registry
run: docker push beryju/passbook:0.7.6-beta
run: docker build
--no-cache
-t beryju/passbook:0.7.12-beta
-t beryju/passbook:latest
-f Dockerfile .
- name: Push Docker Container to Registry (versioned)
run: docker push beryju/passbook:0.7.12-beta
- name: Push Docker Container to Registry (latest)
run: docker push beryju/passbook:latest
build-gatekeeper:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Docker Login Registry
env:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
run: docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
- name: Building Docker Image
run: |
cd gatekeeper
docker build \
--no-cache \
-t beryju/passbook-gatekeeper:0.7.12-beta \
-t beryju/passbook-gatekeeper:latest \
-f Dockerfile .
- name: Push Docker Container to Registry (versioned)
run: docker push beryju/passbook-gatekeeper:0.7.12-beta
- name: Push Docker Container to Registry (latest)
run: docker push beryju/passbook-gatekeeper:latest
build-static:
runs-on: [ubuntu-latest]
runs-on: ubuntu-latest
services:
postgres:
image: postgres:latest
@ -41,24 +66,24 @@ jobs:
run: docker build
--no-cache
--network=$(docker network ls | grep github | awk '{print $1}')
-t beryju/passbook-static:0.7.6-beta
-t beryju/passbook-static:0.7.12-beta
-t beryju/passbook-static:latest
-f static.Dockerfile .
- name: Push Docker Container to Registry
run: docker push beryju/passbook-static:0.7.6-beta
package-helm:
- name: Push Docker Container to Registry (versioned)
run: docker push beryju/passbook-static:0.7.12-beta
- name: Push Docker Container to Registry (latest)
run: docker push beryju/passbook-static:latest
test-release:
needs:
- build-server
- build-static
runs-on: [ubuntu-latest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Install Helm
- name: Run test suite in final docker images
run: |
apt update && apt install -y curl
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
helm init
- name: Helm package
run: |
helm dependency update helm/passbook
helm package helm/passbook
export PASSBOOK_DOMAIN=localhost
docker-compose pull
docker-compose up --no-start
docker-compose start postgresql redis
docker-compose run -u root server bash -c "pip install --no-cache -r requirements-dev.txt && ./manage.py test"

60
.github/workflows/tag.yml vendored Normal file
View File

@ -0,0 +1,60 @@
on:
push:
tags:
- 'version/*'
name: passbook-version-tag
jobs:
build:
name: Create Release from Tag
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Pre-release test
run: |
export PASSBOOK_DOMAIN=localhost
docker-compose pull
docker build \
--no-cache \
-t beryju/passbook:latest \
-f Dockerfile .
docker-compose up --no-start
docker-compose start postgresql redis
docker-compose run -u root server bash -c "pip install --no-cache -r requirements-dev.txt && ./manage.py test"
- name: Install Helm
run: |
apt update && apt install -y curl
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
- name: Helm package
run: |
helm dependency update helm/
helm package helm/
mv passbook-*.tgz passbook-chart.tgz
- name: Extract verison number
id: get_version
uses: actions/github-script@0.2.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
return context.payload.ref.replace(/\/refs\/tags\/version\//, '');
- name: Create Release
id: create_release
uses: actions/create-release@v1.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ steps.get_version.outputs.result }}
draft: false
prerelease: false
- name: Upload packaged Helm Chart
id: upload-release-asset
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./passbook-chart.tgz
asset_name: passbook-chart.tgz
asset_content_type: application/gzip

View File

@ -1,5 +1,7 @@
# passbook
![](https://github.com/BeryJu/passbook/workflows/passbook-ci/badge.svg)
## Quick instance
```

View File

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

View File

@ -2,7 +2,7 @@
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
image:
tag: 0.7.6-beta
tag: 0.7.12-beta
nameOverride: ""

View File

@ -1,2 +1,2 @@
"""passbook"""
__version__ = "0.7.6-beta"
__version__ = "0.7.12-beta"

View File

@ -1,5 +1,6 @@
"""passbook audit models"""
from enum import Enum
from uuid import UUID
from inspect import getmodule, stack
from typing import Optional, Dict, Any
@ -32,11 +33,15 @@ def sanitize_dict(source: Dict[Any, Any]) -> Dict[Any, Any]:
source[key] = sanitize_dict(value)
elif isinstance(value, models.Model):
model_content_type = ContentType.objects.get_for_model(value)
source[key] = {
"app": model_content_type.app_label,
"name": model_content_type.model,
"pk": value.pk,
}
source[key] = sanitize_dict(
{
"app": model_content_type.app_label,
"name": model_content_type.model,
"pk": value.pk,
}
)
elif isinstance(value, UUID):
source[key] = value.hex
return source

View File

@ -1,9 +1,11 @@
"""audit event tests"""
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from guardian.shortcuts import get_anonymous_user
from passbook.audit.models import Event, EventAction
from passbook.core.models import Policy
class TestAuditEvent(TestCase):
@ -11,6 +13,21 @@ class TestAuditEvent(TestCase):
def test_new_with_model(self):
"""Create a new Event passing a model as kwarg"""
event = Event.new(EventAction.CUSTOM, model=get_anonymous_user())
event.save()
self.assertIsNotNone(event.pk)
event = Event.new(EventAction.CUSTOM, test={"model": get_anonymous_user()})
event.save() # We save to ensure nothing is un-saveable
model_content_type = ContentType.objects.get_for_model(get_anonymous_user())
self.assertEqual(
event.context.get("test").get("model").get("app"),
model_content_type.app_label,
)
def test_new_with_uuid_model(self):
"""Create a new Event passing a model (with UUID PK) as kwarg"""
temp_model = Policy.objects.create()
event = Event.new(EventAction.CUSTOM, model=temp_model)
event.save() # We save to ensure nothing is un-saveable
model_content_type = ContentType.objects.get_for_model(temp_model)
self.assertEqual(
event.context.get("model").get("app"), model_content_type.app_label
)
self.assertEqual(event.context.get("model").get("pk"), temp_model.pk.hex)

View File

@ -23,7 +23,7 @@ def _redirect_with_qs(view, get_query_set=None):
"""Wrapper to redirect whilst keeping GET Parameters"""
target = reverse(view)
if get_query_set:
target += "?" + urlencode(get_query_set)
target += "?" + urlencode(get_query_set.items())
return redirect(target)

View File

@ -100,8 +100,8 @@ def gravatar(email, size=None, rating=None):
# gravatar uses md5 for their URLs, so md5 can't be avoided
gravatar_url = "%savatar/%s" % (
"https://secure.gravatar.com/",
md5(email.encode("utf-8")).hexdigest(),
) # nosec
md5(email.encode("utf-8")).hexdigest(), # nosec
)
parameters = [p for p in (("s", size or "158"), ("r", rating or "g"),) if p[1]]

View File

@ -20,6 +20,5 @@ class CreateAssignPermView(CreateView):
self.object._meta.app_label,
self.object._meta.model_name,
)
print(full_permission)
assign_perm(full_permission, self.request.user, self.object)
return response

View File

@ -1,7 +1,7 @@
"""passbook Application Security Gateway Forms"""
from django import forms
from oauth2_provider.generators import generate_client_id, generate_client_secret
from oidc_provider.models import Client
from oidc_provider.models import Client, ResponseType
from passbook.providers.app_gw.models import ApplicationGatewayProvider
@ -16,9 +16,14 @@ class ApplicationGatewayProviderForm(forms.ModelForm):
client_id=generate_client_id(), client_secret=generate_client_secret()
)
self.instance.client.name = self.instance.name
self.instance.client.response_types.set(
[ResponseType.objects.get_by_natural_key("code")]
)
self.instance.client.redirect_uris = [
f"http://{self.instance.host}/oauth2/callback",
f"https://{self.instance.host}/oauth2/callback",
f"http://{self.instance.external_host}/oauth2/callback",
f"https://{self.instance.external_host}/oauth2/callback",
f"http://{self.instance.internal_host}/oauth2/callback",
f"https://{self.instance.internal_host}/oauth2/callback",
]
self.instance.client.scope = ["openid", "email"]
self.instance.client.save()
@ -27,8 +32,9 @@ class ApplicationGatewayProviderForm(forms.ModelForm):
class Meta:
model = ApplicationGatewayProvider
fields = ["name", "host"]
fields = ["name", "internal_host", "external_host"]
widgets = {
"name": forms.TextInput(),
"host": forms.TextInput(),
"internal_host": forms.TextInput(),
"external_host": forms.TextInput(),
}

View File

@ -0,0 +1,24 @@
# Generated by Django 2.2.9 on 2020-01-02 15:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_providers_app_gw", "0003_applicationgatewayprovider"),
]
operations = [
migrations.RenameField(
model_name="applicationgatewayprovider",
old_name="host",
new_name="external_host",
),
migrations.AddField(
model_name="applicationgatewayprovider",
name="internal_host",
field=models.TextField(default=""),
preserve_default=False,
),
]

View File

@ -14,7 +14,8 @@ class ApplicationGatewayProvider(Provider):
"""This provider uses oauth2_proxy with the OIDC Provider."""
name = models.TextField()
host = models.TextField()
internal_host = models.TextField()
external_host = models.TextField()
client = models.ForeignKey(Client, on_delete=models.CASCADE)

View File

@ -40,10 +40,10 @@ services:
environment:
OAUTH2_PROXY_CLIENT_ID: {{ provider.client.client_id }}
OAUTH2_PROXY_CLIENT_SECRET: {{ provider.client.client_secret }}
OAUTH2_PROXY_REDIRECT_URL: https://{{ provider.host }}/oauth2/callback
OAUTH2_PROXY_OIDC_ISSUER_URL: https://{{ request.META.host }}/application/oidc
OAUTH2_PROXY_REDIRECT_URL: https://{{ provider.external_host }}/oauth2/callback
OAUTH2_PROXY_OIDC_ISSUER_URL: https://{{ request.META.HTTP_HOST }}/application/oidc
OAUTH2_PROXY_COOKIE_SECRET: {{ cookie_secret }}
OAUTH2_PROXY_UPSTREAM: http://{{ provider.host }}</textarea>
OAUTH2_PROXY_UPSTREAM: http://{{ provider.internal_host }}</textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">{% trans 'Close' %}</button>

View File

@ -1,21 +1,38 @@
"""OIDC Permission checking"""
from typing import Optional
from django.contrib import messages
from django.db.models.deletion import Collector
from django.http import HttpRequest, HttpResponse
from django.shortcuts import redirect
from oidc_provider.models import Client
from structlog import get_logger
from passbook.audit.models import Event, EventAction
from passbook.core.models import Application
from passbook.core.models import Application, Provider, User
from passbook.policies.engine import PolicyEngine
LOGGER = get_logger()
def check_permissions(request, user, client):
def check_permissions(
request: HttpRequest, user: User, client: Client
) -> Optional[HttpResponse]:
"""Check permissions, used for
https://django-oidc-provider.readthedocs.io/en/latest/
sections/settings.html#oidc-after-userlogin-hook"""
try:
application = client.openidprovider.application
# because oidc_provider is also used by app_gw, we can't be
# sure an OpenIDPRovider instance exists. hence we look through all related models
# and choose the one that inherits from Provider, which is guaranteed to
# have the application property
collector = Collector(using="default")
collector.collect([client])
for _, related in collector.data.items():
related_object = next(iter(related))
if isinstance(related_object, Provider):
application = related_object.application
break
except Application.DoesNotExist:
return redirect("passbook_providers_oauth:oauth2-permission-denied")
LOGGER.debug(

View File

@ -13,11 +13,11 @@ class MetricsView(View):
def get(self, request: HttpRequest) -> HttpResponse:
"""Check for HTTP-Basic auth"""
auth_header = request.META.get("HTTP_AUTHORIZATION", "")
token_type, _, credentials = auth_header.partition(" ")
creds = f"monitor:{settings.SECRET_KEY}"
expected = b64encode(str.encode(creds)).decode()
auth_type, _, credentials = auth_header.partition(" ")
credentials = f"monitor:{settings.SECRET_KEY}"
expected = b64encode(str.encode(credentials)).decode()
if token_type != "Basic" or credentials != expected:
if auth_type != "Basic" or credentials != expected:
raise Http404
return ExportToDjangoView(request)

View File

@ -34,8 +34,7 @@ ENV PASSBOOK_POSTGRESQL__USER=passbook
ENV PASSBOOK_POSTGRESQL__PASSWORD="EK-5jnKfjrGRm<77"
RUN ./manage.py collectstatic --no-input
FROM docker.beryju.org/pixie/server
FROM beryju/pixie:latest
COPY --from=static-build /app/static /data/static/
COPY --from=static-build /app/static/robots.txt /data/robots.txt
WORKDIR /data
COPY --from=static-build /app/static /web-root/static/
COPY --from=static-build /app/static/robots.txt /web-root/robots.txt