Coverage for cas_server/forms.py: 98%
64 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-18 09:41 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-18 09:41 +0000
1# This program is distributed in the hope that it will be useful, but WITHOUT
2# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
3# FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
4# more details.
5#
6# You should have received a copy of the GNU General Public License version 3
7# along with this program; if not, write to the Free Software Foundation, Inc., 51
8# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
9#
10# (c) 2015-2016 Valentin Samir
11"""forms for the app"""
12from .default_settings import settings
14from django import forms
15from django.forms import widgets
17import cas_server.utils as utils
18import cas_server.models as models
20import sys
21if sys.version_info < (3, ): 21 ↛ 22line 21 didn't jump to line 22 because the condition on line 21 was never true
22 from django.utils.translation import ugettext_lazy as _
23else:
24 from django.utils.translation import gettext_lazy as _
27class BootsrapForm(forms.Form):
28 """
29 Bases: :class:`django.forms.Form`
31 Form base class to use boostrap then rendering the form fields
32 """
33 def __init__(self, *args, **kwargs):
34 super(BootsrapForm, self).__init__(*args, **kwargs)
35 for field in self.fields.values():
36 # Only tweak the field if it will be displayed
37 if not isinstance(field.widget, widgets.HiddenInput):
38 attrs = {}
39 if (
40 isinstance(field.widget, (widgets.Input, widgets.Select, widgets.Textarea)) and
41 not isinstance(field.widget, (widgets.CheckboxInput,))
42 ):
43 attrs['class'] = "form-control"
44 if isinstance(field.widget, (widgets.Input, widgets.Textarea)) and field.label:
45 attrs["placeholder"] = field.label
46 if field.required:
47 attrs["required"] = "required"
48 field.widget.attrs.update(attrs)
51class BaseLogin(BootsrapForm):
52 """
53 Bases: :class:`BootsrapForm`
55 Base form with all field possibly hidden on the login pages
56 """
57 #: The service url for which the user want a ticket
58 service = forms.CharField(widget=forms.HiddenInput(), required=False)
59 #: A valid LoginTicket to prevent POST replay
60 lt = forms.CharField(widget=forms.HiddenInput(), required=False)
61 #: Is the service asking the authentication renewal ?
62 renew = forms.BooleanField(widget=forms.HiddenInput(), required=False)
63 #: Url to redirect to if the authentication fail (user not authenticated or bad service)
64 gateway = forms.CharField(widget=forms.HiddenInput(), required=False)
65 method = forms.CharField(widget=forms.HiddenInput(), required=False)
68class WarnForm(BaseLogin):
69 """
70 Bases: :class:`BaseLogin`
72 Form used on warn page before emiting a ticket
73 """
74 #: ``True`` if the user has been warned of the ticket emission
75 warned = forms.BooleanField(widget=forms.HiddenInput(), required=False)
78class FederateSelect(BaseLogin):
79 """
80 Bases: :class:`BaseLogin`
82 Form used on the login page when ``settings.CAS_FEDERATE`` is ``True``
83 allowing the user to choose an identity provider.
84 """
85 #: The providers the user can choose to be used as authentication backend
86 provider = forms.ModelChoiceField(
87 queryset=models.FederatedIendityProvider.objects.filter(display=True).order_by(
88 "pos",
89 "verbose_name",
90 "suffix"
91 ),
92 to_field_name="suffix",
93 label=_('Identity provider'),
94 )
95 #: A checkbox to ask to be warn before emiting a ticket for another service
96 warn = forms.BooleanField(
97 label=_('Warn me before logging me into other sites.'),
98 required=False
99 )
100 #: A checkbox to remember the user choices of :attr:`provider<FederateSelect.provider>`
101 remember = forms.BooleanField(label=_('Remember the identity provider'), required=False)
104class UserCredential(BaseLogin):
105 """
106 Bases: :class:`BaseLogin`
108 Form used on the login page to retrive user credentials
109 """
110 #: The user username
111 username = forms.CharField(
112 label=_('username'),
113 widget=forms.TextInput(attrs={'autofocus': 'autofocus'})
114 )
115 #: The user password
116 password = forms.CharField(label=_('password'), widget=forms.PasswordInput)
117 #: A checkbox to ask to be warn before emiting a ticket for another service
118 warn = forms.BooleanField(
119 label=_('Warn me before logging me into other sites.'),
120 required=False
121 )
123 def clean(self):
124 """
125 Validate that the submited :attr:`username` and :attr:`password` are valid
127 :raises django.forms.ValidationError: if the :attr:`username` and :attr:`password`
128 are not valid.
129 :return: The cleaned POST data
130 :rtype: dict
131 """
132 cleaned_data = super(UserCredential, self).clean()
133 if "username" in cleaned_data and "password" in cleaned_data:
134 auth = utils.import_attr(settings.CAS_AUTH_CLASS)(cleaned_data["username"])
135 if auth.test_password(cleaned_data["password"]):
136 cleaned_data["username"] = auth.username
137 else:
138 raise forms.ValidationError(
139 _(u"The credentials you provided cannot be determined to be authentic.")
140 )
141 return cleaned_data
144class FederateUserCredential(UserCredential):
145 """
146 Bases: :class:`UserCredential`
148 Form used on a auto submited page for linking the views
149 :class:`FederateAuth<cas_server.views.FederateAuth>` and
150 :class:`LoginView<cas_server.views.LoginView>`.
152 On successful authentication on a provider, in the view
153 :class:`FederateAuth<cas_server.views.FederateAuth>` a
154 :class:`FederatedUser<cas_server.models.FederatedUser>` is created by
155 :meth:`cas_server.federate.CASFederateValidateUser.verify_ticket` and the user is redirected
156 to :class:`LoginView<cas_server.views.LoginView>`. This form is then automatically filled
157 with infos matching the created :class:`FederatedUser<cas_server.models.FederatedUser>`
158 using the ``ticket`` as one time password and submited using javascript. If javascript is
159 not enabled, a connect button is displayed.
161 This stub authentication form, allow to implement the federated mode with very few
162 modificatons to the :class:`LoginView<cas_server.views.LoginView>` view.
163 """
165 def __init__(self, *args, **kwargs):
166 super(FederateUserCredential, self).__init__(*args, **kwargs)
167 # All fields are hidden and auto filled by the /login view logic
168 for name, field in self.fields.items():
169 field.widget = forms.HiddenInput()
170 self[name].display = False
172 def clean(self):
173 """
174 Validate that the submited :attr:`username` and :attr:`password` are valid using
175 the :class:`CASFederateAuth<cas_server.auth.CASFederateAuth>` auth class.
177 :raises django.forms.ValidationError: if the :attr:`username` and :attr:`password`
178 do not correspond to a :class:`FederatedUser<cas_server.models.FederatedUser>`.
179 :return: The cleaned POST data
180 :rtype: dict
181 """
182 cleaned_data = super(FederateUserCredential, self).clean()
183 try:
184 user = models.FederatedUser.get_from_federated_username(cleaned_data["username"])
185 user.ticket = ""
186 user.save()
187 # should not happed as if the FederatedUser do not exists, super should
188 # raise before a ValidationError("bad user")
189 except models.FederatedUser.DoesNotExist: # pragma: no cover (should not happend)
190 raise forms.ValidationError(
191 _(u"User not found in the temporary database, please try to reconnect")
192 )
193 return cleaned_data
196class TicketForm(forms.ModelForm):
197 """
198 Bases: :class:`django.forms.ModelForm`
200 Form for Tickets in the admin interface
201 """
202 class Meta:
203 model = models.Ticket
204 exclude = []
205 service = forms.CharField(label=_('service'), widget=forms.TextInput)