forms.py 9.1 KB
Newer Older
Valentin Samir's avatar
Valentin Samir committed
1 2 3 4 5 6 7 8 9
# 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.
#
Valentin Samir's avatar
Valentin Samir committed
10
# (c) 2015-2016 Valentin Samir
Valentin Samir's avatar
Valentin Samir committed
11
"""forms for the app"""
12
from .default_settings import settings
Valentin Samir's avatar
Valentin Samir committed
13

Valentin Samir's avatar
Valentin Samir committed
14
from django import forms
Valentin Samir's avatar
Valentin Samir committed
15
from django.utils.translation import ugettext_lazy as _
Valentin Samir's avatar
Valentin Samir committed
16

Valentin Samir's avatar
Valentin Samir committed
17 18
import cas_server.utils as utils
import cas_server.models as models
Valentin Samir's avatar
Valentin Samir committed
19

Valentin Samir's avatar
style  
Valentin Samir committed
20

21 22 23 24 25 26 27 28 29 30 31 32 33 34
class BootsrapForm(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 (name, field) in self.fields.items():
            # Only tweak the fiel if it will be displayed
            if not isinstance(field.widget, forms.HiddenInput):
                # tell to display the field (used in form.html)
                self[name].display = True
                attrs = {}
                if isinstance(field.widget, forms.CheckboxInput):
                    self[name].checkbox = True
                else:
                    attrs['class'] = "form-control"
35
                    if field.label:  # pragma: no branch (currently all field are hidden or labeled)
36 37 38 39 40 41 42
                        attrs["placeholder"] = field.label
                if field.required:
                    attrs["required"] = "required"
                field.widget.attrs.update(attrs)


class WarnForm(BootsrapForm):
43 44 45 46 47 48 49
    """
        Bases: :class:`django.forms.Form`

        Form used on warn page before emiting a ticket
    """

    #: The service url for which the user want a ticket
50
    service = forms.CharField(widget=forms.HiddenInput(), required=False)
51
    #: Is the service asking the authentication renewal ?
52
    renew = forms.BooleanField(widget=forms.HiddenInput(), required=False)
53
    #: Url to redirect to if the authentication fail (user not authenticated or bad service)
54 55
    gateway = forms.CharField(widget=forms.HiddenInput(), required=False)
    method = forms.CharField(widget=forms.HiddenInput(), required=False)
56
    #: ``True`` if the user has been warned of the ticket emission
57
    warned = forms.BooleanField(widget=forms.HiddenInput(), required=False)
58
    #: A valid LoginTicket to prevent POST replay
59
    lt = forms.CharField(widget=forms.HiddenInput(), required=False)
Valentin Samir's avatar
PEP8  
Valentin Samir committed
60

Valentin Samir's avatar
style  
Valentin Samir committed
61

62
class FederateSelect(BootsrapForm):
Valentin Samir's avatar
Valentin Samir committed
63
    """
64 65 66 67
        Bases: :class:`django.forms.Form`

        Form used on the login page when ``settings.CAS_FEDERATE`` is ``True``
        allowing the user to choose an identity provider.
Valentin Samir's avatar
Valentin Samir committed
68
    """
69
    #: The providers the user can choose to be used as authentication backend
70
    provider = forms.ModelChoiceField(
71
        queryset=models.FederatedIendityProvider.objects.filter(display=True).order_by(
72 73 74 75 76
            "pos",
            "verbose_name",
            "suffix"
        ),
        to_field_name="suffix",
Valentin Samir's avatar
Valentin Samir committed
77 78
        label=_('Identity provider'),
    )
79
    #: The service url for which the user want a ticket
Valentin Samir's avatar
Valentin Samir committed
80 81
    service = forms.CharField(label=_('service'), widget=forms.HiddenInput(), required=False)
    method = forms.CharField(widget=forms.HiddenInput(), required=False)
82
    #: A checkbox to remember the user choices of :attr:`provider<FederateSelect.provider>`
Valentin Samir's avatar
Valentin Samir committed
83
    remember = forms.BooleanField(label=_('Remember the identity provider'), required=False)
84
    #: A checkbox to ask to be warn before emiting a ticket for another service
85 86 87 88
    warn = forms.BooleanField(
        label=_('Warn me before logging me into other sites.'),
        required=False
    )
89
    #: Is the service asking the authentication renewal ?
90
    renew = forms.BooleanField(widget=forms.HiddenInput(), required=False)
Valentin Samir's avatar
Valentin Samir committed
91 92


93
class UserCredential(BootsrapForm):
94 95 96 97 98 99
    """
         Bases: :class:`django.forms.Form`

         Form used on the login page to retrive user credentials
     """
    #: The user username
100
    username = forms.CharField(label=_('username'))
101
    #: The service url for which the user want a ticket
102
    service = forms.CharField(label=_('service'), widget=forms.HiddenInput(), required=False)
103
    #: The user password
Valentin Samir's avatar
Valentin Samir committed
104
    password = forms.CharField(label=_('password'), widget=forms.PasswordInput)
105
    #: A valid LoginTicket to prevent POST replay
Valentin Samir's avatar
Valentin Samir committed
106
    lt = forms.CharField(widget=forms.HiddenInput(), required=False)
Valentin Samir's avatar
Valentin Samir committed
107
    method = forms.CharField(widget=forms.HiddenInput(), required=False)
108
    #: A checkbox to ask to be warn before emiting a ticket for another service
109 110 111 112
    warn = forms.BooleanField(
        label=_('Warn me before logging me into other sites.'),
        required=False
    )
113
    #: Is the service asking the authentication renewal ?
114
    renew = forms.BooleanField(widget=forms.HiddenInput(), required=False)
Valentin Samir's avatar
Valentin Samir committed
115

Valentin Samir's avatar
Valentin Samir committed
116
    def __init__(self, *args, **kwargs):
Valentin Samir's avatar
Valentin Samir committed
117 118 119
        super(UserCredential, self).__init__(*args, **kwargs)

    def clean(self):
120 121 122 123 124 125 126 127
        """
            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
        """
Valentin Samir's avatar
Valentin Samir committed
128
        cleaned_data = super(UserCredential, self).clean()
129
        auth = utils.import_attr(settings.CAS_AUTH_CLASS)(cleaned_data.get("username"))
Valentin Samir's avatar
Valentin Samir committed
130
        if auth.test_password(cleaned_data.get("password")):
Valentin Samir's avatar
Valentin Samir committed
131
            cleaned_data["username"] = auth.username
Valentin Samir's avatar
Valentin Samir committed
132
        else:
133 134 135
            raise forms.ValidationError(
                _(u"The credentials you provided cannot be determined to be authentic.")
            )
Valentin Samir's avatar
Valentin Samir committed
136 137 138 139
        return cleaned_data


class FederateUserCredential(UserCredential):
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
    """
        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.
    """
    #: the user username with the ``@`` component
Valentin Samir's avatar
Valentin Samir committed
160
    username = forms.CharField(widget=forms.HiddenInput())
161
    #: The service url for which the user want a ticket
Valentin Samir's avatar
Valentin Samir committed
162
    service = forms.CharField(widget=forms.HiddenInput(), required=False)
163
    #: The ``ticket`` used to authenticate the user against a provider
Valentin Samir's avatar
Valentin Samir committed
164
    password = forms.CharField(widget=forms.HiddenInput())
165
    #: alias of :attr:`password`
Valentin Samir's avatar
Valentin Samir committed
166
    ticket = forms.CharField(widget=forms.HiddenInput())
167
    #: A valid LoginTicket to prevent POST replay
Valentin Samir's avatar
Valentin Samir committed
168 169
    lt = forms.CharField(widget=forms.HiddenInput(), required=False)
    method = forms.CharField(widget=forms.HiddenInput(), required=False)
170
    #: Has the user asked to be warn before emiting a ticket for another service
Valentin Samir's avatar
Valentin Samir committed
171
    warn = forms.BooleanField(widget=forms.HiddenInput(), required=False)
172
    #: Is the service asking the authentication renewal ?
173
    renew = forms.BooleanField(widget=forms.HiddenInput(), required=False)
Valentin Samir's avatar
Valentin Samir committed
174 175

    def clean(self):
176 177 178 179 180 181 182 183 184
        """
            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
        """
Valentin Samir's avatar
Valentin Samir committed
185 186
        cleaned_data = super(FederateUserCredential, self).clean()
        try:
187
            user = models.FederatedUser.get_from_federated_username(cleaned_data["username"])
Valentin Samir's avatar
Valentin Samir committed
188 189
            user.ticket = ""
            user.save()
190
        # should not happed as if the FederatedUser do not exists, super should
191 192 193 194 195
        # 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")
            )
Valentin Samir's avatar
Valentin Samir committed
196
        return cleaned_data
Valentin Samir's avatar
Valentin Samir committed
197 198 199


class TicketForm(forms.ModelForm):
200 201 202 203 204
    """
        Bases: :class:`django.forms.ModelForm`

        Form for Tickets in the admin interface
    """
Valentin Samir's avatar
Valentin Samir committed
205 206 207
    class Meta:
        model = models.Ticket
        exclude = []
208
    service = forms.CharField(label=_('service'), widget=forms.TextInput)