def clean_email(self): # we override the default django auth clean_email to provide more # detailed messages in case of inactive users email = self.cleaned_data['email'] try: user = AstakosUser.objects.get_by_identifier(email) self.users_cache = [user] if not user.is_active: if not user.has_auth_provider('local', auth_backend='astakos'): provider = auth_providers.get_provider('local', user) msg = mark_safe(provider.get_unusable_password_msg) raise forms.ValidationError(msg) msg = mark_safe(user.get_inactive_message('local')) raise forms.ValidationError(msg) provider = auth_providers.get_provider('local', user) if not user.has_usable_password(): msg = provider.get_unusable_password_msg raise forms.ValidationError(mark_safe(msg)) if not user.can_change_password(): msg = provider.get_cannot_change_password_msg raise forms.ValidationError(mark_safe(msg)) except AstakosUser.DoesNotExist: raise forms.ValidationError(_(astakos_messages.EMAIL_UNKNOWN)) return email
def add(request, template_name='im/auth/ldap_add.html'): provider = auth.get_provider('ldap', request.user) # Check that provider's policy allows to add provider to account if not provider.get_add_policy: messages.error(request, provider.get_add_disabled_msg) return HttpResponseRedirect(reverse('edit_profile')) if request.method == "GET": return render_response(template_name, login_form=LDAPLoginForm(request=request), context_instance=get_context( request, provider=LDAP_PROVIDER)) form = LDAPLoginForm(data=request.POST, request=request) if form.is_valid(): provider = auth.get_provider('ldap', request.user) user = form.ldap_user_cache provider_info = dict(user.ldap_user.attrs) try: user_info = populate_user_attributes(provider, provider_info) user_id = user_info.pop('identifier') except (ValueError, KeyError): logger.exception( "Failed to map attributes from LDAP provider." " Provider attributes: %s", provider_info) msg = 'Invalid LDAP response. Please contact support.' messages.error(request, msg) return HttpResponseRedirect(reverse('login')) affiliation = 'LDAP' # TODO: Add LDAP server name? user_info['affiliation'] = affiliation provider_info = dict([(k, smart_unicode(v, errors="ignore")) for k, v in provider_info.items() if k in provider.get_provider_info_attributes()]) if hasattr(user, 'group_names') and provider.get_policy('mirror_groups'): groups = [ Group.objects.get_or_create(name=group_name)[0] for group_name in user.group_names ] user_info['groups'] = groups return handle_third_party_login(request, provider_module="ldap", identifier=user_id, provider_info=provider_info, affiliation=affiliation, user_info=user_info) else: return render_response(template_name, form=LDAPLoginForm(request=request), context_instance=get_context( request, provider=LDAP_PROVIDER))
def add(request, template_name='im/auth/ldap_add.html'): provider = auth.get_provider('ldap', request.user) # Check that provider's policy allows to add provider to account if not provider.get_add_policy: messages.error(request, provider.get_add_disabled_msg) return HttpResponseRedirect(reverse('edit_profile')) if request.method == "GET": return render_response( template_name, login_form=LDAPLoginForm(request=request), context_instance=get_context(request, provider=LDAP_PROVIDER) ) form = LDAPLoginForm(data=request.POST, request=request) if form.is_valid(): provider = auth.get_provider('ldap', request.user) user = form.ldap_user_cache provider_info = dict(user.ldap_user.attrs) try: user_info = populate_user_attributes(provider, provider_info) user_id = user_info.pop('identifier') except (ValueError, KeyError): logger.exception("Failed to map attributes from LDAP provider." " Provider attributes: %s", provider_info) msg = 'Invalid LDAP response. Please contact support.' messages.error(request, msg) return HttpResponseRedirect(reverse('login')) affiliation = 'LDAP' # TODO: Add LDAP server name? user_info['affiliation'] = affiliation provider_info = dict([(k, smart_unicode(v, errors="ignore")) for k, v in provider_info.items() if k in provider.get_provider_info_attributes()]) if hasattr(user, 'group_names') and provider.get_policy('mirror_groups'): groups = [Group.objects.get_or_create(name=group_name)[0] for group_name in user.group_names] user_info['groups'] = groups return handle_third_party_login(request, provider_module="ldap", identifier=user_id, provider_info=provider_info, affiliation=affiliation, user_info=user_info) else: return render_response( template_name, form=LDAPLoginForm(request=request), context_instance=get_context(request, provider=LDAP_PROVIDER) )
def signup(request, template_name='im/signup.html', on_success='index', extra_context=None, activation_backend=None): """ Allows a user to create a local account. In case of GET request renders a form for entering the user information. In case of POST handles the signup. The user activation will be delegated to the backend specified by the ``activation_backend`` keyword argument if present, otherwise to the ``astakos.im.activation_backends.InvitationBackend`` if settings.ASTAKOS_INVITATIONS_ENABLED is True or ``astakos.im.activation_backends.SimpleBackend`` if not (see activation_backends); Upon successful user creation, if ``next`` url parameter is present the user is redirected there otherwise renders the same page with a success message. On unsuccessful creation, renders ``template_name`` with an error message. **Arguments** ``template_name`` A custom template to render. This is optional; if not specified, this will default to ``im/signup.html``. ``extra_context`` An dictionary of variables to add to the template context. ``on_success`` Resolvable view name to redirect on registration success. **Template:** im/signup.html or ``template_name`` keyword argument. """ extra_context = extra_context or {} if request.user.is_authenticated(): logger.info("%s already signed in, redirect to index", request.user.log_display) return HttpResponseRedirect(reverse('index')) provider = get_query(request).get('provider', 'local') try: auth.get_provider(provider) except auth.InvalidProvider, e: messages.error(request, e.message) return HttpResponseRedirect(reverse("signup"))
def clean(self): """ Override default behavior in order to check user's activation later """ username = self.cleaned_data.get('username') if username: try: user = AstakosUser.objects.get_by_identifier(username) if not user.has_auth_provider('local'): provider = auth_providers.get_provider('local', user) msg = provider.get_login_disabled_msg raise forms.ValidationError(mark_safe(msg)) except AstakosUser.DoesNotExist: pass try: super(LoginForm, self).clean() except forms.ValidationError, e: if self.user_cache is None: raise if not self.user_cache.is_active: msg = self.user_cache.get_inactive_message('local') raise forms.ValidationError(msg) if self.request: if not self.request.session.test_cookie_worked(): raise
def populate_user_attributes(provider, provider_info): """Populate user attributes based on the providers attribute mapping. Map attributes returned by the provider to user attributes based on the attribute mapping of the provider. If the value is missing and attribute is not mutable (cannot by set by the user) it will fail. """ user_attributes = {} if isinstance(provider, basestring): provider = auth_providers.get_provider(provider) for attr, (provider_attr, mutable) in provider.get_user_attr_map().items(): try: if callable(provider_attr): value = provider_attr(provider_info) else: value = provider_info[provider_attr] if isinstance(value, list): value = value[0] user_attributes[attr] = smart_unicode(value) except (KeyError, IndexError): if mutable: user_attributes[attr] = None else: msg = ("Provider '%s' response does not have a value for" " attribute '%s'. Provider returned those attributes:" " %s" % (provider, provider_attr, provider_info)) logger.error(msg) raise ValueError(msg) return user_attributes
def handle_third_party_signup(request, userid, provider_module, third_party_key, provider_info=None, pending_user_params=None, template="im/third_party_check_local.html", extra_context=None): if provider_info is None: provider_info = {} if pending_user_params is None: pending_user_params = {} if extra_context is None: extra_context = {} # build provider module object provider_data = { 'affiliation': pending_user_params.get('affiliation', provider_module), 'info_data': provider_info } provider = auth.get_provider(provider_module, request.user, userid, **provider_data) # user wants to add another third party login method if third_party_key: messages.error(request, provider.get_invalid_login_msg) return HttpResponseRedirect(reverse('login') + "?key=%s" % third_party_key) if not provider.get_create_policy: messages.error(request, provider.get_disabled_for_create_msg) return HttpResponseRedirect(reverse('login')) # TODO: this could be stored in session # TODO: create a management command to clean old PendingThirdPartyUser user, created = PendingThirdPartyUser.objects.get_or_create( third_party_identifier=userid, provider=provider_module, ) # update pending user for param, value in pending_user_params.iteritems(): setattr(user, param, value) user.info = json.dumps(provider_info) user.generate_token() user.save() extra_context['provider'] = provider.module extra_context['provider_title'] = provider.get_title_msg extra_context['token'] = user.token extra_context['signup_url'] = reverse('signup') + \ "?third_party_token=%s" % user.token extra_context['add_url'] = reverse('index') + \ "?key=%s#other-login-methods" % user.token extra_context['can_create'] = provider.get_create_policy extra_context['can_add'] = provider.get_add_policy return HttpResponseRedirect(extra_context['signup_url'])
def clean(self): username = self.cleaned_data.get('username') password = self.cleaned_data.get('password') if username: try: user = AstakosUser.objects.get_by_identifier(username) if not user.has_auth_provider('ldap'): provider = auth_providers.get_provider('ldap', user) msg = provider.get_login_disabled_msg raise forms.ValidationError(mark_safe(msg)) except AstakosUser.DoesNotExist: pass # Set user cache to None, so that methods of 'AuthenticationForm' # work self.user_cache = None if username and password: self.ldap_user_cache = LDAPBackend().authenticate(username=username, password=password) if self.ldap_user_cache is None: if self.request: if not self.request.session.test_cookie_worked(): raise raise forms.ValidationError( self.error_messages['invalid_login']) self.check_for_test_cookie() return self.cleaned_data
def _handle_third_party_auto_signup(request, provider, provider_info, identifier, user_info): """Create AstakosUser for third party user without requiring signup form. Handle third party signup by automatically creating an AstakosUser. This is performed when the user's profile is automatically set by the provider. """ try: email = user_info['email'] first_name = user_info['first_name'] last_name = user_info['last_name'] except KeyError as e: raise Exception("Invalid user info. Missing '%s'", str(e)) has_signed_terms = not get_latest_terms() user = auth.make_user(email=email, first_name=first_name, last_name=last_name, has_signed_terms=has_signed_terms) provider_data = { 'affiliation': user_info.get('affiliation', provider), 'info': provider_info } provider = auth_providers.get_provider(module=provider, user_obj=user, identifier=identifier, **provider_data) provider.add_to_user() # Handle user activation activation_backend = activation_backends.get_backend() result = activation_backend.handle_registration(user) activation_backend.send_result_notifications(result, user) return user
def password_change(request, template_name='registration/password_change_form.html', post_change_redirect=None, password_change_form=ExtendedPasswordChangeForm): create_password = False provider = auth.get_provider('local', request.user) # no local backend user wants to create a password if not request.user.has_auth_provider('local'): if not provider.get_add_policy: messages.error(request, provider.get_add_disabled_msg) return HttpResponseRedirect(reverse('edit_profile')) create_password = True password_change_form = ExtendedSetPasswordForm if post_change_redirect is None: post_change_redirect = reverse('edit_profile') if request.method == "POST": form_kwargs = dict( user=request.user, data=request.POST, ) if not create_password: form_kwargs['session_key'] = request.session.session_key form = password_change_form(**form_kwargs) if form.is_valid(): form.save() if create_password: provider = auth.get_provider('local', request.user) messages.success(request, provider.get_added_msg) else: messages.success(request, astakos_messages.PASSWORD_RESET_CONFIRM_DONE) return HttpResponseRedirect(post_change_redirect) else: form = password_change_form(user=request.user) return render_to_response(template_name, { 'form': form, }, context_instance=RequestContext( request, {'create_password': create_password}))
def handle_third_party_signup(request, userid, provider_module, third_party_key, provider_info=None, pending_user_params=None, template="im/third_party_check_local.html", extra_context=None): if provider_info is None: provider_info = {} if pending_user_params is None: pending_user_params = {} if extra_context is None: extra_context = {} # build provider module object provider_data = { 'affiliation': pending_user_params.get('affiliation', provider_module), 'info_data': provider_info } provider = auth.get_provider(provider_module, request.user, userid, **provider_data) # user wants to add another third party login method if third_party_key: messages.error(request, provider.get_invalid_login_msg) return HttpResponseRedirect(reverse('login') + "?key=%s" % third_party_key) if not provider.get_create_policy: messages.error(request, provider.get_disabled_for_create_msg) return HttpResponseRedirect(reverse('login')) # TODO: this could be stored in session # TODO: create a management command to clean old PendingThirdPartyUser user, created = PendingThirdPartyUser.objects.get_or_create( third_party_identifier=userid, provider=provider_module, ) # update pending user for param, value in pending_user_params.iteritems(): setattr(user, param, value) user.info = json.dumps(provider_info) user.generate_token() # skip non required fields validation errors. Reset the field instead of # raising a validation exception. try: user.full_clean() except ValidationError, e: non_required_fields = ['email', 'first_name', 'last_name', 'affiliation'] for field in e.message_dict.keys(): if field in non_required_fields: setattr(user, field, None)
def handle_third_party_signup(request, userid, provider_module, third_party_key, provider_info=None, pending_user_params=None, template="im/third_party_check_local.html", extra_context=None): if provider_info is None: provider_info = {} if pending_user_params is None: pending_user_params = {} if extra_context is None: extra_context = {} # build provider module object provider_data = { 'affiliation': pending_user_params.get('affiliation', provider_module), 'info_data': provider_info } provider = auth_providers.get_provider(provider_module, request.user, userid, **provider_data) # user wants to add another third party login method if third_party_key: messages.error(request, provider.get_invalid_login_msg) return HttpResponseRedirect(reverse('login') + "?key=%s" % third_party_key) if not provider.get_create_policy: messages.error(request, provider.get_disabled_for_create_msg) return HttpResponseRedirect(reverse('login')) # TODO: this could be stored in session # TODO: create a management command to clean old PendingThirdPartyUser user, created = PendingThirdPartyUser.objects.get_or_create( third_party_identifier=userid, provider=provider_module, ) # update pending user for param, value in pending_user_params.iteritems(): setattr(user, param, value) user.info = json.dumps(provider_info) user.generate_token() # skip non required fields validation errors. Reset the field instead of # raising a validation exception. try: user.full_clean() except ValidationError, e: non_required_fields = ['email', 'first_name', 'last_name', 'affiliation'] for field in e.message_dict.keys(): if field in non_required_fields: setattr(user, field, None)
def password_change(request, template_name='registration/password_change_form.html', post_change_redirect=None, password_change_form=ExtendedPasswordChangeForm): create_password = False provider = auth.get_provider('local', request.user) # no local backend user wants to create a password if not request.user.has_auth_provider('local'): if not provider.get_add_policy: messages.error(request, provider.get_add_disabled_msg) return HttpResponseRedirect(reverse('edit_profile')) create_password = True password_change_form = ExtendedSetPasswordForm if post_change_redirect is None: post_change_redirect = reverse('edit_profile') if request.method == "POST": form_kwargs = dict( user=request.user, data=request.POST, ) if not create_password: form_kwargs['session_key'] = request.session.session_key form = password_change_form(**form_kwargs) if form.is_valid(): form.save() if create_password: provider = auth.get_provider('local', request.user) messages.success(request, provider.get_added_msg) else: messages.success(request, astakos_messages.PASSWORD_RESET_CONFIRM_DONE) return HttpResponseRedirect(post_change_redirect) else: form = password_change_form(user=request.user) return render_to_response(template_name, { 'form': form, }, context_instance=RequestContext(request, {'create_password': create_password}))
def logout(request, template='registration/logged_out.html', extra_context=None): """ Wraps `django.contrib.auth.logout`. """ extra_context = extra_context or {} response = HttpResponse() if request.user.is_authenticated(): email = request.user.email auth_logout(request) else: response['Location'] = reverse('index') response.status_code = 301 return response next = restrict_next( request.GET.get('next'), domain=settings.COOKIE_DOMAIN ) if next: response['Location'] = next response.status_code = 302 elif settings.LOGOUT_NEXT: response['Location'] = settings.LOGOUT_NEXT response.status_code = 301 else: last_provider = request.COOKIES.get( 'astakos_last_login_method', 'local') try: provider = auth.get_provider(last_provider) except auth.InvalidProvider: provider = auth.get_provider('local') message = provider.get_logout_success_msg extra = provider.get_logout_success_extra_msg if extra: message += "<br />" + extra messages.success(request, message) response['Location'] = reverse('index') response.status_code = 301 return response
def clean_email(self): email = self.cleaned_data['email'] if not email: raise forms.ValidationError(_(astakos_messages.REQUIRED_FIELD)) if reserved_verified_email(email): provider_id = self.provider provider = auth_providers.get_provider(provider_id) extra_message = provider.get_add_to_existing_account_msg raise forms.ValidationError(mark_safe( _(astakos_messages.EMAIL_USED) + ' ' + extra_message)) return email
def wrapper(request, *args, **kwargs): provider = auth.get_provider(provider_id) if not provider or not provider.is_active(): raise PermissionDenied for pkey, value in perms.iteritems(): attr = 'get_%s_policy' % pkey.lower() if getattr(provider, attr) != value: #TODO: add session message return HttpResponseRedirect(reverse('login')) return func(request, *args)
def save(self, commit=True, **kwargs): try: self.user = AstakosUser.objects.get(id=self.user.id) if settings.NEWPASSWD_INVALIDATE_TOKEN or \ self.cleaned_data.get('renew'): self.user.renew_token() provider = auth_providers.get_provider('local', self.user) if provider.get_add_policy: provider.add_to_user() except BaseException, e: logger.exception(e)
def save(self, commit=True): try: self.user = AstakosUser.objects.get(id=self.user.id) if settings.NEWPASSWD_INVALIDATE_TOKEN or \ self.cleaned_data.get('renew'): self.user.renew_token() provider = auth_providers.get_provider('local', self.user) if provider.get_add_policy: provider.add_to_user() except BaseException, e: logger.exception(e)
def __init__(self, *args, **kwargs): """ Changes the order of fields, and removes the username field. """ self.provider = kwargs.pop('provider', None) self.request = kwargs.pop('request', None) if not self.provider or self.provider == 'local': raise Exception('Invalid provider, %r' % self.provider) # ThirdPartyUserCreationForm should always get instantiated with # a third_party_token value self.third_party_token = kwargs.pop('third_party_token', None) if not self.third_party_token: raise Exception('ThirdPartyUserCreationForm' ' requires third_party_token') super(ThirdPartyUserCreationForm, self).__init__(*args, **kwargs) if not get_latest_terms(): del self.fields['has_signed_terms'] if 'has_signed_terms' in self.fields: # Overriding field label since we need to apply a link # to the terms within the label terms_link_html = '<a href="%s" target="_blank">%s</a>' \ % (reverse('latest_terms'), _("the terms")) self.fields['has_signed_terms'].label = \ mark_safe("I agree with %s" % terms_link_html) auth_provider = auth_providers.get_provider(self.provider) user_attr_map = auth_provider.get_user_attr_map() for field in ['email', 'first_name', 'last_name']: if not user_attr_map[field][1]: self.ro_fields.append(field) self.fields[field].widget.attrs['readonly'] = True self.fields[field].help_text = _(READ_ONLY_FIELD_MSG)
def handle_third_party_login(request, provider_module, identifier, provider_info=None, affiliation=None, third_party_key=None): if not provider_info: provider_info = {} if not affiliation: affiliation = provider_module.title() next_redirect = request.GET.get('next', request.session.get('next_url', None)) if 'next_url' in request.session: del request.session['next_url'] third_party_request_params = get_third_party_session_params(request) from_login = third_party_request_params.get('from_login', False) switch_from = third_party_request_params.get('switch_from', False) provider_data = {'affiliation': affiliation, 'info': provider_info} provider = auth.get_provider(provider_module, request.user, identifier, **provider_data) # an existing user accessed the view if request.user.is_authenticated(): if request.user.has_auth_provider(provider.module, identifier=identifier): return HttpResponseRedirect(reverse('edit_profile')) if provider.verified_exists(): provider.log("add failed (identifier exists to another user)") messages.error(request, provider.get_add_exists_msg) return HttpResponseRedirect(reverse('edit_profile')) # automatically add identifier provider to user if not switch_from and not provider.get_add_policy: # TODO: handle existing uuid message separately provider.log("user cannot add provider") messages.error(request, provider.get_add_failed_msg) return HttpResponseRedirect(reverse('edit_profile')) user = request.user if switch_from: existing_provider = \ request.user.auth_providers.active().get( pk=int(switch_from), module=provider_module).settings # this is not a provider removal so we don't not use # provider.remove_from_user. Use low level access to the provider # db instance. if not provider.verified_exists(): if provider.get_add_policy: existing_provider._instance.delete() existing_provider.log("removed") provider.add_to_user() provider.log("added") else: messages.error(request, provider.get_add_exists_msg) return HttpResponseRedirect(reverse('edit_profile')) messages.success(request, provider.get_switch_success_msg) return HttpResponseRedirect(reverse('edit_profile')) provider.add_to_user() provider.log("added") provider = user.get_auth_provider(provider_module, identifier) messages.success(request, provider.get_added_msg) return HttpResponseRedirect(reverse('edit_profile')) # astakos user exists ? try: user = AstakosUser.objects.get_auth_provider_user( provider_module, identifier=identifier, user__email_verified=True, ) except AstakosUser.DoesNotExist: # TODO: add a message ? redirec to login ? if astakos_messages.AUTH_PROVIDER_SIGNUP_FROM_LOGIN: messages.warning(request, astakos_messages.AUTH_PROVIDER_SIGNUP_FROM_LOGIN) raise if not third_party_key: third_party_key = get_pending_key(request) provider = user.get_auth_provider(provider_module, identifier) if user.is_active: if not provider.get_login_policy: messages.error(request, provider.get_login_disabled_msg) return HttpResponseRedirect(reverse('login')) # authenticate user response = prepare_response(request, user, next_redirect, 'renew' in request.GET) messages.success(request, provider.get_login_success_msg) add_pending_auth_provider(request, third_party_key, provider) response.set_cookie('astakos_last_login_method', provider_module) return response else: message = user.get_inactive_message(provider_module, identifier) messages.error(request, message) return HttpResponseRedirect(login_url(request))
def signup(request, template_name='im/signup.html', on_success='index', extra_context=None, activation_backend=None): """ Allows a user to create a local account. In case of GET request renders a form for entering the user information. In case of POST handles the signup. The user activation will be delegated to the backend specified by the ``activation_backend`` keyword argument if present, otherwise to the ``astakos.im.activation_backends.InvitationBackend`` if settings.ASTAKOS_INVITATIONS_ENABLED is True or ``astakos.im.activation_backends.SimpleBackend`` if not (see activation_backends); Upon successful user creation, if ``next`` url parameter is present the user is redirected there otherwise renders the same page with a success message. On unsuccessful creation, renders ``template_name`` with an error message. **Arguments** ``template_name`` A custom template to render. This is optional; if not specified, this will default to ``im/signup.html``. ``extra_context`` An dictionary of variables to add to the template context. ``on_success`` Resolvable view name to redirect on registration success. **Template:** im/signup.html or ``template_name`` keyword argument. """ extra_context = extra_context or {} if request.user.is_authenticated(): logger.info("%s already signed in, redirect to index", request.user.log_display) transaction.rollback() return HttpResponseRedirect(reverse('index')) provider = get_query(request).get('provider', 'local') if not auth.get_provider(provider).get_create_policy: logger.error("%s provider not available for signup", provider) transaction.rollback() raise PermissionDenied instance = None # user registered using third party provider third_party_token = request.REQUEST.get('third_party_token', None) unverified = None if third_party_token: # retreive third party entry. This was created right after the initial # third party provider handshake. pending = get_object_or_404(PendingThirdPartyUser, token=third_party_token) provider = pending.provider # clone third party instance into the corresponding AstakosUser instance = pending.get_user_instance() get_unverified = AstakosUserAuthProvider.objects.unverified # check existing unverified entries unverified = get_unverified(pending.provider, identifier=pending.third_party_identifier) if unverified and request.method == 'GET': messages.warning(request, unverified.get_pending_registration_msg) if unverified.user.moderated: messages.warning(request, unverified.get_pending_resend_activation_msg) else: messages.warning(request, unverified.get_pending_moderation_msg) # prepare activation backend based on current request if not activation_backend: activation_backend = activation_backends.get_backend() form_kwargs = {'instance': instance, 'request': request} if third_party_token: form_kwargs['third_party_token'] = third_party_token form = activation_backend.get_signup_form(provider, None, **form_kwargs) if request.method == 'POST': form = activation_backend.get_signup_form(provider, request.POST, **form_kwargs) if form.is_valid(): commited = False try: user = form.save(commit=False) # delete previously unverified accounts if AstakosUser.objects.user_exists(user.email): AstakosUser.objects.get_by_identifier(user.email).delete() # store_user so that user auth providers get initialized form.store_user(user, request) result = activation_backend.handle_registration(user) if result.status == \ activation_backend.Result.PENDING_MODERATION: # user should be warned that his account is not active yet status = messages.WARNING else: status = messages.SUCCESS message = result.message activation_backend.send_result_notifications(result, user) # commit user entry transaction.commit() # commited flag # in case an exception get raised from this point commited = True if user and user.is_active: # activation backend directly activated the user # log him in next = request.POST.get('next', '') response = prepare_response(request, user, next=next) return response messages.add_message(request, status, message) return HttpResponseRedirect(reverse(on_success)) except Exception, e: if not commited: transaction.rollback() raise
def login(request, template_name="im/login.html", on_failure='im/login.html', signup_template="/im/third_party_check_local.html", extra_context=None): """ on_failure: the template name to render on login failure """ if request.method == 'GET': return handle_get_to_login_view(request, primary_provider=LDAP_PROVIDER, login_form=LDAPLoginForm(request), template_name=template_name, extra_context=extra_context) # 'limited' attribute is used by recapatcha was_limited = getattr(request, 'limited', False) next = get_query(request).get('next', '') third_party_token = get_query(request).get('key', False) form = LDAPLoginForm(data=request.POST, was_limited=was_limited, request=request) provider = LDAP_PROVIDER if not form.is_valid(): if third_party_token: messages.info(request, provider.get_login_to_add_msg) return render_to_response(on_failure, { 'login_form': form, 'next': next, 'key': third_party_token }, context_instance=get_context( request, primary_provider=LDAP_PROVIDER)) # get the user from the cache user = form.ldap_user_cache provider = auth.get_provider('ldap', user) affiliation = 'LDAP' provider_info = dict(user.ldap_user.attrs) try: user_info = populate_user_attributes(provider, provider_info) user_id = user_info.pop('identifier') except (ValueError, KeyError): logger.exception( "Failed to map attributes from LDAP provider." " Provider attributes: %s", provider_info) msg = 'Invalid LDAP response. Please contact support.' messages.error(request, msg) return HttpResponseRedirect(reverse('login')) provider_info = dict([(k, smart_unicode(v, errors="ignore")) for k, v in provider_info.items() if k in provider.get_provider_info_attributes()]) user_info['affiliation'] = affiliation if hasattr(user, 'group_names') and provider.get_policy('mirror_groups'): groups = [ Group.objects.get_or_create(name=group_name)[0] for group_name in user.group_names ] user_info['groups'] = groups try: return handle_third_party_login(request, provider_module="ldap", identifier=user_id, provider_info=provider_info, affiliation=affiliation, user_info=user_info) except AstakosUser.DoesNotExist: third_party_key = get_pending_key(request) return handle_third_party_signup(request, user_id, 'ldap', third_party_key, provider_info, user_info, signup_template, extra_context)
def login(request, template_name="im/login.html", on_failure='im/login.html', signup_template="/im/third_party_check_local.html", extra_context=None): """ on_failure: the template name to render on login failure """ if request.method == 'GET': return handle_get_to_login_view(request, primary_provider=LDAP_PROVIDER, login_form=LDAPLoginForm(request), template_name=template_name, extra_context=extra_context) # 'limited' attribute is used by recapatcha was_limited = getattr(request, 'limited', False) next = get_query(request).get('next', '') third_party_token = get_query(request).get('key', False) form = LDAPLoginForm(data=request.POST, was_limited=was_limited, request=request) provider = LDAP_PROVIDER if not form.is_valid(): if third_party_token: messages.info(request, provider.get_login_to_add_msg) return render_to_response( on_failure, {'login_form': form, 'next': next, 'key': third_party_token}, context_instance=get_context(request, primary_provider=LDAP_PROVIDER)) # get the user from the cache user = form.ldap_user_cache provider = auth.get_provider('ldap', user) affiliation = 'LDAP' provider_info = dict(user.ldap_user.attrs) try: user_info = populate_user_attributes(provider, provider_info) user_id = user_info.pop('identifier') except (ValueError, KeyError): logger.exception("Failed to map attributes from LDAP provider." " Provider attributes: %s", provider_info) msg = 'Invalid LDAP response. Please contact support.' messages.error(request, msg) return HttpResponseRedirect(reverse('login')) provider_info = dict([(k, smart_unicode(v, errors="ignore")) for k, v in provider_info.items() if k in provider.get_provider_info_attributes()]) user_info['affiliation'] = affiliation if hasattr(user, 'group_names') and provider.get_policy('mirror_groups'): groups = [Group.objects.get_or_create(name=group_name)[0] for group_name in user.group_names] user_info['groups'] = groups try: return handle_third_party_login(request, provider_module="ldap", identifier=user_id, provider_info=provider_info, affiliation=affiliation, user_info=user_info) except AstakosUser.DoesNotExist: third_party_key = get_pending_key(request) return handle_third_party_signup(request, user_id, 'ldap', third_party_key, provider_info, user_info, signup_template, extra_context)
from astakos.im.views.util import render_response from astakos.im.forms import LDAPLoginForm from astakos.im.util import get_query, get_context from astakos.im.views.im import handle_get_to_login_view from django.shortcuts import render_to_response from django.utils.encoding import smart_unicode from astakos.im.views.decorators import login_required import logging retries = settings.RATELIMIT_RETRIES_ALLOWED - 1 rate = str(retries) + '/m' logger = logging.getLogger(__name__) LDAP_PROVIDER = auth.get_provider('ldap') @requires_auth_provider('ldap') @require_http_methods(["GET", "POST"]) @csrf_exempt @requires_anonymous @cookie_fix @ratelimit(field='username', method='POST', rate=rate) def login(request, template_name="im/login.html", on_failure='im/login.html', signup_template="/im/third_party_check_local.html", extra_context=None): """ on_failure: the template name to render on login failure
from synnefo.lib.services import get_public_endpoint from astakos.im.user_utils import send_feedback, logout as auth_logout, \ invite as invite_func, change_user_email from astakos.im import settings from astakos.im import presentation from astakos.im import auth_providers as auth from astakos.im import quotas from astakos.im.views.util import render_response, _resources_catalog from astakos.im.views.decorators import cookie_fix, signed_terms_required,\ required_auth_methods_assigned, valid_astakos_user_required, login_required from astakos.api import projects as projects_api from astakos.api.util import _dthandler logger = logging.getLogger(__name__) PRIMARY_PROVIDER = auth.get_provider(settings.IM_MODULES[0]) def handle_get_to_login_view(request, primary_provider, login_form, template_name="im/login.html", extra_context=None): """Common handling of a GET request to a login view. Handle a GET request to a login view either by redirecting the user to landing page in case the user is authenticated, or by rendering the login template with the 'primary_provider' correctly set. """ extra_context = extra_context or {}
from astakos.im.models import PendingThirdPartyUser from astakos.im.forms import LoginForm, ExtendedPasswordChangeForm, \ ExtendedSetPasswordForm from astakos.im import settings import astakos.im.messages as astakos_messages from astakos.im import auth_providers as auth from astakos.im.views.util import render_response from astakos.im.views.decorators import cookie_fix, requires_anonymous, \ signed_terms_required, requires_auth_provider, login_required from ratelimit.decorators import ratelimit retries = settings.RATELIMIT_RETRIES_ALLOWED - 1 rate = str(retries) + '/m' LOCAL_PROVIDER = auth.get_provider('local') @requires_auth_provider('local') @require_http_methods(["GET", "POST"]) @csrf_exempt @requires_anonymous @cookie_fix @ratelimit(field='username', method='POST', rate=rate) def login(request, template_name="im/login.html", on_failure='im/login.html', extra_context=None): """ on_failure: the template name to render on login failure """ if request.method == 'GET': extra_context = extra_context or {}
return None if user.check_password(password): return user else: msg = 'Invalid password during authentication for %s' logger.log(settings.LOGGING_LEVEL, msg, username) def get_user(self, user_id): try: return AstakosUser.objects.get(pk=user_id) except AstakosUser.DoesNotExist: return None LDAP_PROVIDER = auth.get_provider('ldap') class MockedAstakosUser(object): """Mock AstakosUser object to be used by LDAPBackend. The 'LDAPAuthenticationBackend' requires the creation or existence of an Django User object. However, the creation of the 'AstakosUser' by the 'LDAPAuthenticationBackend' does not match with how Astakos is handling thirt party authentication providers. To overcome this issue we create a mock object, whose attributes will be populated by the 'LDAPAuthenticationBackend'. """ def set_unusable_password(self): pass
from synnefo.lib.services import get_public_endpoint from astakos.im.user_utils import send_feedback, logout as auth_logout, \ invite as invite_func, change_user_email from astakos.im import settings from astakos.im import presentation from astakos.im import auth_providers as auth from astakos.im import quotas from astakos.im.views.util import render_response, _resources_catalog from astakos.im.views.decorators import cookie_fix, signed_terms_required,\ required_auth_methods_assigned, valid_astakos_user_required, login_required from astakos.api import projects as projects_api from astakos.api.util import _dthandler logger = logging.getLogger(__name__) PRIMARY_PROVIDER = auth.get_provider(settings.IM_MODULES[0]) def handle_get_to_login_view(request, primary_provider, login_form, template_name="im/login.html", extra_context=None): """Common handling of a GET request to a login view. Handle a GET request to a login view either by redirecting the user to landing page in case the user is authenticated, or by rendering the login template with the 'primary_provider' correctly set. """ extra_context = extra_context or {} third_party_token = request.GET.get('key', False)
def handle_third_party_signup(request, userid, provider_module, third_party_key, provider_info=None, pending_user_params=None, template="im/third_party_check_local.html", extra_context=None): if provider_info is None: provider_info = {} if pending_user_params is None: pending_user_params = {} if extra_context is None: extra_context = {} # build provider module object provider_data = { 'affiliation': pending_user_params.get('affiliation', provider_module), 'info_data': provider_info } provider = auth.get_provider(provider_module, request.user, userid, **provider_data) # user wants to add another third party login method if third_party_key: messages.error(request, provider.get_invalid_login_msg) return HttpResponseRedirect( reverse('login') + "?key=%s" % third_party_key) if not provider.get_create_policy: messages.error(request, provider.get_disabled_for_create_msg) return HttpResponseRedirect(reverse('login')) # TODO: this could be stored in session # TODO: create a management command to clean old PendingThirdPartyUser user, created = PendingThirdPartyUser.objects.get_or_create( third_party_identifier=userid, provider=provider_module, ) # update pending user for param, value in pending_user_params.iteritems(): setattr(user, param, value) user.info = json.dumps(provider_info) user.generate_token() user.save() extra_context['provider'] = provider.module extra_context['provider_title'] = provider.get_title_msg extra_context['token'] = user.token extra_context['signup_url'] = reverse('signup') + \ "?third_party_token=%s" % user.token extra_context['add_url'] = reverse('index') + \ "?key=%s#other-login-methods" % user.token extra_context['can_create'] = provider.get_create_policy extra_context['can_add'] = provider.get_add_policy return HttpResponseRedirect(extra_context['signup_url'])
im/signup.html or ``template_name`` keyword argument. """ extra_context = extra_context or {} if request.user.is_authenticated(): logger.info("%s already signed in, redirect to index", request.user.log_display) return HttpResponseRedirect(reverse('index')) provider = get_query(request).get('provider', 'local') try: auth.get_provider(provider) except auth.InvalidProvider, e: messages.error(request, e.message) return HttpResponseRedirect(reverse("signup")) if not auth.get_provider(provider).get_create_policy: logger.error("%s provider not available for signup", provider) raise PermissionDenied instance = None # user registered using third party provider third_party_token = request.REQUEST.get('third_party_token', None) unverified = None pending = None if third_party_token: # retreive third party entry. This was created right after the initial # third party provider handshake. pending = get_object_or_404(PendingThirdPartyUser, token=third_party_token)
def login(request, on_failure='im/login.html'): """ on_failure: the template name to render on login failure """ if request.method == 'GET': return HttpResponseRedirect(reverse('login')) was_limited = getattr(request, 'limited', False) form = LoginForm(data=request.POST, was_limited=was_limited, request=request) next = get_query(request).get('next', '') third_party_token = get_query(request).get('key', False) provider = auth.get_provider('local') if not form.is_valid(): if third_party_token: messages.info(request, provider.get_login_to_add_msg) return render_to_response(on_failure, { 'login_form': form, 'next': next, 'key': third_party_token }, context_instance=RequestContext(request)) # get the user from the cache user = form.user_cache provider = auth.get_provider('local', user) if not provider.get_login_policy: message = provider.get_login_disabled_msg messages.error(request, message) return HttpResponseRedirect(reverse('login')) message = None if not user: message = provider.get_authentication_failed_msg elif not user.is_active: message = user.get_inactive_message('local') elif not user.has_auth_provider('local'): # valid user logged in with no auth providers set, add local provider # and let him log in if not user.get_available_auth_providers(): user.add_auth_provider('local') else: message = _(astakos_messages.NO_LOCAL_AUTH) if message: messages.error(request, message) return render_to_response(on_failure, {'login_form': form}, context_instance=RequestContext(request)) response = prepare_response(request, user, next) if third_party_token: # use requests to assign the account he just authenticated with with # a third party provider account try: request.user.add_pending_auth_provider(third_party_token) except PendingThirdPartyUser.DoesNotExist: provider = auth.get_provider('local', request.user) messages.error(request, provider.get_add_failed_msg) provider = user.get_auth_provider('local') messages.success(request, provider.get_login_success_msg) response.set_cookie('astakos_last_login_method', 'local') return response
def login(request, template_name="im/login.html", on_failure='im/login.html', extra_context=None): """ on_failure: the template name to render on login failure """ if request.method == 'GET': extra_context = extra_context or {} third_party_token = request.GET.get('key', False) if third_party_token: messages.info(request, astakos_messages.AUTH_PROVIDER_LOGIN_TO_ADD) if request.user.is_authenticated(): return HttpResponseRedirect(reverse('landing')) extra_context["primary_provider"] = LOCAL_PROVIDER return render_response( template_name, login_form=LoginForm(request=request), context_instance=get_context(request, extra_context) ) was_limited = getattr(request, 'limited', False) form = LoginForm(data=request.POST, was_limited=was_limited, request=request) next = get_query(request).get('next', '') third_party_token = get_query(request).get('key', False) provider = auth.get_provider('local') if not form.is_valid(): if third_party_token: messages.info(request, provider.get_login_to_add_msg) return render_to_response( on_failure, {'login_form': form, 'next': next, 'key': third_party_token}, context_instance=get_context(request, primary_provider=LOCAL_PROVIDER)) # get the user from the cache user = form.user_cache provider = auth.get_provider('local', user) if not provider.get_login_policy: message = provider.get_login_disabled_msg messages.error(request, message) return HttpResponseRedirect(reverse('login')) message = None if not user: message = provider.get_authentication_failed_msg elif not user.is_active: message = user.get_inactive_message('local') elif not user.has_auth_provider('local'): # valid user logged in with no auth providers set, add local provider # and let him log in if not user.get_available_auth_providers(): user.add_auth_provider('local') else: message = _(astakos_messages.NO_LOCAL_AUTH) if message: messages.error(request, message) return render_to_response(on_failure, {'login_form': form}, context_instance=RequestContext(request)) response = prepare_response(request, user, next) if third_party_token: # use requests to assign the account he just authenticated with with # a third party provider account try: request.user.add_pending_auth_provider(third_party_token) except PendingThirdPartyUser.DoesNotExist: provider = auth.get_provider('local', request.user) messages.error(request, provider.get_add_failed_msg) provider = user.get_auth_provider('local') messages.success(request, provider.get_login_success_msg) response.set_cookie('astakos_last_login_method', 'local') provider.update_last_login_at() return response
def signup(request, template_name='im/signup.html', on_success='index', extra_context=None, activation_backend=None): """ Allows a user to create a local account. In case of GET request renders a form for entering the user information. In case of POST handles the signup. The user activation will be delegated to the backend specified by the ``activation_backend`` keyword argument if present, otherwise to the ``astakos.im.activation_backends.InvitationBackend`` if settings.ASTAKOS_INVITATIONS_ENABLED is True or ``astakos.im.activation_backends.SimpleBackend`` if not (see activation_backends); Upon successful user creation, if ``next`` url parameter is present the user is redirected there otherwise renders the same page with a success message. On unsuccessful creation, renders ``template_name`` with an error message. **Arguments** ``template_name`` A custom template to render. This is optional; if not specified, this will default to ``im/signup.html``. ``extra_context`` An dictionary of variables to add to the template context. ``on_success`` Resolvable view name to redirect on registration success. **Template:** im/signup.html or ``template_name`` keyword argument. """ extra_context = extra_context or {} if request.user.is_authenticated(): logger.info("%s already signed in, redirect to index", request.user.log_display) return HttpResponseRedirect(reverse('index')) provider = get_query(request).get('provider', 'local') if not auth.get_provider(provider).get_create_policy: logger.error("%s provider not available for signup", provider) raise PermissionDenied instance = None # user registered using third party provider third_party_token = request.REQUEST.get('third_party_token', None) unverified = None if third_party_token: # retreive third party entry. This was created right after the initial # third party provider handshake. pending = get_object_or_404(PendingThirdPartyUser, token=third_party_token) provider = pending.provider # clone third party instance into the corresponding AstakosUser instance = pending.get_user_instance() get_unverified = AstakosUserAuthProvider.objects.unverified # check existing unverified entries unverified = get_unverified(pending.provider, identifier=pending.third_party_identifier) if unverified and request.method == 'GET': messages.warning(request, unverified.get_pending_registration_msg) if unverified.user.moderated: messages.warning(request, unverified.get_pending_resend_activation_msg) else: messages.warning(request, unverified.get_pending_moderation_msg) # prepare activation backend based on current request if not activation_backend: activation_backend = activation_backends.get_backend() form_kwargs = {'instance': instance, 'request': request} if third_party_token: form_kwargs['third_party_token'] = third_party_token form = activation_backend.get_signup_form( provider, None, **form_kwargs) if request.method == 'POST': form = activation_backend.get_signup_form( provider, request.POST, **form_kwargs) if form.is_valid(): user = form.save(commit=False) # delete previously unverified accounts if AstakosUser.objects.user_exists(user.email): AstakosUser.objects.get_by_identifier(user.email).delete() # store_user so that user auth providers get initialized form.store_user(user, request) result = activation_backend.handle_registration(user) if result.status == \ activation_backend.Result.PENDING_MODERATION: # user should be warned that his account is not active yet status = messages.WARNING else: status = messages.SUCCESS message = result.message activation_backend.send_result_notifications(result, user) # commit user entry transaction.commit() if user and user.is_active: # activation backend directly activated the user # log him in next = request.POST.get('next', '') response = prepare_response(request, user, next=next) return response messages.add_message(request, status, message) return HttpResponseRedirect(reverse(on_success)) return render_response(template_name, signup_form=form, third_party_token=third_party_token, provider=provider, context_instance=get_context(request, extra_context))
def handle_third_party_login(request, provider_module, identifier, provider_info=None, affiliation=None, third_party_key=None): if not provider_info: provider_info = {} if not affiliation: affiliation = provider_module.title() next_redirect = request.GET.get( 'next', request.session.get('next_url', None)) if 'next_url' in request.session: del request.session['next_url'] third_party_request_params = get_third_party_session_params(request) from_login = third_party_request_params.get('from_login', False) switch_from = third_party_request_params.get('switch_from', False) provider_data = { 'affiliation': affiliation, 'info': provider_info } provider = auth.get_provider(provider_module, request.user, identifier, **provider_data) # an existing user accessed the view if request.user.is_authenticated(): if request.user.has_auth_provider(provider.module, identifier=identifier): return HttpResponseRedirect(reverse('edit_profile')) if provider.verified_exists(): provider.log("add failed (identifier exists to another user)") messages.error(request, provider.get_add_exists_msg) return HttpResponseRedirect(reverse('edit_profile')) # automatically add identifier provider to user if not switch_from and not provider.get_add_policy: # TODO: handle existing uuid message separately provider.log("user cannot add provider") messages.error(request, provider.get_add_failed_msg) return HttpResponseRedirect(reverse('edit_profile')) user = request.user if switch_from: existing_provider = \ request.user.auth_providers.active().get( pk=int(switch_from), module=provider_module).settings # this is not a provider removal so we don't not use # provider.remove_from_user. Use low level access to the provider # db instance. if not provider.verified_exists(): if provider.get_add_policy: existing_provider._instance.delete() existing_provider.log("removed") provider.add_to_user() provider.log("added") else: messages.error(request, provider.get_add_exists_msg) return HttpResponseRedirect(reverse('edit_profile')) messages.success(request, provider.get_switch_success_msg) return HttpResponseRedirect(reverse('edit_profile')) provider.add_to_user() provider.log("added") provider = user.get_auth_provider(provider_module, identifier) messages.success(request, provider.get_added_msg) return HttpResponseRedirect(reverse('edit_profile')) # astakos user exists ? try: user = AstakosUser.objects.get_auth_provider_user( provider_module, identifier=identifier, user__email_verified=True, ) except AstakosUser.DoesNotExist: # TODO: add a message ? redirec to login ? if astakos_messages.AUTH_PROVIDER_SIGNUP_FROM_LOGIN: messages.warning(request, astakos_messages.AUTH_PROVIDER_SIGNUP_FROM_LOGIN) raise if not third_party_key: third_party_key = get_pending_key(request) provider = user.get_auth_provider(provider_module, identifier) if user.is_active: if not provider.get_login_policy: messages.error(request, provider.get_login_disabled_msg) return HttpResponseRedirect(reverse('login')) # authenticate user response = prepare_response(request, user, next_redirect, 'renew' in request.GET) messages.success(request, provider.get_login_success_msg) add_pending_auth_provider(request, third_party_key, provider) response.set_cookie('astakos_last_login_method', provider_module) return response else: message = user.get_inactive_message(provider_module, identifier) messages.error(request, message) return HttpResponseRedirect(login_url(request))