Compare commits
4 commits
03daede441
...
34af2b1fab
Author | SHA1 | Date | |
---|---|---|---|
34af2b1fab | |||
d83309d4cd | |||
59c2045ac6 | |||
a24d96b098 |
15 changed files with 174 additions and 50 deletions
34
pandora/app/oidc.py
Normal file
34
pandora/app/oidc.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
import mozilla_django_oidc.auth
|
||||||
|
|
||||||
|
from user.utils import prepare_user
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class OIDCAuthenticationBackend(mozilla_django_oidc.auth.OIDCAuthenticationBackend):
|
||||||
|
def create_user(self, claims):
|
||||||
|
user = super(OIDCAuthenticationBackend, self).create_user(claims)
|
||||||
|
username = claims.get("preferred_username")
|
||||||
|
n = 1
|
||||||
|
if username and username != user.username:
|
||||||
|
uname = username
|
||||||
|
while User.objects.filter(username=uname).exclude(id=user.id).exists():
|
||||||
|
n += 1
|
||||||
|
uname = '%s (%s)' % (username, n)
|
||||||
|
user.username = uname
|
||||||
|
user.save()
|
||||||
|
prepare_user(user)
|
||||||
|
return user
|
||||||
|
|
||||||
|
def update_user(self, user, claims):
|
||||||
|
print("update user", user, claims)
|
||||||
|
#user.save()
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def generate_username(email):
|
||||||
|
return unicodedata.normalize('NFKC', email)[:150]
|
|
@ -184,6 +184,7 @@ def init(request, data):
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
config['site']['oidc'] = bool(getattr(settings, 'OIDC_RP_CLIENT_ID', False))
|
||||||
response['data']['site'] = config
|
response['data']['site'] = config
|
||||||
response['data']['user'] = init_user(request.user, request)
|
response['data']['user'] = init_user(request.user, request)
|
||||||
request.session['last_init'] = str(datetime.now())
|
request.session['last_init'] = str(datetime.now())
|
||||||
|
|
|
@ -34,6 +34,13 @@ def api(request):
|
||||||
return response
|
return response
|
||||||
if request.META.get('CONTENT_TYPE') == 'application/json':
|
if request.META.get('CONTENT_TYPE') == 'application/json':
|
||||||
r = json.loads(request.body.decode('utf-8'))
|
r = json.loads(request.body.decode('utf-8'))
|
||||||
|
if 'action' not in r:
|
||||||
|
logger.error("invalid api request: %s", r)
|
||||||
|
response = render_to_json_response(json_response(status=400,
|
||||||
|
text='Invalid request'))
|
||||||
|
response['Access-Control-Allow-Origin'] = '*'
|
||||||
|
return response
|
||||||
|
else:
|
||||||
action = r['action']
|
action = r['action']
|
||||||
data = r.get('data', {})
|
data = r.get('data', {})
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -111,6 +111,7 @@ ROOT_URLCONF = 'urls'
|
||||||
|
|
||||||
INSTALLED_APPS = (
|
INSTALLED_APPS = (
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
|
'mozilla_django_oidc',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.sites',
|
'django.contrib.sites',
|
||||||
|
@ -158,6 +159,27 @@ INSTALLED_APPS = (
|
||||||
)
|
)
|
||||||
|
|
||||||
AUTH_USER_MODEL = 'system.User'
|
AUTH_USER_MODEL = 'system.User'
|
||||||
|
AUTH_PROFILE_MODULE = 'user.UserProfile'
|
||||||
|
AUTH_CHECK_USERNAME = True
|
||||||
|
|
||||||
|
AUTHENTICATION_BACKENDS = (
|
||||||
|
'django.contrib.auth.backends.ModelBackend',
|
||||||
|
)
|
||||||
|
|
||||||
|
# OpenID Connect login support
|
||||||
|
LOGIN_REDIRECT_URL = "/grid"
|
||||||
|
LOGOUT_REDIRECT_URL = "/grid"
|
||||||
|
OIDC_USERNAME_ALGO = "app.oidc.generate_username"
|
||||||
|
OIDC_RP_CLIENT_ID = None
|
||||||
|
|
||||||
|
# define those in local_settings to enable OCID based login
|
||||||
|
#OIDC_RP_CLIENT_ID = '<client id>'
|
||||||
|
#OIDC_RP_CLIENT_SECRET = '<client secret>'
|
||||||
|
#OIDC_RP_SIGN_ALGO = "RS256"
|
||||||
|
#OIDC_OP_JWKS_ENDPOINT = "<jwks endpoint>"
|
||||||
|
#OIDC_OP_AUTHORIZATION_ENDPOINT = "<authorization endpoint>"
|
||||||
|
#OIDC_OP_TOKEN_ENDPOINT = "<token endpoint>"
|
||||||
|
#OIDC_OP_USER_ENDPOINT = "<user endpoint>"
|
||||||
|
|
||||||
# Log errors into db
|
# Log errors into db
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
|
@ -193,8 +215,6 @@ CACHES = {
|
||||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||||
|
|
||||||
|
|
||||||
AUTH_PROFILE_MODULE = 'user.UserProfile'
|
|
||||||
AUTH_CHECK_USERNAME = True
|
|
||||||
FFMPEG = 'ffmpeg'
|
FFMPEG = 'ffmpeg'
|
||||||
FFPROBE = 'ffprobe'
|
FFPROBE = 'ffprobe'
|
||||||
USE_VP9 = True
|
USE_VP9 = True
|
||||||
|
@ -323,3 +343,7 @@ except NameError:
|
||||||
|
|
||||||
INSTALLED_APPS = tuple(list(INSTALLED_APPS) + LOCAL_APPS)
|
INSTALLED_APPS = tuple(list(INSTALLED_APPS) + LOCAL_APPS)
|
||||||
|
|
||||||
|
if OIDC_RP_CLIENT_ID:
|
||||||
|
AUTHENTICATION_BACKENDS = list(AUTHENTICATION_BACKENDS) + [
|
||||||
|
'app.oidc.OIDCAuthenticationBackend'
|
||||||
|
]
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import os
|
import os
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
from django.urls import path, re_path
|
from django.urls import path, re_path, include
|
||||||
from oxdjango.http import HttpFileResponse
|
from oxdjango.http import HttpFileResponse
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -36,6 +36,8 @@ def serve_static_file(path, location, content_type):
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
#path('admin/', admin.site.urls),
|
#path('admin/', admin.site.urls),
|
||||||
|
|
||||||
|
path('oidc/', include('mozilla_django_oidc.urls')),
|
||||||
|
|
||||||
re_path(r'^api/locale.(?P<lang>.*).json$', translation.views.locale_json),
|
re_path(r'^api/locale.(?P<lang>.*).json$', translation.views.locale_json),
|
||||||
re_path(r'^api/upload/text/?$', text.views.upload),
|
re_path(r'^api/upload/text/?$', text.views.upload),
|
||||||
re_path(r'^api/upload/document/?$', document.views.upload),
|
re_path(r'^api/upload/document/?$', document.views.upload),
|
||||||
|
|
|
@ -3,6 +3,38 @@ from django.contrib.gis.geoip2 import GeoIP2, GeoIP2Exception
|
||||||
import ox
|
import ox
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_user(user):
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.conf import settings
|
||||||
|
from itemlist.models import List, Position
|
||||||
|
from django.db.models import Max
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
first_user_qs = User.objects.all()
|
||||||
|
if user.id:
|
||||||
|
first_user_qs = first_user_qs.exclude(id=user.id)
|
||||||
|
if first_user_qs.count() == 0:
|
||||||
|
user.is_superuser = True
|
||||||
|
user.is_staff = True
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
for l in settings.CONFIG['personalLists']:
|
||||||
|
list = List(name=l['title'], user=user)
|
||||||
|
for key in ('query', 'public', 'featured'):
|
||||||
|
if key in l:
|
||||||
|
setattr(list, key, l[key])
|
||||||
|
if key == 'query':
|
||||||
|
for c in list.query['conditions']:
|
||||||
|
if c['key'] == 'user':
|
||||||
|
c['value'] = c['value'].format(username=user.username)
|
||||||
|
list.save()
|
||||||
|
pos = Position(list=list, section='personal', user=user)
|
||||||
|
qs = Position.objects.filter(user=user, section='personal')
|
||||||
|
pos.position = (qs.aggregate(Max('position'))['position__max'] or 0) + 1
|
||||||
|
pos.save()
|
||||||
|
|
||||||
|
|
||||||
def get_ip(request):
|
def get_ip(request):
|
||||||
if 'HTTP_X_FORWARDED_FOR' in request.META:
|
if 'HTTP_X_FORWARDED_FOR' in request.META:
|
||||||
ip = request.META['HTTP_X_FORWARDED_FOR'].split(',')[0]
|
ip = request.META['HTTP_X_FORWARDED_FOR'].split(',')[0]
|
||||||
|
|
|
@ -28,7 +28,7 @@ from user.models import Group
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from .decorators import capability_required_json
|
from .decorators import capability_required_json
|
||||||
from .utils import rename_user
|
from .utils import rename_user, prepare_user
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
@ -177,28 +177,10 @@ def signup(request, data):
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
first_user = User.objects.count() == 0
|
|
||||||
user = User(username=data['username'], email=data['email'])
|
user = User(username=data['username'], email=data['email'])
|
||||||
user.set_password(data['password'])
|
user.set_password(data['password'])
|
||||||
#make first user admin
|
|
||||||
user.is_superuser = first_user
|
|
||||||
user.is_staff = first_user
|
|
||||||
user.save()
|
user.save()
|
||||||
#create default user lists:
|
prepare_user(user)
|
||||||
for l in settings.CONFIG['personalLists']:
|
|
||||||
list = List(name=l['title'], user=user)
|
|
||||||
for key in ('query', 'public', 'featured'):
|
|
||||||
if key in l:
|
|
||||||
setattr(list, key, l[key])
|
|
||||||
if key == 'query':
|
|
||||||
for c in list.query['conditions']:
|
|
||||||
if c['key'] == 'user':
|
|
||||||
c['value'] = c['value'].format(username=user.username)
|
|
||||||
list.save()
|
|
||||||
pos = Position(list=list, section='personal', user=user)
|
|
||||||
qs = Position.objects.filter(user=user, section='personal')
|
|
||||||
pos.position = (qs.aggregate(Max('position'))['position__max'] or 0) + 1
|
|
||||||
pos.save()
|
|
||||||
if request.session.session_key:
|
if request.session.session_key:
|
||||||
models.SessionData.objects.filter(session_key=request.session.session_key).update(user=user)
|
models.SessionData.objects.filter(session_key=request.session.session_key).update(user=user)
|
||||||
ui = json.loads(request.session.get('ui', 'null'))
|
ui = json.loads(request.session.get('ui', 'null'))
|
||||||
|
|
|
@ -20,3 +20,4 @@ future
|
||||||
pytz
|
pytz
|
||||||
pypdfium2
|
pypdfium2
|
||||||
Pillow>=10
|
Pillow>=10
|
||||||
|
mozilla-django-oidc==4.0.1
|
||||||
|
|
|
@ -337,7 +337,11 @@ pandora.ui.accountSignoutDialog = function() {
|
||||||
that.close();
|
that.close();
|
||||||
pandora.UI.set({page: ''});
|
pandora.UI.set({page: ''});
|
||||||
pandora.api.signout({}, function(result) {
|
pandora.api.signout({}, function(result) {
|
||||||
|
if (pandora.site.site.oidc) {
|
||||||
|
pandora.oidcLogout();
|
||||||
|
} else {
|
||||||
pandora.signout(result.data);
|
pandora.signout(result.data);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -100,6 +100,10 @@ pandora.ui.appPanel = function() {
|
||||||
pandora.$ui.siteDialog = pandora.ui.siteDialog(page).open();
|
pandora.$ui.siteDialog = pandora.ui.siteDialog(page).open();
|
||||||
}
|
}
|
||||||
} else if (['signup', 'signin'].indexOf(page) > -1) {
|
} else if (['signup', 'signin'].indexOf(page) > -1) {
|
||||||
|
if (pandora.site.site.oidc) {
|
||||||
|
pandora.oidcLogin()
|
||||||
|
return
|
||||||
|
}
|
||||||
if (pandora.user.level == 'guest') {
|
if (pandora.user.level == 'guest') {
|
||||||
if (pandora.$ui.accountDialog && pandora.$ui.accountDialog.is(':visible')) {
|
if (pandora.$ui.accountDialog && pandora.$ui.accountDialog.is(':visible')) {
|
||||||
pandora.$ui.accountDialog.options(pandora.ui.accountDialogOptions(page));
|
pandora.$ui.accountDialog.options(pandora.ui.accountDialogOptions(page));
|
||||||
|
|
|
@ -424,18 +424,26 @@ pandora.ui.folders = function(section) {
|
||||||
}).bindEvent({
|
}).bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
var $dialog = pandora.ui.iconDialog({
|
var $dialog = pandora.ui.iconDialog({
|
||||||
buttons: title != Ox._('Featured ' + folderItems) ? [
|
buttons: title != Ox._('Featured ' + folderItems) ? [].concat(
|
||||||
|
pandora.site.site.oidc ? []
|
||||||
|
: [
|
||||||
Ox.Button({title: Ox._('Sign Up...')}).bindEvent({
|
Ox.Button({title: Ox._('Sign Up...')}).bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
$dialog.close();
|
$dialog.close();
|
||||||
pandora.$ui.accountDialog = pandora.ui.accountDialog('signup').open();
|
pandora.$ui.accountDialog = pandora.ui.accountDialog('signup').open();
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
|
],
|
||||||
|
[
|
||||||
Ox.Button({title: Ox._('Sign In...')}).bindEvent({
|
Ox.Button({title: Ox._('Sign In...')}).bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
$dialog.close();
|
$dialog.close();
|
||||||
|
if (pandora.site.site.oidc) {
|
||||||
|
pandora.oidcLogin()
|
||||||
|
} else {
|
||||||
pandora.$ui.accountDialog = pandora.ui.accountDialog('signin').open();
|
pandora.$ui.accountDialog = pandora.ui.accountDialog('signin').open();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
{},
|
{},
|
||||||
Ox.Button({title: Ox._('Not Now')}).bindEvent({
|
Ox.Button({title: Ox._('Not Now')}).bindEvent({
|
||||||
|
@ -443,7 +451,8 @@ pandora.ui.folders = function(section) {
|
||||||
$dialog.close();
|
$dialog.close();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
] : [
|
]
|
||||||
|
): [
|
||||||
Ox.Button({title: Ox._('Close')}).bindEvent({
|
Ox.Button({title: Ox._('Close')}).bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
$dialog.close();
|
$dialog.close();
|
||||||
|
|
|
@ -171,13 +171,13 @@ pandora.ui.home = function() {
|
||||||
}),
|
}),
|
||||||
$signinButton = Ox.Button({
|
$signinButton = Ox.Button({
|
||||||
title: Ox._('Sign In'),
|
title: Ox._('Sign In'),
|
||||||
width: 74
|
width: pandora.site.site.oidc ? 156 : 74
|
||||||
})
|
})
|
||||||
.css({
|
.css({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: 0,
|
left: 0,
|
||||||
top: '112px',
|
top: '112px',
|
||||||
right: '82px',
|
right: pandora.site.site.oidc ? '164px' : '82px',
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
margin: 'auto',
|
margin: 'auto',
|
||||||
opacity: 0
|
opacity: 0
|
||||||
|
@ -248,7 +248,13 @@ pandora.ui.home = function() {
|
||||||
adjustRatio();
|
adjustRatio();
|
||||||
|
|
||||||
if (pandora.user.level == 'guest') {
|
if (pandora.user.level == 'guest') {
|
||||||
|
if (pandora.site.site.oidc) {
|
||||||
|
$signinButton.options({
|
||||||
|
width: 156
|
||||||
|
})
|
||||||
|
} else {
|
||||||
$signupButton.appendTo(that);
|
$signupButton.appendTo(that);
|
||||||
|
}
|
||||||
$signinButton.appendTo(that);
|
$signinButton.appendTo(that);
|
||||||
} else {
|
} else {
|
||||||
$preferencesButton.appendTo(that);
|
$preferencesButton.appendTo(that);
|
||||||
|
|
|
@ -46,7 +46,9 @@ pandora.ui.mainMenu = function() {
|
||||||
{ id: 'tasks', title: Ox._('Tasks...'), disabled: isGuest },
|
{ id: 'tasks', title: Ox._('Tasks...'), disabled: isGuest },
|
||||||
{ id: 'archives', title: Ox._('Archives...'), disabled: /*isGuest*/ true },
|
{ id: 'archives', title: Ox._('Archives...'), disabled: /*isGuest*/ true },
|
||||||
{},
|
{},
|
||||||
{ id: 'signup', title: Ox._('Sign Up...'), disabled: !isGuest },
|
!pandora.site.site.oidc
|
||||||
|
? { id: 'signup', title: Ox._('Sign Up...'), disabled: !isGuest }
|
||||||
|
: [],
|
||||||
isGuest ? { id: 'signin', title: Ox._('Sign In...')}
|
isGuest ? { id: 'signin', title: Ox._('Sign In...')}
|
||||||
: { id: 'signout', title: Ox._('Sign Out...')}
|
: { id: 'signout', title: Ox._('Sign Out...')}
|
||||||
] },
|
] },
|
||||||
|
|
|
@ -2650,6 +2650,20 @@ pandora.logEvent = function(data, event, element) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pandora.oidcLogin = function() {
|
||||||
|
Ox.LoadingScreen().css({zIndex: 100}).addClass('OxScreen').appendTo(document.body).show().start()
|
||||||
|
document.location.href = '/oidc/authenticate/';
|
||||||
|
};
|
||||||
|
|
||||||
|
pandora.oidcLogout = function() {
|
||||||
|
Ox.LoadingScreen().css({zIndex: 100}).addClass('OxScreen').appendTo(document.body).show().start()
|
||||||
|
const form = document.createElement("form");
|
||||||
|
form.setAttribute("method", "post");
|
||||||
|
form.setAttribute("action", "/oidc/logout/");
|
||||||
|
document.body.appendChild(form);
|
||||||
|
form.submit();
|
||||||
|
};
|
||||||
|
|
||||||
pandora.openLicenseDialog = function() {
|
pandora.openLicenseDialog = function() {
|
||||||
if (!Ox.Focus.focusedElementIsInput() && !pandora.hasDialogOrScreen()) {
|
if (!Ox.Focus.focusedElementIsInput() && !pandora.hasDialogOrScreen()) {
|
||||||
pandora.ui.licenseDialog().open().bindEvent({
|
pandora.ui.licenseDialog().open().bindEvent({
|
||||||
|
|
|
@ -302,6 +302,8 @@ if __name__ == "__main__":
|
||||||
if old <= 6581:
|
if old <= 6581:
|
||||||
run('./bin/pip', 'install', '-U', 'pip')
|
run('./bin/pip', 'install', '-U', 'pip')
|
||||||
run('./bin/pip', 'install', '-r', 'requirements.txt')
|
run('./bin/pip', 'install', '-r', 'requirements.txt')
|
||||||
|
if old <= 6659:
|
||||||
|
run('./bin/pip', 'install', '-r', 'requirements.txt')
|
||||||
else:
|
else:
|
||||||
if len(sys.argv) == 1:
|
if len(sys.argv) == 1:
|
||||||
branch = get_branch()
|
branch = get_branch()
|
||||||
|
|
Loading…
Reference in a new issue