mirror of
https://gitlab.crans.org/bde/nk20
synced 2025-07-23 17:26:46 +02:00
Compare commits
12 Commits
oidc
...
0903c8da57
Author | SHA1 | Date | |
---|---|---|---|
0903c8da57 | |||
03932672f3 | |||
d58a299a8b | |||
c4404ef995 | |||
f0e9a7d3dc | |||
e6f3084588 | |||
145e55da75 | |||
d3ba95cdca | |||
8ffb0ebb56 | |||
5038af9e34 | |||
819b4214c9 | |||
b8a93b0b75 |
@ -21,6 +21,3 @@ EMAIL_PASSWORD=CHANGE_ME
|
||||
# Wiki configuration
|
||||
WIKI_USER=NoteKfet2020
|
||||
WIKI_PASSWORD=
|
||||
|
||||
# OIDC
|
||||
OIDC_RSA_PRIVATE_KEY=CHANGE_ME
|
||||
|
@ -61,8 +61,8 @@ Bien que cela permette de créer une instance sur toutes les distributions,
|
||||
6. (Optionnel) **Création d'une clé privée OpenID Connect**
|
||||
|
||||
Pour activer le support d'OpenID Connect, il faut générer une clé privée, par
|
||||
exemple avec openssl (`openssl genrsa -out oidc.key 4096`), et copier la clé dans .env dans le champ
|
||||
`OIDC_RSA_PRIVATE_KEY`.
|
||||
exemple avec openssl (`openssl genrsa -out oidc.key 4096`), et renseigner son
|
||||
emplacement dans `OIDC_RSA_PRIVATE_KEY` (par défaut `/var/secrets/oidc.key`).
|
||||
|
||||
7. Enjoy :
|
||||
|
||||
@ -237,8 +237,8 @@ Sinon vous pouvez suivre les étapes décrites ci-dessous.
|
||||
7. **Création d'une clé privée OpenID Connect**
|
||||
|
||||
Pour activer le support d'OpenID Connect, il faut générer une clé privée, par
|
||||
exemple avec openssl (`openssl genrsa -out oidc.key 4096`), et renseigner le champ
|
||||
`OIDC_RSA_PRIVATE_KEY` dans le .env (par défaut `/var/secrets/oidc.key`).
|
||||
exemple avec openssl (`openssl genrsa -out oidc.key 4096`), et renseigner son
|
||||
emplacement dans `OIDC_RSA_PRIVATE_KEY` (par défaut `/var/secrets/oidc.key`).
|
||||
|
||||
8. *Enjoy \o/*
|
||||
|
||||
|
@ -38,6 +38,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</a>
|
||||
|
||||
<input id="alias" type="text" class="form-control" placeholder="Nom/note ...">
|
||||
<button id="trigger" class="btn btn-secondary">Click me !</button>
|
||||
|
||||
<hr>
|
||||
|
||||
@ -63,15 +64,46 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
refreshBalance();
|
||||
}
|
||||
|
||||
function process_qrcode() {
|
||||
let name = alias_obj.val();
|
||||
$.get("/api/note/note?search=" + name + "&format=json").done(
|
||||
function (res) {
|
||||
let note = res.results[0];
|
||||
$.post("/api/activity/entry/?format=json", {
|
||||
csrfmiddlewaretoken: CSRF_TOKEN,
|
||||
activity: {{ activity.id }},
|
||||
note: note.id,
|
||||
guest: null
|
||||
}).done(function () {
|
||||
addMsg(interpolate(gettext(
|
||||
"Entry made for %s whose balance is %s €"),
|
||||
[note.name, note.balance / 100]), "success", 4000);
|
||||
reloadTable(true);
|
||||
}).fail(function (xhr) {
|
||||
errMsg(xhr.responseJSON, 4000);
|
||||
});
|
||||
}).fail(function (xhr) {
|
||||
errMsg(xhr.responseJSON, 4000);
|
||||
});
|
||||
}
|
||||
|
||||
alias_obj.keyup(function(event) {
|
||||
let code = event.originalEvent.keyCode
|
||||
if (65 <= code <= 122 || code === 13) {
|
||||
debounce(reloadTable)()
|
||||
}
|
||||
if (code === 0)
|
||||
process_qrcode();
|
||||
});
|
||||
|
||||
$(document).ready(init);
|
||||
|
||||
alias_obj2 = document.getElementById("alias");
|
||||
$("#trigger").click(function (e) {
|
||||
addMsg("Clicked", "success", 1000);
|
||||
alias_obj.val(alias_obj.val() + "\0");
|
||||
alias_obj2.dispatchEvent(new KeyboardEvent('keyup'));
|
||||
})
|
||||
function init() {
|
||||
$(".table-row").click(function (e) {
|
||||
let target = e.target.parentElement;
|
||||
@ -168,4 +200,4 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
@ -63,8 +63,7 @@ class FoodListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, Li
|
||||
valid_regex = is_regex(pattern)
|
||||
suffix = '__iregex' if valid_regex else '__istartswith'
|
||||
prefix = '^' if valid_regex else ''
|
||||
qs = qs.filter(Q(**{f'name{suffix}': prefix + pattern})
|
||||
| Q(**{f'owner__name{suffix}': prefix + pattern}))
|
||||
qs = qs.filter(Q(**{f'name{suffix}': prefix + pattern}))
|
||||
else:
|
||||
qs = qs.none()
|
||||
search_table = qs.filter(PermissionBackend.filter_queryset(self.request, Food, 'view'))
|
||||
@ -72,7 +71,7 @@ class FoodListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, Li
|
||||
open_table = self.get_queryset().order_by('expiry_date').filter(
|
||||
Q(polymorphic_ctype__model='transformedfood')
|
||||
| Q(polymorphic_ctype__model='basicfood', basicfood__date_type='DLC')).filter(
|
||||
expiry_date__lt=timezone.now(), end_of_life='').filter(
|
||||
expiry_date__lt=timezone.now()).filter(
|
||||
PermissionBackend.filter_queryset(self.request, Food, 'view'))
|
||||
# table served
|
||||
served_table = self.get_queryset().order_by('-pk').filter(
|
||||
|
@ -60,7 +60,10 @@
|
||||
{% if user_object.pk == user.pk %}
|
||||
<div class="text-center">
|
||||
<a class="small badge badge-secondary" href="{% url 'member:auth_token' %}">
|
||||
<i class="fa fa-cogs"></i>{% trans 'API token' %}
|
||||
<i class="fa fa-cogs"></i> {% trans 'API token' %}
|
||||
</a>
|
||||
<a class="small badge badge-secondary" href="{% url 'member:qr_code' user_object.pk %}">
|
||||
<i class="fa fa-qrcode"></i> {% trans 'QR Code' %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
36
apps/member/templates/member/qr_code.html
Normal file
36
apps/member/templates/member/qr_code.html
Normal file
@ -0,0 +1,36 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card bg-light">
|
||||
<h3 class="card-header text-center">
|
||||
{% trans "QR Code for" %} {{ user_object.username }} ({{ user_object.first_name }} {{user_object.last_name }})
|
||||
</h3>
|
||||
<div class="text-center" id="qrcode">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block extrajavascript %}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js" integrity="sha512-CNgIRecGo7nphbeZ04Sc13ka07paqdeTu0WR1IM4kNcpmBAUSHSQX0FslNhTDadL4O5SAGapGt4FodqL8My0mA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script>
|
||||
var qrc = new QRCode(document.getElementById("qrcode"), {
|
||||
text: "{{ user_object.pk }}\0",
|
||||
width: 1024,
|
||||
height: 1024
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block extracss %}
|
||||
<style>
|
||||
img {
|
||||
width: 100%
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
@ -25,4 +25,5 @@ urlpatterns = [
|
||||
path('user/<int:pk>/aliases/', views.ProfileAliasView.as_view(), name="user_alias"),
|
||||
path('user/<int:pk>/trust', views.ProfileTrustView.as_view(), name="user_trust"),
|
||||
path('manage-auth-token/', views.ManageAuthTokens.as_view(), name='auth_token'),
|
||||
path('user/<int:pk>/qr_code/', views.QRCodeView.as_view(), name='qr_code'),
|
||||
]
|
||||
|
@ -402,6 +402,14 @@ class ManageAuthTokens(LoginRequiredMixin, TemplateView):
|
||||
context['token'] = Token.objects.get_or_create(user=self.request.user)[0]
|
||||
return context
|
||||
|
||||
class QRCodeView(LoginRequiredMixin, DetailView):
|
||||
"""
|
||||
Affiche le QR Code
|
||||
"""
|
||||
model = User
|
||||
context_object_name = "user_object"
|
||||
template_name = "member/qr_code.html"
|
||||
extra_context = {"title": _("QR Code")}
|
||||
|
||||
# ******************************* #
|
||||
# CLUB #
|
||||
|
@ -1,10 +1,8 @@
|
||||
# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from oauth2_provider.oauth2_validators import OAuth2Validator
|
||||
from oauth2_provider.scopes import BaseScopes
|
||||
from member.models import Club
|
||||
from note.models import Alias
|
||||
from note_kfet.middlewares import get_current_request
|
||||
|
||||
from .backends import PermissionBackend
|
||||
@ -19,46 +17,25 @@ class PermissionScopes(BaseScopes):
|
||||
"""
|
||||
|
||||
def get_all_scopes(self):
|
||||
scopes = {f"{p.id}_{club.id}": f"{p.description} (club {club.name})"
|
||||
for p in Permission.objects.all() for club in Club.objects.all()}
|
||||
scopes['openid'] = "OpenID Connect"
|
||||
return scopes
|
||||
return {f"{p.id}_{club.id}": f"{p.description} (club {club.name})"
|
||||
for p in Permission.objects.all() for club in Club.objects.all()}
|
||||
|
||||
def get_available_scopes(self, application=None, request=None, *args, **kwargs):
|
||||
if not application:
|
||||
return []
|
||||
scopes = [f"{p.id}_{p.membership.club.id}"
|
||||
for t in Permission.PERMISSION_TYPES
|
||||
for p in PermissionBackend.get_raw_permissions(get_current_request(), t[0])]
|
||||
scopes.append('openid')
|
||||
return scopes
|
||||
return [f"{p.id}_{p.membership.club.id}"
|
||||
for t in Permission.PERMISSION_TYPES
|
||||
for p in PermissionBackend.get_raw_permissions(get_current_request(), t[0])]
|
||||
|
||||
def get_default_scopes(self, application=None, request=None, *args, **kwargs):
|
||||
if not application:
|
||||
return []
|
||||
scopes = [f"{p.id}_{p.membership.club.id}"
|
||||
for p in PermissionBackend.get_raw_permissions(get_current_request(), 'view')]
|
||||
scopes.append('openid')
|
||||
return scopes
|
||||
return [f"{p.id}_{p.membership.club.id}"
|
||||
for p in PermissionBackend.get_raw_permissions(get_current_request(), 'view')]
|
||||
|
||||
|
||||
class PermissionOAuth2Validator(OAuth2Validator):
|
||||
oidc_claim_scope = OAuth2Validator.oidc_claim_scope
|
||||
oidc_claim_scope.update({"name": 'openid',
|
||||
"normalized_name": 'openid',
|
||||
"email": 'openid',
|
||||
})
|
||||
|
||||
def get_additional_claims(self, request):
|
||||
return {
|
||||
"name": request.user.username,
|
||||
"normalized_name": Alias.normalize(request.user.username),
|
||||
"email": request.user.email,
|
||||
}
|
||||
|
||||
def get_discovery_claims(self, request):
|
||||
claims = super().get_discovery_claims(self)
|
||||
return claims + ["name", "normalized_name", "email"]
|
||||
oidc_claim_scope = None # fix breaking change of django-oauth-toolkit 2.0.0
|
||||
|
||||
def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs):
|
||||
"""
|
||||
@ -77,8 +54,6 @@ class PermissionOAuth2Validator(OAuth2Validator):
|
||||
if scope in scopes:
|
||||
valid_scopes.add(scope)
|
||||
|
||||
if 'openid' in scopes:
|
||||
valid_scopes.add('openid')
|
||||
|
||||
request.scopes = valid_scopes
|
||||
|
||||
return valid_scopes
|
||||
|
@ -19,7 +19,6 @@ EXCLUDED = [
|
||||
'oauth2_provider.accesstoken',
|
||||
'oauth2_provider.grant',
|
||||
'oauth2_provider.refreshtoken',
|
||||
'oauth2_provider.idtoken',
|
||||
'sessions.session',
|
||||
]
|
||||
|
||||
|
@ -171,7 +171,7 @@ class ScopesView(LoginRequiredMixin, TemplateView):
|
||||
available_scopes = scopes.get_available_scopes(app)
|
||||
context["scopes"][app] = OrderedDict()
|
||||
items = [(k, v) for (k, v) in all_scopes.items() if k in available_scopes]
|
||||
# items.sort(key=lambda x: (int(x[0].split("_")[1]), int(x[0].split("_")[0])))
|
||||
items.sort(key=lambda x: (int(x[0].split("_")[1]), int(x[0].split("_")[0])))
|
||||
for k, v in items:
|
||||
context["scopes"][app][k] = v
|
||||
|
||||
|
@ -270,7 +270,7 @@ OAUTH2_PROVIDER = {
|
||||
'PKCE_REQUIRED': False, # PKCE (fix a breaking change of django-oauth-toolkit 2.0.0)
|
||||
'OIDC_ENABLED': True,
|
||||
'OIDC_RSA_PRIVATE_KEY':
|
||||
os.getenv('OIDC_RSA_PRIVATE_KEY', 'CHANGE_ME_IN_ENV_SETTINGS').replace('\\n', '\n'), # for multilines
|
||||
os.getenv('OIDC_RSA_PRIVATE_KEY', '/var/secrets/oidc.key'),
|
||||
'SCOPES': { 'openid': "OpenID Connect scope" },
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user