Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • acides/hiboo
  • frju365/hiboo
  • pascoual/hiboo
  • thedarky/hiboo
  • jeremy/hiboo
  • cyrinux/hiboo
  • a.f/hiboo
  • mickge/hiboo
  • llaq/hiboo
  • vaguelysalaried/hiboo
  • felinn-glotte/hiboo
  • AntoninDelFabbro/hiboo
  • docemmetbrown/hiboo
13 results
Show changes
Showing
with 364 additions and 209 deletions
{% raw %}
{% extends "base.html" %}
{% block title %}
<a href="/docs" class="docs-header-img d-inline-block me-2 rounded" style="">
<img src="/docs/assets/img/logo_hiboo.png" alt="logo_hiboo"/>
</a>
{% trans %}Documentation{% endtrans %}{% endblock %}
{% endraw %}
{% raw %}
{% block submenu %}
{% endraw %}
{% include "menu.html" %}
{% raw %}
{% endblock %}
{% endraw %}
{% raw %}
{% block content %}
{% endraw %}
<div class="row">
{% include "aside.html" %}
<div class="col-md-9 docs-content">
{{ page.content }}
</div>
</div>
{% include "search.html" %}
{% raw %}
{% endblock %}
{% endraw %}
{% raw %}
{% block actions %}
{% endraw %}
<a href="{{ config.repo_url }}" class="btn btn-outline-secondary">{% raw %}{% trans %}Edit on Gitlab{% endtrans %}{% endraw %}</a>
{% raw %}
{% endblock %}
{% endraw %}
<nav class="navbar navbar-expand-lg">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#docsNavbarNav"
aria-controls="docsNavbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="docsNavbarNav">
<ul class="navbar-nav me-auto">
{% for nav_item in nav %}
{% if nav_item.children %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
{{ nav_item.title }}
</a>
<ul class="dropdown-menu">
{% for nav_item in nav_item.children %}
<li {% if nav_item.active %}current{% endif %}>
<a class="dropdown-item" href="{{ nav_item.url|url }}">{{ nav_item.title }}</a>
</li>
{% endfor %}
</ul>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link {% if nav_item.active %}current{% endif %}" href="{{ nav_item.url|url }}">{{ nav_item.title }}</a>
</li>
{% endif %}
{% endfor %}
<li class="nav-item ml-3">
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#mkdocs_search_modal">
{% raw %}{{ render_icon("search") }}{% endraw %}
{% trans %}Search{% endtrans %}
</a>
</li>
</ul>
</div>
</nav>
<script>var base_url = {{ base_url|tojson }};</script>
{%- for script in config.extra_javascript %}
{{ script|script_tag }}
{%- endfor %}
<div class="modal docs-search" id="mkdocs_search_modal" tabindex="-1" role="dialog" aria-labelledby="searchModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="searchModalLabel">{% trans %}Search{% endtrans %}</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% trans %}Close{% endtrans %}"></button>
</div>
<div class="modal-body">
<p>{% trans %}From here you can search these documents. Enter your search terms below.{% endtrans %}</p>
<form>
<div class="form-group">
<input type="search" class="form-control mb-3" placeholder="{% trans %}Search...{% endtrans %}" id="mkdocs-search-query" title="{% trans %}Type search term here{% endtrans %}">
</div>
</form>
<div id="mkdocs-search-results" data-no-results-text="{% trans %}No results found{% endtrans %}"></div>
</div>
<div class="modal-footer">
</div>
</div>
</div>
</div>
<li>
<a class="d-inline-block rounded docs-side ds-link ms-2" href="{{ toc_item.url }}">
{{ toc_item.title }}
</a>
{% if toc_item.children %}
<ul class="list-unstyled ms-4">
{% for toc_item in toc_item.children %}
{% include "toc/toc-item.html" %}
{% endfor %}
</ul>
{% endif %}
</li>
<ul class="list-unstyled small">
{% for toc_item in page.toc %}
{% include "toc/toc-item.html" %}
{% endfor %}
</ul>
import flask
from flask_bootstrap import Bootstrap5
from werkzeug.middleware import proxy_fix
from hiboo import models, configuration, debug, utils
......@@ -10,15 +11,21 @@ def create_app_from_config(config):
app = flask.Flask(__name__)
app.wsgi_app = proxy_fix.ProxyFix(app.wsgi_app)
# Initialize flask_bootstrap
bootstrap = Bootstrap5(app)
# Initialize application extensions
config.init_app(app)
models.db.init_app(app)
utils.limiter.init_app(app)
utils.translation.init_app(app)
utils.translation.init_app(app, locale_selector=utils.get_locale)
utils.login.init_app(app)
utils.login.user_loader(models.User.get)
utils.migrate.init_app(app, models.db)
utils.redis.init_app(app)
# Initialize cache
cache_config = config.get_config_by_prefix('CACHE_')
utils.cache.init_app(app, config=cache_config)
# Initialize debugging tools
if app.config.get("DEBUG"):
......@@ -32,14 +39,17 @@ def create_app_from_config(config):
return dict(config=app.config, utils=utils)
# Import views
from hiboo import account, user, profile, service, application, sso, api
from hiboo import account, user, group, profile, moderation, service, application, sso, api, docs
app.register_blueprint(account.blueprint, url_prefix='/account')
app.register_blueprint(user.blueprint, url_prefix='/user')
app.register_blueprint(group.blueprint, url_prefix='/group')
app.register_blueprint(profile.blueprint, url_prefix='/profile')
app.register_blueprint(service.blueprint, url_prefix='/service')
app.register_blueprint(application.blueprint, url_prefix='/application')
app.register_blueprint(moderation.blueprint, url_prefix='/moderation')
app.register_blueprint(sso.blueprint, url_prefix='/sso')
app.register_blueprint(api.blueprint, url_prefix='/api')
app.register_blueprint(docs.blueprint, url_prefix='/docs')
# Enable global CLI
from hiboo import cli
......
......@@ -3,4 +3,4 @@ import flask
blueprint = flask.Blueprint("account", __name__, template_folder="templates")
from hiboo.account import login, home, settings
\ No newline at end of file
from hiboo.account import login, home, settings
from hiboo.captcha import captcha
from hiboo.format import NameFormat
from wtforms import validators, fields
from flask_babel import lazy_gettext as _
from flask_bootstrap import SwitchField
import flask_wtf
class LoginForm(flask_wtf.FlaskForm):
username = fields.StringField(_('Username'), [validators.DataRequired()])
password = fields.PasswordField(_('Password'), [validators.DataRequired()])
remember_me = fields.BooleanField(_('Remember me'), default=False)
remember_me = SwitchField(_('Remember me'), default=False)
submit = fields.SubmitField(_('Sign in'))
......@@ -17,12 +19,12 @@ class TotpForm(flask_wtf.FlaskForm):
class SignupForm(flask_wtf.FlaskForm):
username = fields.StringField(_('Username'), [
validators.DataRequired(),
validators.Regexp("(^[a-z0-9-_]+$)", message=(_("Your username must be \
comprised of lowercase letters, numbers and '-' '_' only")))
], description = (_("Your username must be \
comprised of lowercase letters, numbers and '-' '_' only")))
formatter = NameFormat.registry["lowercase"]
username = fields.StringField(
_('Username'),
formatter.validators(),
description = (_("The username can be between 3 and 30 characters long. {}".format(formatter.message)))
)
password = fields.PasswordField(_('Password'), [validators.DataRequired()])
password2 = fields.PasswordField(_('Confirm password'),
[validators.DataRequired(), validators.EqualTo('password')])
......
import enum
from hiboo.account import blueprint
from hiboo.profile import common
from hiboo import security, models
......@@ -9,7 +10,12 @@ import flask_login
@blueprint.route("/home")
@security.authentication_required()
def home():
return flask.render_template("account_home.html")
user = flask_login.current_user
page = flask.request.args.get('page', 1, type=int)
events = user.history.filter(models.History.user == user).order_by(
models.History.created_at.desc()).paginate(page=page, per_page=25)
# TODO also query user.groups non individual events (renamed or removed group)
return flask.render_template("account_home.html", events=events)
@blueprint.route("/profiles")
......@@ -22,4 +28,4 @@ def profiles():
(service, profile) for profile in
models.Profile.filter(service, user).all()
])
return flask.render_template("account_profiles.html", profiles=profiles, common=common)
\ No newline at end of file
return flask.render_template("account_profiles.html", profiles=profiles, common=common)
......@@ -3,27 +3,23 @@ from hiboo.account import blueprint, forms
from flask_babel import lazy_gettext as _
from flask import session
from authlib.jose import JsonWebToken
from io import BytesIO
import datetime
import flask_login
import flask
import pyotp
import qrcode
import base64
@blueprint.route("/signin/password", methods=["GET", "POST"])
def signin_password():
form = forms.LoginForm()
if form.validate_on_submit():
user = models.User.login(form.username.data, form.password.data)
if form.remember_me.data is True:
session.permanent = True
if user and models.Auth.TOTP in user.auths and user.auths[models.Auth.TOTP].enabled:
session["username"] = user.username
session["username"] = user.name
return flask.redirect(utils.url_for(".signin_totp"))
elif user:
flask_login.login_user(user)
if form.remember_me.data is True:
session.permanent = True
return flask.redirect(utils.url_or_intent(".home"))
else:
flask.flash(_("Wrong credentials"), "danger")
......@@ -35,7 +31,7 @@ def signin_password():
def signin_totp():
form = forms.TotpForm()
username = session.get("username") or flask.abort(403)
user = models.User.query.filter_by(username=username).first() or flask.abort(403)
user = models.User.query.filter_by(name=username).first() or flask.abort(403)
if form.validate_on_submit():
if user.auths[models.Auth.TOTP].check_totp(form.totp.data):
flask_login.login_user(user)
......@@ -76,7 +72,7 @@ def signup():
flask.flash(_("A user with the same username exists already"), "danger")
else:
user = models.User()
user.username = form.username.data
user.name = form.username.data
auth = models.Auth(models.Auth.PASSWORD, enabled=True)
auth.set_password(form.password.data)
user.auths = {models.Auth.PASSWORD: auth}
......
......@@ -58,7 +58,7 @@ def totp_enable():
key = user.auths[models.Auth.TOTP].value
issuer = flask.current_app.config['WEBSITE_NAME']
totp_uri = pyotp.totp.TOTP(key).provisioning_uri(
name=user.username,
name=user.name,
issuer_name=issuer)
img = qrcode.make(totp_uri).get_image()
buffered = BytesIO()
......@@ -80,13 +80,13 @@ def totp_enable():
flask.flash(_("Scan this QR code or use the informations below it to configure your TOTP client"), "info")
return flask.render_template(
"account_auth_totp_enable.html",
key=key, name=user.username, issuer=issuer, qr=qr, form=form
key=key, name=user.name, issuer=issuer, qr=qr, form=form
)
@blueprint.route("/auth/totp/disable", methods=["GET", "POST"])
@security.authentication_required()
@security.confirmation_required("disable TOTP")
@security.confirmation_required(_("disable TOTP"))
def totp_disable():
user = flask_login.current_user
auth = user.auths[models.Auth.TOTP]
......
{% extends "form.html" %}
{% block title %}{% trans %}New password{% endtrans %}{% endblock %}
{% block title %}
{% trans %}New password{% endtrans %}
{% endblock %}
{% extends "form.html" %}
{% block title %}{% trans %}Reset your password{% endtrans %}{% endblock %}
{% block title %}
{% trans %}Reset your password{% endtrans %}
{% endblock %}
{% extends "base.html" %}
{% block title %}{% trans %}Two-factor authentication (2FA){% endtrans %}{% endblock %}
{% block subtitle %}{% trans %}with time-based one-time password (TOTP){% endtrans %}{% endblock %}
{% block title %}
{% trans %}Two-factor authentication (2FA){% endtrans %}
{% endblock %}
{% block subtitle %}
{% trans %}with time-based one-time password (TOTP){% endtrans %}
{% endblock %}
{% block content %}
<div class="col-md-6 col">
<p>{% trans %}TOTP is an optional secondary layer of the authentication process used to enforce the protection of your account with a one-time password. You can read <a href="https://en.wikipedia.org/wiki/Time-based_one-time_password">this Wikipedia page</a> if you want to learn more about this mechanism.{% endtrans %}
</p>
</div>
{% if enabled %}
<div class="col-md-6 col">
<blockquote class="quote-success">
<h5>{% trans %}Two-factor authentication is enabled{% endtrans %}</h5>
<p>{% trans %}Click on <i>Disable TOTP</i> to disable it{% endtrans %}</p>
</p>
</blockquote>
<div class="col-md-6 col">
<p>{% trans %}TOTP is an optional secondary layer of the authentication
process used to enforce the protection of your account with a one-time
password. You can read
<a href="https://en.wikipedia.org/wiki/Time-based_one-time_password">this
Wikipedia page</a>
if you want to learn more about this mechanism.{% endtrans %}</p>
</div>
{% if enabled %}
<div class="col-md-6 col">
<div class="alert alert-success">
<h5 class="alert-heading">{% trans %}Two-factor authentication is enabled{% endtrans %}</h5>
<p>{% trans %}Click on <i>Disable TOTP</i> to disable it{% endtrans %}</p>
</div>
<div class="card">
<div class="card-body">
<h5>{% trans %}Test your one-time password{% endtrans %}</h5>
<p>{% trans %}Feel free to use this form in order to check your client configuration{% endtrans %}</p>
</div>
<div class="card-footer">
{{ macros.form(form, layout=None) }}
{{ render_form(form) }}
</div>
</div>
</div>
{% else %}
<div class="col-md-6 col">
<blockquote class="quote-info">
<h5>{% trans %}Two-factor authentication is disabled{% endtrans %}</h5>
<p>{% trans %}Click on <i>Enable TOTP</i> to configure it{% endtrans %}</p>
</blockquote>
<blockquote class="quote-warning">
<h5>{% trans %}Attention{% endtrans %}</h5>
<p>{% trans %}You will need a working TOTP client in order to complete this configuration. Several open-source apps can help you for this (and some on mobile are available on <a href="https://search.f-droid.org/?q=totp&lang=fr">F-Droid</a>){% endtrans %}</p>
</blockquote>
</div>
{% endif %}
{% else %}
<div class="col-md-6 col">
<div class="alert alert-info">
<h5 class="alert-heading">{% trans %}Two-factor authentication is disabled{% endtrans %}</h5>
<p>{% trans %}Click on <i>Enable TOTP</i> to configure it{% endtrans %}</p>
</div>
<div class="alert alert-warning">
<h5 class="alert-heading">{% trans %}Attention{% endtrans %}</h5>
<p>{% trans %}You will need a working TOTP client in order to complete
this configuration. Several open-source apps can help you for this
(and some on mobile are available on
<a href="https://search.f-droid.org/?q=totp&lang=fr">
F-Droid</a>){% endtrans %}</p>
</div>
</div>
{% endif %}
{% endblock %}
{% block actions %}
{% if enabled %}
<a href="{{ url_for(".totp_disable") }}" class="btn btn-warning">{% trans %}Disable TOTP{% endtrans %}</a>
{% else %}
<a href="{{ url_for(".totp_enable") }}" class="btn btn-info">{% trans %}Enable TOTP{% endtrans %}</a>
{% endif %}
{% if enabled %}
<a href="{{ url_for(".totp_disable") }}" class="btn btn-outline-warning">{% trans %}Disable TOTP{% endtrans %}</a>
{% else %}
<a href="{{ url_for(".totp_enable") }}" class="btn btn-outline-info">{% trans %}Enable TOTP{% endtrans %}</a>
{% endif %}
{% endblock %}
{% extends "base.html" %}
{% block title %}{% trans %}Two-factor authentication (2FA){% endtrans %}{% endblock %}
{% block subtitle %}{% trans %}with time-based one-time password (TOTP){% endtrans %}{% endblock %}
{% block title %}
{% trans %}Two-factor authentication (2FA){% endtrans %}
{% endblock %}
{% block subtitle %}
{% trans %}with time-based one-time password (TOTP){% endtrans %}
{% endblock %}
{% block content %}
<div class="col-md-6 col">
<div class="card">
<img class="card-img-top w-50 mx-auto" src="data:image/png;base64,{{ qr }}" alt="TOTP QR code">
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between align-items-center">
<b>{% trans %}Secret key{% endtrans %}</b> <code>{{ key }}</code>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<b>{% trans %}Name{% endtrans %}</b> <code>{{ name }}</code>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<b>{% trans %}Issuer{% endtrans %}</b> <code>{{ issuer }}</code>
</li>
</ul>
<div class="card-footer">
{{ macros.form(form, layout=None) }}
<div class="col-md-6 col">
<div class="card">
<img class="card-img-top w-50 mx-auto p-2"
src="data:image/png;base64,{{ qr }}"
alt="TOTP QR code"/>
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between align-items-center">
<b>{% trans %}Secret key{% endtrans %}</b> <code>{{ key }}</code>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<b>{% trans %}Name{% endtrans %}</b> <code>{{ name }}</code>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<b>{% trans %}Issuer{% endtrans %}</b> <code>{{ issuer }}</code>
</li>
</ul>
<div class="card-footer">
{{ render_form(form) }}
</div>
</div>
</div>
</div>
{% endblock %}
{% block actions %}
<a href="{{ url_for(".totp_disable") }}" class="btn btn-warning">{% trans %}Cancel{% endtrans %}</a>
<a href="{{ url_for(".totp_disable") }}" class="btn btn-outline-warning">{% trans %}Cancel{% endtrans %}</a>
{% endblock %}
{% extends "form.html" %}
{% block title %}{% trans %}Update contact info{% endtrans %}{% endblock %}
{% block content %}
{{ macros.form(form) }}
{% endblock %}
\ No newline at end of file
{% block title %}
{% trans %}Update contact info{% endtrans %}
{% endblock %}
{% extends "base.html" %}
{% block title %}{% trans %}My account{% endtrans %}{% endblock %}
{% block subtitle %}{% trans %}status and history{% endtrans %}{% endblock %}
{% block title %}
{% trans %}My account{% endtrans %}
{% endblock %}
{% block subtitle %}
{% trans %}status and history{% endtrans %}
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-6 col">
<section class="content">
<div class="row">
<div class="col-md-6 col">
{{ macros.infobox(_("Account age"), "{} days".format((current_user.created_at.today() - current_user.created_at).total_seconds() // 86400), "blue", "calendar") }}
</div>
<div class="col-md-6 col">
{{ macros.infobox(_("Profile count"), current_user.profiles.filter_by(status="active").count(), "red", "users") }}
<div class="row">
<div class="col-12 col-xl-6">
<section class="content">
<div class="row">
<div class="col-md-6 col">
{{ macros.infobox(_("Account age"), "{} days".format((current_user.created_at.today() - current_user.created_at).total_seconds() // 86400), "primary", "calendar-event-fill") }}
</div>
<div class="col-md-6 col">
{{ macros.infobox(_("Profile count"), current_user.profiles.filter_by(status="active").count(), "danger", "person-vcard-fill") }}
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 col">
{{ macros.infobox(_("Pending requests"), current_user.profiles.filter_by(status="request").count(), "green", "hourglass") }}
<div class="row">
<div class="col-md-6 col">
{{ macros.infobox(_("Pending requests"), current_user.profiles.filter_by(status="request").count(), "success", "hourglass-split") }}
</div>
<div class="col-md-6 col">
{{ macros.infobox(_("Role"), _("administrator") if current_user.is_admin else _("registered user"), "warning", "person-badge-fill") }}
</div>
</div>
<div class="col-md-6 col">
{{ macros.infobox(_("Role"), _("administrator") if current_user.is_admin else _("registered user"), "yellow", "lock") }}
{% if current_user.groups %}
<div class="row">
<div class="col-md-6 col">
{{ macros.infobox(_("Membership"), current_user.groups | map(attribute='groupname') | join(', '), "purple", "diagram-3-fill") }}
</div>
</div>
</div>
</section>
{% endif %}
</section>
</div>
<div class="col-12 col-xl-6">
{{ macros.timeline(events) }}
</div>
</div>
<div class="col-md-6 col">
{{ macros.timeline(current_user.history) }}
</div>
</div>
{% endblock %}
{% extends "base.html" %}
{% block title %}{% trans %}My account{% endtrans %}{% endblock %}
{% block subtitle %}{% trans %}my profiles{% endtrans %}{% endblock %}
{% block title %}
{% trans %}My account{% endtrans %}
{% endblock %}
{% block subtitle %}
{% trans %}my profiles{% endtrans %}
{% endblock %}
{% block content %}
<div class="row">
{% for service, profile in profiles %}
{% set service = profile.service %}
{% set status = profile.STATUSES[profile.status] %}
<div class="col-xs col-md-6 col-lg-4">
<div class="card card-widget widget-user-2">
<div class="widget-user-header bg-{{ macros.colors[loop.index0 % 7] }}">
<h3>{{ service.name }} / {{ profile.username }}</h3>
{% if profile.comment %}<h5>{{ profile.comment }}&nbsp;</h5>
{% else %}<h5>{% trans %}No profile description{% endtrans %}&nbsp;</h5>
{% endif %}
</div>
<div class="card-body p-0">
<div class="py-2 px-3">{{ macros.profile_status(profile) }}</div>
<ul class="nav flex-column nav-pills">
{% if not service.single_profile %}
{% if service.policy in ("open", "burst") and profile.__class__.filter(service, current_user).count() < service.max_profiles %}
<li class="nav-item">
<a class="nav-link" href="{{ utils.url_for("profile.create", service_uuid=service.uuid) }}">
<i class="fas fa-user-plus"></i>&nbsp;
{% trans %}Create another profile{% endtrans %}
</a>
</li>
{% elif service.policy in ("managed", "burst") %}
<li class="nav-item">
<a class="nav-link" href="{{ utils.url_for("profile.create", service_uuid=service.uuid) }}">
<i class="fas fa-user-plus"></i>&nbsp;
{% trans %}Request another profile{% endtrans %}
</a>
</li>
{% endif %}
{% if service.policy not in ("locked",) %}
<li class="nav-item">
<a class="nav-link" href="{{ utils.url_for("profile.claim", service_uuid=service.uuid) }}">
<i class="fas fa-hand-paper"></i>&nbsp;
{% trans %}Claim another profile{% endtrans %}
</a>
</li>
{% endif %}
{% endif %}
{% if profile.transition_step %}
{% set transition = profile.TRANSITIONS[profile.transition] %}
{% set color, status = profile.STATUSES[transition.to] %}
{% set when = transition.delta(profile) %}
<li class="nav-item">
<a class="nav-link" href="#">
<i class="fas fa-clock"></i>&nbsp;
{% trans status, when %}Profile will be {{ status }} in {{ when }}{% endtrans %}
</a>
</li>
{% endif %}
{% for action in profile.actions %}
<li class="nav-item">
<a class="nav-link" href="{{ action.url(profile=profile) }}">
<i class="{{ action.icon }}"></i>&nbsp;
{{ action.description | capitalize }}
</a>
</li>
{% endfor %}
</ul>
<div class="row g-3">
{% for service, profile in profiles %}
{% set service = profile.service %}
{% set status = profile.STATUSES[profile.status] %}
<div class="col-xs col-md-6 col-lg-4">
<div class="card">
<div class="card-header text-white"
style="background-color: var(--bs-{{ macros.colors[loop.index0 % 7] }})">
<h3>{{ service.name }} / {{ profile.name }}</h3>
{% if profile.comment %}
<h5>{{ profile.comment }}</h5>
{% else %}
<h5>{% trans %}No profile description{% endtrans %}</h5>
{% endif %}
</div>
<div class="card-body">
<div class="py-2 px-3">
{{ macros.profile_status(profile) }}
</div>
<div class="list-group list-group-flush">
{% if not service.single_profile %}
{% if service.policy in ("open", "burst") and profile.__class__.filter(service, current_user).count() < service.max_profiles %}
<a class="list-group-item list-group-item-action"
href="{{ utils.url_for("profile.create", service_uuid=service.uuid) }}">
{{ render_icon("plus-circle-fill") }}
{% trans %}Create another profile{% endtrans %}</a>
{% elif service.policy in ("managed", "burst") %}
<a class="list-group-item list-group-item-action"
href="{{ utils.url_for("profile.create", service_uuid=service.uuid) }}">
{{ render_icon("question-circle-fill") }}
{% trans %}Request another profile{% endtrans %}</a>
{% endif %}
{% if service.policy not in ("locked",) %}
<a class="list-group-item list-group-item-action"
href="{{ utils.url_for("profile.claim", service_uuid=service.uuid) }}">
{{ render_icon("hand-index-fill") }}
{% trans %}Claim another profile{% endtrans %}</a>
{% endif %}
{% endif %}
<a class="list-group-item list-group-item-action"
href="{{ utils.url_for("profile.edit", profile_uuid=profile.uuid) }}">
{{ render_icon("pen-fill") }}
{% trans %}Edit this profile{% endtrans %}</a>
{% if profile.transition_step %}
{% set transition = profile.TRANSITIONS[profile.transition] %}
{% set color, status = profile.STATUSES[transition.to] %}
{% set when = transition.delta(profile) %}
<a class="list-group-item list-group-item-action" href="#">
{{ render_icon("clock") }}
{% trans status, when %}Profile will be {{ status }} in {{ when }}{% endtrans %}</a>
{% endif %}
{% for action in profile.actions %}
<a class="list-group-item list-group-item-action"
href="{{ action.url(profile=profile) }}">
{{ render_icon(action.icon) }}
{{ action.description | capitalize }}</a>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
{% endblock %}
{% extends "base.html" %}
{% block title %}{% trans %}Sign in{% endtrans %}{% endblock %}
{% block subtitle %}{% trans %}to access your account{% endtrans %}{% endblock %}
{% block content %}
{{ macros.form(form) }}
{% block title %}
{% trans %}Sign in{% endtrans %}
{% endblock %}
{% block subtitle %}
{% trans %}to access your account{% endtrans %}
{% endblock %}
{% block actions %}
<a href="{{ utils.url_for(".signup") }}" class="btn btn-success">{% trans %}Sign up{% endtrans %}</a>
{% block content %}
<div class="row mb-3">
<div class="col-lg-6">
{{ render_form(form, form_type='horizontal', horizontal_columns=('lg', 3, 9) ) }}
</div>
</div>
{% if config['OPEN_SIGNUP'] %}
{% set signup_url = url_for("account.signup") %}
<br><p class="text-secondary">{% trans %}If you don't already have an account, you can <a href="{{ signup_url }}">sign up</a> now!{% endtrans %}</p>
{% endif %}
{% endblock %}
{% extends "base.html" %}
{% block title %}{% trans %}Time-based one-time password (TOTP) verify{% endtrans %}{% endblock %}
{% block subtitle %}{% trans %}to access your account{% endtrans %}{% endblock %}
{% block content %}
{{ macros.form(form) }}
{% extends "form.html" %}
{% block title %}
{% trans %}Time-based one-time password (TOTP) verify{% endtrans %}
{% endblock %}
{% block subtitle %}
{% trans %}to access your account{% endtrans %}
{% endblock %}
{% block actions %}
<a href="{{ utils.url_for(".signup") }}" class="btn btn-success">{% trans %}Sign up{% endtrans %}</a>
<a href="{{ utils.url_for(".signup") }}" class="btn btn-success">
{% trans %}Sign up{% endtrans %}</a>
{% endblock %}