forked from Github-Mirrors/canaille
Added a command to clean tokens and codes. Fixes #17
This commit is contained in:
parent
2fc6af0fc9
commit
d020cee00e
6 changed files with 104 additions and 6 deletions
|
@ -46,6 +46,15 @@ pip install gunicorn
|
|||
gunicorn "canaille:create_app()"
|
||||
```
|
||||
|
||||
## Recurrent jobs
|
||||
|
||||
You might want to clean up your database to avoid it growing too much. You can regularly delete
|
||||
expired tokens and authorization codes with:
|
||||
|
||||
```
|
||||
flask clean
|
||||
```
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions are welcome!
|
||||
|
|
|
@ -7,6 +7,7 @@ import canaille.admin.tokens
|
|||
import canaille.admin.authorizations
|
||||
import canaille.admin.clients
|
||||
import canaille.consents
|
||||
import canaille.commands
|
||||
import canaille.oauth
|
||||
import canaille.account
|
||||
import canaille.tokens
|
||||
|
@ -106,6 +107,16 @@ def setup_ldap_tree(app):
|
|||
conn.unbind_s()
|
||||
|
||||
|
||||
def setup_ldap(app):
|
||||
g.ldap = ldap.initialize(app.config["LDAP"]["URI"])
|
||||
g.ldap.simple_bind_s(app.config["LDAP"]["BIND_DN"], app.config["LDAP"]["BIND_PW"])
|
||||
|
||||
|
||||
def teardown_ldap(app):
|
||||
if "ldap" in g:
|
||||
g.ldap.unbind_s()
|
||||
|
||||
|
||||
def setup_app(app):
|
||||
if SENTRY and app.config.get("SENTRY_DSN"):
|
||||
sentry_sdk.init(dsn=app.config["SENTRY_DSN"], integrations=[FlaskIntegration()])
|
||||
|
@ -123,6 +134,7 @@ def setup_app(app):
|
|||
setup_ldap_tree(app)
|
||||
app.register_blueprint(canaille.account.bp)
|
||||
app.register_blueprint(canaille.oauth.bp, url_prefix="/oauth")
|
||||
app.register_blueprint(canaille.commands.bp)
|
||||
app.register_blueprint(canaille.consents.bp, url_prefix="/consent")
|
||||
app.register_blueprint(canaille.tokens.bp, url_prefix="/token")
|
||||
app.register_blueprint(canaille.well_known.bp, url_prefix="/.well-known")
|
||||
|
@ -153,15 +165,11 @@ def setup_app(app):
|
|||
|
||||
@app.before_request
|
||||
def before_request():
|
||||
g.ldap = ldap.initialize(app.config["LDAP"]["URI"])
|
||||
g.ldap.simple_bind_s(
|
||||
app.config["LDAP"]["BIND_DN"], app.config["LDAP"]["BIND_PW"]
|
||||
)
|
||||
setup_ldap(app)
|
||||
|
||||
@app.after_request
|
||||
def after_request(response):
|
||||
if "ldap" in g:
|
||||
g.ldap.unbind_s()
|
||||
teardown_ldap(app)
|
||||
return response
|
||||
|
||||
@app.context_processor
|
||||
|
|
22
canaille/commands.py
Normal file
22
canaille/commands.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
from flask import Blueprint, current_app
|
||||
from canaille.models import AuthorizationCode, Token
|
||||
|
||||
|
||||
bp = Blueprint("commands", __name__)
|
||||
|
||||
|
||||
@bp.cli.command("clean")
|
||||
def clean():
|
||||
from canaille import setup_ldap
|
||||
|
||||
setup_ldap(current_app)
|
||||
|
||||
for t in Token.filter():
|
||||
if t.is_expired():
|
||||
t.delete()
|
||||
|
||||
for a in AuthorizationCode.filter():
|
||||
if a.is_expired():
|
||||
a.delete()
|
||||
|
||||
teardown_ldap()
|
|
@ -234,6 +234,13 @@ class Token(LDAPObject, TokenMixin):
|
|||
|
||||
return self.expire_date >= datetime.datetime.now()
|
||||
|
||||
def is_expired(self):
|
||||
return (
|
||||
datetime.datetime.strptime(self.oauthIssueDate, "%Y%m%d%H%M%SZ")
|
||||
+ datetime.timedelta(seconds=int(self.oauthTokenLifetime))
|
||||
< datetime.datetime.now()
|
||||
)
|
||||
|
||||
|
||||
class Consent(LDAPObject):
|
||||
object_class = ["oauthConsent"]
|
||||
|
|
|
@ -26,6 +26,7 @@ include_package_data = true
|
|||
python_requires = >= 3.6
|
||||
install_requires =
|
||||
authlib
|
||||
click
|
||||
email_validator
|
||||
flask
|
||||
flask-babel
|
||||
|
|
51
tests/test_command_clean.py
Normal file
51
tests/test_command_clean.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
import datetime
|
||||
from canaille.commands import clean
|
||||
from canaille.models import AuthorizationCode, Token
|
||||
from werkzeug.security import gen_salt
|
||||
|
||||
|
||||
def test_clean_command(testclient, slapd_connection, client, user):
|
||||
AuthorizationCode.ocs_by_name(slapd_connection)
|
||||
code = AuthorizationCode(
|
||||
oauthCode="my-code",
|
||||
oauthClient=client.dn,
|
||||
oauthSubject=user.dn,
|
||||
oauthRedirectURI="https://foo.bar/callback",
|
||||
oauthResponseType="code",
|
||||
oauthScope="openid profile",
|
||||
oauthNonce="nonce",
|
||||
oauthAuthorizationDate=(
|
||||
datetime.datetime.now() - datetime.timedelta(days=1)
|
||||
).strftime("%Y%m%d%H%M%SZ"),
|
||||
oauthAuthorizationLifetime="3600",
|
||||
oauthCodeChallenge="challenge",
|
||||
oauthCodeChallengeMethod="method",
|
||||
oauthRevokation="",
|
||||
)
|
||||
code.save(slapd_connection)
|
||||
|
||||
Token.ocs_by_name(slapd_connection)
|
||||
token = Token(
|
||||
oauthAccessToken="my-token",
|
||||
oauthClient=client.dn,
|
||||
oauthSubject=user.dn,
|
||||
oauthTokenType=None,
|
||||
oauthRefreshToken=gen_salt(48),
|
||||
oauthScope="openid profile",
|
||||
oauthIssueDate=(datetime.datetime.now() - datetime.timedelta(days=1)).strftime(
|
||||
"%Y%m%d%H%M%SZ"
|
||||
),
|
||||
oauthTokenLifetime=str(3600),
|
||||
)
|
||||
token.save(slapd_connection)
|
||||
|
||||
assert AuthorizationCode.get("my-code", conn=slapd_connection)
|
||||
assert Token.get("my-token", conn=slapd_connection)
|
||||
assert code.is_expired()
|
||||
assert token.is_expired()
|
||||
|
||||
runner = testclient.app.test_cli_runner()
|
||||
res = runner.invoke(clean)
|
||||
|
||||
assert not AuthorizationCode.get("my-code", conn=slapd_connection)
|
||||
assert not Token.get("my-token", conn=slapd_connection)
|
Loading…
Reference in a new issue