Skip to content

Commit

Permalink
New Shibboleth authentication
Browse files Browse the repository at this point in the history
* Dictionary style settings for shibboleth configuration
* Implement a parser for shib2 encoded multivalue fields.
* Parse only a single student number, based on a config value
* Authentication backend is rewritten, but should be more robust
  and handle problems better than before.
  e.g. before, oo long input values were just cut, and thus multiple
  different inputs might have resulted same output.
  Now we raise errors instead and require someone to fix the actual
  problem.
* Better debug output.. So we can actually find out these problems :)
  • Loading branch information
raphendyr committed Aug 6, 2019
1 parent a84f982 commit 7174417
Show file tree
Hide file tree
Showing 11 changed files with 675 additions and 169 deletions.
8 changes: 8 additions & 0 deletions aplus/local_settings.example.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
# 'social_django',
#)

## Shibboleth options
#SHIBBOLETH_ENVIRONMENT_VARS = {
# # required for the shibboleth system:
# 'PREFIX': 'SHIB_', # apache2: SHIB_, nginx: HTTP_SHIB_ (NOTE: client can inject HTTP_ vars!)
# 'STUDENT_DOMAIN': 'example.com', # domain where student numbers are selected
# # ..more options in aplus/settings.py
#}

## Database
#DATABASES = {
# 'default': {
Expand Down
43 changes: 31 additions & 12 deletions aplus/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,19 +124,38 @@

#INSTALLED_APPS += ('shibboleth_login',)

# Apache module mod_uwsgi was unable to create UTF-8 environment variables.
# Problem was avoided by URL encoding in Shibboleth:
# <RequestMapper type="Native">
# <RequestMap applicationId="default" encoding="URL" />
# </RequestMapper>
SHIBBOLETH_VARIABLES_URL_ENCODED = True
# Shibboleth Login configs
#SHIBBOLETH_LOGIN = {
# # users, who do not exists, are created
# 'ALLOW_CREATE_NEW_USERS': True,
# # if user is not found using USER_ID, then we can try with EMAIL
# # the search must yield only single user for it to succeed
# 'ALLOW_SEARCH_WITH_EMAIL': False,
#}

# Fields to receive from the Shibboleth (defaults).
#SHIB_USER_ID_KEY = 'SHIB_eppn'
#SHIB_FIRST_NAME_KEY = 'SHIB_displayName'
#SHIB_LAST_NAME_KEY = 'SHIB_sn'
#SHIB_MAIL_KEY = 'SHIB_mail'
#SHIB_STUDENT_ID_KEY = 'SHIB_schacPersonalUniqueCode'
#SHIBBOLETH_ENVIRONMENT_VARS = {
# # Apache module mod_uwsgi is unable to create UTF-8 environment variables.
# # Problem is avoided by enabling URL encoding in Shibboleth:
# # <RequestMapper type="Native">
# # <RequestMap applicationId="default" encoding="URL" />
# # </RequestMapper>
# # This is also recommended for nginx
# 'URL_DECODE': True, # set to False, if you are passing values in UTF-8
# # required:
# 'PREFIX': 'SHIB_',
# 'STUDENT_DOMAIN': 'example.com', # currently only student numbers from this domain are used
# # optional:
# 'USER_ID': 'eppn',
# 'FIRST_NAME': 'givenName',
# 'LAST_NAME': 'sn',
# 'COMMON_NAME': 'cn', # used if first or last name is missing
# 'FULL_NAME': 'displayName', # used if first or last name is missing
# 'EMAIL': 'mail',
# 'STUDENT_IDS': 'schacPersonalUniqueCode',
# 'STUDENT_URN': ':schac:personalUniqueCode:',
# 'STUDENT_FILTERS': {3: 'int', 2: 'studentID'},
#}


## Google OAuth2 settings
Expand Down Expand Up @@ -399,7 +418,7 @@
# Complain if BASE_URL is not set
try:
if not BASE_URL:
raise RuntimeError('Local setting BASE_URL should be non-empty')
raise RuntimeError('Local setting BASE_URL can not be empty')
except NameError as e:
raise RuntimeError('BASE_URL must be specified in local settings') from e

Expand Down
33 changes: 18 additions & 15 deletions doc/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,14 +232,17 @@ If you are using shibboleth
1. Shibboleth configuration in The `local_settings.py`

Map your federations variables to ones used in A+.
Here is the default mapping, which you can override in `local_settings.py`:

SHIB_USER_ID_KEY = 'SHIB_eppn'
SHIB_FIRST_NAME_KEY = 'SHIB_givenName'
SHIB_LAST_NAME_KEY = 'SHIB_sn'
SHIB_MAIL_KEY = 'SHIB_mail'
SHIB_STUDENT_ID_KEY = 'SHIB_schacPersonalUniqueCode'
Most of the values are common, so only defining `PREFIX` should be enough.
Currently, `STUDENT_DOMAIN` is a required variable, as A+ presumes student numbers to be from a single domain.
Rest of the options are documented in `settings.py`.

sudo tee -a /srv/aplus/a-plus/aplus/local_settings.py << EOF
# Shibboleth
SHIBBOLETH_ENVIRONMENT_VARS = {
'PREFIX': 'SHIB_',
'STUDENT_DOMAIN': 'example.com', # XXX: change this!
}
EOF

1. Reload shibboleth

Expand Down Expand Up @@ -352,17 +355,17 @@ This module uses fastcgi and shibboleth scripts to provide similar integration a

1. Shibboleth configuration in The `local_settings.py`

Shibboleth under NGINX delivers shibboleth variables via the request environment,
thus following mapping is required.
If your federation uses different variables, remember to change them.
Map your federations variables to ones used in A+.
Most of the values are common, so only defining `PREFIX` should be enough.
Currently, `STUDENT_DOMAIN` is a required variable, as A+ presumes student numbers to be from a single domain.
Rest of the options are documented in `settings.py`.

sudo tee -a /srv/aplus/a-plus/aplus/local_settings.py << EOF
# Shibboleth
SHIB_USER_ID_KEY = 'HTTP_SHIB_EPPN'
SHIB_FIRST_NAME_KEY = 'HTTP_SHIB_GIVENNAME'
SHIB_LAST_NAME_KEY = 'HTTP_SHIB_SN'
SHIB_MAIL_KEY = 'HTTP_SHIB_MAIL'
SHIB_STUDENT_ID_KEY = 'HTTP_SHIB_SCHACPERSONALUNIQUECODE'
SHIBBOLETH_ENVIRONMENT_VARS = {
'PREFIX': 'HTTP_SHIB_',
'STUDENT_DOMAIN': 'example.com', # XXX: change this!
}
EOF

1. Reload shibboleth
Expand Down
19 changes: 12 additions & 7 deletions doc/nginx/aplus-nginx-shib.conf
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,19 @@ server {
# authorizer so we must prevent spoofing.
# from: https://github.com/nginx-shib/nginx-http-shibboleth/blob/master/includes/shib_clear_headers
include shib_clear_headers;
# NOTE: add all fields which might be read by your A+ installation!
# TODO: get newer headers-more module with wildcard support!
more_clear_input_headers
Shib-EPPN
Shib-GivenName
Shib-SN
Shib-mail
Shib-SchacPersonalUniqueCode
Persistent-Id
Shib-Targeted-Id;
shib-eppn
shib-givenName
shib-sn
shib-cn
shib-displayName
shib-mail
shib-preferredLanguage
shib-schacPersonalUniqueCode
persistent-id
shib-targeted-id;

proxy_pass_header Server;
include proxy_params;
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ beautifulsoup4~=4.7.1
Django~=2.2.1
django-bootstrap-form==3.4
django-html5-colorfield==1.0
django-settingsdict ~= 1.1.1
djangorestframework~=3.9.4
djangorestframework-csv~=2.1.0
-e git+https://github.com/raphendyr/drf-extensions.git@reimplement_nested_routes_v4#egg=drf_extensions==0.5.1
Expand Down
70 changes: 70 additions & 0 deletions shibboleth_login/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import re

from django.apps import AppConfig
from django_settingsdict import SettingsDict


class AplusShibbolethLoginConfig(AppConfig):
name = 'shibboleth_login'
verbose_name = 'A+ Shibboleth Login'


def _only_prefix(value, env):
return re.sub(r'^((?:HTTP_)?[^_-]+[_-]).*$', r'\1', value)


def _drop_prefix(value, env):
prefix = env.get('PREFIX', '')
if value.startswith(prefix):
return value[len(prefix):]
return value


app_settings = SettingsDict(
'SHIBBOLETH_LOGIN',
defaults={
'ALLOW_CREATE_NEW_USERS': True,
'ALLOW_SEARCH_WITH_EMAIL': False,
},
)


env_settings = SettingsDict(
'SHIBBOLETH_ENVIRONMENT_VARS',
required=[
# This is typically SHIB_ for Apache 2 and
# HTTP_SHIB_ for nginx with http roxy
'PREFIX',
# Domain where student numbers are valid
# NOTE: this is temporary, until A+ supports multiple domains
'STUDENT_DOMAIN',
],
defaults={
'USER_ID': 'eppn',
'FIRST_NAME': 'givenName',
'LAST_NAME': 'sn',
'COMMON_NAME': 'cn',
'FULL_NAME': 'displayName',
'EMAIL': 'mail',
'LANGUAGE': 'preferredLanguage',
# student values are based on Haka Federation
# https://wiki.eduuni.fi/display/CSCHAKA/Federation
'STUDENT_IDS': 'schacPersonalUniqueCode',
'STUDENT_URN': ':schac:personalUniqueCode:',
'STUDENT_FILTERS': {
2: 'studentID',
3: 'int',
},
'URL_DECODE': True,
},
migrate=[
# new name, old name, migration script
('PREFIX', 'SHIB_USER_ID_KEY', _only_prefix),
('USER_ID', 'SHIB_USER_ID_KEY', _drop_prefix),
('FIRST_NAME', 'SHIB_FIRST_NAME_KEY', _drop_prefix),
('LAST_NAME', 'SHIB_LAST_NAME_KEY', _drop_prefix),
('EMAIL', 'SHIB_MAIL_KEY', _drop_prefix),
('STUDENT_IDS', 'SHIB_STUDENT_ID_KEY', _drop_prefix),
('URL_DECODE', 'SHIBBOLETH_VARIABLES_URL_ENCODED'),
],
)
Loading

0 comments on commit 7174417

Please sign in to comment.