Add missing dots in docstrings, proper sorting of imports and small docstring typo...
[Melange.git] / app / soc / views / helper / access.py
blobb25d767736825082c591f4ed76d7f368abe4e5e2
1 #!/usr/bin/python2.5
3 # Copyright 2008 the Melange authors.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Access control helper.
19 The functions in this module can be used to check access control
20 related requirements. When the specified required conditions are not
21 met, an exception is raised. This exception contains a views that
22 either prompts for authentication, or informs the user that they
23 do not meet the required criteria.
24 """
26 __authors__ = [
27 '"Todd Larsen" <tlarsen@google.com>',
28 '"Sverre Rabbelier" <sverre@rabbelier.nl>',
29 '"Pawel Solyga" <pawel.solyga@gmail.com>',
33 from google.appengine.api import users
35 from django.utils.translation import ugettext_lazy
37 from soc.logic import accounts
38 from soc.logic.models import host as host_logic
39 from soc.logic.models import user as user_logic
40 from soc.logic.models import request as request_logic
41 from soc.views import helper
42 from soc.views import out_of_band
45 DEF_NO_USER_LOGIN_MSG_FMT = ugettext_lazy(
46 'Please create <a href="/user/edit">User Profile</a>'
47 ' in order to view this page.')
49 DEF_DEV_LOGOUT_LOGIN_MSG_FMT = ugettext_lazy(
50 'Please <a href="%%(sign_out)s">sign out</a>'
51 ' and <a href="%%(sign_in)s">sign in</a>'
52 ' again as %(role)s to view this page.')
54 DEF_PAGE_DENIED_MSG = ugettext_lazy(
55 'Access to this page has been restricted')
57 DEF_LOGOUT_MSG_FMT = ugettext_lazy(
58 'Please <a href="%(sign_out)s">sign out</a> in order to view this page')
61 def checkAccess(access_type, request, rights):
62 """Runs all the defined checks for the specified type.
64 Args:
65 access_type: the type of request (such as 'list' or 'edit')
66 request: the Django request object
67 rights: a dictionary containing access check functions
69 Rights usage:
70 The rights dictionary is used to check if the current user is allowed
71 to view the page specified. The functions defined in this dictionary
72 are always called with the django request object as argument. On any
73 request, regardless of what type, the functions in the 'any_access' value
74 are called. If the specified type is not in the rights dictionary, all
75 the functions in the 'unspecified' value are called. When the specified
76 type _is_ in the rights dictionary, all the functions in that access_type's
77 value are called.
79 Returns:
80 True: If all the required access checks have been made successfully
81 False: If a check failed, in this case self._response will contain
82 the response provided by the failed access check.
83 """
85 # Call each access checker
86 for check in rights['any_access']:
87 check(request)
89 if access_type not in rights:
90 for check in rights['unspecified']:
91 # No checks defined, so do the 'generic' checks and bail out
92 check(request)
93 return
95 for check in rights[access_type]:
96 check(request)
99 def allow(request):
100 """Never returns an alternate HTTP response.
102 Args:
103 request: a Django HTTP request
106 return
108 def deny(request):
109 """Returns an alternate HTTP response.
111 Args:
112 request: a Django HTTP request
114 Returns:
115 a subclass of django.http.HttpResponse which contains the
116 alternate response that should be returned by the calling view.
119 context = {}
120 context['title'] = 'Access denied'
122 raise out_of_band.AccessViolation(DEF_PAGE_DENIED_MSG, context=context)
125 def checkIsLoggedIn(request):
126 """Returns an alternate HTTP response if Google Account is not logged in.
128 Args:
129 request: a Django HTTP request
131 Raises:
132 AccessViolationResponse: if the required authorization is not met
134 Returns:
135 None if the user is logged in, or a subclass of
136 django.http.HttpResponse which contains the alternate response
137 that should be returned by the calling view.
140 if users.get_current_user():
141 return
143 raise out_of_band.LoginRequest()
146 def checkNotLoggedIn(request):
147 """Returns an alternate HTTP response if Google Account is not logged in.
149 Args:
150 request: a Django HTTP request
152 Raises:
153 AccessViolationResponse: if the required authorization is not met
155 Returns:
156 None if the user is logged in, or a subclass of
157 django.http.HttpResponse which contains the alternate response
158 that should be returned by the calling view.
161 if not users.get_current_user():
162 return
164 raise out_of_band.LoginRequest(message_fmt=DEF_LOGOUT_MSG_FMT)
167 def checkIsUser(request):
168 """Returns an alternate HTTP response if Google Account has no User entity.
170 Args:
171 request: a Django HTTP request
173 Raises:
174 AccessViolationResponse: if the required authorization is not met
176 Returns:
177 None if User exists for a Google Account, or a subclass of
178 django.http.HttpResponse which contains the alternate response
179 should be returned by the calling view.
182 checkIsLoggedIn(request)
184 user = user_logic.logic.getForFields(
185 {'account': users.get_current_user()}, unique=True)
187 if user:
188 return
190 raise out_of_band.LoginRequest(message_fmt=DEF_NO_USER_LOGIN_MSG_FMT)
193 def checkIsDeveloper(request):
194 """Returns an alternate HTTP response if Google Account is not a Developer.
196 Args:
197 request: a Django HTTP request
199 Raises:
200 AccessViolationResponse: if the required authorization is not met
202 Returns:
203 None if Google Account is logged in and logged-in user is a Developer,
204 or a subclass of django.http.HttpResponse which contains the alternate
205 response should be returned by the calling view.
208 checkIsUser(request)
210 if accounts.isDeveloper(account=users.get_current_user()):
211 return
213 login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
214 'role': 'a site developer '}
216 raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
219 def checkIsHost(request):
220 """Returns an alternate HTTP response if Google Account has no Host entity
221 for the specified program.
223 Args:
224 request: a Django HTTP request
226 Raises:
227 AccessViolationResponse: if the required authorization is not met
229 Returns:
230 None if Host exists for the specified program, or a subclass of
231 django.http.HttpResponse which contains the alternate response
232 should be returned by the calling view.
235 try:
236 # if the current user is a developer we allow access
237 checkIsInvited(request)
238 return
239 except out_of_band.Error:
240 pass
242 checkIsUser(request)
244 user = user_logic.logic.getForFields(
245 {'account': users.get_current_user()}, unique=True)
247 host = host_logic.logic.getForFields(
248 {'user': user}, unique=True)
250 if host:
251 return
253 login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
254 'role': 'a host '}
256 raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
259 def checkIsInvited(request):
260 """Returns an alternate HTTP response if Google Account has no Host entity
261 for the specified program.
263 Args:
264 request: a Django HTTP request
266 Raises:
267 AccessViolationResponse: if the required authorization is not met
269 Returns:
270 None if Host exists for the specified program, or a subclass of
271 django.http.HttpResponse which contains the alternate response
272 should be returned by the calling view.
275 try:
276 # if the current user is a developer we allow access
277 checkIsDeveloper(request)
278 return
279 except out_of_band.Error:
280 pass
282 checkIsUser(request)
284 login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
285 'role': 'a host for this program'}
287 splitpath = request.path.split('/')
288 splitpath = splitpath[1:] # cut off leading ''
290 if len(splitpath) < 4:
291 # TODO: perhaps this needs a better explanation?
292 deny(request)
294 role = splitpath[0]
295 group_id = splitpath[2]
296 user_id = splitpath[3]
298 user = user_logic.logic.getForFields(
299 {'account': users.get_current_user()}, unique=True)
301 if user_id != user.link_id:
302 # TODO: perhaps this needs a better explanation?
303 deny(request)
305 properties = {
306 'link_id': user_id,
307 'role': role,
308 'scope_path': group_id,
309 'group_accepted': True,
312 request = request_logic.logic.getForFields(properties, unique=True)
314 if request:
315 return
317 raise out_of_band.LoginRequest(message_fmt=login_message_fmt)
320 def checkIsDocumentPublic(request):
321 """Checks whether a document is public.
323 Args:
324 request: a Django HTTP request
327 # TODO(srabbelier): A proper check needs to be done to see if the document
328 # is public or not, probably involving analysing it's scope or such.
329 allow(request)