Skip to content

Commit 1d386d4

Browse files
authored
Merge branch 'master' into fix_620
2 parents b1927e3 + 4e0bdf1 commit 1d386d4

16 files changed

Lines changed: 310 additions & 41 deletions

README.md

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,18 @@ Enjoy!
166166
The BornHack website can act as an OIDC IDP. You are welcome to use it for your projects.
167167

168168

169-
### OIDC User Claims
169+
### OIDC Scopes and User Claims
170+
The website has a view to inspect which OIDC user claims are returned when using the various claim scopes. It can be accessed at https://bornhack.dk/profile/oidc/
170171

171-
The supported standard and custom OIDC user claims can be seen in `bornhack/oauth_validators.py` https://github.com/bornhack/bornhack-website/blob/master/src/bornhack/oauth_validators
172172

173+
### OIDC User Claims Source Code
173174

174-
### OIDC Scopes
175+
The supported standard and custom OIDC user claims can be seen in `bornhack/oauth_validators.py` https://github.com/bornhack/bornhack-website/blob/master/src/bornhack/oauth_validators.py
175176

176-
Supported oauth2 scopes are split between standard OIDC claim scopes, custom OIDC claim scopes, and API scopes. The current list of supported scopes can be seen in the `OAUTH2_PROVIDER["SCOPES"]` dict in `bornhack/settings.py` https://github.com/bornhack/bornhack-website/blob/master/src/bornhack/settings.py
177+
178+
### OIDC Scopes Source Code
179+
180+
Supported oauth2 scopes are divided into standard OIDC claim scopes, custom OIDC claim scopes, and API scopes. The current list of supported scopes can be seen in the `OAUTH2_PROVIDER["SCOPES"]` dict in `bornhack/settings.py` https://github.com/bornhack/bornhack-website/blob/master/src/bornhack/settings.py
177181

178182

179183
## Notes
@@ -183,29 +187,22 @@ If your database user in your dev setup is not a postgres superuser you will enc
183187

184188
### Add a camp
185189

186-
Add a new camp by running:
187-
188-
```
189-
(venv) $ python src/manage.py createcamp {camp-slug}
190-
```
190+
First do a commit with:
191191

192-
Then go to the admin interface to edit the camp details, adding the same slug
193-
that you just used and some current dates.
194-
195-
You can also specify details like:
196-
197-
* A sponsors page, `{camp-slug}_sponsors.html`, to `sponsors/templates`.
198192
* A frontpage, `{camp-slug}_camp_detail.html`, to `camps/templates`.
199-
* A call for speakers page, `{camp-slug}_call_for_speakers.html`, to `program/templates`.
200193
* A `static_src/img/{camp-slug}/logo` and add two logos:
201194
* `{camp-slug}-logo-large.png`
202195
* `{camp-slug}-logo-small.png`
203196

197+
Then go to the admin interface and add the camp.
198+
199+
204200
## Contributors
205201
* Alexander Færøy https://github.com/ahf
206202
* Benjamin Bach https://github.com/benjaoming
207203
* coral https://github.com/coral
208204
* Flemming Jacobsen https://github.com/batmule
205+
* Florian Klink https://github.com/flokli
209206
* Henrik Kramshøj https://github.com/kramse
210207
* Janus Troelsen https://github.com/ysangkok
211208
* Jeppe Ernst https://github.com/Ern-st

src/bornhack/oauth_validators.py

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,23 @@ class BornhackOAuth2Validator(OAuth2Validator):
88

99
# supported user claims and the scopes they require
1010
# https://django-oauth-toolkit.readthedocs.io/en/latest/oidc.html#using-oidc-scopes-to-determine-which-claims-are-returned
11-
oidc_claim_scope = OAuth2Validator.oidc_claim_scope
12-
oidc_claim_scope.update(
13-
{
14-
# the OIDC standard user claims we support, and the OIDC stanard scopes they require
15-
"address": "address",
16-
"email": "email",
17-
"email_verified": "email",
18-
"nickname": "profile",
19-
"phone_number": "phone",
20-
"phone_number_verified": "phone",
21-
"updated_at": "profile",
22-
# the custom user claims we support, and the (mostly cusom) scopes they require
23-
"bornhack:v2:description": "profile",
24-
"bornhack:v2:groups": "groups:read",
25-
"bornhack:v2:permissions": "permissions:read",
26-
"bornhack:v2:teams": "teams:read",
27-
},
28-
)
11+
oidc_claim_scope = {
12+
# the OIDC standard user claims we support, and the OIDC standard scopes they require
13+
"sub": "openid",
14+
"email": "email",
15+
"email_verified": "email",
16+
"phone_number": "phone",
17+
"preferred_username": "profile",
18+
"updated_at": "profile",
19+
# the custom user claims available under standard OIDC scopes
20+
"bornhack:v2:description": "profile",
21+
"bornhack:v2:public_credit_name": "profile",
22+
# the custom user claims we support under custom OIDC scopes
23+
"bornhack:v2:groups": "groups:read",
24+
"bornhack:v2:location": "location:read",
25+
"bornhack:v2:permissions": "permissions:read",
26+
"bornhack:v2:teams": "teams:read",
27+
}
2928

3029
def get_claim_dict(self, request) -> dict[str, str]:
3130
"""Return username (usually a uuid) instead of user pk in the 'sub' claim."""
@@ -45,12 +44,12 @@ def get_additional_claims(self, request) -> dict[str, str | list[dict[str, str]]
4544
if request.user.is_anonymous:
4645
return claims
4746

47+
# these claims are always available
4848
claims.update(
4949
{
5050
# standard OIDC claims
5151
"email": request.user.email,
5252
"email_verified": True,
53-
"nickname": request.user.profile.get_public_credit_name,
5453
"updated_at": int(request.user.profile.updated.timestamp()),
5554
# bornhack custom claims
5655
"bornhack:v2:teams": [
@@ -68,15 +67,31 @@ def get_additional_claims(self, request) -> dict[str, str | list[dict[str, str]]
6867
},
6968
)
7069

70+
# include bornhack:v2:public_credit_name?
71+
if (
72+
request.user.profile.public_credit_name_approved
73+
and request.user.profile.public_credit_name
74+
):
75+
claims["bornhack:v2:public_credit_name"] = (
76+
request.user.profile.public_credit_name
77+
)
78+
79+
# include location?
7180
if request.user.profile.location:
72-
claims["address"] = {"formatted": request.user.profile.location}
81+
claims["bornhack:v2:location"] = request.user.profile.location
7382

83+
# include phonenumber?
7484
if request.user.profile.phonenumber:
7585
claims["phone_number"] = request.user.profile.phonenumber
76-
claims["phone_number_verified"] = True
7786

87+
# include profile description?
7888
if request.user.profile.description:
7989
claims["bornhack:v2:description"] = request.user.profile.description
90+
91+
# include preferred_username?
92+
if request.user.profile.preferred_username:
93+
claims["preferred_username"] = request.user.profile.preferred_username
94+
8095
return claims
8196

8297
def get_discovery_claims(self, request) -> list[str]:
@@ -86,17 +101,19 @@ def get_discovery_claims(self, request) -> list[str]:
86101
"""
87102
return [
88103
# OIDC standard claims
89-
"address",
90104
"email",
91105
"email_verified",
92106
"nickname",
93107
"phone_number",
94108
"phone_number_verified",
109+
"preferred_username",
95110
"sub",
96111
"updated_at",
97112
# custom user claims
98113
"bornhack:v2:description",
99114
"bornhack:v2:groups",
115+
"bornhack:v2:location",
100116
"bornhack:v2:permissions",
117+
"bornhack:v2:public_credit_name",
101118
"bornhack:v2:teams",
102119
]

src/bornhack/settings.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,19 +221,24 @@
221221
"SCOPES": {
222222
# required
223223
"openid": "OpenID Connect scope",
224+
224225
# deprecated api scope, remove after 2025 camp
225226
"profile:read": "Allow the remote site to read your bornhack.dk username (uuid), user id, profile public credit name, profile description, and a list of team memberships using the profile API endpoint (scope profile:read). NOTE: This scope is being deprecated soon! Ask the BornHack website team for more info.",
227+
226228
# standard OIDC claim scopes
227229
"profile": "Allow the remote site to read your profile public_credit_name, description, and update time (scope: profile)",
228230
"email": "Allow the remote site to read your email address (scope: email)",
229231
"address": "Allow the remote site to read your profile location (scope: address)",
230232
"phone": "Allow the remote site to read your profile phonenumber (scope: phone)",
233+
231234
# custom bornhack user claim scopes
232235
"groups:read": "Allow the remote site to read a list of your group memberships (scope: groups:read).",
236+
"location:read": "Allow the remote site to read your profile location (scope: loocation:read)",
233237
"permissions:read": "Allow the remote site to read a list of your assigned permissions (scope: permissions:read).",
234-
"teams:read": "Allow the remote site to read a list of your team memberships and team lead status (scope: teams)",
238+
"teams:read": "Allow the remote site to read a list of your team memberships and team lead status (scope: teams:read)",
239+
235240
# api scopes
236-
"phonebook:admin": "Allow the remote site to read the camp phonebook including service numbers and unlisted numbers. Also allows access to the POC API. Only relevant for POC team leads (scope: phonebook:admin).",
241+
"phonebook:admin": "Allow the remote site to read the camp phonebook, including service numbers and unlisted numbers. Also allow the remote site to use to the POC API. This scope is only relevant for POC team leads (scope: phonebook:admin).",
237242
"phonebook:read": "Allow the remote site to read the camp phonebook (scope: phonebook:read).",
238243
},
239244
"PKCE_REQUIRED": True,
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
{% extends 'base.html' %}
2+
{% load commonmark %}
3+
{% load static %}
4+
{% load imageutils %}
5+
6+
{% block title %}
7+
{{ camp.title }}
8+
{% endblock %}
9+
10+
{% block content %}
11+
<div class="row mt-3">
12+
<div class="col-12 text-center">
13+
<img src="{% static camp.logo_large %}" width="800" class="img-responsive" id="front-logo" />
14+
</div>
15+
</div>
16+
17+
18+
<div class="row mt-3">
19+
<div class="col-12 col-lg-9">
20+
<div class="lead">
21+
<b>BornHack</b> is a 7 day <b>outdoor tent camp</b> where hackers, makers and people with an interest in technology or security come together to celebrate technology, socialise, learn and <b>have fun</b>.
22+
</div>
23+
</div>
24+
<div class="col-12 col-lg-3">
25+
{% thumbnail 'img/bornhack-2020' 'logotent_pink_people.jpg' 'BornHack Logotent with pink light and people' %}
26+
</div>
27+
</div>
28+
29+
30+
<div class="row mt-3">
31+
<div class="col-12 col-lg-3">
32+
{% thumbnail 'img/bornhack-2020' 'participant_tent_laptop.jpg' 'hacker with laptop in front of tent' %}
33+
</div>
34+
<div class="col-12 col-lg-9">
35+
<div class="lead">
36+
<strong>BornHack 2026</strong> will be the eleventh BornHack. It will take place from <strong>Wednesday the 15th of July to Wednesday the 22nd of July 2026</strong> at our venue on the Danish island of Funen.
37+
</div>
38+
</div>
39+
</div>
40+
41+
<br />
42+
43+
<div class="row mt-3">
44+
<div class="col-12 col-lg-9">
45+
<div class="lead">
46+
The BornHack team looks forward to organising another great event for the hacker community. We <a href="{% url 'teams:list' camp_slug=camp.slug %}">still need volunteers</a>, so please let us know if you want to help!
47+
</div>
48+
</div>
49+
<div class="col-12 col-lg-3">
50+
{% thumbnail 'img/bornhack-2021' 'volunteers.jpg' 'The BornHack 2021 volunteers' %}
51+
</div>
52+
</div>
53+
54+
<br />
55+
56+
<div class="row mt-3">
57+
<div class="col-12 col-lg-3">
58+
{% thumbnail 'img/bornhack-2018/fonsmark' 'FB1_9970_cropped.JPG' 'Danish politicians, government officals and IT-developers debating at BornHack 2018' %}
59+
</div>
60+
<div class="col-12 col-lg-9">
61+
<div class="lead">We want to encourage <strong>hackers, makers, politicians, activists, developers, artists, sysadmins, engineers</strong> with something to say to read our <a href="{% url 'program:call_for_participation' camp_slug=camp.slug %}">call for participation</a>.</div>
62+
</div>
63+
</div>
64+
65+
<br />
66+
67+
<div class="row mt-3">
68+
<div class="col-12 col-lg-9">
69+
<div class="lead">
70+
BornHack aims to <strong>keep ticket prices affordable</strong> for everyone and to that end <strong>we need sponsors</strong>. Please see our <a href="{% url 'sponsors' camp_slug=camp.slug %}">call for sponsors</a> if you want to sponsor us, or if you work for a company you think might be able to help.
71+
</div>
72+
</div>
73+
<div class="col-12 col-lg-3">
74+
{% thumbnail 'img/bornhack-2017/fonsmark' 'FB1_7675_cropped.jpg' 'Organisers thanking the BornHack 2017 sponsors' %}
75+
</div>
76+
</div>
77+
78+
<br />
79+
80+
<div class="row mt-3">
81+
<div class="col-12">
82+
<p class="lead">You are very welcome to ask questions and show your interest on our different channels:</p>
83+
{% include 'includes/contact.html' %}
84+
</div>
85+
</div>
86+
<p align="center">
87+
{% thumbnail 'img/bornhack-2020' 'ellen_tesla_coil.jpg' 'Little girl and father making arcs with tesla coil' %}
88+
{% thumbnail 'img/bornhack-2020' 'dome_lights.jpg' 'Geodesic dome and tents with lights' %}
89+
{% thumbnail 'img/bornhack-2017/fonsmark' 'FB1_7724_cropped.jpg' 'A welcoming hug upon arrival at BornHack 2017! <3' %}
90+
{% thumbnail 'img/bornhack-2016/fonsmark' 'FB1_5168.JPG' '#irl_bar by night at BornHack 2016' %}
91+
{% thumbnail 'img/bornhack-2018/flummer' 'DSC_7054_cropped.jpg' 'The BornHack 2018 badge' %}
92+
{% thumbnail 'img/bornhack-2019' 'masked_jeopardy_beer_wall.jpg' 'Masked hacker jeopardy contenders behind beer can wall' %}
93+
{% thumbnail 'img/bornhack-2020' 'badge_cyber.jpg' 'The BornHack 2020 badge displaying "cyber"' %}
94+
{% thumbnail 'img/bornhack-2020' 'night_stars_lights.jpg' 'Stars above colorfully lit trees' %}
95+
{% thumbnail 'img/bornhack-2020' 'bar_people.jpg' 'Bornhack bar in 2020' %}
96+
{% thumbnail 'img/bornhack-2017/fonsmark' 'FB1_7521_cropped.jpg' 'Amelia Andersdotter presenting at BornHack 2017' %}
97+
</p>
98+
{% endblock content %}

src/profiles/forms.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from django import forms
2+
from bornhack.oauth_validators import BornhackOAuth2Validator
3+
4+
def get_scopes() -> list[str]:
5+
validator = BornhackOAuth2Validator()
6+
return ((scope, scope) for scope in sorted(set(validator.oidc_claim_scope.values())) if scope!="openid")
7+
8+
class OIDCForm(forms.Form):
9+
scopes = forms.MultipleChoiceField(
10+
choices=get_scopes,
11+
help_text="Select the scopes to simulate. The 'openid' scope is always included.",
12+
)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 4.2.20 on 2025-04-28 15:00
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('profiles', '0017_alter_profile_phonenumber'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='profile',
15+
name='preferred_username',
16+
field=models.SlugField(blank=True, help_text="When using your BornHack account to login to other sites with OIDC this value is served as the OIDC standard claim 'preferred_username'. You can set this to the username you would prefer to use on remote sites where you login with your BornHack account."),
17+
),
18+
]

src/profiles/models.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,18 @@ class Meta:
6767
help_text="The phonenumber you can be reached on. This field can be updated automatically when registering a DECT number in the phonebook.",
6868
)
6969

70-
# default to near general camping
7170
location = PointField(
7271
blank=True,
7372
null=True,
7473
help_text="Your location at BornHack. This value is available on public maps.",
7574
)
7675

76+
preferred_username = models.SlugField(
77+
blank=True,
78+
max_length=50,
79+
help_text="When using your BornHack account to login to other sites with OIDC this value is served as the OIDC standard claim 'preferred_username'. You can set this to the username you would prefer to use on remote sites where you login with your BornHack account.",
80+
)
81+
7782
@property
7883
def email(self):
7984
return self.user.email

0 commit comments

Comments
 (0)