django-cas-server/cas_server/forms.py

206 lines
8 KiB
Python
Raw Normal View History

2015-05-27 20:10:06 +00:00
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
# more details.
#
# You should have received a copy of the GNU General Public License version 3
# along with this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
2016-06-30 22:00:53 +00:00
# (c) 2015-2016 Valentin Samir
2015-05-27 19:56:39 +00:00
"""forms for the app"""
from .default_settings import settings
2015-05-17 21:24:41 +00:00
2015-05-16 21:43:46 +00:00
from django import forms
from django.forms import widgets
2015-05-16 21:43:46 +00:00
2015-06-21 16:56:16 +00:00
import cas_server.utils as utils
import cas_server.models as models
2015-05-16 21:43:46 +00:00
2020-04-25 00:59:37 +00:00
import sys
if sys.version_info < (3, ):
from django.utils.translation import ugettext_lazy as _
else:
from django.utils.translation import gettext_lazy as _
2016-06-03 12:19:43 +00:00
class BootsrapForm(forms.Form):
2016-07-31 18:30:27 +00:00
"""
Bases: :class:`django.forms.Form`
Form base class to use boostrap then rendering the form fields
"""
def __init__(self, *args, **kwargs):
super(BootsrapForm, self).__init__(*args, **kwargs)
for field in self.fields.values():
# Only tweak the field if it will be displayed
if not isinstance(field.widget, widgets.HiddenInput):
attrs = {}
if (
isinstance(field.widget, (widgets.Input, widgets.Select, widgets.Textarea)) and
not isinstance(field.widget, (widgets.CheckboxInput,))
):
attrs['class'] = "form-control"
if isinstance(field.widget, (widgets.Input, widgets.Textarea)) and field.label:
attrs["placeholder"] = field.label
if field.required:
attrs["required"] = "required"
field.widget.attrs.update(attrs)
2016-07-31 18:30:27 +00:00
class BaseLogin(BootsrapForm):
2016-07-20 16:28:23 +00:00
"""
2016-07-31 18:30:27 +00:00
Bases: :class:`BootsrapForm`
2016-07-20 16:28:23 +00:00
2016-07-31 18:30:27 +00:00
Base form with all field possibly hidden on the login pages
2016-07-20 16:28:23 +00:00
"""
#: The service url for which the user want a ticket
service = forms.CharField(widget=forms.HiddenInput(), required=False)
2016-07-31 18:30:27 +00:00
#: A valid LoginTicket to prevent POST replay
lt = forms.CharField(widget=forms.HiddenInput(), required=False)
2016-07-20 16:28:23 +00:00
#: Is the service asking the authentication renewal ?
renew = forms.BooleanField(widget=forms.HiddenInput(), required=False)
2016-07-20 16:28:23 +00:00
#: Url to redirect to if the authentication fail (user not authenticated or bad service)
gateway = forms.CharField(widget=forms.HiddenInput(), required=False)
method = forms.CharField(widget=forms.HiddenInput(), required=False)
2016-07-31 18:30:27 +00:00
class WarnForm(BaseLogin):
"""
Bases: :class:`BaseLogin`
Form used on warn page before emiting a ticket
"""
2016-07-20 16:28:23 +00:00
#: ``True`` if the user has been warned of the ticket emission
warned = forms.BooleanField(widget=forms.HiddenInput(), required=False)
2015-06-12 16:10:52 +00:00
2016-06-03 12:19:43 +00:00
2016-07-31 18:30:27 +00:00
class FederateSelect(BaseLogin):
2016-07-03 15:54:11 +00:00
"""
2016-07-31 18:30:27 +00:00
Bases: :class:`BaseLogin`
2016-07-20 16:28:23 +00:00
Form used on the login page when ``settings.CAS_FEDERATE`` is ``True``
allowing the user to choose an identity provider.
2016-07-03 15:54:11 +00:00
"""
2016-07-20 16:28:23 +00:00
#: The providers the user can choose to be used as authentication backend
provider = forms.ModelChoiceField(
queryset=models.FederatedIendityProvider.objects.filter(display=True).order_by(
"pos",
"verbose_name",
"suffix"
),
to_field_name="suffix",
2016-06-17 17:28:49 +00:00
label=_('Identity provider'),
)
2016-07-20 16:28:23 +00:00
#: A checkbox to ask to be warn before emiting a ticket for another service
warn = forms.BooleanField(
label=_('Warn me before logging me into other sites.'),
required=False
)
2016-08-01 00:07:50 +00:00
#: A checkbox to remember the user choices of :attr:`provider<FederateSelect.provider>`
remember = forms.BooleanField(label=_('Remember the identity provider'), required=False)
2016-06-17 17:28:49 +00:00
2016-07-31 18:30:27 +00:00
class UserCredential(BaseLogin):
2016-07-20 16:28:23 +00:00
"""
2016-07-31 18:30:27 +00:00
Bases: :class:`BaseLogin`
2016-07-20 16:28:23 +00:00
Form used on the login page to retrive user credentials
"""
#: The user username
username = forms.CharField(
label=_('username'),
widget=forms.TextInput(attrs={'autofocus': 'autofocus'})
)
2016-07-20 16:28:23 +00:00
#: The user password
2015-05-22 15:55:00 +00:00
password = forms.CharField(label=_('password'), widget=forms.PasswordInput)
2016-07-20 16:28:23 +00:00
#: A checkbox to ask to be warn before emiting a ticket for another service
warn = forms.BooleanField(
label=_('Warn me before logging me into other sites.'),
required=False
)
2015-05-16 21:43:46 +00:00
def clean(self):
2016-07-20 16:28:23 +00:00
"""
Validate that the submited :attr:`username` and :attr:`password` are valid
:raises django.forms.ValidationError: if the :attr:`username` and :attr:`password`
are not valid.
:return: The cleaned POST data
:rtype: dict
"""
2015-05-16 21:43:46 +00:00
cleaned_data = super(UserCredential, self).clean()
if "username" in cleaned_data and "password" in cleaned_data:
auth = utils.import_attr(settings.CAS_AUTH_CLASS)(cleaned_data["username"])
if auth.test_password(cleaned_data["password"]):
cleaned_data["username"] = auth.username
else:
raise forms.ValidationError(
_(u"The credentials you provided cannot be determined to be authentic.")
)
2016-06-17 17:28:49 +00:00
return cleaned_data
class FederateUserCredential(UserCredential):
2016-07-20 16:28:23 +00:00
"""
Bases: :class:`UserCredential`
2016-07-20 16:28:23 +00:00
Form used on a auto submited page for linking the views
:class:`FederateAuth<cas_server.views.FederateAuth>` and
:class:`LoginView<cas_server.views.LoginView>`.
On successful authentication on a provider, in the view
:class:`FederateAuth<cas_server.views.FederateAuth>` a
:class:`FederatedUser<cas_server.models.FederatedUser>` is created by
:meth:`cas_server.federate.CASFederateValidateUser.verify_ticket` and the user is redirected
to :class:`LoginView<cas_server.views.LoginView>`. This form is then automatically filled
with infos matching the created :class:`FederatedUser<cas_server.models.FederatedUser>`
using the ``ticket`` as one time password and submited using javascript. If javascript is
not enabled, a connect button is displayed.
This stub authentication form, allow to implement the federated mode with very few
modificatons to the :class:`LoginView<cas_server.views.LoginView>` view.
"""
2016-07-31 18:30:27 +00:00
def __init__(self, *args, **kwargs):
super(FederateUserCredential, self).__init__(*args, **kwargs)
# All fields are hidden and auto filled by the /login view logic
for name, field in self.fields.items():
field.widget = forms.HiddenInput()
self[name].display = False
2016-06-17 17:28:49 +00:00
def clean(self):
2016-07-20 16:28:23 +00:00
"""
Validate that the submited :attr:`username` and :attr:`password` are valid using
the :class:`CASFederateAuth<cas_server.auth.CASFederateAuth>` auth class.
:raises django.forms.ValidationError: if the :attr:`username` and :attr:`password`
do not correspond to a :class:`FederatedUser<cas_server.models.FederatedUser>`.
:return: The cleaned POST data
:rtype: dict
"""
2016-06-17 17:28:49 +00:00
cleaned_data = super(FederateUserCredential, self).clean()
try:
user = models.FederatedUser.get_from_federated_username(cleaned_data["username"])
2016-06-17 17:28:49 +00:00
user.ticket = ""
user.save()
# should not happed as if the FederatedUser do not exists, super should
# raise before a ValidationError("bad user")
except models.FederatedUser.DoesNotExist: # pragma: no cover (should not happend)
raise forms.ValidationError(
_(u"User not found in the temporary database, please try to reconnect")
)
2016-06-17 17:28:49 +00:00
return cleaned_data
2015-05-16 21:43:46 +00:00
class TicketForm(forms.ModelForm):
2016-07-20 16:28:23 +00:00
"""
Bases: :class:`django.forms.ModelForm`
Form for Tickets in the admin interface
"""
2015-05-16 21:43:46 +00:00
class Meta:
model = models.Ticket
exclude = []
service = forms.CharField(label=_('service'), widget=forms.TextInput)