From 56d872af15074ca14158dad82223388c6ad7a21d Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Fri, 8 Mar 2019 12:47:50 +0100 Subject: [PATCH 01/28] add PropertyMapping Model, add Subclass for SAML, test with AWS --- .../admin/templates/administration/base.html | 70 ++++++++------- .../administration/property_mapping/list.html | 52 +++++++++++ passbook/admin/urls.py | 13 ++- passbook/admin/views/property_mapping.py | 90 +++++++++++++++++++ passbook/core/auth/factors/password.py | 2 +- .../core/migrations/0017_propertymapping.py | 26 ++++++ .../0018_provider_property_mappings.py | 18 ++++ passbook/core/models.py | 20 ++++- passbook/core/templates/generic/delete.html | 4 +- passbook/saml_idp/base.py | 19 ++-- passbook/saml_idp/forms.py | 19 +++- .../migrations/0002_samlpropertymapping.py | 30 +++++++ passbook/saml_idp/models.py | 20 ++++- passbook/saml_idp/processors/aws.py | 10 +-- .../saml_idp/templates/saml/idp/login.html | 52 ++++------- .../templates/saml/xml/attributes.xml | 9 +- 16 files changed, 368 insertions(+), 86 deletions(-) create mode 100644 passbook/admin/templates/administration/property_mapping/list.html create mode 100644 passbook/admin/views/property_mapping.py create mode 100644 passbook/core/migrations/0017_propertymapping.py create mode 100644 passbook/core/migrations/0018_provider_property_mappings.py create mode 100644 passbook/saml_idp/migrations/0002_samlpropertymapping.py diff --git a/passbook/admin/templates/administration/base.html b/passbook/admin/templates/administration/base.html index fadc37d70b..208108c0d1 100644 --- a/passbook/admin/templates/administration/base.html +++ b/passbook/admin/templates/administration/base.html @@ -5,35 +5,45 @@ {% block nav_secondary %} {% endblock %} diff --git a/passbook/admin/templates/administration/property_mapping/list.html b/passbook/admin/templates/administration/property_mapping/list.html new file mode 100644 index 0000000000..7bf8c2da23 --- /dev/null +++ b/passbook/admin/templates/administration/property_mapping/list.html @@ -0,0 +1,52 @@ +{% extends "administration/base.html" %} + +{% load i18n %} +{% load utils %} + +{% block title %} +{% title %} +{% endblock %} + +{% block content %} +
+

{% trans "Property Mappings" %}

+ {% trans "Property Mappings allow you expose provider-specific attributes." %} +
+ +
+ + + + + + + + + + {% for property_mapping in object_list %} + + + + + + {% endfor %} + +
{% trans 'Name' %}{% trans 'Type' %}
{{ property_mapping.name }} ({{ property_mapping.slug }}){{ property_mapping|verbose_name }} + {% trans 'Edit' %} + {% trans 'Delete' %} +
+
+{% endblock %} diff --git a/passbook/admin/urls.py b/passbook/admin/urls.py index e2d3a62360..fa4c9ec0a7 100644 --- a/passbook/admin/urls.py +++ b/passbook/admin/urls.py @@ -2,8 +2,8 @@ from django.urls import include, path from passbook.admin.views import (applications, audit, factors, groups, - invitations, overview, policy, providers, - sources, users) + invitations, overview, policy, + property_mapping, providers, sources, users) urlpatterns = [ path('', overview.AdministrationOverviewView.as_view(), name='overview'), @@ -43,6 +43,15 @@ urlpatterns = [ factors.FactorUpdateView.as_view(), name='factor-update'), path('factors//delete/', factors.FactorDeleteView.as_view(), name='factor-delete'), + # Factors + path('property-mappings/', property_mapping.PropertyMappingListView.as_view(), + name='property-mappings'), + path('property-mappings/create/', + property_mapping.PropertyMappingCreateView.as_view(), name='property-mapping-create'), + path('property-mappings//update/', + property_mapping.PropertyMappingUpdateView.as_view(), name='property-mapping-update'), + path('property-mappings//delete/', + property_mapping.PropertyMappingDeleteView.as_view(), name='property-mapping-delete'), # Invitations path('invitations/', invitations.InvitationListView.as_view(), name='invitations'), path('invitations/create/', diff --git a/passbook/admin/views/property_mapping.py b/passbook/admin/views/property_mapping.py new file mode 100644 index 0000000000..c4cf7fad33 --- /dev/null +++ b/passbook/admin/views/property_mapping.py @@ -0,0 +1,90 @@ +"""passbook PropertyMapping administration""" +from django.contrib import messages +from django.contrib.messages.views import SuccessMessageMixin +from django.http import Http404 +from django.urls import reverse_lazy +from django.utils.translation import ugettext as _ +from django.views.generic import CreateView, DeleteView, ListView, UpdateView + +from passbook.admin.mixins import AdminRequiredMixin +from passbook.core.models import PropertyMapping +from passbook.lib.utils.reflection import path_to_class + + +def all_subclasses(cls): + """Recursively return all subclassess of cls""" + return set(cls.__subclasses__()).union( + [s for c in cls.__subclasses__() for s in all_subclasses(c)]) + + +class PropertyMappingListView(AdminRequiredMixin, ListView): + """Show list of all property_mappings""" + + model = PropertyMapping + template_name = 'administration/property_mapping/list.html' + ordering = 'name' + + def get_context_data(self, **kwargs): + kwargs['types'] = { + x.__name__: x._meta.verbose_name for x in all_subclasses(PropertyMapping)} + return super().get_context_data(**kwargs) + + def get_queryset(self): + return super().get_queryset().select_subclasses() + + +class PropertyMappingCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView): + """Create new PropertyMapping""" + + template_name = 'generic/create.html' + success_url = reverse_lazy('passbook_admin:property-mappings') + success_message = _('Successfully created Property Mapping') + + def get_context_data(self, **kwargs): + kwargs = super().get_context_data(**kwargs) + property_mapping_type = self.request.GET.get('type') + model = next(x for x in all_subclasses(PropertyMapping) + if x.__name__ == property_mapping_type) + kwargs['type'] = model._meta.verbose_name + return kwargs + + def get_form_class(self): + property_mapping_type = self.request.GET.get('type') + model = next(x for x in all_subclasses(PropertyMapping) + if x.__name__ == property_mapping_type) + if not model: + raise Http404 + return path_to_class(model.form) + + +class PropertyMappingUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView): + """Update property_mapping""" + + model = PropertyMapping + template_name = 'generic/update.html' + success_url = reverse_lazy('passbook_admin:property-mappings') + success_message = _('Successfully updated Property Mapping') + + def get_form_class(self): + form_class_path = self.get_object().form + form_class = path_to_class(form_class_path) + return form_class + + def get_object(self, queryset=None): + return PropertyMapping.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first() + + +class PropertyMappingDeleteView(SuccessMessageMixin, AdminRequiredMixin, DeleteView): + """Delete property_mapping""" + + model = PropertyMapping + template_name = 'generic/delete.html' + success_url = reverse_lazy('passbook_admin:property-mappings') + success_message = _('Successfully deleted Property Mapping') + + def get_object(self, queryset=None): + return PropertyMapping.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first() + + def delete(self, request, *args, **kwargs): + messages.success(self.request, self.success_message) + return super().delete(request, *args, **kwargs) diff --git a/passbook/core/auth/factors/password.py b/passbook/core/auth/factors/password.py index 1367595170..d8956d3777 100644 --- a/passbook/core/auth/factors/password.py +++ b/passbook/core/auth/factors/password.py @@ -37,7 +37,7 @@ class PasswordFactor(FormView, AuthenticationFactor): send_email.delay(self.pending_user.email, _('Forgotten password'), 'email/account_password_reset.html', { 'url': self.request.build_absolute_uri( - reverse('passbook_core:passbook_core:auth-password-reset', + reverse('passbook_core:auth-password-reset', kwargs={ 'nonce': nonce.uuid }) diff --git a/passbook/core/migrations/0017_propertymapping.py b/passbook/core/migrations/0017_propertymapping.py new file mode 100644 index 0000000000..c53c910c10 --- /dev/null +++ b/passbook/core/migrations/0017_propertymapping.py @@ -0,0 +1,26 @@ +# Generated by Django 2.1.7 on 2019-03-08 10:40 + +import uuid + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('passbook_core', '0016_auto_20190227_1355'), + ] + + operations = [ + migrations.CreateModel( + name='PropertyMapping', + fields=[ + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.TextField()), + ], + options={ + 'verbose_name': 'Property Mapping', + 'verbose_name_plural': 'Property Mappings', + }, + ), + ] diff --git a/passbook/core/migrations/0018_provider_property_mappings.py b/passbook/core/migrations/0018_provider_property_mappings.py new file mode 100644 index 0000000000..a845d1c81f --- /dev/null +++ b/passbook/core/migrations/0018_provider_property_mappings.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.7 on 2019-03-08 10:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('passbook_core', '0017_propertymapping'), + ] + + operations = [ + migrations.AddField( + model_name='provider', + name='property_mappings', + field=models.ManyToManyField(blank=True, default=None, to='passbook_core.PropertyMapping'), + ), + ] diff --git a/passbook/core/models.py b/passbook/core/models.py index 2f6d526d8d..7772a6d0f7 100644 --- a/passbook/core/models.py +++ b/passbook/core/models.py @@ -60,6 +60,8 @@ class User(AbstractUser): class Provider(models.Model): """Application-independent Provider instance. For example SAML2 Remote, OAuth2 Application""" + property_mappings = models.ManyToManyField('PropertyMapping', default=None, blank=True) + objects = InheritanceManager() # This class defines no field for easier inheritance @@ -412,7 +414,7 @@ class Invitation(UUIDModel): verbose_name_plural = _('Invitations') class Nonce(UUIDModel): - """One-time link for password resets/signup-confirmations""" + """One-time link for password resets/sign-up-confirmations""" expires = models.DateTimeField(default=default_nonce_duration) user = models.ForeignKey('User', on_delete=models.CASCADE) @@ -424,3 +426,19 @@ class Nonce(UUIDModel): 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.""" + + name = models.TextField() + + form = '' + objects = InheritanceManager() + + def __str__(self): + return "Property Mapping %s" % self.name + + class Meta: + + verbose_name = _('Property Mapping') + verbose_name_plural = _('Property Mappings') diff --git a/passbook/core/templates/generic/delete.html b/passbook/core/templates/generic/delete.html index 19e570e322..33a51dd0f3 100644 --- a/passbook/core/templates/generic/delete.html +++ b/passbook/core/templates/generic/delete.html @@ -6,13 +6,13 @@ {% block content %}
{% block above_form %} -

{% blocktrans with object_type=object|fieldtype|title %}Delete {{ object_type }}{% endblocktrans %}

+

{% blocktrans with object_type=object|verbose_name %}Delete {{ object_type }}{% endblocktrans %}

{% endblock %}
{% csrf_token %}

- {% blocktrans with object_type=object|fieldtype|title name=object %} + {% blocktrans with object_type=object|verbose_name name=object %} Are you sure you want to delete {{ object_type }} "{{ object }}"? {% endblocktrans %}

diff --git a/passbook/saml_idp/base.py b/passbook/saml_idp/base.py index dcf03580f8..eb9a921b1a 100644 --- a/passbook/saml_idp/base.py +++ b/passbook/saml_idp/base.py @@ -6,7 +6,6 @@ from logging import getLogger from bs4 import BeautifulSoup -from passbook.lib.config import CONFIG from passbook.saml_idp import exceptions, utils, xml_render MINUTES = 60 @@ -52,9 +51,7 @@ class Processor: _session_index = None _subject = None _subject_format = 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent' - _system_params = { - 'ISSUER': CONFIG.y('saml_idp.issuer'), - } + _system_params = {} @property def dotted_path(self): @@ -67,7 +64,7 @@ class Processor: self.name = remote.name self._remote = remote self._logger = getLogger(__name__) - + self._system_params['ISSUER'] = self._remote.issuer self._logger.info('processor configured') def _build_assertion(self): @@ -170,6 +167,16 @@ class Processor: 'Value': self._django_request.user.username, }, ] + from passbook.saml_idp.models import SAMLPropertyMapping + for mapping in self._remote.property_mappings.all().select_subclasses(): + if isinstance(mapping, SAMLPropertyMapping): + mapping_payload = { + 'Name': mapping.saml_name, + 'ValueArray': mapping.values + } + if mapping.friendly_name: + mapping_payload['FriendlyName'] = mapping.friendly_name + self._assertion_params['ATTRIBUTES'].append(mapping_payload) self._assertion_xml = xml_render.get_assertion_xml( 'saml/xml/assertions/generic.xml', self._assertion_params, signed=True) @@ -227,7 +234,7 @@ class Processor: self._subject = sp_config self._subject_format = 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent' self._system_params = { - 'ISSUER': CONFIG.y('saml_idp.issuer'), + 'ISSUER': self._remote.issuer } def _validate_request(self): diff --git a/passbook/saml_idp/forms.py b/passbook/saml_idp/forms.py index 99341ee5b5..00f34bb985 100644 --- a/passbook/saml_idp/forms.py +++ b/passbook/saml_idp/forms.py @@ -2,7 +2,8 @@ from django import forms -from passbook.saml_idp.models import SAMLProvider, get_provider_choices +from passbook.saml_idp.models import (SAMLPropertyMapping, SAMLProvider, + get_provider_choices) from passbook.saml_idp.utils import CertificateBuilder @@ -21,7 +22,7 @@ class SAMLProviderForm(forms.ModelForm): class Meta: model = SAMLProvider - fields = ['name', 'acs_url', 'processor_path', 'issuer', + fields = ['name', 'property_mappings', 'acs_url', 'processor_path', 'issuer', 'assertion_valid_for', 'signing', 'signing_cert', 'signing_key', ] labels = { 'acs_url': 'ACS URL', @@ -31,3 +32,17 @@ class SAMLProviderForm(forms.ModelForm): 'name': forms.TextInput(), 'issuer': forms.TextInput(), } + + +class SAMLPropertyMappingForm(forms.ModelForm): + """SAML Property Mapping form""" + + class Meta: + + model = SAMLPropertyMapping + fields = ['name', 'saml_name', 'friendly_name', 'values'] + widgets = { + 'name': forms.TextInput(), + 'saml_name': forms.TextInput(), + 'friendly_name': forms.TextInput(), + } diff --git a/passbook/saml_idp/migrations/0002_samlpropertymapping.py b/passbook/saml_idp/migrations/0002_samlpropertymapping.py new file mode 100644 index 0000000000..7fe8de7205 --- /dev/null +++ b/passbook/saml_idp/migrations/0002_samlpropertymapping.py @@ -0,0 +1,30 @@ +# Generated by Django 2.1.7 on 2019-03-08 10:40 + +import django.contrib.postgres.fields +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('passbook_core', '0017_propertymapping'), + ('passbook_saml_idp', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='SAMLPropertyMapping', + fields=[ + ('propertymapping_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.PropertyMapping')), + ('saml_name', models.TextField()), + ('friendly_name', models.TextField(blank=True, default=None, null=True)), + ('values', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), size=None)), + ], + options={ + 'verbose_name': 'SAML Property Mapping', + 'verbose_name_plural': 'SAML Property Mappings', + }, + bases=('passbook_core.propertymapping',), + ), + ] diff --git a/passbook/saml_idp/models.py b/passbook/saml_idp/models.py index 42589e5741..4d9d2f4008 100644 --- a/passbook/saml_idp/models.py +++ b/passbook/saml_idp/models.py @@ -1,10 +1,11 @@ """passbook saml_idp Models""" +from django.contrib.postgres.fields import ArrayField from django.db import models from django.shortcuts import reverse from django.utils.translation import gettext as _ -from passbook.core.models import Provider +from passbook.core.models import PropertyMapping, Provider from passbook.lib.utils.reflection import class_to_path, path_to_class from passbook.saml_idp.base import Processor @@ -53,6 +54,23 @@ class SAMLProvider(Provider): verbose_name_plural = _('SAML Providers') +class SAMLPropertyMapping(PropertyMapping): + """SAML Property mapping, allowing Name/FriendlyName mapping to a list of strings""" + + saml_name = models.TextField() + friendly_name = models.TextField(default=None, blank=True, null=True) + values = ArrayField(models.TextField()) + + form = 'passbook.saml_idp.forms.SAMLPropertyMappingForm' + + def __str__(self): + return "SAML Property Mapping %s" % self.saml_name + + class Meta: + + verbose_name = _('SAML Property Mapping') + verbose_name_plural = _('SAML Property Mappings') + def get_provider_choices(): """Return tuple of class_path, class name of all providers.""" return [(class_to_path(x), x.__name__) for x in Processor.__subclasses__()] diff --git a/passbook/saml_idp/processors/aws.py b/passbook/saml_idp/processors/aws.py index 2b3c05ecad..44953b1fba 100644 --- a/passbook/saml_idp/processors/aws.py +++ b/passbook/saml_idp/processors/aws.py @@ -11,16 +11,12 @@ class AWSProcessor(Processor): def _format_assertion(self): """Formats _assertion_params as _assertion_xml.""" - self._assertion_params['ATTRIBUTES'] = [ + super()._format_assertion() + self._assertion_params['ATTRIBUTES'].append( { 'Name': 'https://aws.amazon.com/SAML/Attributes/RoleSessionName', 'Value': self._django_request.user.username, - }, - { - 'Name': 'https://aws.amazon.com/SAML/Attributes/Role', - # 'Value': 'arn:aws:iam::471432361072:saml-provider/passbook_dev, - # arn:aws:iam::471432361072:role/saml_role' } - ] + ) self._assertion_xml = xml_render.get_assertion_xml( 'saml/xml/assertions/generic.xml', self._assertion_params, signed=True) diff --git a/passbook/saml_idp/templates/saml/idp/login.html b/passbook/saml_idp/templates/saml/idp/login.html index a357a7b390..d8b929931b 100644 --- a/passbook/saml_idp/templates/saml/idp/login.html +++ b/passbook/saml_idp/templates/saml/idp/login.html @@ -9,40 +9,26 @@ {% block card %} -> - {% csrf_token %} - - - - - -
{% endblock %} + +{% block scripts %} +{{ block.super }} +{{ form.media.js }} +{% endblock %} diff --git a/passbook/core/forms/factors.py b/passbook/core/forms/factors.py index 30a5875466..11d0061c67 100644 --- a/passbook/core/forms/factors.py +++ b/passbook/core/forms/factors.py @@ -2,6 +2,7 @@ from django import forms from passbook.core.models import DummyFactor, PasswordFactor +from passbook.lib.fields import DynamicArrayField GENERAL_FIELDS = ['name', 'slug', 'order', 'policies', 'enabled'] @@ -16,6 +17,9 @@ class PasswordFactorForm(forms.ModelForm): 'name': forms.TextInput(), 'order': forms.NumberInput(), } + field_classes = { + 'backends': DynamicArrayField + } class DummyFactorForm(forms.ModelForm): """Form to create/edit Dummy Factor""" diff --git a/passbook/core/static/css/passbook.css b/passbook/core/static/css/passbook.css new file mode 100644 index 0000000000..86711d8b1d --- /dev/null +++ b/passbook/core/static/css/passbook.css @@ -0,0 +1,23 @@ +.dynamic-array-widget .array-item { + display: flex; + align-items: center; + margin-bottom: 15px; +} + +.dynamic-array-widget .remove_sign { + width: 10px; + height: 2px; + background: #a41515; + border-radius: 1px; +} + +.dynamic-array-widget .remove { + height: 15px; + display: flex; + align-items: center; + margin-left: 5px; +} + +.dynamic-array-widget .remove:hover { + cursor: pointer; +} diff --git a/passbook/core/static/js/passbook.js b/passbook/core/static/js/passbook.js index a52cfe21e5..cb89bbd27c 100644 --- a/passbook/core/static/js/passbook.js +++ b/passbook/core/static/js/passbook.js @@ -16,3 +16,33 @@ const typeHandler = function (e) { $source.on('input', typeHandler) // register for oninput $source.on('propertychange', typeHandler) // for IE8 + +window.addEventListener('load', function () { + + function addRemoveEventListener(widgetElement) { + widgetElement.querySelectorAll('.array-remove').forEach(function (element) { + element.addEventListener('click', function () { + this.parentNode.parentNode.remove(); + }); + }); + } + + document.querySelectorAll('.dynamic-array-widget').forEach(function (widgetElement) { + + addRemoveEventListener(widgetElement); + + widgetElement.querySelector('.add-array-item').addEventListener('click', function () { + var first = widgetElement.querySelector('.array-item'); + var newElement = first.cloneNode(true); + var id_parts = newElement.querySelector('input').getAttribute('id').split('_'); + var id = id_parts.slice(0, -1).join('_') + '_' + String(parseInt(id_parts.slice(-1)[0]) + 1); + newElement.querySelector('input').setAttribute('id', id); + newElement.querySelector('input').value = ''; + + addRemoveEventListener(newElement); + first.parentElement.insertBefore(newElement, first.parentNode.lastChild); + }); + + }); + +}); diff --git a/passbook/core/templates/base/skeleton.html b/passbook/core/templates/base/skeleton.html index 69a60d503b..dad2289969 100644 --- a/passbook/core/templates/base/skeleton.html +++ b/passbook/core/templates/base/skeleton.html @@ -16,6 +16,7 @@ + - {% block head %} + + + + + + {% block title %} + {% title %} {% endblock %} - </head> - <body {% if is_login %} class="login-pf" {% endif %}> - {% block body %} - {% endblock %} - <script src="{% static 'js/jquery.min.js' %}"></script> - <script src="{% static 'js/bootstrap.min.js' %}"></script> - <script src="{% static 'js/patternfly.min.js' %}"></script> - <script src="{% static 'js/passbook.js' %}"></script> - {% block scripts %} - {% endblock %} - <div class="modals"> - {% include 'partials/about_modal.html' %} - </div> - </body> + + + + + + + + {% block head %} + {% endblock %} + + + + {% if 'impersonate_id' in request.session %} +
+ + {% blocktrans with user=user %}You're currently impersonating {{ user }}.{% endblocktrans %} + {% trans 'Stop impersonation' %} + +
+ {% endif %} + {% block body %} + {% endblock %} + + + + + {% block scripts %} + {% endblock %} +
+ {% include 'partials/about_modal.html' %} +
+ + From a5dc193cfd92065fdf5da24aa94621e8a572cafd Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 10 Mar 2019 12:17:48 +0100 Subject: [PATCH 21/28] bump version: 0.1.8-beta -> 0.1.9-beta --- .bumpversion.cfg | 2 +- .gitlab-ci.yml | 2 +- helm/passbook/Chart.yaml | 4 ++-- helm/passbook/values.yaml | 2 +- passbook/__init__.py | 2 +- passbook/admin/__init__.py | 2 +- passbook/api/__init__.py | 2 +- passbook/audit/__init__.py | 2 +- passbook/captcha_factor/__init__.py | 2 +- passbook/core/__init__.py | 2 +- passbook/hibp_policy/__init__.py | 2 +- passbook/ldap/__init__.py | 2 +- passbook/lib/__init__.py | 2 +- passbook/oauth_client/__init__.py | 2 +- passbook/oauth_provider/__init__.py | 2 +- passbook/otp/__init__.py | 2 +- passbook/password_expiry_policy/__init__.py | 2 +- passbook/saml_idp/__init__.py | 2 +- 18 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 595a88bd77..4890f4910a 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.1.8-beta +current_version = 0.1.9-beta tag = True commit = True parse = (?P\d+)\.(?P\d+)\.(?P\d+)\-(?P.*) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4ae596d6e1..48447b42ca 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -53,7 +53,7 @@ package-docker: before_script: - echo "{\"auths\":{\"docker.$NEXUS_URL\":{\"auth\":\"$NEXUS_AUTH\"}}}" > /kaniko/.docker/config.json script: - - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.pkg.beryju.org/passbook:latest --destination docker.pkg.beryju.org/passbook:0.1.8-beta + - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.pkg.beryju.org/passbook:latest --destination docker.pkg.beryju.org/passbook:0.1.9-beta stage: build only: - tags diff --git a/helm/passbook/Chart.yaml b/helm/passbook/Chart.yaml index a3b6368e41..02d43573ca 100644 --- a/helm/passbook/Chart.yaml +++ b/helm/passbook/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v1 -appVersion: "0.1.8-beta" +appVersion: "0.1.9-beta" description: A Helm chart for passbook. name: passbook -version: "0.1.8-beta" +version: "0.1.9-beta" icon: https://passbook.beryju.org/images/logo.png diff --git a/helm/passbook/values.yaml b/helm/passbook/values.yaml index c0c7eff4fe..25edd8e9f2 100644 --- a/helm/passbook/values.yaml +++ b/helm/passbook/values.yaml @@ -5,7 +5,7 @@ replicaCount: 1 image: - tag: 0.1.8-beta + tag: 0.1.9-beta nameOverride: "" diff --git a/passbook/__init__.py b/passbook/__init__.py index 11653b3e00..fb410dcada 100644 --- a/passbook/__init__.py +++ b/passbook/__init__.py @@ -1,2 +1,2 @@ """passbook""" -__version__ = '0.1.8-beta' +__version__ = '0.1.9-beta' diff --git a/passbook/admin/__init__.py b/passbook/admin/__init__.py index 8b1e8c4aeb..7be3e847a6 100644 --- a/passbook/admin/__init__.py +++ b/passbook/admin/__init__.py @@ -1,2 +1,2 @@ """passbook admin""" -__version__ = '0.1.8-beta' +__version__ = '0.1.9-beta' diff --git a/passbook/api/__init__.py b/passbook/api/__init__.py index cd6f09ba5e..c08b616191 100644 --- a/passbook/api/__init__.py +++ b/passbook/api/__init__.py @@ -1,2 +1,2 @@ """passbook api""" -__version__ = '0.1.8-beta' +__version__ = '0.1.9-beta' diff --git a/passbook/audit/__init__.py b/passbook/audit/__init__.py index 875b919c05..9dad3e6865 100644 --- a/passbook/audit/__init__.py +++ b/passbook/audit/__init__.py @@ -1,2 +1,2 @@ """passbook audit Header""" -__version__ = '0.1.8-beta' +__version__ = '0.1.9-beta' diff --git a/passbook/captcha_factor/__init__.py b/passbook/captcha_factor/__init__.py index 57bd027230..81c8c7c190 100644 --- a/passbook/captcha_factor/__init__.py +++ b/passbook/captcha_factor/__init__.py @@ -1,2 +1,2 @@ """passbook captcha_factor Header""" -__version__ = '0.1.8-beta' +__version__ = '0.1.9-beta' diff --git a/passbook/core/__init__.py b/passbook/core/__init__.py index 1e3d2c6243..2ead9f0cff 100644 --- a/passbook/core/__init__.py +++ b/passbook/core/__init__.py @@ -1,2 +1,2 @@ """passbook core""" -__version__ = '0.1.8-beta' +__version__ = '0.1.9-beta' diff --git a/passbook/hibp_policy/__init__.py b/passbook/hibp_policy/__init__.py index bee6aa1079..9223c559b6 100644 --- a/passbook/hibp_policy/__init__.py +++ b/passbook/hibp_policy/__init__.py @@ -1,2 +1,2 @@ """passbook hibp_policy""" -__version__ = '0.1.8-beta' +__version__ = '0.1.9-beta' diff --git a/passbook/ldap/__init__.py b/passbook/ldap/__init__.py index 966d739491..87655cb406 100644 --- a/passbook/ldap/__init__.py +++ b/passbook/ldap/__init__.py @@ -1,2 +1,2 @@ """Passbook ldap app Header""" -__version__ = '0.1.8-beta' +__version__ = '0.1.9-beta' diff --git a/passbook/lib/__init__.py b/passbook/lib/__init__.py index 23656ff3fb..89b1092845 100644 --- a/passbook/lib/__init__.py +++ b/passbook/lib/__init__.py @@ -1,2 +1,2 @@ """passbook lib""" -__version__ = '0.1.8-beta' +__version__ = '0.1.9-beta' diff --git a/passbook/oauth_client/__init__.py b/passbook/oauth_client/__init__.py index 155a8400c7..e8667934ca 100644 --- a/passbook/oauth_client/__init__.py +++ b/passbook/oauth_client/__init__.py @@ -1,2 +1,2 @@ """passbook oauth_client Header""" -__version__ = '0.1.8-beta' +__version__ = '0.1.9-beta' diff --git a/passbook/oauth_provider/__init__.py b/passbook/oauth_provider/__init__.py index 213644f5ec..48a5abe678 100644 --- a/passbook/oauth_provider/__init__.py +++ b/passbook/oauth_provider/__init__.py @@ -1,2 +1,2 @@ """passbook oauth_provider Header""" -__version__ = '0.1.8-beta' +__version__ = '0.1.9-beta' diff --git a/passbook/otp/__init__.py b/passbook/otp/__init__.py index 339126d1c7..977ae5891f 100644 --- a/passbook/otp/__init__.py +++ b/passbook/otp/__init__.py @@ -1,2 +1,2 @@ """passbook otp Header""" -__version__ = '0.1.8-beta' +__version__ = '0.1.9-beta' diff --git a/passbook/password_expiry_policy/__init__.py b/passbook/password_expiry_policy/__init__.py index fcf49a8ec2..ef1c615cab 100644 --- a/passbook/password_expiry_policy/__init__.py +++ b/passbook/password_expiry_policy/__init__.py @@ -1,2 +1,2 @@ """passbook password_expiry""" -__version__ = '0.1.8-beta' +__version__ = '0.1.9-beta' diff --git a/passbook/saml_idp/__init__.py b/passbook/saml_idp/__init__.py index fb175dfa90..9ffa0b09c8 100644 --- a/passbook/saml_idp/__init__.py +++ b/passbook/saml_idp/__init__.py @@ -1,2 +1,2 @@ """passbook saml_idp Header""" -__version__ = '0.1.8-beta' +__version__ = '0.1.9-beta' From c012c6be5ca38229c7160e4be20a4d764d96012d Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 10 Mar 2019 15:34:24 +0100 Subject: [PATCH 22/28] fix k8s service routing http traffic to workers --- helm/passbook/templates/passbook-web-deployment.yaml | 1 + helm/passbook/templates/passbook-web-service.yaml | 1 + helm/passbook/templates/passbook-worker-deployment.yaml | 1 + 3 files changed, 3 insertions(+) diff --git a/helm/passbook/templates/passbook-web-deployment.yaml b/helm/passbook/templates/passbook-web-deployment.yaml index 78830221d3..868a067083 100644 --- a/helm/passbook/templates/passbook-web-deployment.yaml +++ b/helm/passbook/templates/passbook-web-deployment.yaml @@ -7,6 +7,7 @@ metadata: helm.sh/chart: {{ include "passbook.chart" . }} app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} + passbook.io/component: web spec: replicas: {{ .Values.replicaCount }} selector: diff --git a/helm/passbook/templates/passbook-web-service.yaml b/helm/passbook/templates/passbook-web-service.yaml index 1cbb65de60..9406e6c67a 100644 --- a/helm/passbook/templates/passbook-web-service.yaml +++ b/helm/passbook/templates/passbook-web-service.yaml @@ -17,3 +17,4 @@ spec: selector: app.kubernetes.io/name: {{ include "passbook.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} + passbook.io/component: web diff --git a/helm/passbook/templates/passbook-worker-deployment.yaml b/helm/passbook/templates/passbook-worker-deployment.yaml index f4b6c8513b..625bbd188f 100644 --- a/helm/passbook/templates/passbook-worker-deployment.yaml +++ b/helm/passbook/templates/passbook-worker-deployment.yaml @@ -7,6 +7,7 @@ metadata: helm.sh/chart: {{ include "passbook.chart" . }} app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} + passbook.io/component: worker spec: replicas: {{ .Values.replicaCount }} selector: From 7fe0300b86c592988db137142bcd1d6f769c3f06 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 10 Mar 2019 15:36:49 +0100 Subject: [PATCH 23/28] Fix button on policy test page --- passbook/admin/templates/administration/policy/test.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/passbook/admin/templates/administration/policy/test.html b/passbook/admin/templates/administration/policy/test.html index 6a5ddcbb10..ae4ebf84c3 100644 --- a/passbook/admin/templates/administration/policy/test.html +++ b/passbook/admin/templates/administration/policy/test.html @@ -5,3 +5,7 @@ {% block above_form %}

{% blocktrans with policy=policy %}Test policy {{ policy }}{% endblocktrans %}

{% endblock %} + +{% block action %} +{% trans 'Test' %} +{% endblock %} From 0e425418df4566f8cf100e2857856e454664e2e1 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 10 Mar 2019 15:46:49 +0100 Subject: [PATCH 24/28] better show loading state when testing a policy --- .../templates/administration/policy/test.html | 15 +++++++++++++++ passbook/admin/templates/generic/form.html | 2 ++ 2 files changed, 17 insertions(+) diff --git a/passbook/admin/templates/administration/policy/test.html b/passbook/admin/templates/administration/policy/test.html index ae4ebf84c3..932e197054 100644 --- a/passbook/admin/templates/administration/policy/test.html +++ b/passbook/admin/templates/administration/policy/test.html @@ -9,3 +9,18 @@ {% block action %} {% trans 'Test' %} {% endblock %} + +{% block beneath_form %} + +{% endblock %} + +{% block scripts %} +{{ block.super }} + +{% endblock %} diff --git a/passbook/admin/templates/generic/form.html b/passbook/admin/templates/generic/form.html index 4de06b3386..88bbec8567 100644 --- a/passbook/admin/templates/generic/form.html +++ b/passbook/admin/templates/generic/form.html @@ -19,6 +19,8 @@
+ {% block beneath_form %} + {% endblock %}
{% endblock %} From 42b30f450797278510c13b26e225dc4bc236d050 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 10 Mar 2019 15:53:38 +0100 Subject: [PATCH 25/28] prepare 0.1.10 release --- debian/changelog | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/debian/changelog b/debian/changelog index 20367e56cd..679515b614 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,17 @@ +passbook (0.1.10) stable; urgency=high + + * bump version: 0.1.7-beta -> 0.1.8-beta + * consistently using PolicyEngine + * add more Verbosity to PolicyEngine, rewrite SAML Authorisation check + * slightly refactor Factor View, add more unittests + * add impersonation middleware, add to templates + * bump version: 0.1.8-beta -> 0.1.9-beta + * fix k8s service routing http traffic to workers + * Fix button on policy test page + * better show loading state when testing a policy + + -- Jens Langhammer Sun, 10 Mar 2019 14:52:40 +0000 + passbook (0.1.7) stable; urgency=medium * bump version: 0.1.3-beta -> 0.1.4-beta From eebbae067730dfe756f013256df5453ef5d2a825 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 10 Mar 2019 15:54:50 +0100 Subject: [PATCH 26/28] bump version: 0.1.9-beta -> 0.1.10-beta --- .bumpversion.cfg | 2 +- .gitlab-ci.yml | 2 +- helm/passbook/Chart.yaml | 4 ++-- helm/passbook/values.yaml | 2 +- passbook/__init__.py | 2 +- passbook/admin/__init__.py | 2 +- passbook/api/__init__.py | 2 +- passbook/audit/__init__.py | 2 +- passbook/captcha_factor/__init__.py | 2 +- passbook/core/__init__.py | 2 +- passbook/hibp_policy/__init__.py | 2 +- passbook/ldap/__init__.py | 2 +- passbook/lib/__init__.py | 2 +- passbook/oauth_client/__init__.py | 2 +- passbook/oauth_provider/__init__.py | 2 +- passbook/otp/__init__.py | 2 +- passbook/password_expiry_policy/__init__.py | 2 +- passbook/saml_idp/__init__.py | 2 +- 18 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 4890f4910a..29c810a10c 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.1.9-beta +current_version = 0.1.10-beta tag = True commit = True parse = (?P\d+)\.(?P\d+)\.(?P\d+)\-(?P.*) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 48447b42ca..398fa5628e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -53,7 +53,7 @@ package-docker: before_script: - echo "{\"auths\":{\"docker.$NEXUS_URL\":{\"auth\":\"$NEXUS_AUTH\"}}}" > /kaniko/.docker/config.json script: - - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.pkg.beryju.org/passbook:latest --destination docker.pkg.beryju.org/passbook:0.1.9-beta + - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.pkg.beryju.org/passbook:latest --destination docker.pkg.beryju.org/passbook:0.1.10-beta stage: build only: - tags diff --git a/helm/passbook/Chart.yaml b/helm/passbook/Chart.yaml index 02d43573ca..5b133024d7 100644 --- a/helm/passbook/Chart.yaml +++ b/helm/passbook/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v1 -appVersion: "0.1.9-beta" +appVersion: "0.1.10-beta" description: A Helm chart for passbook. name: passbook -version: "0.1.9-beta" +version: "0.1.10-beta" icon: https://passbook.beryju.org/images/logo.png diff --git a/helm/passbook/values.yaml b/helm/passbook/values.yaml index 25edd8e9f2..cf75920f8c 100644 --- a/helm/passbook/values.yaml +++ b/helm/passbook/values.yaml @@ -5,7 +5,7 @@ replicaCount: 1 image: - tag: 0.1.9-beta + tag: 0.1.10-beta nameOverride: "" diff --git a/passbook/__init__.py b/passbook/__init__.py index fb410dcada..700e02bb31 100644 --- a/passbook/__init__.py +++ b/passbook/__init__.py @@ -1,2 +1,2 @@ """passbook""" -__version__ = '0.1.9-beta' +__version__ = '0.1.10-beta' diff --git a/passbook/admin/__init__.py b/passbook/admin/__init__.py index 7be3e847a6..f772a64223 100644 --- a/passbook/admin/__init__.py +++ b/passbook/admin/__init__.py @@ -1,2 +1,2 @@ """passbook admin""" -__version__ = '0.1.9-beta' +__version__ = '0.1.10-beta' diff --git a/passbook/api/__init__.py b/passbook/api/__init__.py index c08b616191..0c5b0717a5 100644 --- a/passbook/api/__init__.py +++ b/passbook/api/__init__.py @@ -1,2 +1,2 @@ """passbook api""" -__version__ = '0.1.9-beta' +__version__ = '0.1.10-beta' diff --git a/passbook/audit/__init__.py b/passbook/audit/__init__.py index 9dad3e6865..ee9db2eccd 100644 --- a/passbook/audit/__init__.py +++ b/passbook/audit/__init__.py @@ -1,2 +1,2 @@ """passbook audit Header""" -__version__ = '0.1.9-beta' +__version__ = '0.1.10-beta' diff --git a/passbook/captcha_factor/__init__.py b/passbook/captcha_factor/__init__.py index 81c8c7c190..814a2c720b 100644 --- a/passbook/captcha_factor/__init__.py +++ b/passbook/captcha_factor/__init__.py @@ -1,2 +1,2 @@ """passbook captcha_factor Header""" -__version__ = '0.1.9-beta' +__version__ = '0.1.10-beta' diff --git a/passbook/core/__init__.py b/passbook/core/__init__.py index 2ead9f0cff..34384aea8d 100644 --- a/passbook/core/__init__.py +++ b/passbook/core/__init__.py @@ -1,2 +1,2 @@ """passbook core""" -__version__ = '0.1.9-beta' +__version__ = '0.1.10-beta' diff --git a/passbook/hibp_policy/__init__.py b/passbook/hibp_policy/__init__.py index 9223c559b6..0630e11015 100644 --- a/passbook/hibp_policy/__init__.py +++ b/passbook/hibp_policy/__init__.py @@ -1,2 +1,2 @@ """passbook hibp_policy""" -__version__ = '0.1.9-beta' +__version__ = '0.1.10-beta' diff --git a/passbook/ldap/__init__.py b/passbook/ldap/__init__.py index 87655cb406..f23eb47eaa 100644 --- a/passbook/ldap/__init__.py +++ b/passbook/ldap/__init__.py @@ -1,2 +1,2 @@ """Passbook ldap app Header""" -__version__ = '0.1.9-beta' +__version__ = '0.1.10-beta' diff --git a/passbook/lib/__init__.py b/passbook/lib/__init__.py index 89b1092845..755c6777fc 100644 --- a/passbook/lib/__init__.py +++ b/passbook/lib/__init__.py @@ -1,2 +1,2 @@ """passbook lib""" -__version__ = '0.1.9-beta' +__version__ = '0.1.10-beta' diff --git a/passbook/oauth_client/__init__.py b/passbook/oauth_client/__init__.py index e8667934ca..6a32e8af18 100644 --- a/passbook/oauth_client/__init__.py +++ b/passbook/oauth_client/__init__.py @@ -1,2 +1,2 @@ """passbook oauth_client Header""" -__version__ = '0.1.9-beta' +__version__ = '0.1.10-beta' diff --git a/passbook/oauth_provider/__init__.py b/passbook/oauth_provider/__init__.py index 48a5abe678..a2a618929f 100644 --- a/passbook/oauth_provider/__init__.py +++ b/passbook/oauth_provider/__init__.py @@ -1,2 +1,2 @@ """passbook oauth_provider Header""" -__version__ = '0.1.9-beta' +__version__ = '0.1.10-beta' diff --git a/passbook/otp/__init__.py b/passbook/otp/__init__.py index 977ae5891f..09b157720b 100644 --- a/passbook/otp/__init__.py +++ b/passbook/otp/__init__.py @@ -1,2 +1,2 @@ """passbook otp Header""" -__version__ = '0.1.9-beta' +__version__ = '0.1.10-beta' diff --git a/passbook/password_expiry_policy/__init__.py b/passbook/password_expiry_policy/__init__.py index ef1c615cab..d63ba506ef 100644 --- a/passbook/password_expiry_policy/__init__.py +++ b/passbook/password_expiry_policy/__init__.py @@ -1,2 +1,2 @@ """passbook password_expiry""" -__version__ = '0.1.9-beta' +__version__ = '0.1.10-beta' diff --git a/passbook/saml_idp/__init__.py b/passbook/saml_idp/__init__.py index 9ffa0b09c8..907a72ce9a 100644 --- a/passbook/saml_idp/__init__.py +++ b/passbook/saml_idp/__init__.py @@ -1,2 +1,2 @@ """passbook saml_idp Header""" -__version__ = '0.1.9-beta' +__version__ = '0.1.10-beta' From c4b429825dd95adb9ee2893a215ea90d52edbb8e Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 10 Mar 2019 16:39:41 +0100 Subject: [PATCH 27/28] fix helm labels being on deployments and not pods --- helm/passbook/templates/passbook-web-deployment.yaml | 2 +- helm/passbook/templates/passbook-worker-deployment.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/helm/passbook/templates/passbook-web-deployment.yaml b/helm/passbook/templates/passbook-web-deployment.yaml index 868a067083..2308300cab 100644 --- a/helm/passbook/templates/passbook-web-deployment.yaml +++ b/helm/passbook/templates/passbook-web-deployment.yaml @@ -7,7 +7,6 @@ metadata: helm.sh/chart: {{ include "passbook.chart" . }} app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} - passbook.io/component: web spec: replicas: {{ .Values.replicaCount }} selector: @@ -19,6 +18,7 @@ spec: labels: app.kubernetes.io/name: {{ include "passbook.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} + passbook.io/component: web spec: volumes: - name: config-volume diff --git a/helm/passbook/templates/passbook-worker-deployment.yaml b/helm/passbook/templates/passbook-worker-deployment.yaml index 625bbd188f..79cd11002a 100644 --- a/helm/passbook/templates/passbook-worker-deployment.yaml +++ b/helm/passbook/templates/passbook-worker-deployment.yaml @@ -7,7 +7,6 @@ metadata: helm.sh/chart: {{ include "passbook.chart" . }} app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} - passbook.io/component: worker spec: replicas: {{ .Values.replicaCount }} selector: @@ -19,6 +18,7 @@ spec: labels: app.kubernetes.io/name: {{ include "passbook.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} + passbook.io/component: worker spec: volumes: - name: config-volume From 5e11b6687edf801326d7245ec190e6a79affcf3c Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 10 Mar 2019 17:08:33 +0100 Subject: [PATCH 28/28] automatically deploy after release --- .gitlab-ci.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 398fa5628e..01729e1006 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,6 +8,7 @@ stages: - test - build - docs + - deploy image: python:3.6 services: - postgres:latest @@ -113,3 +114,18 @@ package-debian: # - mkdocs build # - 'rsync -avh --delete web/* "beryjuorg@ory1-web-prod-1.ory1.beryju.org:passbook.beryju.org/"' # - 'rsync -avh --delete site/* "beryjuorg@ory1-web-prod-1.ory1.beryju.org:passbook.beryju.org/docs/"' + +deploy: + environment: + name: production + url: https://passbook-prod.default.k8s.beryju.org/ + stage: deploy + only: + - tags + - /^version/.*$/ + script: + - curl https://raw.githubusercontent.com/helm/helm/master/scripts/get | bash + - helm init --client-only + - helm repo add beryju.org https://pkg.beryju.org/repository/helm/ + - helm repo update + - helm upgrade passbook-prod beryju.org/passbook --devel