Commit bc431e96 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/api/__init__.py
	snf-astakos-app/astakos/im/templates/im/astakosgroup_list.html
parents 8dfdb7bf 0125fccb
......@@ -107,7 +107,8 @@ class ActivationBackend(object):
def handle_activation(
self, user, activation_template_name='im/activation_email.txt',
greeting_template_name='im/welcome_email.txt',
admin_email_template_name='im/admin_notification.txt'
admin_email_template_name='im/account_creation_notification.txt',
helpdesk_email_template_name='im/helpdesk_notification.txt'
):
"""
If the user is already active returns immediately.
......@@ -123,10 +124,17 @@ class ActivationBackend(object):
if self._is_preaccepted(user):
if user.email_verified:
activate(user, greeting_template_name)
activate(
user,
greeting_template_name,
helpdesk_email_template_name
)
return RegistationCompleted()
else:
send_activation(user, activation_template_name)
send_activation(
user,
activation_template_name
)
return VerificationSent()
else:
send_account_creation_notification(
......
......@@ -169,18 +169,18 @@ def get_menu(request, with_extra_links=False, with_signout=True):
append(item(url=absolute(request, reverse('edit_profile')),
name="My account"))
if with_extra_links:
# if user.has_usable_password() and user.provider in ('local', ''):
# append(item(
# url=absolute(request, reverse('password_change')),
# name="Change password"))
# if EMAILCHANGE_ENABLED:
# append(item(
# url=absolute(request, reverse('email_change')),
# name="Change email"))
# if INVITATIONS_ENABLED:
# append(item(
# url=absolute(request, reverse('invite')),
# name="Invitations"))
if user.has_usable_password() and user.provider in ('local', ''):
append(item(
url=absolute(request, reverse('password_change')),
name="Change password"))
if EMAILCHANGE_ENABLED:
append(item(
url=absolute(request, reverse('email_change')),
name="Change email"))
if INVITATIONS_ENABLED:
append(item(
url=absolute(request, reverse('invite')),
name="Invitations"))
append(item(
url=absolute(request, reverse('group_list')),
......@@ -196,11 +196,13 @@ def get_menu(request, with_extra_links=False, with_signout=True):
# item(
# url=absolute(request,
# reverse('group_search')),
# name="Join"),)
))
# name="Join"),
# )
)
)
append(item(
url=absolute(request, reverse('resource_list')),
name="Report"))
url=absolute(request, reverse('resource_usage')),
name="Usage"))
append(item(
url=absolute(request, reverse('feedback')),
name="Feedback"))
......
......@@ -110,7 +110,7 @@ class AstakosCallpoint():
b = get_backend()
return b.list_users(filter)
def get_user_status(self, user_id):
def get_user_usage(self, user_id):
b = get_backend()
return b.get_resource_usage(user_id)
......
......@@ -200,7 +200,7 @@ class AstakosAPI(Specificator):
)
)
def get_user_status(
def get_user_usage(
self,
user_id=Nonnegative
):
......
......@@ -44,8 +44,7 @@ import logging
logger = logging.getLogger(__name__)
# providers registry
PROVIDERS = SortedDict()
_PROVIDERS = {}
PROVIDERS = {}
class AuthProviderBase(type):
......@@ -60,7 +59,7 @@ class AuthProviderBase(type):
newcls = super(AuthProviderBase, cls).__new__(cls, name, bases, dct)
if include:
_PROVIDERS[type_id] = newcls
PROVIDERS[type_id] = newcls
return newcls
......@@ -152,14 +151,23 @@ class ShibbolethAuthProvider(AuthProvider):
login_prompt_template = 'im/auth/shibboleth_login_prompt.html'
class TwitterAuthProvider(AuthProvider):
module = 'twitter'
title = _('Twitter')
description = _('Allows you to login to your account using your twitter '
'account')
add_prompt = _('Connect with your Twitter account.')
@property
def add_url(self):
return reverse('astakos.im.target.twitter.login')
login_template = 'im/auth/twitter_login.html'
login_prompt_template = 'im/auth/twitter_login_prompt.html'
def get_provider(id, user_obj=None, default=None):
"""
Return a provider instance from the auth providers registry.
"""
return PROVIDERS.get(id, default)(user_obj)
for module in astakos_settings.IM_MODULES:
if module in _PROVIDERS:
PROVIDERS[module] = _PROVIDERS[module]
......@@ -48,11 +48,14 @@ def im_modules(request):
return {'im_modules': IM_MODULES}
def auth_providers(request):
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]}
active_auth_providers = []
for module in IM_MODULES:
provider = AUTH_PROVIDERS.get(module, None)
if provider:
active_auth_providers.append(provider())
return {'auth_providers': active_auth_providers,
'master_auth_provider': active_auth_providers[0]}
def next(request):
return {'next': get_query(request).get('next', '')}
......
......@@ -163,7 +163,7 @@ def send_group_creation_notification(template_name, dictionary=None):
return _send_admin_notification(template_name, dictionary, subject=subject)
def send_helpdesk_notification(user, template_name='im/account_notification.txt'):
def send_helpdesk_notification(user, template_name='im/helpdesk_notification.txt'):
"""
Send email to DEFAULT_CONTACT_EMAIL to notify for a new user activation.
......@@ -275,8 +275,12 @@ def send_change_email(ec, request, email_template_name='registration/email_chang
logger.log(LOGGING_LEVEL, msg)
def activate(user, email_template_name='im/welcome_email.txt',
helpdesk_email_template_name='im/helpdesk_notification.txt', verify_email=False):
def activate(
user,
email_template_name='im/welcome_email.txt',
helpdesk_email_template_name='im/helpdesk_notification.txt',
verify_email=False
):
"""
Activates the specific user and sends email.
......
......@@ -639,7 +639,7 @@ class AstakosUser(User):
provider = self.add_auth_provider(pending.provider,
identifier=pending.third_party_identifier)
if email_re.match(pending.email) and pending.email != self.email:
if email_re.match(pending.email or '') and pending.email != self.email:
self.additionalmail_set.get_or_create(email=pending.email)
pending.delete()
......@@ -1061,7 +1061,6 @@ def astakosuser_post_save(sender, instance, created, **kwargs):
set_default_group(instance)
# TODO handle socket.error & IOError
register_users((instance,))
instance.renew_token()
def resource_post_save(sender, instance, created, **kwargs):
......@@ -1096,7 +1095,7 @@ def on_quota_disturbed(sender, users, **kwargs):
send_quota(users)
def renew_token(sender, instance, **kwargs):
if not instance.id:
if not instance.auth_token:
instance.renew_token()
post_syncdb.connect(fix_superusers)
......
......@@ -5,7 +5,7 @@ from django.conf import settings
AUTH_TOKEN_DURATION = getattr(settings, 'ASTAKOS_AUTH_TOKEN_DURATION', 30 * 24)
# Authenticate via Twitter.
TWITTER_KEY = getattr(settings, 'ASTAKOS_TWITTER_KEY', '')
TWITTER_TOKEN = getattr(settings, 'ASTAKOS_TWITTER_TOKEN', '')
TWITTER_SECRET = getattr(settings, 'ASTAKOS_TWITTER_SECRET', '')
DEFAULT_USER_LEVEL = getattr(settings, 'ASTAKOS_DEFAULT_USER_LEVEL', 4)
......
......@@ -160,6 +160,7 @@ def login(
extra_context['provider'] = 'shibboleth'
extra_context['token'] = user.token
extra_context['signup_url'] = reverse('shibboleth_signup', args=(user.token,))
return render_response(
template,
......
# Copyright 2011-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 django.http import HttpResponseBadRequest
from django.utils.translation import ugettext as _
from django.contrib import messages
from django.template import RequestContext
from django.views.decorators.http import require_http_methods
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.core.exceptions import ImproperlyConfigured
from django.shortcuts import get_object_or_404
from urlparse import urlunsplit, urlsplit
from astakos.im.util import prepare_response, get_context
from astakos.im.views import requires_anonymous, render_response, \
requires_auth_provider
from astakos.im.settings import ENABLE_LOCAL_ACCOUNT_MIGRATION, BASEURL
from astakos.im.models import AstakosUser, PendingThirdPartyUser
from astakos.im.forms import LoginForm
from astakos.im.activation_backends import get_backend, SimpleBackend
from astakos.im import settings
import astakos.im.messages as astakos_messages
import logging
logger = logging.getLogger(__name__)
import oauth2 as oauth
import cgi
consumer = oauth.Consumer(settings.TWITTER_TOKEN, settings.TWITTER_SECRET)
client = oauth.Client(consumer)
request_token_url = 'http://twitter.com/oauth/request_token'
access_token_url = 'http://twitter.com/oauth/access_token'
authenticate_url = 'http://twitter.com/oauth/authenticate'
@requires_auth_provider('twitter', login=True)
@require_http_methods(["GET", "POST"])
def login(request):
resp, content = client.request(request_token_url, "GET")
if resp['status'] != '200':
messages.error(request, 'Invalid Twitter response')
return HttpResponseRedirect(reverse('edit_profile'))
request.session['request_token'] = dict(cgi.parse_qsl(content))
url = "%s?oauth_token=%s" % (authenticate_url,
request.session['request_token']['oauth_token'])
return HttpResponseRedirect(url)
@requires_auth_provider('twitter', login=True)
@require_http_methods(["GET", "POST"])
def authenticated(
request,
template='im/third_party_check_local.html',
extra_context={}
):
if not 'request_token' in request.session:
messages.error(request, 'Twitter handshake failed')
return HttpResponseRedirect(reverse('edit_profile'))
token = oauth.Token(request.session['request_token']['oauth_token'],
request.session['request_token']['oauth_token_secret'])
client = oauth.Client(consumer, token)
# Step 2. Request the authorized access token from Twitter.
resp, content = client.request(access_token_url, "GET")
if resp['status'] != '200':
try:
del request.session['request_token']
except:
pass
messages.error(request, 'Invalid Twitter response')
return HttpResponseRedirect(reverse('edit_profile'))
access_token = dict(cgi.parse_qsl(content))
userid = access_token['user_id']
# an existing user accessed the view
if request.user.is_authenticated():
if request.user.has_auth_provider('twitter', identifier=userid):
return HttpResponseRedirect(reverse('edit_profile'))
# automatically add eppn provider to user
user = request.user
if not request.user.can_add_auth_provider('twitter',
identifier=userid):
messages.error(request, 'Account already exists.')
return HttpResponseRedirect(reverse('edit_profile'))
user.add_auth_provider('twitter', identifier=userid)
return HttpResponseRedirect(reverse('edit_profile'))
try:
# astakos user exists ?
user = AstakosUser.objects.get_auth_provider_user(
'twitter',
identifier=userid
)
if user.is_active:
# authenticate user
return prepare_response(request,
user,
request.GET.get('next'),
'renew' in request.GET)
elif not user.activation_sent:
message = _('Your request is pending activation')
#TODO: use astakos_messages
if not settings.MODERATION_ENABLED:
url = user.get_resend_activation_url()
msg_extra = _('<a href="%s">Resend activation email?</a>') % url
message = message + u' ' + msg_extra
messages.error(request, message)
return HttpResponseRedirect(reverse('login'))
else:
#TODO: use astakos_messages
message = _(u'Account disabled. Please contact support')
messages.error(request, message)
return HttpResponseRedirect(reverse('login'))
except AstakosUser.DoesNotExist, e:
#TODO: use astakos_messages
# eppn not stored in astakos models, create pending profile
user, created = PendingThirdPartyUser.objects.get_or_create(
third_party_identifier=userid,
provider='twitter',
)
# update pending user
user.affiliation = 'Twitter'
user.generate_token()
user.save()
extra_context['provider'] = 'twitter'
extra_context['token'] = user.token
extra_context['signup_url'] = reverse('twitter_signup', args=(user.token,))
return render_response(
template,
context_instance=get_context(request, extra_context)
)
@requires_auth_provider('twitter', login=True, create=True)
@require_http_methods(["GET"])
@requires_anonymous
def signup(
request,
token,
backend=None,
on_creation_template='im/third_party_registration.html',
extra_context={}):
extra_context = extra_context or {}
if not token:
#TODO: use astakos_messages
return HttpResponseBadRequest(_('Missing key parameter.'))
pending = get_object_or_404(PendingThirdPartyUser, token=token)
d = pending.__dict__
d.pop('_state', None)
d.pop('id', None)
d.pop('token', None)
d.pop('created', None)
user = AstakosUser(**d)
try:
backend = backend or get_backend(request)
except ImproperlyConfigured, e:
messages.error(request, e)
else:
extra_context['form'] = backend.get_signup_form(
provider='twitter',
instance=user
)
extra_context['provider'] = 'twitter'
extra_context['third_party_token'] = token
return render_response(
on_creation_template,
context_instance=get_context(request, extra_context)
)
......@@ -37,9 +37,10 @@ from celery.schedules import crontab
from functools import wraps
from astakos.im.endpoints.qh import send_quota
from astakos.im.endpoints.aquarium.producer import (report_credits_event,
from astakos.im.endpoints.aquarium.producer import (
report_credits_event,
report_user_event
)
)
from astakos.im.endpoints.aquarium.client import AquariumClient
import logging
......
......@@ -9,7 +9,7 @@ Is active: {{user.is_active}}
Level: {{user.level}}
Invitations: {{user.invitations}}
Για την ενεργοποίησή του μπορείτε να χρησιμοποιήσετε το command line εργαλείο snf-manage sendactivation
Για την ενεργοποίησή του μπορείτε να χρησιμοποιήσετε το command line εργαλείο snf-manage user-activation-send
--
The following account has been created:
......@@ -21,4 +21,4 @@ Is active: {{user.is_active}}
Level: {{user.level}}
Invitations: {{user.invitations}}
For its activation you can use the command line tool snf-manage sendactivation
For its activation you can use the command line tool snf-manage user-activation-send
--- A translation in English follows ---
{% if group_creation %}
Έχει δημιουργηθεί ο παρακάτω λογαριασμός:
{% else %}
Έχει ενεργοποιηθεί ο παρακάτω λογαριασμός:
{% endif %}
Email: {{user.email}}
First name: {{user.first_name}}
Last name: {{user.last_name}}
Is active: {{user.is_active}}
Level: {{user.level}}
Invitations: {{user.invitations}}
{% if group_creation %}
Για την ενεργοποίησή του μπορείτε να χρησιμοποιήσετε το command line εργαλείο snf-manage user_send_activation
{% endif %}
--
{% if group_creation %}
The following account has been created:
{% else %}
The following account has been activated:
{% endif %}
Email: {{user.email}}
First name: {{user.first_name}}
Last name: {{user.last_name}}
Is active: {{user.is_active}}
Level: {{user.level}}
Invitations: {{user.invitations}}
{% if group_creation %}
For its activation you can use the command line tool snf-manage user_send_activation
{% endif %}
\ No newline at end of file
......@@ -220,7 +220,7 @@
{% if not form %}
{% with page|concat:sorting as args %}
{% with q|paginate:args as page_obj %}
{% if page_obj.object_list %}
<div >
<form method="GET" class="minimal" action="#allGroups" id="mygroups">
<div class="form-row">
......@@ -234,7 +234,6 @@
</select>
</div>
</form>
<table class="alt-style complex" id="allGroups">
<caption>MY PROJECTS</caption>
<thead>
......@@ -347,7 +346,7 @@
</span>
</p>
</div>
{% endif%}
{% endwith %}
{% endwith %}
{% endif %}
......
LOGIN using 11
<a href="/im/login/shibboleth?{% ifnotequal next "" %}&next={{ next|urlencode }}{% endifnotequal %}{% ifnotequal code ""%}{% if next != "" %}&{% else %}?{% endif %}code={{ code }}{% endifnotequal %}"
alt="{{ o|title }}">{{ provider }}</a>
<h2><a href="/im/login/shibboleth">LOGIN OR CREATE ACCOUNT USING SHIBBOLETH</a></h2>
<br />LOGIN or SIGNUP using
<a href="/im/login/twitter?{% ifnotequal next "" %}&next={{ next|urlencode }}{% endifnotequal %}{% ifnotequal code ""%}{% if next != "" %}&{% else %}?{% endif %}code={{ code }}{% endifnotequal %}"
alt="{{ provider.get_title_display }}">{{ provider.get_title_display }}</a>
......@@ -14,7 +14,7 @@
<div class="form-stacked">
<h2><span>Already have an account?</span></h2>
<a href="{% url astakos.im.views.index %}?key={{ token }}">YES</a>
<a href="{% url shibboleth_signup token %}">NO</a>
<a href="{{ signup_url }}">NO</a>
</div>
{% endif %}
{% endblock %}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment