Commit 4360efbc authored by Pietro Albini's avatar Pietro Albini

Add permissions support

parent f94329f7
Pipeline #76 passed with stage
in 0 seconds
......@@ -51,6 +51,7 @@ def run(data, gunicorn_config, port, public, workers, debug):
if debug:
extra_files = [
os.path.join(src_directory, "data/navbar.yml"),
os.path.join(src_directory, "data/permissions.yml"),
]
app.run(debug=True, port=port, host=host, extra_files=extra_files)
......
......@@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import functools
import uuid
import flask
......@@ -106,8 +107,56 @@ class Permissions:
self.config = yaml.load(raw.decode("utf-8"))
def allowed_teams(self):
"""Return a list of teams allowed to log in"""
return list(self.config["teams"].keys())
def check(self, name):
"""Check if the current user has the permission"""
# The user must be authenticated
if "auth_teams" not in flask.g:
return False
# The permission must exist
if name not in self.config["permissions"]:
raise RuntimeError("Missing permission: %s" % name)
# Check if one of the user's teams has the permission
for team in flask.g.auth_teams:
if self.config["teams"][team] == "*":
return True
elif name in self.config["teams"][team]:
return True
return False
def permission(perms):
"""Process the endpoint only if the user has permission"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# Don't check anything if the user is not logged in
if "auth_id" not in flask.g:
return flask.abort(403)
# Generate the list of allowed permissions
names = []
if type(perms) == dict:
for name, condition in perms.items():
if condition(*args, **kwargs):
names.append(name)
else:
names.append(perms)
# Check each allowed permission
for name in names:
if flask.current_app.permissions.check(name):
return func(*args, **kwargs)
return flask.abort(403)
return wrapper
return decorator
def prepare_blueprint(app):
"""Prepare the auth blueprint"""
......@@ -119,7 +168,13 @@ def prepare_blueprint(app):
extension_responses=[openid_teams.TeamsResponse],
)
sessions = Sessions(app.db)
permissions = Permissions()
app.permissions = Permissions()
@app.context_processor
def add_permission_function():
return {
"permission": app.permissions.check,
}
@app.before_request
def check_auth():
......@@ -156,7 +211,7 @@ def prepare_blueprint(app):
"https://login.ubuntu.com/+openid",
ask_for=["nickname"],
extensions=[
openid_teams.TeamsRequest(permissions.allowed_teams())
openid_teams.TeamsRequest(app.permissions.allowed_teams())
],
)
else:
......@@ -164,6 +219,7 @@ def prepare_blueprint(app):
return flask.redirect(flask.url_for("pages.index"))
@bp.route("/logout")
@permission("auth.logout")
def logout():
sessions.delete(flask.session["auth"])
del flask.session["auth"]
......@@ -173,6 +229,7 @@ def prepare_blueprint(app):
@bp.route("/sessions")
@permission("auth.sessions.manage")
def sessions_list():
return flask.render_template(
"auth/sessions.html",
......@@ -180,11 +237,16 @@ def prepare_blueprint(app):
)
@bp.route("/sessions/+all/revoke")
@permission("auth.sessions.manage")
def sessions_revoke_all():
sessions.delete_all(flask.g.auth_id)
return flask.redirect(flask.url_for(".sessions_list"))
@bp.route("/sessions/<id>")
@permission({
"auth.sessions.manage": lambda id: True,
"auth.sessions.own": lambda id: flask.g.auth_id == id,
})
def sessions_show(id):
data = sessions.get(id)
if data is None:
......@@ -204,6 +266,10 @@ def prepare_blueprint(app):
)
@bp.route("/sessions/<id>/revoke")
@permission({
"auth.sessions.manage": lambda id: True,
"auth.sessions.own": lambda id: flask.g.auth_id == id,
})
def sessions_revoke(id):
sessions.delete(id)
return flask.redirect(flask.url_for(".sessions_list"))
......
permissions: []
permissions:
- auth.logout
- auth.sessions.manage
- auth.sessions.own
teams:
ubuntu-it-www: "*"
ubuntu-it-council: "*"
ubuntu-it-members: []
ubuntu-it-newsletter: []
ubuntu-it-www: "*"
ubuntu-it-members:
- auth.sessions.own
- auth.logout
ubuntu-it-newsletter:
- auth.sessions.own
- auth.logout
......@@ -48,15 +48,21 @@
<nav class="sites-list">
<div class="container">
<ul class="left">
{% if permission("auth.sessions.manage") %}
<li><a href="{{ url_for("auth.sessions_list") }}">
Sessioni attive: {{ g.auth_sessions_count }}
</a></li>
{% endif %}
</ul>
<ul class="right">
{% if permission("auth.sessions.own") %}
<li><a href="{{ url_for("auth.sessions_show", id=g.auth_id) }}">
{{ g.auth_name }}
</a></li>
{% endif %}
{% if permission("auth.logout") %}
<li><a href="{{ url_for("auth.logout") }}">Esci</a></li>
{% endif %}
</ul>
</div>
</nav>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment