Hmm, yes, Shibboleth will require some extra trickery in multiple views with redirects to the respective campus portal, etc. I've never done it myself, but I believe the API is pretty well documented.
-James
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/CAD4ANxVp4C2QcxAcY6Xui1bc6Z-hcV--sfOSEy%3DmcaW_w%2BpGHw%40mail.gmail.com.
"A pallet of authentication systems..."
Yep, you work for an educational entity, as do I. :-D
You can pursue the LegacyUser model as your custom user model. That would make your LegacyUser objects the 'local' Django users that have been referenced. Not sure about the name though, might indicate that there are CurrentUsers floating about the system.
Another option is to rename LegacyUser to something like AppUser, and then add an extra user_type field to the model for filtering/identification.
Or if you only have one type of user, you probably don't need to worry about it.
Keep us in the loop, I'd be interested in a high-level solution.
-James
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/81081481-BDD7-4247-BDA9-FBD9DDDF7BA7%40cederstrand.dk.
BTW, I reread you're last note. The references to AbstractBaseUser were meant for inheritance, meaning that your custom user would inherit all of the same properties and not need to redefine the wheel. You can't inherit from AbstractUser though because you are modifying the username field.
-James
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'school_auth.backends.SchoolModelBackend',
)
class SchoolModelBackend(object):
def authenticate(self, school_id=None, username=None, password=None, **kwargs):
if LegacyUser.validate(school=school_id, username=username, password=password):
# Password hash validation
try:
school_user = SchoolUser.objects.get(user__school=school_id, user__name=username)
except SchoolUser.DoesNotExist:
school_user = SchoolUser.objects.create_user(school_id=school_id, username=username)
# Annotate the user object with the path of the backend.
school_user.backend = "%s.%s" % (self.__class__.__module__, self.__class__.__name__)
return school_user
#
# if LDAP.validate(school=school_id, username=username, password=password):
# pass
# if WAYF.validate(school=school_id, username=username, password=password):
# pass
return None
def get_group_permissions(self, user_obj, obj=None):
raise NotImplementedError()
def get_all_permissions(self, user_obj, obj=None):
raise NotImplementedError()
def has_perm(self, user_obj, perm, obj=None):
if not user_obj.is_active:
return False
return perm in self.get_all_permissions(user_obj, obj)
def has_module_perms(self, user_obj, app_label):
if not user_obj.is_active:
return False
for perm in self.get_all_permissions(user_obj):
if perm[:perm.index('.')] == app_label:
return True
return False
def get_user(self, user_id):
try:
return SchoolUser.objects.get(pk=user_id)
except SchoolUser.DoesNotExist:
return None
class SchoolAuthenticationForm(AuthenticationForm):
school = forms.ModelChoiceField(queryset=School.objects.active(), empty_label=_('Please select a school'))
username = forms.CharField(max_length=40)
password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
class Meta:
model = LegacyUser
fields = ['school', 'name', 'password']
def __init__(self, request=None, *args, **kwargs):
"""
The 'request' parameter is set for custom auth use by subclasses.
The form data comes in via the standard 'data' kwarg.
"""
self.request = request
self.user_cache = None
super().__init__(*args, **kwargs)
# Set the label for the "username" field.
self.username_field = LegacyUser._meta.get_field(SchoolUser.USERNAME_FIELD)
if self.fields['username'].label is None:
self.fields['username'].label = capfirst(self.username_field.verbose_name)
def clean(self):
school = self.cleaned_data.get('school')
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
if school and username and password:
self.user_cache = SchoolModelBackend().authenticate(school_id=school.pk, username=username,
password=password)
if self.user_cache is None:
raise forms.ValidationError(
self.error_messages['invalid_login'],
code='invalid_login',
params={'username': self.username_field.verbose_name},
)
else:
self.confirm_login_allowed(self.user_cache)
return self.cleaned_data
class SchoolUserManager(BaseUserManager):
def create_user(self, school_id, username):
user = LegacyUser.objects.get(school=school_id, name=username)
school_user = self.model(user=user)
school_user.save()
return school_user
class SchoolUser(models.Model):
"""
Custom User model. We don't inherit AbstractBaseUser because we don't want the password field.
"""
USERNAME_FIELD = 'name'
user = models.OneToOneField(User, null=False)
last_login = models.DateTimeField(_('last login'), default=timezone.now)
objects = SchoolUserManager()
REQUIRED_FIELDS = []
@property
def school(self):
return self.user.school
def __str__(self):
return self.get_username()
def get_username(self):
return self.user.name
@property
def is_active(self):
return self.user.active
@property
def is_staff(self):
return self.user.is_admin()
def natural_key(self):
return self.user.school_id, self.user.name
@staticmethod
def is_anonymous():
return False
@staticmethod
def is_authenticated():
return True
def set_password(self, raw_password):
self.user.set_password(raw_password)
def check_password(self, raw_password):
return self.user.validate_password(raw_password)
def set_unusable_password(self):
pass
@staticmethod
def has_usable_password():
return True
def get_full_name(self):
return self.user.name
def get_short_name(self):
return self.user.name
def get_session_auth_hash(self):
"""
Returns an HMAC of the password field.
"""
key_salt = "django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash"
return salted_hmac(key_salt, self.user.password).hexdigest()
def email_user(self, subject, message, from_email=None, **kwargs):
"""
Sends an email to this User.
"""
from django.core.mail import send_mail
send_mail(subject, message, from_email, [self.email], **kwargs)
@property
def is_superuser(self):
# This can never be a Django superuser
return Falsedef get_group_permissions(self, obj=None):
raise NotImplementedError()
def get_all_permissions(self, obj=None):
raise NotImplementedError()
def has_perm(self, perm, obj=None):
[...] # My custom per-app permissions
def has_perms(self, perm_list, obj=None):
for perm in perm_list:
if not self.has_perm(perm, obj):
return False
return True
def has_module_perms(self, app_label):
[...] # My custom per-module permissionsschool_auth/urls.py:from django.conf.urls import urlfrom django.contrib.auth.views import login, logoutfrom .forms import SchoolAuthenticationFormurlpatterns = [
url(r'^login/$', login, kwargs={'authentication_form': SchoolAuthenticationForm, 'template_name': 'school_auth/login.html'}, name='school_auth.login'),
url(r'^logout/$', logout, name='school_auth.logout'),
]Thanks,Erik
Wow, nice. You are probably right in not inheriting from PermissionMixin and AbstractBaseUser and just re-implementing the needed functionality. I ended up doing the same thing for my custom user and auth backend as well. Not as convenient, but hey, it works right? ;-)
Bravo.
-James
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/BC66EBBA-A032-42B1-8DB0-F2CFFEE48BA6%40cederstrand.dk.