mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
SSO tweaks (#4169)
* Slight tweaks to login settings page * Cleanup login screen * Custom socialaccount connect page - Better rendering, just looks nicer etc * Add custom account templates - signup_closed - social signup * Catch potential email errors when signing up new user * Add custom template for authentication error * Bug fix for account base.html
This commit is contained in:
parent
82bdd7780d
commit
41318e4056
@ -3,11 +3,13 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||||
|
from django.db.utils import IntegrityError, OperationalError
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
import rest_framework.views as drfviews
|
import rest_framework.views as drfviews
|
||||||
@ -16,6 +18,8 @@ from rest_framework import serializers
|
|||||||
from rest_framework.exceptions import ValidationError as DRFValidationError
|
from rest_framework.exceptions import ValidationError as DRFValidationError
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
logger = logging.getLogger('inventree')
|
||||||
|
|
||||||
|
|
||||||
def log_error(path):
|
def log_error(path):
|
||||||
"""Log an error to the database.
|
"""Log an error to the database.
|
||||||
@ -32,12 +36,19 @@ def log_error(path):
|
|||||||
if kind in settings.IGNORED_ERRORS:
|
if kind in settings.IGNORED_ERRORS:
|
||||||
return
|
return
|
||||||
|
|
||||||
Error.objects.create(
|
# Log error to stderr
|
||||||
kind=kind.__name__,
|
logger.error(info)
|
||||||
info=info,
|
|
||||||
data='\n'.join(traceback.format_exception(kind, info, data)),
|
try:
|
||||||
path=path,
|
Error.objects.create(
|
||||||
)
|
kind=kind.__name__,
|
||||||
|
info=info,
|
||||||
|
data='\n'.join(traceback.format_exception(kind, info, data)),
|
||||||
|
path=path,
|
||||||
|
)
|
||||||
|
except (OperationalError, IntegrityError):
|
||||||
|
# Not much we can do if logging the error throws a db exception
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def exception_handler(exc, context):
|
def exception_handler(exc, context):
|
||||||
|
@ -23,6 +23,7 @@ from crispy_forms.helper import FormHelper
|
|||||||
from crispy_forms.layout import Field, Layout
|
from crispy_forms.layout import Field, Layout
|
||||||
|
|
||||||
from common.models import InvenTreeSetting
|
from common.models import InvenTreeSetting
|
||||||
|
from InvenTree.exceptions import log_error
|
||||||
|
|
||||||
logger = logging.getLogger('inventree')
|
logger = logging.getLogger('inventree')
|
||||||
|
|
||||||
@ -239,10 +240,20 @@ class CustomUrlMixin:
|
|||||||
|
|
||||||
class CustomAccountAdapter(CustomUrlMixin, RegistratonMixin, OTPAdapter, DefaultAccountAdapter):
|
class CustomAccountAdapter(CustomUrlMixin, RegistratonMixin, OTPAdapter, DefaultAccountAdapter):
|
||||||
"""Override of adapter to use dynamic settings."""
|
"""Override of adapter to use dynamic settings."""
|
||||||
|
|
||||||
def send_mail(self, template_prefix, email, context):
|
def send_mail(self, template_prefix, email, context):
|
||||||
"""Only send mail if backend configured."""
|
"""Only send mail if backend configured."""
|
||||||
if settings.EMAIL_HOST:
|
if settings.EMAIL_HOST:
|
||||||
return super().send_mail(template_prefix, email, context)
|
try:
|
||||||
|
result = super().send_mail(template_prefix, email, context)
|
||||||
|
except Exception:
|
||||||
|
# An exception ocurred while attempting to send email
|
||||||
|
# Log it (for admin users) and return silently
|
||||||
|
log_error('account email')
|
||||||
|
result = False
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@ -1070,3 +1070,24 @@ a {
|
|||||||
margin-left: 0.25rem;
|
margin-left: 0.25rem;
|
||||||
margin-right: 0.25rem;
|
margin-right: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sso-header {
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-provider-list {
|
||||||
|
width: 100%;
|
||||||
|
list-style-type: none;
|
||||||
|
padding-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-provider-link {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-provider-link a {
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
}
|
@ -11,9 +11,14 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
{% if not email_configured %}
|
||||||
|
<div class='alert alert-block alert-danger'>
|
||||||
|
{% trans "Outgoing email has not been configured. Some login and sign-up features may not work correctly!" %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<table class='table table-striped table-condensed'>
|
<table class='table table-striped table-condensed'>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% include "InvenTree/settings/setting.html" with key="LOGIN_ENABLE_SSO" icon="fa-user-shield" %}
|
|
||||||
{% include "InvenTree/settings/setting.html" with key="LOGIN_ENABLE_PWD_FORGOT" icon="fa-user-lock" %}
|
{% include "InvenTree/settings/setting.html" with key="LOGIN_ENABLE_PWD_FORGOT" icon="fa-user-lock" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="LOGIN_MAIL_REQUIRED" icon="fa-at" %}
|
{% include "InvenTree/settings/setting.html" with key="LOGIN_MAIL_REQUIRED" icon="fa-at" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="LOGIN_ENFORCE_MFA" icon='fa-key' %}
|
{% include "InvenTree/settings/setting.html" with key="LOGIN_ENFORCE_MFA" icon='fa-key' %}
|
||||||
@ -24,8 +29,13 @@
|
|||||||
{% include "InvenTree/settings/setting.html" with key="LOGIN_ENABLE_REG" icon="fa-user-plus" %}
|
{% include "InvenTree/settings/setting.html" with key="LOGIN_ENABLE_REG" icon="fa-user-plus" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="LOGIN_SIGNUP_MAIL_TWICE" icon="fa-at" %}
|
{% include "InvenTree/settings/setting.html" with key="LOGIN_SIGNUP_MAIL_TWICE" icon="fa-at" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="LOGIN_SIGNUP_PWD_TWICE" icon="fa-user-lock" %}
|
{% include "InvenTree/settings/setting.html" with key="LOGIN_SIGNUP_PWD_TWICE" icon="fa-user-lock" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="LOGIN_SIGNUP_SSO_AUTO" icon="fa-key" %}
|
|
||||||
{% include "InvenTree/settings/setting.html" with key="SIGNUP_GROUP" icon="fa-users" %}
|
{% include "InvenTree/settings/setting.html" with key="SIGNUP_GROUP" icon="fa-users" %}
|
||||||
|
<tr>
|
||||||
|
<th><h5>{% trans 'Single Sign On' %}</h5></th>
|
||||||
|
<td colspan='4'></td>
|
||||||
|
</tr>
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="LOGIN_ENABLE_SSO" icon="fa-user-shield" %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="LOGIN_SIGNUP_SSO_AUTO" icon="fa-key" %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
@ -76,6 +76,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% block extra_body %}
|
{% block extra_body %}
|
||||||
@ -87,6 +88,7 @@
|
|||||||
<!-- general JS -->
|
<!-- general JS -->
|
||||||
{% include "third_party_js.html" %}
|
{% include "third_party_js.html" %}
|
||||||
|
|
||||||
|
<script type='text/javascript' src='{% static "script/inventree/inventree.js" %}'></script>
|
||||||
<script type='text/javascript' src='{% static "script/inventree/message.js" %}'></script>
|
<script type='text/javascript' src='{% static "script/inventree/message.js" %}'></script>
|
||||||
|
|
||||||
<script type='text/javascript'>
|
<script type='text/javascript'>
|
||||||
@ -95,7 +97,7 @@ $(document).ready(function () {
|
|||||||
|
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
showMessage(messsage);
|
showMessage("{{ messsage }}");
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@ -109,7 +111,7 @@ $(document).ready(function () {
|
|||||||
var icon = window.FontAwesome.icon({prefix: 'fab', iconName: tag});
|
var icon = window.FontAwesome.icon({prefix: 'fab', iconName: tag});
|
||||||
|
|
||||||
if (icon) {
|
if (icon) {
|
||||||
el.append(` <span class='fab fa-${tag}'></span>`);
|
el.prepend(`<span class='fab fa-${tag}'></span> `);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -13,19 +13,18 @@
|
|||||||
{% inventree_customize 'login_message' as login_message %}
|
{% inventree_customize 'login_message' as login_message %}
|
||||||
{% mail_configured as mail_conf %}
|
{% mail_configured as mail_conf %}
|
||||||
|
|
||||||
<h1>{% trans "Sign In" %}</h1>
|
<div class='d-flex flex-wrap'>
|
||||||
|
<h3>{% trans "Sign In" %}</h3>
|
||||||
{% if enable_reg %}
|
{% include "spacer.html" %}
|
||||||
{% get_providers as socialaccount_providers %}
|
{% if enable_reg %}
|
||||||
{% if socialaccount_providers %}
|
<div class='float-right'>
|
||||||
<p>{% blocktrans with site.name as site_name %}Please sign in with one
|
{% trans "Not a member?" %}
|
||||||
of your existing third party accounts or <a class="btn btn-primary btn-small" href="{{ signup_url }}">sign up</a>
|
<a class="btn btn-primary btn-small" href="{{ signup_url }}">
|
||||||
for a account and sign in below:{% endblocktrans %}</p>
|
<span class='fas fa-user-plus'></span> {% trans "Sign Up" %}
|
||||||
{% else %}
|
</a>
|
||||||
<p>{% blocktrans %}If you have not created an account yet, then please
|
</div>
|
||||||
<a href="{{ signup_url }}">sign up</a> first.{% endblocktrans %}</p>
|
{% endif %}
|
||||||
{% endif %}
|
</div>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<form class="login" method="POST" action="{% url 'account_login' %}">
|
<form class="login" method="POST" action="{% url 'account_login' %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
@ -34,15 +33,14 @@ for a account and sign in below:{% endblocktrans %}</p>
|
|||||||
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
|
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<hr>
|
<div class="btn-group" role="group" style='width: 100%;'>
|
||||||
{% if login_message %}
|
|
||||||
<div>{{ login_message | safe }}<hr></div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="btn-group" role="group">
|
|
||||||
<button class="btn btn-success" type="submit">
|
<button class="btn btn-success" type="submit">
|
||||||
<span class='fas fa-sign-in-alt'></span> {% trans "Sign In" %}
|
<span class='fas fa-sign-in-alt'></span> {% trans "Sign In" %}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{% if login_message %}
|
||||||
|
<div>{{ login_message | safe }}<hr></div>
|
||||||
|
{% endif %}
|
||||||
{% if mail_conf and enable_pwd_forgot %}
|
{% if mail_conf and enable_pwd_forgot %}
|
||||||
<a class="" href="{% url 'account_reset_password' %}"><small>{% trans "Forgot Password?" %}</small></a>
|
<a class="" href="{% url 'account_reset_password' %}"><small>{% trans "Forgot Password?" %}</small></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -50,10 +48,15 @@ for a account and sign in below:{% endblocktrans %}</p>
|
|||||||
|
|
||||||
{% if enable_sso %}
|
{% if enable_sso %}
|
||||||
<hr>
|
<hr>
|
||||||
<span class='float-right'>{% trans "Sign in using third-party SSO" %}</span>
|
|
||||||
|
<div class='sso-header'>
|
||||||
|
<em>{% trans "or log in with" %}</em>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{% include "socialaccount/snippets/provider_list.html" with process="login" %}
|
{% include "socialaccount/snippets/provider_list.html" with process="login" %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% include "socialaccount/snippets/login_extra.html" %}
|
{% include "socialaccount/snippets/login_extra.html" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
{% settings_value 'LOGIN_ENABLE_REG' as enable_reg %}
|
{% settings_value 'LOGIN_ENABLE_REG' as enable_reg %}
|
||||||
{% settings_value 'LOGIN_ENABLE_SSO' as enable_sso %}
|
{% settings_value 'LOGIN_ENABLE_SSO' as enable_sso %}
|
||||||
|
|
||||||
<h1>{% trans "Sign Up" %}</h1>
|
<h3>{% trans "Sign Up" %}</h3>
|
||||||
|
|
||||||
<p>{% blocktrans %}Already have an account? Then please <a href="{{ login_url }}">sign in</a>.{% endblocktrans %}</p>
|
<p>{% blocktrans %}Already have an account? Then please <a href="{{ login_url }}">sign in</a>.{% endblocktrans %}</p>
|
||||||
|
|
||||||
|
19
InvenTree/templates/account/signup_closed.html
Normal file
19
InvenTree/templates/account/signup_closed.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{% extends "account/base.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block head_title %}{% trans "Sign Up Closed" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h3>{% trans "Sign Up Closed" %}</h3>
|
||||||
|
|
||||||
|
<p>{% trans "Sign up is currently closed." %}</p>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<a href='{% url "account_login" %}'>
|
||||||
|
{% trans "Return to login page" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
23
InvenTree/templates/socialaccount/authentication_error.html
Normal file
23
InvenTree/templates/socialaccount/authentication_error.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{% extends "socialaccount/base.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block head_title %}{% trans "Social Network Login Failure" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h3>{% trans "Account Login Failure" %}</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{% trans "An error occurred while attempting to login via your social network account." %}
|
||||||
|
<br>
|
||||||
|
{% trans "Contact your system administrator for further information." %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<a href='{% url "account_login" %}'>
|
||||||
|
{% trans "Return to login page" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
28
InvenTree/templates/socialaccount/login.html
Normal file
28
InvenTree/templates/socialaccount/login.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{% extends "socialaccount/base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block head_title %}{% trans "Sign In" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% if process == "connect" %}
|
||||||
|
<h3>{% blocktrans with provider.name as provider %}Connect {{ provider }}{% endblocktrans %}</h3>
|
||||||
|
|
||||||
|
<p>{% blocktrans with provider.name as provider %}You are about to connect a new third party account from {{ provider }}.{% endblocktrans %}</p>
|
||||||
|
{% else %}
|
||||||
|
<h3>{% blocktrans with provider.name as provider %}Sign In Via {{ provider }}{% endblocktrans %}</h3>
|
||||||
|
|
||||||
|
<p>{% blocktrans with provider.name as provider %}You are about to sign in using a third party account from {{ provider }}.{% endblocktrans %}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button class='btn btn-success sso-provider-link' type="submit"><span class='fas fa-sign-in-alt'></span> {% trans "Continue" %}</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<a href='{% url "account_login" %}'>
|
||||||
|
{% trans "Return to login page" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
31
InvenTree/templates/socialaccount/signup.html
Normal file
31
InvenTree/templates/socialaccount/signup.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{% extends "socialaccount/base.html" %}
|
||||||
|
|
||||||
|
{% load i18n crispy_forms_tags inventree_extras %}
|
||||||
|
|
||||||
|
{% block head_title %}{% trans "Signup" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h3>{% trans "Sign Up" %}</h3>
|
||||||
|
|
||||||
|
<p>{% blocktrans with provider_name=account.get_provider.name site_name=site.name %}You are about to use your {{provider_name}} account to login to
|
||||||
|
{{site_name}}.<br>As a final step, please complete the following form:{% endblocktrans %}</p>
|
||||||
|
|
||||||
|
<form class="signup" id="signup_form" method="post" action="{% url 'socialaccount_signup' %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form|crispy }}
|
||||||
|
{% if redirect_field_value %}
|
||||||
|
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
|
||||||
|
{% endif %}
|
||||||
|
<button class='btn btn-success sso-provider-link' type="submit">
|
||||||
|
<span class='fas fa-user-plus'></span> {% trans "Sign Up" %} »
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<a href='{% url "account_login" %}'>
|
||||||
|
{% trans "Return to login page" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -2,17 +2,21 @@
|
|||||||
|
|
||||||
{% get_providers as socialaccount_providers %}
|
{% get_providers as socialaccount_providers %}
|
||||||
|
|
||||||
|
<ul class='sso-provider-list'>
|
||||||
{% for provider in socialaccount_providers %}
|
{% for provider in socialaccount_providers %}
|
||||||
|
<li class='sso-provider-link'>
|
||||||
{% if provider.id == "openid" %}
|
{% if provider.id == "openid" %}
|
||||||
{% for brand in provider.get_brands %}
|
{% for brand in provider.get_brands %}
|
||||||
<a title="{{brand.name}}" brand_name='{{ provider.id }}'
|
<a title="{{brand.name}}" brand_name='{{ provider.id }}'
|
||||||
class="btn btn-primary socialaccount_provider {{provider.id}} {{brand.id}}"
|
class="btn btn-light socialaccount_provider {{provider.id}} {{brand.id}}"
|
||||||
href="{% provider_login_url provider.id openid=brand.openid_url process=process %}">
|
href="{% provider_login_url provider.id openid=brand.openid_url process=process %}">
|
||||||
<span class='brand-icon' brand_name='{{provider.id}}'></span> {{brand.name}}</a>
|
<span class='brand-icon' brand_name='{{provider.id}}'></span> {{ brand.name }}</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a title="{{provider.name}}" brand_name='{{ provider.id }}'
|
<a title="{{provider.name}}" brand_name='{{ provider.id }}'
|
||||||
class="btn btn-primary socialaccount_provider {{provider.id}}"
|
class="btn btn-light socialaccount_provider {{provider.id}}"
|
||||||
href="{% provider_login_url provider.id process=process scope=scope auth_params=auth_params %}"
|
href="{% provider_login_url provider.id process=process scope=scope auth_params=auth_params %}"
|
||||||
><span class='brand-icon' brand_name='{{provider.id}}'></span> {{provider.name}}</a>
|
><span class='brand-icon' brand_name='{{provider.id}}'></span> {{ provider.name }}</a>
|
||||||
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
Loading…
Reference in New Issue
Block a user