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

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 

13 

14from django import forms 

15from django.forms import widgets 

16 

17import cas_server.utils as utils 

18import cas_server.models as models 

19 

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 _ 

25 

26 

27class BootsrapForm(forms.Form): 

28 """ 

29 Bases: :class:`django.forms.Form` 

30 

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) 

49 

50 

51class BaseLogin(BootsrapForm): 

52 """ 

53 Bases: :class:`BootsrapForm` 

54 

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) 

66 

67 

68class WarnForm(BaseLogin): 

69 """ 

70 Bases: :class:`BaseLogin` 

71 

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) 

76 

77 

78class FederateSelect(BaseLogin): 

79 """ 

80 Bases: :class:`BaseLogin` 

81 

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) 

102 

103 

104class UserCredential(BaseLogin): 

105 """ 

106 Bases: :class:`BaseLogin` 

107 

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 ) 

122 

123 def clean(self): 

124 """ 

125 Validate that the submited :attr:`username` and :attr:`password` are valid 

126 

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 

142 

143 

144class FederateUserCredential(UserCredential): 

145 """ 

146 Bases: :class:`UserCredential` 

147 

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>`. 

151 

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. 

160 

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 """ 

164 

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 

171 

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. 

176 

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 

194 

195 

196class TicketForm(forms.ModelForm): 

197 """ 

198 Bases: :class:`django.forms.ModelForm` 

199 

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)