1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
|
# 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.
#
# (c) 2015-2020 Valentin Samir
"""forms for the app"""
from .default_settings import settings
from django import forms
from django.forms import widgets
import cas_server.utils as utils
import cas_server.models as models
import sys
if sys.version_info < (3, ):
from django.utils.translation import ugettext_lazy as _
else:
from django.utils.translation import gettext_lazy as _
class BootsrapForm(forms.Form):
"""
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)
class BaseLogin(BootsrapForm):
"""
Bases: :class:`BootsrapForm`
Base form with all field possibly hidden on the login pages
"""
#: The service url for which the user want a ticket
service = forms.CharField(widget=forms.HiddenInput(), required=False)
#: A valid LoginTicket to prevent POST replay
lt = forms.CharField(widget=forms.HiddenInput(), required=False)
#: Is the service asking the authentication renewal ?
renew = forms.BooleanField(widget=forms.HiddenInput(), required=False)
#: 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)
class WarnForm(BaseLogin):
"""
Bases: :class:`BaseLogin`
Form used on warn page before emiting a ticket
"""
#: ``True`` if the user has been warned of the ticket emission
warned = forms.BooleanField(widget=forms.HiddenInput(), required=False)
class FederateSelect(BaseLogin):
"""
Bases: :class:`BaseLogin`
Form used on the login page when ``settings.CAS_FEDERATE`` is ``True``
allowing the user to choose an identity provider.
"""
#: 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",
label=_('Identity provider'),
)
#: 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
)
#: A checkbox to remember the user choices of :attr:`provider<FederateSelect.provider>`
remember = forms.BooleanField(label=_('Remember the identity provider'), required=False)
class UserCredential(BaseLogin):
"""
Bases: :class:`BaseLogin`
Form used on the login page to retrive user credentials
"""
#: The user username
username = forms.CharField(
label=_('username'),
widget=forms.TextInput(attrs={'autofocus': 'autofocus'})
)
#: The user password
password = forms.CharField(label=_('password'), widget=forms.PasswordInput)
#: 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
)
def clean(self):
"""
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
"""
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.")
)
return cleaned_data
class FederateUserCredential(UserCredential):
"""
Bases: :class:`UserCredential`
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.
"""
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
def clean(self):
"""
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
"""
cleaned_data = super(FederateUserCredential, self).clean()
try:
user = models.FederatedUser.get_from_federated_username(cleaned_data["username"])
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")
)
return cleaned_data
class TicketForm(forms.ModelForm):
"""
Bases: :class:`django.forms.ModelForm`
Form for Tickets in the admin interface
"""
class Meta:
model = models.Ticket
exclude = []
service = forms.CharField(label=_('service'), widget=forms.TextInput)
|