tiangolo / full-stack

Full stack, modern web application generator. Using Flask, PostgreSQL DB, Docker, Swagger, automatic HTTPS and more.
MIT License
523 stars 81 forks source link

Allow new users to be created without authentication #4

Closed abnerjacobsen closed 6 years ago

abnerjacobsen commented 6 years ago

Sometimes it is necessary for an API to enable new users to be created without the need for authentication, especially when launching a service that wants to allow the maximum possible user registration.

This change allows defining at the time the project is generated by coockiecutter if the API allows the registration of new users without authentication using a previously created user or not.

tiangolo commented 6 years ago

Thanks! I would prefer to include the least Jinja possible inside the code. That would make it more maintainable and easier to re-create, copy-paste, etc. Without requiring to generate a new project to copy the corresponding parts.

But I think you have an important point. I also think that some developers could want to add some more custom logic, like sending an email to verify a new user, etc.


How about, adding another endpoint, only for public user registration.

That way, if the "superuser" wants to create a new user, he can do it easily, but if a developer wants to add some more custom logic or a custom verification, he can put that in the public endpoint.

What do you think?

abnerjacobsen commented 6 years ago

Okay, I understand your thinking.

I can create another PR with the following changes:

core/config.py: API_OPEN_REGISTRATION = False

api/api_v1/endpoints/users.py:

@docs.register
@doc(
    description='Create new user without the need to be logged in',
    tags=['users'])
@app.route(f'{config.API_V1_STR}/users_open/', methods=['POST'])
@use_kwargs({
    'email': fields.Str(required=True),
    'password': fields.Str(required=True),
    'first_name': fields.Str(),
    'last_name': fields.Str(),
    'group_id': fields.Int(required=True),
})
@marshal_with(UserSchema())
def route_users_post_open(email=None,
                     password=None,
                     first_name=None,
                     last_name=None,
                     group_id=None):

    if not config.API_OPEN_REGISTRATION:
        abort(403, 'Open user resgistration is forbidden on this server')

    user = db_session.query(User).filter(User.email == email).first()

    if user:
        return abort(
            400,
            f'The user with this email already exists in the system: {email}')

    group = db_session.query(Group).filter(Group.id == group_id).first()

    if group is None:
        abort(400, f'There is no group with id: "{group_id}"')
    user = User(
        email=email,
        password=pwd_context.hash(password),
        first_name=first_name,
        last_name=last_name,
        group=group)

    db_session.add(user)
    db_session.commit()
    db_session.refresh(user)
    return user

The API_OPEN_REGISTRATION entry in config.py will allow the application developer to configure whether or not to allow the registration of new users without the need to be logged in with a JWT token.

What do you think ?

tiangolo commented 6 years ago

Sounds great!

Could you please make it use an environment variable? Like it is for SERVER_NAME:

SERVER_NAME = os.getenv('SERVER_NAME')

That would allow users to pass that configuration as an environment variable in the Docker Compose file.

To account for the different ways people could declare False or True, I think it could be something like:

OPEN_REGISTRATION = False
open_registration_env = os.getenv('OPEN_REGISTRATION')
if open_registration_env:
    # Make sure that OPEN_REGISTRATION was not set to 0, false, False or FALSE
    if not open_registration_env['0'] in ['0', 'f', 'F']:
        OPEN_REGISTRATION = True