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

Switch from profile regex to format

Instead of profile regex, we switch to more semantic
profile format. There is no valid mapping between those,
so a default of lowercase is set for all existing services.
parent 245c4977
No related branches found
No related tags found
No related merge requests found
......@@ -158,7 +158,7 @@ class Service(db.Model):
description = db.Column(db.String())
policy = db.Column(db.String(255))
max_profiles = db.Column(db.Integer(), nullable=False, default=1)
profile_regex = db.Column(db.String(255))
profile_format = db.Column(db.String(255))
same_username = db.Column(db.Boolean(), nullable=False, default=False)
config = db.Column(mutable.MutableDict.as_mutable(JSONEncoded))
......
......@@ -5,6 +5,12 @@ blueprint = flask.Blueprint("profile", __name__, template_folder="templates")
import flask_login
from hiboo import models, utils
from hiboo.profile import format
formats = format.ProfileFormat.registry
from hiboo.profile import login, admin, forms, cli
......
from wtforms import validators
from flask_babel import lazy_gettext as _
import string
class ProfileFormat(object):
registry = {}
regex = ".*"
allowed = string.printable
transform = str
message = ""
min_length = 3
max_length = 30
@classmethod
def register(cls, *format_ids):
""" Class decorator
"""
def register_function(format):
for format_id in format_ids:
cls.registry[format_id] = format
return format
return register_function
@classmethod
def validators(cls):
""" Return a username validator for wtforms
"""
return [
validators.DataRequired(),
validators.Length(
min=cls.min_length, max=cls.max_length,
message=_("must be at least {} and at most {} characters long".format(cls.min_length, cls.max_length))
),
validators.Regexp(
"^{}$".format(cls.regex),
message=_("must comprise only of ") + cls.message
)
]
@classmethod
def coalesce(cls, username):
""" Transform a username into its valid form
"""
return filter(cls.allowed.__contains__, cls.transform(username))
@classmethod
def alternatives(cls, username):
""" Generate alternate usernames for a given username
"""
index = 1
while True:
yield username + "_" + str(index)
register = ProfileFormat.register
@register("lowercase", "", None)
class LowercaseAlphanumPunct(ProfileFormat):
""" Lowercase username, including digits and very basic punctuation
"""
regex = "[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?"
allowed = string.digits + string.ascii_lowercase + ".-_"
transform = str.lower
message = _("lowercase letters, digits, dots, dashes, and underscores")
@register("alnum")
class AlphanumPunct(ProfileFormat):
""" Alphanum username, including some very basic punctuation
"""
regex = "[a-zA-Z0-9_]+([a-zA-Z0-9_\.-]+[a-zA-Z0-9_]+)?"
allowed = string.digits + string.ascii_letters + ".-_"
message = _("letters, digits, dots, dashes and underscores")
from hiboo.profile import blueprint, forms
from hiboo.profile import blueprint, forms, formats
from hiboo import models, utils, security
from hiboo import user as hiboo_user
from passlib import context, hash
......@@ -50,10 +50,7 @@ def create(service_uuid, create_for=False):
status = models.Profile.REQUEST
# Actually display the form
form = forms.ProfileForm()
if service.profile_regex:
form.username.validators.append(
validators.Regexp("^{}$".format(service.profile_regex)),
)
form.username.validators = formats[service.profile_format].validators()
if service.same_username:
form.username.data = user.username
form.username.render_kw = {"readonly": True}
......
from wtforms import validators, fields, widgets
from flask_babel import lazy_gettext as _
from hiboo import models, application
from hiboo import models, application, profile
import flask_wtf
......@@ -14,6 +14,11 @@ class ServiceForm(flask_wtf.FlaskForm):
choices=list(models.Service.POLICIES.items()))
max_profiles = fields.IntegerField(_('Maximum profile count'),
[validators.NumberRange(1, 1000)])
profile_regex = fields.StringField(_('Profile username regex'))
profile_format = fields.SelectField(_('Profile username format'),
choices=(
[("", _("Default ({})".format(profile.formats[None].message)))] +
[(name, format.message.capitalize()) for name, format in profile.formats.items() if name]
)
)
same_username = fields.BooleanField(_('Disable per-profile username'))
submit = fields.SubmitField(_('Submit'))
""" Replace profile regex by format
Revision ID: 059b2c50d7e1
Revises: c5109b93fc0f
Create Date: 2020-05-11 17:22:47.462998
"""
from alembic import op
import sqlalchemy as sa
import hiboo
revision = '059b2c50d7e1'
down_revision = 'c5109b93fc0f'
branch_labels = None
depends_on = None
def upgrade():
with op.batch_alter_table('service') as batch_op:
batch_op.add_column(sa.Column('profile_format', sa.String(length=255), nullable=True))
batch_op.drop_column('profile_regex')
def downgrade():
with op.batch_alter_table('service') as batch_op:
batch_op.add_column(sa.Column('profile_regex', sa.VARCHAR(length=255), nullable=True))
batch_op.drop_column('profile_format')
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