*: rewrite user settings to use a single page
This commit is contained in:
parent
be8cc77086
commit
fcf763ed3e
|
@ -18,7 +18,7 @@ from structlog import get_logger
|
||||||
|
|
||||||
from passbook.core.exceptions import PropertyMappingExpressionException
|
from passbook.core.exceptions import PropertyMappingExpressionException
|
||||||
from passbook.core.signals import password_changed
|
from passbook.core.signals import password_changed
|
||||||
from passbook.core.types import UILoginButton, UIUserSettings
|
from passbook.core.types import UILoginButton
|
||||||
from passbook.flows.models import Flow
|
from passbook.flows.models import Flow
|
||||||
from passbook.lib.models import CreatedUpdatedModel
|
from passbook.lib.models import CreatedUpdatedModel
|
||||||
from passbook.policies.models import PolicyBindingModel
|
from passbook.policies.models import PolicyBindingModel
|
||||||
|
@ -249,9 +249,9 @@ class Source(PolicyBindingModel):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ui_user_settings(self) -> Optional[UIUserSettings]:
|
def ui_user_settings(self) -> Optional[str]:
|
||||||
"""Entrypoint to integrate with User settings. Can either return None if no
|
"""Entrypoint to integrate with User settings. Can either return None if no
|
||||||
user settings are available, or an instanace of UIUserSettings."""
|
user settings are available, or a string with the URL to fetch."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
{% extends "base/page.html" %}
|
|
||||||
|
|
||||||
{% load i18n %}
|
|
||||||
{% load passbook_is_active %}
|
|
||||||
{% load static %}
|
|
||||||
{% load passbook_user_settings %}
|
|
||||||
|
|
||||||
{% block page_content %}
|
|
||||||
<div class="pf-c-page__sidebar">
|
|
||||||
<div class="pf-c-page__sidebar-body">
|
|
||||||
<nav class="pf-c-nav" id="page-default-nav-example-primary-nav" aria-label="Global">
|
|
||||||
<section class="pf-c-nav__section">
|
|
||||||
<h2 class="pf-c-nav__section-title">{% trans 'General Settings' %}</h2>
|
|
||||||
<ul class="pf-c-nav__list">
|
|
||||||
<li class="pf-c-nav__item">
|
|
||||||
<a href="{% url 'passbook_core:user-settings' %}"
|
|
||||||
class="pf-c-nav__link {% is_active 'passbook_core:user-settings' %}">{% trans 'User Details' %}</a>
|
|
||||||
</li>
|
|
||||||
<li class="pf-c-nav__item">
|
|
||||||
<a href="{% url 'passbook_core:user-tokens' %}"
|
|
||||||
class="pf-c-nav__link {% is_active 'passbook_core:user-tokens' 'passbook_core:user-tokens-create' 'passbook_core:user-tokens-update' 'passbook_core:user-tokens-delete' %}">{% trans 'Tokens' %}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
{% user_stages as user_stages_loc %}
|
|
||||||
{% if user_stages_loc %}
|
|
||||||
<section class="pf-c-nav__section">
|
|
||||||
<h2 class="pf-c-nav__section-title">{% trans 'Stages' %}</h2>
|
|
||||||
<ul class="pf-c-nav__list">
|
|
||||||
{% for stage in user_stages_loc %}
|
|
||||||
<li class="pf-c-nav__item">
|
|
||||||
<a href="{{ stage.url }}" class="pf-c-nav__link {% if stage.url == request.get_full_path %} pf-m-current {% endif %}">
|
|
||||||
{{ stage.name }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
{% endif %}
|
|
||||||
{% user_sources as user_sources_loc %}
|
|
||||||
{% if user_sources_loc %}
|
|
||||||
<section class="pf-c-nav__section">
|
|
||||||
<h2 class="pf-c-nav__section-title">{% trans 'Sources' %}</h2>
|
|
||||||
<ul class="pf-c-nav__list">
|
|
||||||
{% for source in user_sources_loc %}
|
|
||||||
<li class="pf-c-nav__item">
|
|
||||||
<a href="{{ source.url }}"
|
|
||||||
class="pf-c-nav__link {% if source.url == request.get_full_path %} pf-m-current {% endif %}">
|
|
||||||
{{ source.name }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
{% endif %}
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
|
||||||
{% block content %}
|
|
||||||
<section class="pf-c-page__main-section">
|
|
||||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
|
||||||
<div class="pf-u-w-75">
|
|
||||||
{% block page %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
{% endblock %}
|
|
||||||
</main>
|
|
||||||
{% endblock %}
|
|
|
@ -1,28 +1,78 @@
|
||||||
{% extends "user/base.html" %}
|
|
||||||
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
{% load passbook_user_settings %}
|
||||||
|
|
||||||
{% block page %}
|
<div class="pf-c-page">
|
||||||
<div class="pf-c-card">
|
<main role="main" class="pf-c-page__main" tabindex="-1">
|
||||||
<div class="pf-c-card__header pf-c-title pf-m-md">
|
<section class="pf-c-page__main-section pf-m-light">
|
||||||
{% trans 'Update details' %}
|
<div class="pf-c-content">
|
||||||
</div>
|
<h1>
|
||||||
<div class="pf-c-card__body">
|
<i class="pf-icon pf-icon-user"></i>
|
||||||
<form action="" method="post" class="pf-c-form pf-m-horizontal">
|
{% trans 'User Settings' %}
|
||||||
{% include 'partials/form_horizontal.html' with form=form %}
|
</h1>
|
||||||
{% block beneath_form %}
|
<p>{% trans "Configure settings relevant to your user profile." %}</p>
|
||||||
{% endblock %}
|
</div>
|
||||||
<div class="pf-c-form__group pf-m-action">
|
</section>
|
||||||
<div class="pf-c-form__horizontal-group">
|
<section class="pf-c-page__main-section">
|
||||||
<div class="pf-c-form__actions">
|
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||||
<input class="pf-c-button pf-m-primary" type="submit" value="{% trans 'Update' %}" />
|
<div class="pf-u-w-75">
|
||||||
{% if unenrollment_enabled %}
|
<div class="pf-c-card">
|
||||||
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_flows:default-unenrollment' %}?back={{ request.get_full_path }}">{% trans "Delete account" %}</a>
|
<div class="pf-c-card__header pf-c-title pf-m-md">
|
||||||
{% endif %}
|
{% trans 'Update details' %}
|
||||||
|
</div>
|
||||||
|
<div class="pf-c-card__body">
|
||||||
|
<form action="" method="post" class="pf-c-form pf-m-horizontal">
|
||||||
|
{% include 'partials/form_horizontal.html' with form=form %}
|
||||||
|
{% block beneath_form %}
|
||||||
|
{% endblock %}
|
||||||
|
<div class="pf-c-form__group pf-m-action">
|
||||||
|
<div class="pf-c-form__horizontal-group">
|
||||||
|
<div class="pf-c-form__actions">
|
||||||
|
<input class="pf-c-button pf-m-primary" type="submit" value="{% trans 'Update' %}" />
|
||||||
|
{% if unenrollment_enabled %}
|
||||||
|
<a class="pf-c-button pf-m-danger"
|
||||||
|
href="{% url 'passbook_flows:default-unenrollment' %}?back={{ request.get_full_path }}">{% trans "Delete account" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</section>
|
||||||
</div>
|
<section class="pf-c-page__main-section">
|
||||||
|
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||||
|
<div class="pf-u-w-75">
|
||||||
|
<pb-site-shell url="{% url 'passbook_core:user-tokens' %}">
|
||||||
|
<div slot="body"></div>
|
||||||
|
</pb-site-shell>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% user_stages as user_stages_loc %}
|
||||||
|
{% for stage in user_stages_loc %}
|
||||||
|
<section class="pf-c-page__main-section">
|
||||||
|
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||||
|
<div class="pf-u-w-75">
|
||||||
|
<pb-site-shell url="{{ stage }}">
|
||||||
|
<div slot="body"></div>
|
||||||
|
</pb-site-shell>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endfor %}
|
||||||
|
{% user_sources as user_sources_loc %}
|
||||||
|
{% for source in user_sources_loc %}
|
||||||
|
<section class="pf-c-page__main-section">
|
||||||
|
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||||
|
<div class="pf-u-w-75">
|
||||||
|
<pb-site-shell url="{{ source }}">
|
||||||
|
<div slot="body"></div>
|
||||||
|
</pb-site-shell>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endfor %}
|
||||||
|
</main>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -1,91 +1,81 @@
|
||||||
{% extends "user/base.html" %}
|
|
||||||
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load passbook_utils %}
|
|
||||||
|
|
||||||
{% block content %}
|
<div class="pf-c-card">
|
||||||
<section class="pf-c-page__main-section pf-m-light">
|
<div class="pf-c-card__header pf-c-title pf-m-md">
|
||||||
<div class="pf-c-content">
|
|
||||||
<h1>
|
<h1>
|
||||||
<i class="pf-icon pf-icon-users"></i>
|
<i class="pf-icon pf-icon-users"></i>
|
||||||
{% trans 'Tokens' %}
|
{% trans 'Manage Tokens' %}
|
||||||
</h1>
|
</h1>
|
||||||
<p>{% trans "Tokens can be used to access passbook's API." %}
|
<p>{% trans "Tokens can be used to access passbook's API." %}</p>
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
{% if object_list %}
|
||||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
<div class="pf-c-toolbar">
|
||||||
<div class="pf-c-card">
|
<div class="pf-c-toolbar__content">
|
||||||
{% if object_list %}
|
{% include 'partials/toolbar_search.html' %}
|
||||||
<div class="pf-c-toolbar">
|
<div class="pf-c-toolbar__bulk-select">
|
||||||
<div class="pf-c-toolbar__content">
|
<a href="{% url 'passbook_core:user-tokens-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
|
||||||
{% include 'partials/toolbar_search.html' %}
|
|
||||||
<div class="pf-c-toolbar__bulk-select">
|
|
||||||
<a href="{% url 'passbook_core:user-tokens-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
|
|
||||||
</div>
|
|
||||||
{% include 'partials/pagination.html' %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
|
||||||
<thead>
|
|
||||||
<tr role="row">
|
|
||||||
<th role="columnheader" scope="col">{% trans 'Identifier' %}</th>
|
|
||||||
<th role="columnheader" scope="col">{% trans 'Expires?' %}</th>
|
|
||||||
<th role="columnheader" scope="col">{% trans 'Expiry Date' %}</th>
|
|
||||||
<th role="columnheader" scope="col">{% trans 'Description' %}</th>
|
|
||||||
<th role="cell"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody role="rowgroup">
|
|
||||||
{% for token in object_list %}
|
|
||||||
<tr role="row">
|
|
||||||
<th role="columnheader">
|
|
||||||
<div>{{ token.identifier }}</div>
|
|
||||||
</th>
|
|
||||||
<td role="cell">
|
|
||||||
<span>
|
|
||||||
{{ token.expiring|yesno:"Yes,No" }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td role="cell">
|
|
||||||
<span>
|
|
||||||
{% if not token.expiring %}
|
|
||||||
-
|
|
||||||
{% else %}
|
|
||||||
{{ token.expires }}
|
|
||||||
{% endif %}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td role="cell">
|
|
||||||
<span>
|
|
||||||
{{ token.description }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a class="pf-c-button pf-m-secondary" href="{% url 'passbook_core:user-tokens-update' identifier=token.identifier %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
|
|
||||||
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_core:user-tokens-delete' identifier=token.identifier %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div class="pf-c-pagination pf-m-bottom">
|
|
||||||
{% include 'partials/pagination.html' %}
|
{% include 'partials/pagination.html' %}
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
|
||||||
<div class="pf-c-empty-state">
|
|
||||||
<div class="pf-c-empty-state__content">
|
|
||||||
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
|
|
||||||
<h1 class="pf-c-title pf-m-lg">
|
|
||||||
{% trans 'No Tokens.' %}
|
|
||||||
</h1>
|
|
||||||
<div class="pf-c-empty-state__body">
|
|
||||||
{% trans 'Currently no tokens exist. Click the button below to create one.' %}
|
|
||||||
</div>
|
|
||||||
<a href="{% url 'passbook_core:user-tokens-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||||
{% endblock %}
|
<thead>
|
||||||
|
<tr role="row">
|
||||||
|
<th role="columnheader" scope="col">{% trans 'Identifier' %}</th>
|
||||||
|
<th role="columnheader" scope="col">{% trans 'Expires?' %}</th>
|
||||||
|
<th role="columnheader" scope="col">{% trans 'Expiry Date' %}</th>
|
||||||
|
<th role="columnheader" scope="col">{% trans 'Description' %}</th>
|
||||||
|
<th role="cell"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody role="rowgroup">
|
||||||
|
{% for token in object_list %}
|
||||||
|
<tr role="row">
|
||||||
|
<th role="columnheader">
|
||||||
|
<div>{{ token.identifier }}</div>
|
||||||
|
</th>
|
||||||
|
<td role="cell">
|
||||||
|
<span>
|
||||||
|
{{ token.expiring|yesno:"Yes,No" }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td role="cell">
|
||||||
|
<span>
|
||||||
|
{% if not token.expiring %}
|
||||||
|
-
|
||||||
|
{% else %}
|
||||||
|
{{ token.expires }}
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td role="cell">
|
||||||
|
<span>
|
||||||
|
{{ token.description }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a class="pf-c-button pf-m-secondary" href="{% url 'passbook_core:user-tokens-update' identifier=token.identifier %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
|
||||||
|
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_core:user-tokens-delete' identifier=token.identifier %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="pf-c-pagination pf-m-bottom">
|
||||||
|
{% include 'partials/pagination.html' %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="pf-c-empty-state">
|
||||||
|
<div class="pf-c-empty-state__content">
|
||||||
|
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||||
|
<h1 class="pf-c-title pf-m-lg">
|
||||||
|
{% trans 'No Tokens.' %}
|
||||||
|
</h1>
|
||||||
|
<div class="pf-c-empty-state__body">
|
||||||
|
{% trans 'Currently no tokens exist. Click the button below to create one.' %}
|
||||||
|
</div>
|
||||||
|
<a href="{% url 'passbook_core:user-tokens-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
"""passbook user settings template tags"""
|
"""passbook user settings template tags"""
|
||||||
from typing import Iterable, List
|
from typing import Iterable
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
from django.template.context import RequestContext
|
from django.template.context import RequestContext
|
||||||
|
|
||||||
from passbook.core.models import Source
|
from passbook.core.models import Source
|
||||||
from passbook.core.types import UIUserSettings
|
|
||||||
from passbook.flows.models import Stage
|
from passbook.flows.models import Stage
|
||||||
from passbook.policies.engine import PolicyEngine
|
from passbook.policies.engine import PolicyEngine
|
||||||
|
|
||||||
|
@ -14,26 +13,26 @@ register = template.Library()
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def user_stages(context: RequestContext) -> List[UIUserSettings]:
|
def user_stages(context: RequestContext) -> list[str]:
|
||||||
"""Return list of all stages which apply to user"""
|
"""Return list of all stages which apply to user"""
|
||||||
_all_stages: Iterable[Stage] = Stage.objects.all().select_subclasses()
|
_all_stages: Iterable[Stage] = Stage.objects.all().select_subclasses()
|
||||||
matching_stages: List[UIUserSettings] = []
|
matching_stages: list[str] = []
|
||||||
for stage in _all_stages:
|
for stage in _all_stages:
|
||||||
user_settings = stage.ui_user_settings
|
user_settings = stage.ui_user_settings
|
||||||
if not user_settings:
|
if not user_settings:
|
||||||
continue
|
continue
|
||||||
matching_stages.append(user_settings)
|
matching_stages.append(user_settings)
|
||||||
return sorted(matching_stages, key=lambda x: x.name)
|
return matching_stages
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def user_sources(context: RequestContext) -> List[UIUserSettings]:
|
def user_sources(context: RequestContext) -> list[str]:
|
||||||
"""Return a list of all sources which are enabled for the user"""
|
"""Return a list of all sources which are enabled for the user"""
|
||||||
user = context.get("request").user
|
user = context.get("request").user
|
||||||
_all_sources: Iterable[Source] = Source.objects.filter(
|
_all_sources: Iterable[Source] = Source.objects.filter(
|
||||||
enabled=True
|
enabled=True
|
||||||
).select_subclasses()
|
).select_subclasses()
|
||||||
matching_sources: List[UIUserSettings] = []
|
matching_sources: list[str] = []
|
||||||
for source in _all_sources:
|
for source in _all_sources:
|
||||||
user_settings = source.ui_user_settings
|
user_settings = source.ui_user_settings
|
||||||
if not user_settings:
|
if not user_settings:
|
||||||
|
@ -42,4 +41,4 @@ def user_sources(context: RequestContext) -> List[UIUserSettings]:
|
||||||
policy_engine.build()
|
policy_engine.build()
|
||||||
if policy_engine.passing:
|
if policy_engine.passing:
|
||||||
matching_sources.append(user_settings)
|
matching_sources.append(user_settings)
|
||||||
return sorted(matching_sources, key=lambda x: x.name)
|
return matching_sources
|
||||||
|
|
|
@ -3,17 +3,9 @@ from dataclasses import dataclass
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class UIUserSettings:
|
|
||||||
"""Dataclass for Stage and Source's user_settings"""
|
|
||||||
|
|
||||||
name: str
|
|
||||||
url: str
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class UILoginButton:
|
class UILoginButton:
|
||||||
"""Dataclass for Source's ui_ui_login_button"""
|
"""Dataclass for Source's ui_login_button"""
|
||||||
|
|
||||||
# Name, ran through i18n
|
# Name, ran through i18n
|
||||||
name: str
|
name: str
|
||||||
|
|
|
@ -10,7 +10,6 @@ from model_utils.managers import InheritanceManager
|
||||||
from rest_framework.serializers import BaseSerializer
|
from rest_framework.serializers import BaseSerializer
|
||||||
from structlog import get_logger
|
from structlog import get_logger
|
||||||
|
|
||||||
from passbook.core.types import UIUserSettings
|
|
||||||
from passbook.lib.models import InheritanceForeignKey, SerializerModel
|
from passbook.lib.models import InheritanceForeignKey, SerializerModel
|
||||||
from passbook.policies.models import PolicyBindingModel
|
from passbook.policies.models import PolicyBindingModel
|
||||||
|
|
||||||
|
@ -64,9 +63,9 @@ class Stage(SerializerModel):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ui_user_settings(self) -> Optional[UIUserSettings]:
|
def ui_user_settings(self) -> Optional[str]:
|
||||||
"""Entrypoint to integrate with User settings. Can either return None if no
|
"""Entrypoint to integrate with User settings. Can either return None if no
|
||||||
user settings are available, or an instanace of UIUserSettings."""
|
user settings are available, or a string with the URL to fetch."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
|
@ -7,7 +7,7 @@ from django.urls import reverse, reverse_lazy
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from passbook.core.models import Source, UserSourceConnection
|
from passbook.core.models import Source, UserSourceConnection
|
||||||
from passbook.core.types import UILoginButton, UIUserSettings
|
from passbook.core.types import UILoginButton
|
||||||
|
|
||||||
|
|
||||||
class OAuthSource(Source):
|
class OAuthSource(Source):
|
||||||
|
@ -66,12 +66,9 @@ class OAuthSource(Source):
|
||||||
return f"Callback URL: <pre>{url}</pre>"
|
return f"Callback URL: <pre>{url}</pre>"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ui_user_settings(self) -> Optional[UIUserSettings]:
|
def ui_user_settings(self) -> Optional[str]:
|
||||||
view_name = "passbook_sources_oauth:oauth-client-user"
|
view_name = "passbook_sources_oauth:oauth-client-user"
|
||||||
return UIUserSettings(
|
return reverse(view_name, kwargs={"source_slug": self.slug})
|
||||||
name=self.name,
|
|
||||||
url=reverse(view_name, kwargs={"source_slug": self.slug}),
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"OAuth Source {self.name}"
|
return f"OAuth Source {self.name}"
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
{% extends "user/base.html" %}
|
|
||||||
|
|
||||||
{% load passbook_utils %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page %}
|
|
||||||
<div class="pf-c-card">
|
<div class="pf-c-card">
|
||||||
<div class="pf-c-card__header pf-c-title pf-m-md">
|
<div class="pf-c-card__header pf-c-title pf-m-md">
|
||||||
{% blocktrans with source_name=source.name %}
|
{% blocktrans with source_name=source.name %}
|
||||||
|
@ -26,4 +22,3 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from rest_framework.serializers import BaseSerializer
|
from rest_framework.serializers import BaseSerializer
|
||||||
|
|
||||||
from passbook.core.types import UIUserSettings
|
|
||||||
from passbook.flows.models import ConfigurableStage, Stage
|
from passbook.flows.models import ConfigurableStage, Stage
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,14 +35,11 @@ class OTPStaticStage(ConfigurableStage, Stage):
|
||||||
return OTPStaticStageForm
|
return OTPStaticStageForm
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ui_user_settings(self) -> Optional[UIUserSettings]:
|
def ui_user_settings(self) -> Optional[str]:
|
||||||
return UIUserSettings(
|
return reverse(
|
||||||
name="Static OTP",
|
|
||||||
url=reverse(
|
|
||||||
"passbook_stages_otp_static:user-settings",
|
"passbook_stages_otp_static:user-settings",
|
||||||
kwargs={"stage_uuid": self.stage_uuid},
|
kwargs={"stage_uuid": self.stage_uuid},
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"OTP Static Stage {self.name}"
|
return f"OTP Static Stage {self.name}"
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
{% extends "user/base.html" %}
|
|
||||||
|
|
||||||
{% load passbook_utils %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page %}
|
|
||||||
<div class="pf-c-card">
|
<div class="pf-c-card">
|
||||||
<div class="pf-c-card__header pf-c-title pf-m-md">
|
<div class="pf-c-card__header pf-c-title pf-m-md">
|
||||||
{% trans "Static One-Time Passwords" %}
|
{% trans "Static One-Time Passwords" %}
|
||||||
|
@ -33,4 +29,3 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from rest_framework.serializers import BaseSerializer
|
from rest_framework.serializers import BaseSerializer
|
||||||
|
|
||||||
from passbook.core.types import UIUserSettings
|
|
||||||
from passbook.flows.models import ConfigurableStage, Stage
|
from passbook.flows.models import ConfigurableStage, Stage
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,14 +42,11 @@ class OTPTimeStage(ConfigurableStage, Stage):
|
||||||
return OTPTimeStageForm
|
return OTPTimeStageForm
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ui_user_settings(self) -> Optional[UIUserSettings]:
|
def ui_user_settings(self) -> Optional[str]:
|
||||||
return UIUserSettings(
|
return reverse(
|
||||||
name="Time-based OTP",
|
|
||||||
url=reverse(
|
|
||||||
"passbook_stages_otp_time:user-settings",
|
"passbook_stages_otp_time:user-settings",
|
||||||
kwargs={"stage_uuid": self.stage_uuid},
|
kwargs={"stage_uuid": self.stage_uuid},
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"OTP Time (TOTP) Stage {self.name}"
|
return f"OTP Time (TOTP) Stage {self.name}"
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
{% extends "user/base.html" %}
|
|
||||||
|
|
||||||
{% load passbook_utils %}
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page %}
|
|
||||||
<div class="pf-c-card">
|
<div class="pf-c-card">
|
||||||
<div class="pf-c-card__header pf-c-title pf-m-md">
|
<div class="pf-c-card__header pf-c-title pf-m-md">
|
||||||
{% trans "Time-based One-Time Passwords" %}
|
{% trans "Time-based One-Time Passwords" %}
|
||||||
|
@ -30,4 +26,3 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -8,3 +8,4 @@ class PassbookStagePasswordConfig(AppConfig):
|
||||||
name = "passbook.stages.password"
|
name = "passbook.stages.password"
|
||||||
label = "passbook_stages_password"
|
label = "passbook_stages_password"
|
||||||
verbose_name = "passbook Stages.Password"
|
verbose_name = "passbook Stages.Password"
|
||||||
|
mountpoint = "-/user/password/"
|
||||||
|
|
|
@ -4,15 +4,12 @@ from typing import Optional, Type
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.forms import ModelForm
|
from django.forms import ModelForm
|
||||||
from django.shortcuts import reverse
|
|
||||||
from django.utils.http import urlencode
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from rest_framework.serializers import BaseSerializer
|
from rest_framework.serializers import BaseSerializer
|
||||||
|
from django.shortcuts import reverse
|
||||||
|
|
||||||
from passbook.core.types import UIUserSettings
|
|
||||||
from passbook.flows.models import ConfigurableStage, Stage
|
from passbook.flows.models import ConfigurableStage, Stage
|
||||||
from passbook.flows.views import NEXT_ARG_NAME
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordStage(ConfigurableStage, Stage):
|
class PasswordStage(ConfigurableStage, Stage):
|
||||||
|
@ -51,12 +48,10 @@ class PasswordStage(ConfigurableStage, Stage):
|
||||||
return PasswordStageForm
|
return PasswordStageForm
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ui_user_settings(self) -> Optional[UIUserSettings]:
|
def ui_user_settings(self) -> Optional[str]:
|
||||||
if not self.configure_flow:
|
if not self.configure_flow:
|
||||||
return None
|
return None
|
||||||
base_url = reverse("passbook_flows:configure", kwargs={"stage_uuid": self.pk})
|
return reverse("passbook_stages_password:user-settings", kwargs={"stage_uuid": self.pk})
|
||||||
args = urlencode({NEXT_ARG_NAME: reverse("passbook_core:user-settings")})
|
|
||||||
return UIUserSettings(name=_("Change password"), url=f"{base_url}?{args}")
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Password Stage {self.name}"
|
return f"Password Stage {self.name}"
|
||||||
|
|
|
@ -52,7 +52,7 @@ class PasswordStageView(FormView, StageView):
|
||||||
"""Authentication stage which authenticates against django's AuthBackend"""
|
"""Authentication stage which authenticates against django's AuthBackend"""
|
||||||
|
|
||||||
form_class = PasswordForm
|
form_class = PasswordForm
|
||||||
template_name = "stages/password/backend.html"
|
template_name = "stages/password/flow-form.html"
|
||||||
|
|
||||||
def get_form(self, form_class=None) -> PasswordForm:
|
def get_form(self, form_class=None) -> PasswordForm:
|
||||||
form = super().get_form(form_class=form_class)
|
form = super().get_form(form_class=form_class)
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "base/page.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
{% load passbook_utils %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div class="pf-c-card">
|
||||||
|
<div class="pf-c-card__header pf-c-title pf-m-md">
|
||||||
|
{% trans 'Reset your password' %}
|
||||||
|
</div>
|
||||||
|
<div class="pf-c-card__body">
|
||||||
|
<a class="pf-c-button pf-m-primary" href="{{ url }}">
|
||||||
|
{% trans 'Change password' %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,10 @@
|
||||||
|
"""Password stage urls"""
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from passbook.stages.password.views import UserSettingsCardView
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path(
|
||||||
|
"<uuid:stage_uuid>/change-card/", UserSettingsCardView.as_view(), name="user-settings"
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,20 @@
|
||||||
|
from typing import Any
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.views.generic import TemplateView
|
||||||
|
|
||||||
|
from django.shortcuts import reverse
|
||||||
|
from django.utils.http import urlencode
|
||||||
|
|
||||||
|
from passbook.flows.views import NEXT_ARG_NAME
|
||||||
|
|
||||||
|
class UserSettingsCardView(LoginRequiredMixin, TemplateView):
|
||||||
|
|
||||||
|
template_name = "stages/password/user-settings-card.html"
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
|
||||||
|
base_url = reverse("passbook_flows:configure", kwargs={"stage_uuid": self.kwargs["stage_uuid"]})
|
||||||
|
args = urlencode({NEXT_ARG_NAME: reverse("passbook_core:user-settings")})
|
||||||
|
|
||||||
|
kwargs = super().get_context_data(**kwargs)
|
||||||
|
kwargs["url"] = f"{base_url}?{args}"
|
||||||
|
return kwargs
|
File diff suppressed because one or more lines are too long
Reference in New Issue