Fix day filter
[cds-indico.git] / indico / util / user.py
blobe80d905c8e77cd85fd15730bc4965ee6e989c66a
1 # This file is part of Indico.
2 # Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
4 # Indico is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3 of the
7 # License, or (at your option) any later version.
9 # Indico is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with Indico; if not, see <http://www.gnu.org/licenses/>.
17 from functools import wraps
19 from indico.core.db import db
20 from indico.util.decorators import smart_decorator
22 from MaKaC.common.cache import GenericCache
25 def iter_acl(acl):
26 """Iterates over an ACL in the most efficient order.
28 This first yields users, then local groups, and eventually
29 multipass groups as a remote group check is much more expensive
30 than checking if two users are the same (``==``) or if a user is
31 in a local group (SQL query).
33 :param acl: any iterable containing users/groups
34 """
35 return sorted(acl, key=lambda x: (x.is_group, not getattr(x, 'is_local', None)))
38 def retrieve_principals(iterable, allow_groups=True, legacy=True):
39 """Retrieves principal objects from ``(type, info)`` tuples.
41 See :func:`retrieve_principal` for details.
42 """
44 return filter(None, [retrieve_principal(x, allow_groups=allow_groups, legacy=legacy) for x in iterable])
47 def retrieve_principal(principal, allow_groups=True, legacy=True):
48 """Retrieves principal object from a `(type, id)` tuple.
50 Valid principal types are 'Avatar', 'User' and 'Group'.
52 :param principal: The principal (a tuple/list)
53 :param allow_groups: If group principals are allowed
54 :param legacy: If legacy wrappers or new objects should be returned.
55 """
56 from indico.modules.groups import GroupProxy
57 from indico.modules.groups.legacy import LocalGroupWrapper, LDAPGroupWrapper
58 from indico.modules.users import User
60 type_, id_ = principal
61 if type_ in {'Avatar', 'User'}:
62 user = User.get(int(id_))
63 if not user:
64 return None
65 return user.as_avatar if legacy else user
66 elif type_ == 'Group' and allow_groups:
67 if isinstance(id_, (int, basestring)): # legacy group
68 group = LocalGroupWrapper(id_) if unicode(id_).isdigit() else LDAPGroupWrapper(id_)
69 return group if legacy else group.group
70 else: # new group
71 provider, name_or_id = id_
72 group = GroupProxy(name_or_id, provider)
73 return group.as_legacy_group if legacy else group
74 else:
75 raise ValueError('Unexpected type: {}'.format(type_))
78 def principal_from_fossil(fossil, allow_pending=False, allow_groups=True, legacy=True):
79 """Gets a GroupWrapper or AvatarUserWrapper from a fossil"""
80 from indico.modules.groups import GroupProxy
81 from indico.modules.users import User
83 type_ = fossil['_type']
84 id_ = fossil['id']
85 if type_ == 'Avatar':
86 if isinstance(id_, int) or id_.isdigit():
87 # regular user
88 user = User.get(int(id_))
89 elif allow_pending:
90 data = GenericCache('pending_identities').get(id_)
91 if not data:
92 raise ValueError("Cannot find user '{}' in cache".format(id_))
94 data = {k: '' if v is None else v for (k, v) in data.items()}
96 # check if there is not already a pending user with that e-mail
97 user = User.find_first(email=data['email'], is_pending=True)
98 if not user:
99 user = User(first_name=data.get('first_name') or '', last_name=data.get('last_name') or '',
100 email=data['email'],
101 address=data.get('address', ''), phone=data.get('phone', ''),
102 affiliation=data.get('affiliation', ''), is_pending=True)
103 db.session.add(user)
104 db.session.flush()
105 else:
106 raise ValueError("Id '{}' is not a number and allow_pending=False".format(id_))
107 if user is None:
108 raise ValueError('User does not exist: {}'.format(id_))
109 return user.as_avatar if legacy else user
110 elif not allow_groups:
111 raise ValueError('Unexpected fossil type: {}'.format(type_))
112 elif type_ in {'LocalGroupWrapper', 'LocalGroup'}:
113 group = GroupProxy(int(id_))
114 if group.group is None:
115 raise ValueError('Local group does not exist: {}'.format(id_))
116 return group.as_legacy_group if legacy else group
117 elif type_ in {'LDAPGroupWrapper', 'MultipassGroup'}:
118 provider = fossil['provider']
119 group = GroupProxy(id_, provider)
120 if group.group is None:
121 raise ValueError('Multipass group does not exist: {}:{}'.format(provider, id_))
122 return group.as_legacy_group if legacy else group
123 else:
124 raise ValueError('Unexpected fossil type: {}'.format(type_))
127 @smart_decorator
128 def unify_user_args(fn, legacy=False):
129 """Decorator that unifies new/legacy user arguments.
131 Any argument of the decorated function that contains either a
132 :class:`AvatarUserWrapper` or a :class:`.User` will be converted
133 to the object type specified by the `legacy` argument.
135 :param legacy: If True, all arguments containing users will receive
136 an :class:`AvatarUserWrapper`. Otherwise, they will
137 receive a :class:`.User`.
139 from indico.modules.users import User
141 if legacy:
142 def _convert(arg):
143 return arg.as_avatar if isinstance(arg, User) else arg
144 else:
145 def _convert(arg):
146 from indico.modules.users.legacy import AvatarUserWrapper
147 return arg.user if isinstance(arg, AvatarUserWrapper) else arg
149 @wraps(fn)
150 def wrapper(*args, **kwargs):
151 args = map(_convert, args)
152 kwargs = {k: _convert(v) for k, v in kwargs.iteritems()}
153 return fn(*args, **kwargs)
155 return wrapper