Skip to content
Snippets Groups Projects
Commit b81fb8cf authored by kaiyou's avatar kaiyou
Browse files

Add quick profile creation for the first profile

parent 2f678f09
No related branches found
No related tags found
1 merge request!20Add 'remember me' button
Pipeline #1065 passed
...@@ -162,6 +162,9 @@ class Service(db.Model): ...@@ -162,6 +162,9 @@ class Service(db.Model):
same_username = db.Column(db.Boolean(), nullable=False, default=False) same_username = db.Column(db.Boolean(), nullable=False, default=False)
config = db.Column(mutable.MutableDict.as_mutable(JSONEncoded)) config = db.Column(mutable.MutableDict.as_mutable(JSONEncoded))
def check_username(self, username):
return Profile.query.filter_by(service_uuid=self.uuid, username=username).first()
class Profile(db.Model): class Profile(db.Model):
""" A profile is a per-service custom identity. """ A profile is a per-service custom identity.
......
...@@ -15,13 +15,18 @@ from hiboo.profile import login, admin, forms, cli ...@@ -15,13 +15,18 @@ from hiboo.profile import login, admin, forms, cli
def get_profile(service, **redirect_args): def get_profile(service, **redirect_args):
query = models.Profile.query.filter_by(
service_uuid=service.uuid,
user_uuid=flask_login.current_user.uuid,
status=models.Profile.ACTIVE
)
form = forms.ProfilePickForm() form = forms.ProfilePickForm()
if form.validate_on_submit(): if form.validate_on_submit():
profile = models.Profile.query.get(form.profile_uuid.data) return query.filter_by(uuid=form.profile_uuid.data).first()
if not (profile and count = query.count()
profile.user == flask_login.current_user and if count > 1:
profile.service == service and utils.force_redirect(utils.url_for("profile.pick", **redirect_args))
profile.status == models.Profile.ACTIVE): elif count == 1:
return None return query.one()
return profile else:
utils.force_redirect(utils.url_for("profile.pick", **redirect_args)) utils.force_redirect(utils.url_for("profile.create_quick", **redirect_args))
...@@ -44,15 +44,17 @@ class ProfileFormat(object): ...@@ -44,15 +44,17 @@ class ProfileFormat(object):
def coalesce(cls, username): def coalesce(cls, username):
""" Transform a username into its valid form """ Transform a username into its valid form
""" """
return filter(cls.allowed.__contains__, cls.transform(username)) return ''.join(filter(cls.allowed.__contains__, cls.transform(username)))
@classmethod @classmethod
def alternatives(cls, username): def alternatives(cls, username):
""" Generate alternate usernames for a given username """ Generate alternate usernames for a given username
""" """
yield username
index = 1 index = 1
while True: while True:
yield username + "_" + str(index) yield username + str(index)
index += 1
register = ProfileFormat.register register = ProfileFormat.register
......
from wtforms import validators, fields from wtforms import validators, fields, widgets
from flask_babel import lazy_gettext as _ from flask_babel import lazy_gettext as _
import flask_wtf import flask_wtf
...@@ -9,6 +9,16 @@ class ProfileForm(flask_wtf.FlaskForm): ...@@ -9,6 +9,16 @@ class ProfileForm(flask_wtf.FlaskForm):
comment = fields.StringField(_('Comment')) comment = fields.StringField(_('Comment'))
submit = fields.SubmitField(_('Create profile')) submit = fields.SubmitField(_('Create profile'))
def force_username(self, username):
self.username.data = username
self.username.render_kw = {"readonly": True}
def hide_fields(self):
self.username.widget = widgets.HiddenInput()
self.username.label = ""
self.comment.widget = widgets.HiddenInput()
self.comment.label = ""
class ProfilePickForm(flask_wtf.FlaskForm): class ProfilePickForm(flask_wtf.FlaskForm):
profile_uuid = fields.TextField('profile', []) profile_uuid = fields.TextField('profile', [])
......
...@@ -2,7 +2,6 @@ from hiboo.profile import blueprint, forms, formats ...@@ -2,7 +2,6 @@ from hiboo.profile import blueprint, forms, formats
from hiboo import models, utils, security from hiboo import models, utils, security
from hiboo import user as hiboo_user from hiboo import user as hiboo_user
from passlib import context, hash from passlib import context, hash
from wtforms import validators
from flask_babel import lazy_gettext as _ from flask_babel import lazy_gettext as _
import flask import flask
...@@ -18,12 +17,14 @@ def pick(service_uuid): ...@@ -18,12 +17,14 @@ def pick(service_uuid):
return flask.render_template("profile_pick.html", service=service, profiles=profiles, form=form) return flask.render_template("profile_pick.html", service=service, profiles=profiles, form=form)
@blueprint.route("/create_quick/<service_uuid>", methods=["GET", "POST"], defaults={"quick": True}, endpoint="create_quick")
@blueprint.route("/create_for/<service_uuid>", methods=["GET", "POST"], defaults={"create_for": True}, endpoint="create_for") @blueprint.route("/create_for/<service_uuid>", methods=["GET", "POST"], defaults={"create_for": True}, endpoint="create_for")
@blueprint.route("/create/<service_uuid>", methods=["GET", "POST"]) @blueprint.route("/create/<service_uuid>", methods=["GET", "POST"])
@security.authentication_required() @security.authentication_required()
def create(service_uuid, create_for=False): def create(service_uuid, create_for=False, quick=False):
service = models.Service.query.get(service_uuid) or flask.abort(404) service = models.Service.query.get(service_uuid) or flask.abort(404)
status = models.Profile.ACTIVE status = models.Profile.ACTIVE
format = formats[service.profile_format]
# If the admin passed a user uuid, use that one, otherwise ignore it # If the admin passed a user uuid, use that one, otherwise ignore it
if create_for and flask_login.current_user.is_admin: if create_for and flask_login.current_user.is_admin:
user = hiboo_user.get_user(intent="profile.create_for", create_for=None) user = hiboo_user.get_user(intent="profile.create_for", create_for=None)
...@@ -48,22 +49,28 @@ def create(service_uuid, create_for=False): ...@@ -48,22 +49,28 @@ def create(service_uuid, create_for=False):
elif len(profiles) >= service.max_profiles or service.policy == models.Service.MANAGED: elif len(profiles) >= service.max_profiles or service.policy == models.Service.MANAGED:
flask.flash(_("Your profile creation requires approval"), "warning") flask.flash(_("Your profile creation requires approval"), "warning")
status = models.Profile.REQUEST status = models.Profile.REQUEST
# Actually display the form
form = forms.ProfileForm() form = forms.ProfileForm()
form.username.validators = formats[service.profile_format].validators() form.username.validators = format.validators()
submit = form.validate_on_submit()
# If this is a quick creation, prefill the username with a generated one
if quick:
for username in format.alternatives(format.coalesce(user.username)):
if not service.check_username(username):
form.force_username(username)
break
form.hide_fields()
# If same_username is enabled, enforce the username
if service.same_username: if service.same_username:
form.username.data = user.username form.force_username(user.username)
form.username.render_kw = {"readonly": True} # Handle the creation form
if form.validate_on_submit(): if submit:
username = user.username if service.same_username else form.username.data if service.check_username(form.username.data):
existing = models.Profile.query.filter_by(service_uuid=service_uuid, username=username).first()
if existing:
flask.flash(_("A profile with that username exists already"), "danger") flask.flash(_("A profile with that username exists already"), "danger")
else: else:
profile = models.Profile() profile = models.Profile()
profile.username = username
profile.user = user profile.user = user
profile.service = service profile.service = service
profile.username = form.username.data
profile.comment = form.comment.data profile.comment = form.comment.data
profile.status = status profile.status = status
models.db.session.add(profile) models.db.session.add(profile)
...@@ -71,8 +78,9 @@ def create(service_uuid, create_for=False): ...@@ -71,8 +78,9 @@ def create(service_uuid, create_for=False):
user=user, service=service, profile=profile) user=user, service=service, profile=profile)
models.db.session.commit() models.db.session.commit()
return flask.redirect(utils.url_or_intent("account.home")) return flask.redirect(utils.url_or_intent("account.home"))
return flask.render_template("profile_create.html", form=form, service=service, # Display either the quick version or the full version (one can switch from one to another)
user=user, create_for=create_for) return flask.render_template("profile_quick.html" if quick else "profile_create.html",
form=form, service=service, user=user, create_for=create_for)
@blueprint.route("/claim/<service_uuid>", methods=["GET", "POST"]) @blueprint.route("/claim/<service_uuid>", methods=["GET", "POST"])
......
{% extends "base.html" %}
{% set service_name = service.name %}
{% set username = form.username.data %}
{% block title %}{% trans %}Sign up{% endtrans %}{% endblock %}
{% block subtitle %}
{% trans service_name %}for the service {{ service_name }}{% endtrans %}
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-4 col-s-6 col-xs-12">
<div class="box box-widget widget-user-2">
<div class="widget-user-header bg-primary">
<form method="POST" class="form">
{{ form.hidden_tag() }}
<input type="hidden" name="username" value="{{ form.username.data }}">
<input type="submit" value="Sign up" style="opacity: 0.8" class="btn btn-lg btn-flat bg-gray text-black pull-right">
</form>
<h3 class="widget-header-username">{{ form.username.data }}</h3>
<h5 class="widget-header-desc">{% trans service_name %}Your new profile for {{ service_name }}{% endtrans %}</h5>
</div>
<div class="box-footer">
<p>{% trans service_name %}This is the first time you sign into {{ service_name }}, we first need to sign you up. You may wish to pick another username for that application. If you do, please click the "Custom profile" button.{% endtrans %}</p>
</div>
</div>
</div>
</div>
{% endblock %}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment