Commit 7b98277f authored by Olga Brani's avatar Olga Brani
Browse files

Merge branch 'devel-0.13' of https://code.grnet.gr/git/astakos into devel-0.13

Conflicts:
	snf-astakos-app/astakos/im/templates/im/astakosgroup_list.html
	snf-astakos-app/astakos/im/templates/im/profile.html
parents b17f9027 b80e056b
......@@ -41,8 +41,9 @@ from astakos.im.functions import (
send_activation, send_account_creation_notification, activate
)
from astakos.im.settings import (
INVITATIONS_ENABLED, MODERATION_ENABLED, RE_USER_EMAIL_PATTERNS
INVITATIONS_ENABLED, RE_USER_EMAIL_PATTERNS
)
from astakos.im import settings as astakos_settings
from astakos.im.forms import *
import astakos.im.messages as astakos_messages
......@@ -102,7 +103,7 @@ class ActivationBackend(object):
if provider == request.POST.get('provider', ''):
initial_data = request.POST
return globals()[formclass](initial_data, instance=instance, request=request)
def handle_activation(
self, user, activation_template_name='im/activation_email.txt',
greeting_template_name='im/welcome_email.txt',
......@@ -119,7 +120,7 @@ class ActivationBackend(object):
try:
if user.is_active:
return RegistationCompleted()
if self._is_preaccepted(user):
if user.email_verified:
activate(user, greeting_template_name)
......@@ -196,7 +197,7 @@ class InvitationsBackend(ActivationBackend):
return True
invitation = self.invitation
if not invitation:
return False
return not astakos_settings.MODERATION_ENABLED
if invitation.username == user.email and not invitation.is_consumed:
invitation.consume()
return True
......@@ -212,7 +213,7 @@ class SimpleBackend(ActivationBackend):
def _is_preaccepted(self, user):
if super(SimpleBackend, self)._is_preaccepted(user):
return True
if MODERATION_ENABLED:
if astakos_settings.MODERATION_ENABLED:
return False
return True
......@@ -236,4 +237,4 @@ class NotificationSent(ActivationResult):
class RegistationCompleted(ActivationResult):
def __init__(self):
message = _(astakos_messages.REGISTRATION_COMPLETED)
super(RegistationCompleted, self).__init__(message)
\ No newline at end of file
super(RegistationCompleted, self).__init__(message)
......@@ -203,12 +203,12 @@ def get_menu(request, with_extra_links=False, with_signout=True):
append(item(
url=absolute(request, reverse('feedback')),
name="Feedback"))
append(item(
url=absolute(request, reverse('billing')),
name="Billing"))
append(item(
url=absolute(request, reverse('timeline')),
name="Timeline"))
# append(item(
# url=absolute(request, reverse('billing')),
# name="Billing"))
# append(item(
# url=absolute(request, reverse('timeline')),
# name="Timeline"))
if with_signout:
append(item(
url=absolute(request, reverse('logout')),
......
......@@ -144,6 +144,10 @@ class DjangoBackend(BaseBackend):
u.permissions = permissions
u.policies = policies
u.extended_groups = groups
if not u.has_auth_provider('local'):
u.add_auth_provider('local')
return self._list(AstakosUser, filter=(u.id,))
@safe
......@@ -160,7 +164,7 @@ class DjangoBackend(BaseBackend):
except (ObjectDoesNotExist, IntegrityError), e:
append((service, resource, e))
return rejected
@safe
def remove_policies(self, user_id, policies=()):
user = self._lookup_user(user_id)
......@@ -187,7 +191,7 @@ class DjangoBackend(BaseBackend):
except IntegrityError, e:
append((p, e))
return rejected
@safe
def remove_permissions(self, user_id, permissions=()):
user = self._lookup_user(user_id)
......@@ -199,7 +203,7 @@ class DjangoBackend(BaseBackend):
except (ObjectDoesNotExist, IntegrityError), e:
append((p, e))
return rejected
@safe
def invite_users(self, senderid, recipients=()):
user = self._lookup_user(senderid)
......@@ -213,7 +217,7 @@ class DjangoBackend(BaseBackend):
except (IntegrityError, SMTPException), e:
append((email, e))
return rejected
@safe
def list_users(self, filter=()):
return self._list(AstakosUser, filter=filter)
......@@ -255,7 +259,7 @@ class DjangoBackend(BaseBackend):
# TODO return information for unknown ids
q = Service.objects.filter(id__in=ids)
q.delete()
@safe
def update_service(self, service_id, renew_token=False, **kwargs):
s = self._update_object(Service, service_id, save=False, **kwargs)
......@@ -283,14 +287,14 @@ class DjangoBackend(BaseBackend):
except Exception, e:
append((r, e))
return rejected
@safe
def remove_resources(self, service_id, ids=()):
# TODO return information for unknown ids
q = Resource.objects.filter(service__id=service_id,
id__in=ids)
q.delete()
@safe
def create_group(self, **kwargs):
policies = kwargs.pop('policies', ())
......@@ -302,6 +306,6 @@ class DjangoBackend(BaseBackend):
g.permissions = permissions
g.policies = policies
# g.members = members
# g.members = members
g.owners = owners
return self._list(AstakosGroup, filter=(g.id,))
......@@ -32,7 +32,6 @@
# or implied, of GRNET S.A.
from django.contrib.auth.backends import ModelBackend
from django.core.validators import email_re
from astakos.im.models import AstakosUser
from astakos.im.settings import LOGGING_LEVEL
......@@ -72,21 +71,18 @@ class EmailBackend(ModelBackend):
Used from ``astakos.im.forms.LoginForm`` to authenticate.
"""
def authenticate(self, username=None, password=None):
#If username is an email address, then try to pull it up
if email_re.search(username):
users = AstakosUser.objects.filter(email__iexact=username)
if not users:
return None
for user in users:
if user.check_password(password):
return user
else:
#We have a non-email address username we
#should try username
try:
user = AstakosUser.objects.get(username=username)
except AstakosUser.DoesNotExist:
return None
# First check whether a user having this email exists
users = AstakosUser.objects.filter(email__iexact=username)
for user in users:
if user.check_password(password):
return user
# Since no user has been found by email try with the username
try:
user = AstakosUser.objects.get(username=username)
except AstakosUser.DoesNotExist:
return None
if user.check_password(password):
return user
else:
......
......@@ -34,15 +34,18 @@
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from django.utils.datastructures import SortedDict
from astakos.im import settings
from django.conf import settings
from astakos.im import settings as astakos_settings
import logging
logger = logging.getLogger(__name__)
# providers registry
PROVIDERS = {}
PROVIDERS = SortedDict()
_PROVIDERS = {}
class AuthProviderBase(type):
......@@ -52,12 +55,12 @@ class AuthProviderBase(type):
type_id = dct.get('module')
if type_id:
include = True
if type_id in settings.IM_MODULES:
if type_id in astakos_settings.IM_MODULES:
dct['module_enabled'] = True
newcls = super(AuthProviderBase, cls).__new__(cls, name, bases, dct)
if include:
PROVIDERS[type_id] = newcls
_PROVIDERS[type_id] = newcls
return newcls
......@@ -73,8 +76,21 @@ class AuthProvider(object):
def __init__(self, user=None):
self.user = user
def __getattr__(self, key):
if not key.startswith('get_'):
return super(AuthProvider, self).__getattr__(key)
if key.endswith('_display') or key.endswith('template'):
attr = key.replace('_display', '').replace('get_','')
settings_attr = self.get_setting(attr.upper())
if not settings_attr:
return getattr(self, attr)
return settings_attr
else:
return super(AuthProvider, self).__getattr__(key)
def get_setting(self, name, default=None):
attr = 'AUTH_PROVIDER_%s_%s' % (self.module.upper(), name.upper())
attr = 'ASTAKOS_AUTH_PROVIDER_%s_%s' % (self.module.upper(), name.upper())
return getattr(settings, attr, default)
def is_available_for_login(self):
......@@ -93,23 +109,26 @@ class AuthProvider(object):
self.is_active())
def is_active(self):
return self.module in settings.IM_MODULES
return self.module in astakos_settings.IM_MODULES
class LocalAuthProvider(AuthProvider):
module = 'local'
title = _('Local password')
description = _('Create a local password for your account')
create_prompt = _('Create an account')
add_prompt = _('Create a local password for your account')
@property
def add_url(self):
return reverse('password_change')
add_description = _('Create a local password for your account')
login_template = 'auth/local_login_form.html'
add_template = 'auth/local_add_action.html'
one_per_user = True
login_template = 'im/auth/local_login_form.html'
login_prompt_template = 'im/auth/local_login_prompt.html'
signup_prompt_template = 'im/auth/local_signup_prompt.html'
details_tpl = _('You can login to your account using your'
' %(auth_backend)s password.')
......@@ -123,17 +142,14 @@ class ShibbolethAuthProvider(AuthProvider):
title = _('Academic credentials (Shibboleth)')
description = _('Allows you to login to your account using your academic '
'credentials')
add_prompt = _('Add academic credentials to your account.')
@property
def add_url(self):
return reverse('astakos.im.target.shibboleth.login')
add_description = _('Allows you to login to your account using your academic '
'credentials')
login_template = 'auth/shibboleth_login_form.html'
add_template = 'auth/shibboleth_add_action.html'
details_tpl = _('You can login to your account using your'
' shibboleth credentials. Shibboleth id: %(identifier)s')
login_template = 'im/auth/shibboleth_login.html'
login_prompt_template = 'im/auth/shibboleth_login_prompt.html'
def get_provider(id, user_obj=None, default=None):
......@@ -142,3 +158,8 @@ def get_provider(id, user_obj=None, default=None):
"""
return PROVIDERS.get(id, default)(user_obj)
for module in astakos_settings.IM_MODULES:
if module in _PROVIDERS:
PROVIDERS[module] = _PROVIDERS[module]
......@@ -48,8 +48,11 @@ def im_modules(request):
return {'im_modules': IM_MODULES}
def auth_providers(request):
return {'auth_providers': filter(lambda p:p.module_enabled,
AUTH_PROVIDERS.itervalues())}
active_auth_providers = filter(lambda p:p.module_enabled,
AUTH_PROVIDERS.itervalues())
auth_providers = map(lambda p: p(), active_auth_providers)
return {'auth_providers': auth_providers,
'master_auth_provider': auth_providers[0]}
def next(request):
return {'next': get_query(request).get('next', '')}
......
......@@ -31,6 +31,7 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from urlparse import urljoin
from random import random
from django import forms
from django.utils.translation import ugettext as _
......@@ -47,6 +48,8 @@ from django.utils.encoding import smart_str
from django.conf import settings
from django.forms.models import fields_for_model
from django.db import transaction
from django.utils.encoding import smart_unicode
from django.core import validators
from astakos.im.models import (
AstakosUser, EmailChange, AstakosGroup, Invitation, GroupKind,
......@@ -68,12 +71,16 @@ import astakos.im.messages as astakos_messages
import logging
import hashlib
import recaptcha.client.captcha as captcha
from random import random
import re
logger = logging.getLogger(__name__)
class StoreUserMixin(object):
DOMAIN_VALUE_REGEX = re.compile(
r'^(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.){0,126}(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?))$',
re.IGNORECASE
)
class StoreUserMixin(object):
@transaction.commit_on_success
def store_user(self, user, request):
user.save()
......@@ -205,7 +212,7 @@ class InvitedLocalUserCreationForm(LocalUserCreationForm):
def save(self, commit=True):
user = super(InvitedLocalUserCreationForm, self).save(commit=False)
user.update_invitations_level()
user.set_invitations_level()
user.email_verified = True
if commit:
user.save()
......@@ -393,8 +400,8 @@ class LoginForm(AuthenticationForm):
try:
super(LoginForm, self).clean()
except forms.ValidationError, e:
if self.user_cache is None:
raise
# if self.user_cache is None:
# raise
if self.request:
if not self.request.session.test_cookie_worked():
raise
......@@ -592,7 +599,14 @@ class AstakosGroupCreationForm(forms.ModelForm):
label="",
widget=forms.HiddenInput()
)
name = forms.URLField(widget=forms.TextInput(attrs={'placeholder': 'eg. foo.ece.ntua.gr'}), help_text="Name should be in the form of dns",)
name = forms.CharField(
validators=[validators.RegexValidator(
DOMAIN_VALUE_REGEX,
_(astakos_messages.DOMAIN_VALUE_ERR), 'invalid'
)],
widget=forms.TextInput(attrs={'placeholder': 'eg. foo.ece.ntua.gr'}),
help_text="Name should be in the form of dns"
)
moderation_enabled = forms.BooleanField(
help_text="Check if you want to approve members participation manually",
required=False,
......@@ -611,13 +625,12 @@ class AstakosGroupCreationForm(forms.ModelForm):
qd = args.pop(0).copy()
members_unlimited = qd.pop('members_unlimited', False)
members_uplimit = qd.pop('members_uplimit', None)
# max_participants = None if members_unlimited else members_uplimit
# qd['max_participants']= max_participants.pop(0) if max_participants else None
#substitue QueryDict
args.insert(0, qd)
super(AstakosGroupCreationForm, self).__init__(*args, **kwargs)
self.fields.keyOrder = ['kind', 'name', 'homepage', 'desc',
'issue_date', 'expiration_date',
'moderation_enabled', 'max_participants']
......@@ -666,7 +679,10 @@ class AstakosGroupCreationSummaryForm(forms.ModelForm):
label="",
widget=forms.HiddenInput()
)
name = forms.URLField()
name = forms.CharField(
widget=forms.TextInput(attrs={'placeholder': 'eg. foo.ece.ntua.gr'}),
help_text="Name should be in the form of dns"
)
moderation_enabled = forms.BooleanField(
help_text="Check if you want to approve members participation manually",
required=False,
......@@ -685,8 +701,6 @@ class AstakosGroupCreationSummaryForm(forms.ModelForm):
qd = args.pop(0).copy()
members_unlimited = qd.pop('members_unlimited', False)
members_uplimit = qd.pop('members_uplimit', None)
# max_participants = None if members_unlimited else members_uplimit
# qd['max_participants']= max_participants.pop(0) if max_participants else None
#substitue QueryDict
args.insert(0, qd)
......@@ -742,7 +756,7 @@ class AstakosGroupCreationSummaryForm(forms.ModelForm):
class AstakosGroupUpdateForm(forms.ModelForm):
class Meta:
model = AstakosGroup
fields = ( 'desc','homepage')
fields = ( 'desc','homepage', 'moderation_enabled')
class AddGroupMembersForm(forms.Form):
......@@ -807,30 +821,28 @@ class TimelineForm(forms.Form):
class AstakosGroupSortForm(forms.Form):
sort_by = forms.ChoiceField(label='Sort by',
choices=(('groupname', 'Name'),
('kindname', 'Type'),
('issue_date', 'Issue Date'),
('expiration_date',
'Expiration Date'),
('approved_members_num',
'Participants'),
('is_enabled', 'Status'),
('moderation_enabled', 'Moderation'),
('membership_status',
'Enrollment Status')
),
required=False)
sorting = forms.ChoiceField(
label='Sort by',
choices=(
('groupname', 'Name'),
('issue_date', 'Issue Date'),
('expiration_date', 'Expiration Date'),
('approved_members_num', 'Participants'),
('moderation_enabled', 'Moderation'),
('membership_status', 'Enrollment Status')
),
required=True
)
class MembersSortForm(forms.Form):
sort_by = forms.ChoiceField(label='Sort by',
choices=(('person__email', 'User Id'),
('person__first_name', 'Name'),
('date_joined', 'Status')
),
required=False)
sorting = forms.ChoiceField(
label='Sort by',
choices=(('person__email', 'User Id'),
('person__first_name', 'Name'),
('date_joined', 'Status')
),
required=True
)
class PickResourceForm(forms.Form):
resource = forms.ModelChoiceField(
......
......@@ -60,7 +60,6 @@ from astakos.im.settings import (
EMAIL_CHANGE_EMAIL_SUBJECT
)
import astakos.im.messages as astakos_messages
import astakos.im.models
logger = logging.getLogger(__name__)
......@@ -84,9 +83,11 @@ def logged(func, msg):
def login(request, user):
auth_login(request, user)
astakos.im.models.SessionCatalog(
from astakos.im.models import SessionCatalog
SessionCatalog(
session_key=request.session.session_key,
user=user).save()
user=user
).save()
login = logged(login, '%s logged in.')
logout = logged(auth_logout, '%s logged out.')
......@@ -155,7 +156,9 @@ def send_account_creation_notification(template_name, dictionary=None):
def send_group_creation_notification(template_name, dictionary=None):
group = dictionary.get('group', astakos.im.models.AstakosGroup())
group = dictionary.get('group')
if not group:
return
subject = _(GROUP_CREATION_SUBJECT) % {'group':group.get('name', '')}
return _send_admin_notification(template_name, dictionary, subject=subject)
......
# Copyright 2012 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from collections import defaultdict
from django.core.management.base import BaseCommand, CommandError
from django.db import models
from astakos.im.models import AstakosGroup
class Command(BaseCommand):
args = "<group name>"
help = "Show group info"
def handle(self, *args, **options):
if len(args) != 1:
raise CommandError("Please provide a group name")
group = AstakosGroup.objects
name_or_id = args[0].decode('utf8')
try:
if name_or_id.isdigit():
group = group.get(id=int(name_or_id))
else:
group = group.get(name=name_or_id)
except AstakosGroup.DoesNotExist:
field = 'id' if name_or_id.isdigit() else 'name'
msg = "Unknown user with %s '%s'" % (field, name_or_id)
raise CommandError(msg)
attrs = (
'id',
'name',
'kind',
'homepage',
'desc',
'owners',
'is_enabled',
'max_participants',
'moderation_enabled',
'creation_date',
'issue_date',
'expiration_date',
'approval_date',
'members',
'approved_members',
'quota',
'permissions'
)
for attr in attrs:
val = getattr(group, attr)
if isinstance(val, defaultdict):
val = dict(val)
if isinstance(val, models.Manager):