Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SQL authenticator #508

Merged
merged 47 commits into from
Oct 26, 2022

Conversation

janjagusch
Copy link
Collaborator

I've written a basic authenticator, similar to the dict authenticator, that stores credentials as sha256-hashed values in a SQL database. It also ships with CLI tools for CRUD operations on the credentials table (like creating users or updating passwords).

It's built around sqlmodel, so it should work with any kind of SQL backend.

I don't know how you feel about adding more authenticator plugins, so if you feel like I should rather put this in a separate repository, feel free to close this PR.

@wolfv
Copy link
Member

wolfv commented Mar 23, 2022

Hey, wow, that's cool!

How do you feel about using passlib (https://passlib.readthedocs.io/) instead of plain sha256?

@baszalmstra
Copy link
Contributor

Would it make sense to also add a password salt for extra security?

@wolfv
Copy link
Member

wolfv commented Mar 23, 2022

yeah I was hoping that passlib would do all that :)

@janjagusch
Copy link
Collaborator Author

Hey, wow, that's cool!

How do you feel about using passlib (https://passlib.readthedocs.io/) instead of plain sha256?

Glad you like it. 🙂

Thanks for pointing me to passlib, I'll use that instead.

@janjagusch
Copy link
Collaborator Author

yeah I was hoping that passlib would do all that :)

Yes, I think so too: https://passlib.readthedocs.io/en/stable/#welcome

@janjagusch
Copy link
Collaborator Author

@wolfv, I've added passlib.

do you have a preferred way of testing this? the way I see it is that we'd need a SQL server running in a separate container. alternatively, we could test with sqlite, which might make things easier.

@wolfv
Copy link
Member

wolfv commented Mar 23, 2022

Hey, I didn't check deeply before, but I am just wondering now if you want to use a different database on purpose?

Because you can also create a new table for your plugin and use that table as part of the Quetz SQL database for the storage of the authentication information.

E.g. here is another plugin creating a table: https://github.com/mamba-org/quetz/blob/main/plugins/quetz_conda_suggest/quetz_conda_suggest/db_models.py

@wolfv
Copy link
Member

wolfv commented Mar 23, 2022

You would also have to define a migration but that can be auto-generated: https://github.com/mamba-org/quetz/tree/main/plugins/quetz_conda_suggest/quetz_conda_suggest/migrations/versions

@janjagusch
Copy link
Collaborator Author

Hey, I didn't check deeply before, but I am just wondering now if you want to use a different database on purpose?

That's a good question. I was also thinking about using the Quetz database instead but then thought it might be useful to have this decoupled, as you might have central user management that you want to reuse here. However, I don't have a strong opinion on this (in our use case we could easily use the same DB) and hence wouldn't oppose changing this.

E.g. here is another plugin creating a table: https://github.com/mamba-org/quetz/blob/main/plugins/quetz_conda_suggest/quetz_conda_suggest/db_models.py

Nice, thanks. I'll check this out later.

credentials = list(session.exec(statement))
click.echo(f"WARNING: Resetting the table will delete {len(credentials)} users.")
while (
reset_database := input("Are you sure you want to reset the table? [Y/n]")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is click.confirm IIRC

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or click.prompt?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, thanks for the pointer.

@janjagusch
Copy link
Collaborator Author

Sorry, this PR has gotten a bit stale. I will try to get back to it the week after next week.

@wolfv wolfv added the enhancement New feature or request label Apr 23, 2022
@janjagusch
Copy link
Collaborator Author

You would also have to define a migration but that can be auto-generated: https://github.com/mamba-org/quetz/tree/main/plugins/quetz_conda_suggest/quetz_conda_suggest/migrations/versions

hi @wolfv! the credentials table for the SQL authenticator is now part of the quetz database and written in sqlalchemy instead of sqlmodel. I'm not entirely sure how to create the migration with alembic, though (never used it before). I guess what's confusing me the most is that we have the main migrations directory under quetz/migrations but then quetz_conda_suggest has its own migrations directory, which doesn't contain an env.py or a script.py.mako.

Could you provide me with some more instructions on how to make the migration work, please? Once I have that part working, I'd start building some basic tests.

@wolfv
Copy link
Member

wolfv commented Oct 10, 2022

Sorry for ignoring the questions regarding the database migrations. I believe the quetz command line utility has some helper functions to generate initial migrations from the sqlalchemy schema (even for plugins):

(base) ➜  quetz git:(main) ✗ quetz make-migrations --help

 Usage: quetz make-migrations [OPTIONS] [PATH]

 make database migrations for quetz or a plugin

╭─ Arguments ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│   path      [PATH]  The path of the deployment [default: None]                                                                                                                                           │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --message                          TEXT  revision message [default: None]                                                                                                                                │
│ --plugin                           TEXT  head or heads or plugin name [default: quetz]                                                                                                                   │
│ --initialize    --no-initialize          initialize migrations [default: no-initialize]                                                                                                                  │
│ --help                                   Show this message and exit.                                                                                                                                     │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

This should populate the right files that are then used by quetz init-db to execute the schema migrations.

@simonbohnen
Copy link
Collaborator

Hey Wolf! Thanks for the hint, that looks exactly right!

I've been talking to @janjagusch and will work on finishing up this PR.

@wolfv
Copy link
Member

wolfv commented Oct 10, 2022

great!

@@ -109,7 +109,7 @@ async def authorize(
user_dict = await self._authenticate(request, dao, config)

if user_dict is None:
return Response("login failed")
return Response("login failed", status_code=401)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wolfv, this change affects all authenticators, I believe. I think previously, it would always return 200, even when the login failed. This is not strictly necessary in this PR, so if you want to can remove it/discuss it in a separate PR.

Copy link
Collaborator Author

@janjagusch janjagusch Oct 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest leaving this out for now and opening a separate PR for it. Will probably make it easier to get this PR merged.

Copy link
Collaborator Author

@janjagusch janjagusch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome job, @simonbohnen!

@simonbohnen
Copy link
Collaborator

I'm unable to resolve the conversations, but that shouldn't matter I guess. @wolfv do you have any feedback on the current state? Jan's comment might be relevant.

Copy link
Collaborator Author

@janjagusch janjagusch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm the official PR author, but it was mostly @simonbohnen's work.
Hence, I will give this an unofficial approval and proceed to merge.

Awesome work, @simonbohnen!

@janjagusch janjagusch merged commit 9399645 into mamba-org:main Oct 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants