mirror of
https://github.com/haiwen/seahub.git
synced 2025-05-11 01:17:02 +00:00
add oauth support
This commit is contained in:
parent
8e4f65fef3
commit
50ccccfcb4
@ -14,3 +14,4 @@ pytz==2015.7
|
||||
django-formtools
|
||||
qrcode
|
||||
requests
|
||||
requests_oauthlib==0.8.0
|
||||
|
@ -245,6 +245,7 @@ def login(request, template_name='registration/login.html',
|
||||
enable_shib_login = getattr(settings, 'ENABLE_SHIB_LOGIN', False)
|
||||
enable_krb5_login = getattr(settings, 'ENABLE_KRB5_LOGIN', False)
|
||||
enable_adfs_login = getattr(settings, 'ENABLE_ADFS_LOGIN', False)
|
||||
enable_oauth = getattr(settings, 'ENABLE_OAUTH', False)
|
||||
|
||||
login_bg_image_path = LOGIN_BG_IMAGE_PATH
|
||||
# get path that background image of login page
|
||||
@ -262,6 +263,7 @@ def login(request, template_name='registration/login.html',
|
||||
'enable_shib_login': enable_shib_login,
|
||||
'enable_krb5_login': enable_krb5_login,
|
||||
'enable_adfs_login': enable_adfs_login,
|
||||
'enable_oauth': enable_oauth,
|
||||
'login_bg_image_path': login_bg_image_path,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
0
seahub/oauth/__init__.py
Normal file
0
seahub/oauth/__init__.py
Normal file
3
seahub/oauth/admin.py
Normal file
3
seahub/oauth/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
55
seahub/oauth/backends.py
Normal file
55
seahub/oauth/backends.py
Normal file
@ -0,0 +1,55 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.backends import RemoteUserBackend
|
||||
|
||||
from seahub.base.accounts import User
|
||||
from registration.models import notify_admins_on_activate_request
|
||||
|
||||
class OauthRemoteUserBackend(RemoteUserBackend):
|
||||
"""
|
||||
This backend is to be used in conjunction with the ``RemoteUserMiddleware``
|
||||
found in the middleware module of this package, and is used when the server
|
||||
is handling authentication outside of Django.
|
||||
|
||||
By default, the ``authenticate`` method creates ``User`` objects for
|
||||
usernames that don't already exist in the database. Subclasses can disable
|
||||
this behavior by setting the ``create_unknown_user`` attribute to
|
||||
``False``.
|
||||
"""
|
||||
|
||||
# Create a User object if not already in the database?
|
||||
create_unknown_user = getattr(settings, 'OAUTH_CREATE_UNKNOWN_USER', True)
|
||||
# Create active user by default.
|
||||
activate_after_creation = getattr(settings, 'OAUTH_ACTIVATE_USER_AFTER_CREATION', True)
|
||||
|
||||
def get_user(self, username):
|
||||
try:
|
||||
user = User.objects.get(email=username)
|
||||
except User.DoesNotExist:
|
||||
user = None
|
||||
return user
|
||||
|
||||
def authenticate(self, remote_user):
|
||||
"""
|
||||
The username passed as ``remote_user`` is considered trusted. This
|
||||
method simply returns the ``User`` object with the given username,
|
||||
creating a new ``User`` object if ``create_unknown_user`` is ``True``.
|
||||
|
||||
Returns None if ``create_unknown_user`` is ``False`` and a ``User``
|
||||
object with the given username is not found in the database.
|
||||
"""
|
||||
if not remote_user:
|
||||
return
|
||||
|
||||
username = self.clean_username(remote_user)
|
||||
try:
|
||||
user = User.objects.get(email=username)
|
||||
except User.DoesNotExist:
|
||||
if self.create_unknown_user:
|
||||
user = User.objects.create_user(
|
||||
email=username, is_active=self.activate_after_creation)
|
||||
if user and self.activate_after_creation is False:
|
||||
notify_admins_on_activate_request(user.email)
|
||||
else:
|
||||
user = None
|
||||
|
||||
return user
|
0
seahub/oauth/migrations/__init__.py
Normal file
0
seahub/oauth/migrations/__init__.py
Normal file
3
seahub/oauth/models.py
Normal file
3
seahub/oauth/models.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
3
seahub/oauth/tests.py
Normal file
3
seahub/oauth/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
9
seahub/oauth/urls.py
Normal file
9
seahub/oauth/urls.py
Normal file
@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2012-2016 Seafile Ltd.
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
from seahub.oauth.views import oauth_login, oauth_callback
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'login/$', oauth_login, name='oauth_login'),
|
||||
url(r'callback/$', oauth_callback, name='oauth_callback'),
|
||||
)
|
181
seahub/oauth/views.py
Normal file
181
seahub/oauth/views.py
Normal file
@ -0,0 +1,181 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import logging
|
||||
from requests_oauthlib import OAuth2Session
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import render_to_response
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from seahub import auth
|
||||
from seahub.profile.models import Profile
|
||||
from seahub.utils import is_valid_email
|
||||
import seahub.settings as settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ENABLE_OAUTH = getattr(settings, 'ENABLE_OAUTH', False)
|
||||
if ENABLE_OAUTH:
|
||||
|
||||
if getattr(settings, 'OAUTH_ENABLE_INSECURE_TRANSPORT', False):
|
||||
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
|
||||
|
||||
# Used for oauth workflow.
|
||||
CLIENT_ID = getattr(settings, 'OAUTH_CLIENT_ID', '')
|
||||
CLIENT_SECRET = getattr(settings, 'OAUTH_CLIENT_SECRET', '')
|
||||
AUTHORIZATION_URL = getattr(settings, 'OAUTH_AUTHORIZATION_URL', '')
|
||||
REDIRECT_URL = getattr(settings, 'OAUTH_REDIRECT_URL', '')
|
||||
TOKEN_URL = getattr(settings, 'OAUTH_TOKEN_URL', '')
|
||||
USER_INFO_URL = getattr(settings, 'OAUTH_USER_INFO_URL', '')
|
||||
SCOPE = getattr(settings, 'OAUTH_SCOPE', '')
|
||||
|
||||
# Used for init an user for Seahub.
|
||||
PROVIDER_DOMAIN = getattr(settings, 'OAUTH_PROVIDER_DOMAIN', '')
|
||||
ATTRIBUTE_MAP = {
|
||||
'id': (True, "email"),
|
||||
}
|
||||
ATTRIBUTE_MAP.update(getattr(settings, 'OAUTH_ATTRIBUTE_MAP', {}))
|
||||
|
||||
session = OAuth2Session(client_id=CLIENT_ID,
|
||||
scope=SCOPE, redirect_uri=REDIRECT_URL)
|
||||
|
||||
def oauth_check(func):
|
||||
""" Decorator for check if OAuth valid.
|
||||
"""
|
||||
def _decorated(request):
|
||||
|
||||
error = False
|
||||
if not ENABLE_OAUTH:
|
||||
logger.error('OAuth not enabled.')
|
||||
error = True
|
||||
else:
|
||||
if not CLIENT_ID or not CLIENT_SECRET or not AUTHORIZATION_URL \
|
||||
or not REDIRECT_URL or not TOKEN_URL or not USER_INFO_URL \
|
||||
or not SCOPE or not PROVIDER_DOMAIN:
|
||||
logger.error('OAuth relevant settings invalid.')
|
||||
logger.error('CLIENT_ID: %s' % CLIENT_ID)
|
||||
logger.error('CLIENT_SECRET: %s' % CLIENT_SECRET)
|
||||
logger.error('AUTHORIZATION_URL: %s' % AUTHORIZATION_URL)
|
||||
logger.error('REDIRECT_URL: %s' % REDIRECT_URL)
|
||||
logger.error('TOKEN_URL: %s' % TOKEN_URL)
|
||||
logger.error('USER_INFO_URL: %s' % USER_INFO_URL)
|
||||
logger.error('SCOPE: %s' % SCOPE)
|
||||
logger.error('PROVIDER_DOMAIN: %s' % PROVIDER_DOMAIN)
|
||||
error = True
|
||||
|
||||
if error:
|
||||
return render_to_response('error.html', {
|
||||
'error_msg': _('Error, please contact administrator.'),
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
return func(request)
|
||||
|
||||
return _decorated
|
||||
|
||||
# https://requests-oauthlib.readthedocs.io/en/latest/examples/github.html
|
||||
# https://requests-oauthlib.readthedocs.io/en/latest/examples/google.html
|
||||
@oauth_check
|
||||
def oauth_login(request):
|
||||
"""Step 1: User Authorization.
|
||||
Redirect the user/resource owner to the OAuth provider (i.e. Github)
|
||||
using an URL with a few key OAuth parameters.
|
||||
"""
|
||||
try:
|
||||
authorization_url, state = session.authorization_url(
|
||||
AUTHORIZATION_URL)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return render_to_response('error.html', {
|
||||
'error_msg': _('Error, please contact administrator.'),
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
return HttpResponseRedirect(authorization_url)
|
||||
|
||||
# Step 2: User authorization, this happens on the provider.
|
||||
|
||||
@oauth_check
|
||||
def oauth_callback(request):
|
||||
""" Step 3: Retrieving an access token.
|
||||
The user has been redirected back from the provider to your registered
|
||||
callback URL. With this redirection comes an authorization code included
|
||||
in the redirect URL. We will use that to obtain an access token.
|
||||
"""
|
||||
try:
|
||||
session.fetch_token(TOKEN_URL, client_secret=CLIENT_SECRET,
|
||||
authorization_response=request.get_full_path())
|
||||
user_info_resp = session.get(USER_INFO_URL)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return render_to_response('error.html', {
|
||||
'error_msg': _('Error, please contact administrator.'),
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
def format_user_info(user_info_resp):
|
||||
|
||||
error = False
|
||||
user_info = {}
|
||||
user_info_json = user_info_resp.json()
|
||||
|
||||
for item, attr in ATTRIBUTE_MAP.items():
|
||||
required, user_attr = attr
|
||||
value = str(user_info_json.get(item, ''))
|
||||
|
||||
# ccnet email
|
||||
if user_attr == 'email':
|
||||
user_info[user_attr] = value if is_valid_email(value) else \
|
||||
'%s@%s' % (value, PROVIDER_DOMAIN)
|
||||
else:
|
||||
user_info[user_attr] = value
|
||||
|
||||
if required and not value:
|
||||
error = True
|
||||
|
||||
return user_info, error
|
||||
|
||||
user_info, error = format_user_info(user_info_resp)
|
||||
if error:
|
||||
logger.error('Required user info not found.')
|
||||
logger.error(user_info)
|
||||
return render_to_response('error.html', {
|
||||
'error_msg': _('Error, please contact administrator.'),
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
# seahub authenticate user
|
||||
email = user_info['email']
|
||||
user = auth.authenticate(remote_user=email)
|
||||
|
||||
if not user or not user.is_active:
|
||||
logger.error('User %s not found or inactive.' % email)
|
||||
# a page for authenticate user failed
|
||||
return render_to_response('error.html', {
|
||||
'error_msg': _(u'User %s not found.') % email
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
# User is valid. Set request.user and persist user in the session
|
||||
# by logging the user in.
|
||||
request.user = user
|
||||
auth.login(request, user)
|
||||
user.set_unusable_password()
|
||||
user.save()
|
||||
|
||||
# update user's profile
|
||||
name = user_info['name'] if user_info.has_key('name') else ''
|
||||
contact_email = user_info['contact_email'] if \
|
||||
user_info.has_key('contact_email') else ''
|
||||
|
||||
profile = Profile.objects.get_profile_by_user(email)
|
||||
if not profile:
|
||||
profile = Profile(user=email)
|
||||
|
||||
if name:
|
||||
profile.nickname = name.strip()
|
||||
|
||||
if contact_email:
|
||||
profile.contact_email = contact_email.strip()
|
||||
|
||||
profile.save()
|
||||
|
||||
# redirect user to home page
|
||||
return HttpResponseRedirect(reverse('libraries'))
|
@ -240,7 +240,12 @@ CONSTANCE_DATABASE_CACHE_BACKEND = 'default'
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'seahub.base.accounts.AuthBackend',
|
||||
'seahub.oauth.backends.OauthRemoteUserBackend',
|
||||
|
||||
)
|
||||
|
||||
ENABLE_OAUTH = False
|
||||
|
||||
LOGIN_REDIRECT_URL = '/profile/'
|
||||
LOGIN_URL = SITE_ROOT + 'accounts/login'
|
||||
LOGOUT_REDIRECT_URL = None
|
||||
|
@ -66,6 +66,10 @@ html, body, #wrapper { height:100%; }
|
||||
<button type="submit" class="submit">{% trans "Log In" %}</button>
|
||||
</form>
|
||||
|
||||
{% if enable_oauth %}
|
||||
<a href="{% url 'oauth_login' %}" title="{% trans "Single Sign-On" %}">{% trans "Single Sign-On" %}</a>
|
||||
{% endif %}
|
||||
|
||||
{% if enable_adfs_login %}
|
||||
<a id="adfs-login" href="#" class="normal">ADFS</a>
|
||||
{% endif %}
|
||||
|
@ -110,6 +110,8 @@ urlpatterns = patterns(
|
||||
(r'^sso/$', sso),
|
||||
url(r'^shib-login/', shib_login, name="shib_login"),
|
||||
|
||||
(r'^oauth/', include('seahub.oauth.urls')),
|
||||
|
||||
url(r'^$', libraries, name='libraries'),
|
||||
#url(r'^home/$', direct_to_template, { 'template': 'home.html' } ),
|
||||
url(r'^robots\.txt$', TemplateView.as_view(template_name='robots.txt', content_type='text/plain')),
|
||||
|
Loading…
Reference in New Issue
Block a user