diff --git a/.gitignore b/.gitignore index 9dfa28c..36625a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.py[co] db.sqlite3 *.swp +spejstore.env +env/ diff --git a/auth/__init__.py b/auth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auth/backend.py b/auth/backend.py new file mode 100644 index 0000000..2a343a2 --- /dev/null +++ b/auth/backend.py @@ -0,0 +1,56 @@ +from social_core.backends.oauth import BaseOAuth2 +from six.moves.urllib_parse import urlencode, unquote + +class HSWawOAuth2(BaseOAuth2): + """Hackerspace OAuth authentication backend""" + name = 'hswaw' + ID_KEY = 'username' + AUTHORIZATION_URL = 'https://sso.hackerspace.pl/oauth/authorize' + ACCESS_TOKEN_URL = 'https://sso.hackerspace.pl/oauth/token' + DEFAULT_SCOPE = ['profile:read'] + REDIRECT_STATE = False + SCOPE_SEPARATOR = ',' + EXTRA_DATA = [ + ('expires', 'expires_in') + ] + + def get_user_details(self, response): + """Return user details from GitHub account""" + return {'username': response.get('username'), + 'email': response.get('email'), + } + + def user_data(self, access_token, *args, **kwargs): + """Loads user data from service""" + url = 'https://sso.hackerspace.pl/api/1/profile' + headers = { + 'Authorization': 'Bearer {}'.format(access_token) + + } + return self.get_json(url, headers=headers) + + def auth_url(self): + """Return redirect url""" + state = self.get_or_create_state() + params = self.auth_params(state) + params.update(self.get_scope_argument()) + params.update(self.auth_extra_arguments()) + params = urlencode(params) + return '{0}?{1}'.format(self.authorization_url(), params) + + def get_user(self, user_id): + """ + Return user with given ID from the User model used by this backend. + This is called by django.contrib.auth.middleware. + """ + return self.strategy.get_user(user_id) + + def pipeline(self, pipeline, pipeline_index=0, *args, **kwargs): + out = self.run_pipeline(pipeline, pipeline_index, *args, **kwargs) + if not isinstance(out, dict): + return out + user = out.get('user') + if user: + user.social_user = out.get('social') + user.is_new = out.get('is_new') + return user diff --git a/auth/pipeline.py b/auth/pipeline.py new file mode 100644 index 0000000..74f6918 --- /dev/null +++ b/auth/pipeline.py @@ -0,0 +1,3 @@ +def staff_me_up(backend, details, response, uid, user, *args, **kwargs): + user.is_staff = True + user.save() diff --git a/docker-compose.yml b/docker-compose.yml index f39579a..598ac62 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: "2" +version: "2.3" services: db: build: postgres-hstore @@ -16,3 +16,7 @@ services: - "8000:8000" depends_on: - db + + env_file: + - spejstore-dev.env + - spejstore.env diff --git a/requirements.txt b/requirements.txt index 4e40198..ba2f8a5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ git+https://github.com/djangonauts/django-hstore@61427e474cb2f4be8fdfce225d78a53 git+https://github.com/d42/django-tree@687c01c02d91cada9ca1912e34e482da9e73e27a#egg=django-tree django-appconf==1.0.2 django-flat-responsive==2.0 +social-auth-app-django==2.1.0 django-auth-ldap==1.2.12 Django-Select2==6.3.1 djangorestframework==3.5.4 diff --git a/spejstore/settings.py b/spejstore/settings.py index 1e06b33..426330a 100644 --- a/spejstore/settings.py +++ b/spejstore/settings.py @@ -26,6 +26,7 @@ SECRET_KEY = '#hjthi7_udsyt*9eeyb&nwgw5x=%pk_lnz3+u2tg9@=w3p1m*k' DEBUG = True ALLOWED_HOSTS = [] +LOGIN_REDIRECT_URL = '/admin/' # Application definition @@ -40,6 +41,8 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', 'django.contrib.postgres', + 'social_django', + 'django_hstore', 'tree', 'django_select2', @@ -57,6 +60,7 @@ MIDDLEWARE = [ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'social_django.middleware.SocialAuthExceptionMiddleware', ] ROOT_URLCONF = 'spejstore.urls' @@ -72,6 +76,8 @@ TEMPLATES = [ 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', + 'social_django.context_processors.backends', + 'social_django.context_processors.login_redirect', ], }, }, @@ -125,12 +131,24 @@ from django_auth_ldap.config import LDAPSearch, GroupOfUniqueNamesType, LDAPGrou AUTHENTICATION_BACKENDS = ( 'django_auth_ldap.backend.LDAPBackend', + 'auth.backend.HSWawOAuth2', 'django.contrib.auth.backends.ModelBackend', ) AUTH_LDAP_SERVER_URI = "ldaps://ldap.hackerspace.pl" AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,ou=People,dc=hackerspace,dc=pl" AUTH_LDAP_BIND_AS_AUTHENTICATING_USER = True +SOCIAL_AUTH_PIPELINE = ( + 'social_core.pipeline.social_auth.social_details', + 'social_core.pipeline.social_auth.social_uid', + 'social_core.pipeline.social_auth.social_user', + 'social_core.pipeline.user.get_username', + 'social_core.pipeline.user.create_user', + 'social_core.pipeline.social_auth.associate_user', + 'social_core.pipeline.social_auth.load_extra_data', + 'social_core.pipeline.user.user_details', + 'auth.pipeline.staff_me_up', + 'social_core.pipeline.social_auth.associate_by_email',) member_ldap_query = ( LDAPGroupQuery("cn=fatty,ou=Group,dc=hackerspace,dc=pl") | @@ -196,4 +214,9 @@ REST_FRAMEWORK = { ] } +SOCIAL_AUTH_HSWAW_KEY = os.getenv('SPEJSTORE_CLIENT_ID') +SOCIAL_AUTH_HSWAW_SECRET = os.getenv('SPEJSTORE_SECRET') + +SOCIAL_AUTH_POSTGRES_JSONFIELD = True + LABEL_API = 'http://label.waw.hackerspace.pl:4567' diff --git a/storage/urls.py b/storage/urls.py index fce08cb..40deeee 100644 --- a/storage/urls.py +++ b/storage/urls.py @@ -10,4 +10,5 @@ urlpatterns = [ url(r'^autocomplete.json$', ItemSelectView.as_view(), name='item-complete'), url(r'^autocomplete_prop.json$', PropSelectView.as_view(), name='prop-complete'), url(r'^(?P[^/]*)$', label_lookup, name='label-lookup'), + url('', include('social_django.urls', namespace='social')), ]