blob: 4ab98efe7dfb14e6597412e72064a9f434f134d7 [file] [log] [blame]
#!/usr/bin/env python2.5
#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Module containing the AccessChecker class that contains helper functions
for checking access.
"""
from django.utils.translation import ugettext
from google.appengine.api import users
from google.appengine.ext import db
from soc.logic import host as host_logic
from soc.logic.exceptions import LoginRequest
from soc.logic.exceptions import RedirectRequest
from soc.logic.exceptions import BadRequest
from soc.logic.exceptions import NotFound
from soc.logic.exceptions import AccessViolation
from soc.logic.exceptions import GDocsLoginRequest
from soc.models.org_app_record import OrgAppRecord
from soc.models.org_app_survey import OrgAppSurvey
from soc.models.request import INVITATION_TYPE
from soc.models.request import REQUEST_TYPE
from soc.models.user import User
from soc.views.helper.gdata_apis import oauth as oauth_helper
DEF_AGREE_TO_TOS = ugettext(
'You must agree to the <a href="%(tos_link)s">site-wide Terms of'
' Service</a> in your <a href="/user/edit_profile">User Profile</a>'
' in order to view this page.')
DEF_ALREADY_ADMIN = ugettext(
'You cannot be a organization administrator for %s to access this page.')
DEF_ALREADY_MENTOR = ugettext(
'You cannot be a mentor for %s to access this page.')
DEF_ALREADY_PARTICIPATING = ugettext(
'You cannot become a Student because you are already participating '
'in this program.')
DEF_ALREADY_PARTICIPATING_AS_STUDENT = ugettext(
'You cannot register as a %s since you are already a '
'student in %s.')
DEF_CANNOT_ACCESS_ORG_APP = ugettext(
'You do not have access to this organization application.')
DEF_CANNOT_UPDATE_ENTITY = ugettext(
'This %(name)s cannot be updated.')
DEF_DEV_LOGOUT_LOGIN = ugettext(
'Please <a href="%%(sign_out)s">sign out</a>'
' and <a href="%%(sign_in)s">sign in</a>'
' again as %(role)s to view this page.')
DEF_ENTITY_DOES_NOT_BELONG_TO_YOU = ugettext(
'This %(name)s does not belong to you.')
DEF_HAS_ALREADY_ROLE_FOR_ORG = ugettext(
'You already have %(role)s role for %(org)s.')
DEF_ID_BASED_ENTITY_INVALID = ugettext(
'%(model)s entity, whose id is %(id)s, is invalid at this time.')
DEF_ID_BASED_ENTITY_NOT_EXISTS = ugettext(
'%(model)s entity, whose id is %(id)s, is does not exist.')
DEF_INVITE_DOES_NOT_EXIST = ugettext(
'There is no invite with id %s.')
DEF_INVITE_CANNOT_BE_RESUBMITTED = ugettext(
'Only withdrawn invitations may be resubmitted.')
DEF_INVITE_CANNOT_BE_ACCESSED = ugettext(
'This invite cannot be accessed from this account.')
DEF_INVITE_CANNOT_BE_WITHDRAWN = ugettext(
'Only pending invitations may be withdrawn.')
DEF_INVITE_CANNOT_BE_RESPONDED = ugettext(
'This invite cannot be responded at this moment')
DEF_INVITE_ACCEPTED = ugettext(
'This invite has been accepted.')
DEF_INVITE_REJECTED = ugettext(
'This invite has been rejected.')
DEF_INVITE_WITHDRAWN = ugettext(
'This invite has been withdrawn.')
DEF_REQUEST_DOES_NOT_EXIST = ugettext(
'There is no request with id %s.')
DEF_REQUEST_CANNOT_BE_ACCESSED = ugettext(
'This request cannot be accessed from this account.')
DEF_ACCEPTED_REQUEST_CANNOT_BE_MANAGED = ugettext(
'This request cannot be managed because it is already been accepted.')
DEF_REQUEST_CANNOT_BE_WITHDRAWN = ugettext(
'This %s request cannot be withdrawn.')
DEF_REQUEST_CANNOT_BE_RESUBMITTED = ugettext(
'This %s request cannot be resubmitted.')
DEF_IS_NOT_STUDENT = ugettext(
'This page is inaccessible because you do not have a student role '
'in the program.')
DEF_HAS_NO_PROJECT = ugettext(
'This page is inaccessible because you do not have an accepted project '
'in the program.')
DEF_IS_STUDENT = ugettext(
'This page is inaccessible because you are registered as a student.')
DEF_NO_DOCUMENT = ugettext(
'The document was not found')
DEF_NO_LINK_ID = ugettext(
'Link ID should not be empty')
DEF_NO_ORG_APP = ugettext(
'The organization application for the program %s does not exist.')
DEF_NO_SLOT_TRANSFER = ugettext(
'This page is inaccessible at this time. It is accessible only after '
'the program administrator has made the slot allocations available and '
'before %s')
DEF_NO_SUCH_PROGRAM = ugettext(
'The url is wrong (no program was found).')
DEF_NO_SURVEY_ACCESS = ugettext (
'You cannot take this survey because this survey is not created for'
'your role in the program.')
DEF_NO_USER_LOGIN = ugettext(
'Please create <a href="/user/create">User Profile</a>'
' in order to view this page.')
DEF_NO_USER_PROFILE = ugettext(
'You must not have a User profile to visit this page.')
DEF_NO_USER = ugettext(
'User with the Link ID %s does not exist.')
DEF_NOT_ADMIN = ugettext(
'You need to be a organization administrator for %s to access this page.')
DEF_NOT_DEVELOPER = ugettext(
'You need to be a site developer to access this page.')
DEF_NOT_HOST = ugettext(
'You need to be a program administrator to access this page.')
DEF_NOT_MENTOR = ugettext(
'You need to be a mentor for %s to access this page.')
DEF_NOT_PARTICIPATING = ugettext(
'You are not participating in this program and have no access.')
DEF_NOT_PROPOSER = ugettext(
'You are not allowed to perform this action since you are not the'
'author(proposer) for this proposal.')
DEF_NOT_PUBLIC_DOCUMENT = ugettext(
'This document is not publically readable.')
DEF_NOT_VALID_INVITATION = ugettext(
'This is not a valid invitation.')
DEF_NOT_VALID_REQUEST = ugettext(
'This is not a valid request.')
DEF_ORG_DOES_NOT_EXISTS = ugettext(
'Organization, whose link_id is %(link_id)s, does not exist in '
'%(program)s.')
DEF_ORG_NOT_ACTIVE = ugettext(
'Organization %(name)s is not active in %(program)s.')
DEF_PAGE_INACTIVE = ugettext(
'This page is inactive at this time.')
DEF_PAGE_INACTIVE_BEFORE = ugettext(
'This page is inactive before %s')
DEF_PAGE_INACTIVE_OUTSIDE = ugettext(
'This page is inactive before %s and after %s.')
DEF_PROGRAM_NOT_VISIBLE = ugettext(
'This page is inaccessible because %s is not visible at this time.')
DEF_PROGRAM_NOT_RUNNING = ugettext(
'This page is inaccessible because %s is not running at this time.')
DEF_PROPOSAL_MODIFICATION_REQUEST = ugettext(
'If you would like to update this proposal, request your organization '
'to which this proposal belongs, to grant permission to modify the '
'proposal.')
DEF_PROPOSAL_NOT_PUBLIC = ugettext(
'This proposal is not made public, '
'and you are not the student who submitted the proposal, '
'nor are you a mentor for the organization it was submitted to.')
DEF_PROFILE_INACTIVE = ugettext(
'This page is inaccessible because your profile is inactive in '
'the program at this time.')
DEF_NO_PROFILE = ugettext(
'This page is inaccessible because you do not have a profile '
'in the program at this time.')
DEF_SCOPE_INACTIVE = ugettext(
'The scope for this request is not active.')
DEF_ID_BASED_ENTITY_NOT_EXISTS = ugettext(
'The requested %(model)s entity whose id is %(id)s does not exist.')
DEF_STATISTIC_DOES_NOT_EXIST = ugettext(
'The statistic whose name is %(key_name)s does not exist.')
DEF_KEYNAME_BASED_ENTITY_NOT_EXISTS = ugettext(
'The requested %(model)s entity whose keyname is %(key_name)s does not exist.')
DEF_KEYNAME_BASED_ENTITY_INVALID = ugettext(
'%(model)s entity, whose keyname is %(key_name)s, is invalid at this time.')
unset = object()
def isSet(value):
"""Returns true iff value is not unset.
"""
return value is not unset
class Mutator(object):
"""Helper class for access checking.
Mutates the data object as requested.
"""
def __init__(self, data):
self.data = data
self.unsetAll()
def unsetAll(self):
self.data.action = unset
self.data.can_respond = unset
self.data.document = unset
self.data.invited_user = unset
self.data.invited_profile = unset
self.data.invite = unset
self.data.key_name = unset
self.data.request_entity = unset
self.data.requester = unset
self.data.scope_path = unset
self.data.url_profile = unset
self.data.url_student_info = unset
self.data.url_user = unset
def documentKeyNameFromKwargs(self):
"""Returns the document key fields from kwargs.
Returns False if not all fields were supplied/consumed.
"""
from soc.models.document import Document
fields = []
kwargs = self.data.kwargs.copy()
prefix = kwargs.pop('prefix', None)
fields.append(prefix)
if prefix in ['gsoc_program', 'gsoc_org', 'gci_program', 'gci_org']:
fields.append(kwargs.pop('sponsor', None))
fields.append(kwargs.pop('program', None))
if prefix in ['gsoc_org', 'gci_org']:
fields.append(kwargs.pop('organization', None))
fields.append(kwargs.pop('document', None))
if any(kwargs.values()):
raise BadRequest("Unexpected value for document url")
if not all(fields):
raise BadRequest("Missing value for document url")
self.data.scope_path = '/'.join(fields[1:-1])
self.data.key_name = '/'.join(fields)
self.data.document = Document.get_by_key_name(self.data.key_name)
def userFromKwargs(self):
"""Retrieves a User from kwargs.
"""
key_name = self.data.kwargs['user']
self.data.url_user = User.get_by_key_name(key_name)
if not self.data.url_user:
raise NotFound('Requested user does not exist')
def profileFromKwargs(self, profile_model):
"""Retrieves a profile from kwargs.
Args:
profile_model: The datastore model class
"""
self.userFromKwargs()
fields = ['sponsor', 'program', 'user']
key_name = '/'.join(self.data.kwargs[i] for i in fields)
self.data.url_profile = profile_model.get_by_key_name(
key_name, parent=self.data.url_user)
if not self.data.url_profile:
raise NotFound('Requested user does not have a profile')
def studentFromKwargs(self):
self.profileFromKwargs()
self.data.url_student_info = self.data.url_profile.student_info
if not self.data.url_student_info:
raise NotFound('Requested user is not a student')
def canRespondForUser(self):
assert isSet(self.data.invited_user)
assert isSet(self.data.invite)
if self.data.invited_user.key() != self.data.user.key():
# org admins may see the invitations and can respond to requests
self.data.can_respond = self.data.invite.type == 'Request'
else:
# user that the entity refers to may only respond if it is a Request
self.data.can_respond = self.data.invite.type == 'Invitation'
def commentVisible(self):
assert isSet(self.data.url_user)
self.data.public_comments_visible = False
self.data.private_comments_visible = False
# if the user is not logged in, no comments can be made
if not self.data.user:
return
# if the current user is the proposer, he or she may access public comments
if self.data.user.key() == self.data.url_user.key():
self.data.public_comments_visible = True
return
# All the mentors and org admins from the organization may access public
# and private comments.
if self.data.mentorFor(self.data.proposal_org):
self.data.public_comments_visible = True
self.data.private_comments_visible = True
return
def host(self):
assert isSet(self.data.user)
self.data.host = host_logic.getHostForUser(self.data.user)
if self.data.host or self.data.user.host_for:
self.data.is_host = True
def orgAppFromKwargs(self, raise_not_found=True):
"""Sets the organization application in RequestData object.
Args:
raise_not_found: iff False do not send 404 response.
"""
assert self.data.program
q = OrgAppSurvey.all()
q.filter('program', self.data.program)
self.data.org_app = q.get()
if raise_not_found and not self.data.org_app:
raise NotFound(DEF_NO_ORG_APP % self.data.program.name)
def orgAppRecordIfIdInKwargs(self):
"""Sets the organization application in RequestData object.
"""
assert self.data.org_app
self.data.org_app_record = None
id = self.data.kwargs.get('id')
if id:
self.data.org_app_record = OrgAppRecord.get_by_id(int(id))
if not self.data.org_app_record:
raise NotFound(DEF_NO_ORG_APP % self.data.program.name)
class DeveloperMutator(Mutator):
def canRespondForUser(self):
self.data.can_respond = True
def commentVisible(self):
self.data.public_comments_visible = True
self.data.private_comments_visible = True
def hostFromKwargs(self):
"""Set the host entity for the given user in the kwargs.
"""
self.data.host_user_key = None
key_name = self.data.kwargs.get('link_id', '')
if not key_name:
self.host()
if self.data.is_host:
return
else:
raise NotFound(DEF_NO_LINK_ID)
user_key = db.Key.from_path('User', key_name)
if not user_key:
raise NotFound(DEF_NO_USER % key_name)
self.data.host_user_key = user_key
self.data.host = host_logic.getHostForUser(user_key)
class BaseAccessChecker(object):
"""Helper class for access checking.
Should contain all access checks that apply to both regular users
and developers.
"""
def __init__(self, data):
"""Initializes the access checker object.
"""
self.data = data
# TODO(daniel): get rid of it and use request_data directly
self.gae_user = data.gae_user
def fail(self, message):
"""Raises an AccessViolation with the specified message.
"""
raise AccessViolation(message)
def isLoggedIn(self):
"""Ensures that the user is logged in.
"""
if self.gae_user:
return
raise LoginRequest()
def isLoggedOut(self):
"""Ensures that the user is logged out.
"""
if not self.gae_user:
return
raise RedirectRequest(self.data.logout_url)
def isUser(self):
"""Checks if the current user has an User entity.
"""
self.isLoggedIn()
if self.data.user:
return
raise AccessViolation(DEF_NO_USER_LOGIN)
def isNotUser(self):
"""Checks if the current user does not have an User entity.
To perform this check a User must be logged in.
"""
self.isLoggedIn()
if not self.data.user:
return
raise AccessViolation(DEF_NO_USER_PROFILE)
def isDeveloper(self):
"""Checks if the current user is a Developer."""
if self.data.is_developer:
return
raise AccessViolation(DEF_NOT_DEVELOPER)
def hasProfile(self):
"""Checks if the user has a profile for the current program.
"""
self.isLoggedIn()
if self.data.profile:
return
raise AccessViolation(DEF_NO_PROFILE)
def isProfileActive(self):
"""Checks if the profile of the current user is active.
"""
self.hasProfile()
if self.data.profile.status == 'active':
return
raise AccessViolation(DEF_PROFILE_INACTIVE)
def isInvitePresent(self, invite_id):
"""Checks if the invite entity is not None.
"""
assert isSet(self.data.invite)
if self.data.invite is None:
raise AccessViolation(DEF_INVITE_DOES_NOT_EXIST % invite_id)
if self.data.invite.type != INVITATION_TYPE:
raise AccessViolation(DEF_INVITE_DOES_NOT_EXIST % invite_id)
def isRequestPresent(self, request_id):
"""Checks if the invite entity is not None.
"""
assert isSet(self.data.request_entity)
if self.data.request_entity is None:
raise AccessViolation(DEF_REQUEST_DOES_NOT_EXIST % request_id)
if self.data.request_entity.type != REQUEST_TYPE:
raise AccessViolation(DEF_REQUEST_DOES_NOT_EXIST % request_id)
def canAccessGoogleDocs(self):
"""Checks if user has a valid access token to access Google Documents.
"""
self.isUser()
access_token = oauth_helper.getAccessToken(self.data.user)
if not access_token: #TODO(orc.avs):check token is valid
next = self.data.request.get_full_path()
raise GDocsLoginRequest(next)
class DeveloperAccessChecker(BaseAccessChecker):
"""Helper class for access checking.
Allows most checks.
"""
def __getattr__(self, name):
return lambda *args, **kwargs: None
class AccessChecker(BaseAccessChecker):
"""Helper class for access checking.
"""
def isHost(self):
"""Checks whether the current user has a host role.
"""
self.isLoggedIn()
if self.data.is_host:
return
raise AccessViolation(DEF_NOT_HOST)
def isProgramRunning(self):
"""Checks whether the program is running now by making sure the current
data is between program start and end and the program is visible to
normal users.
"""
if not self.data.program:
raise NotFound(DEF_NO_SUCH_PROGRAM)
self.isProgramVisible()
if self.data.timeline.programActive():
return
raise AccessViolation(
DEF_PROGRAM_NOT_RUNNING % self.data.program.name)
def isProgramVisible(self):
"""Checks whether the program exists and is visible to the user.
Visible programs are either in the visible.
Programs are always visible to hosts.
"""
if not self.data.program:
raise NotFound(DEF_NO_SUCH_PROGRAM)
if self.data.program.status == 'visible':
return
try:
self.isHost()
return
except AccessViolation:
pass
raise AccessViolation(
DEF_PROGRAM_NOT_VISIBLE % self.data.program.name)
def acceptedOrgsAnnounced(self):
"""Checks if the accepted orgs have been announced.
"""
self.isProgramVisible()
if self.data.timeline.orgsAnnounced():
return
period = self.data.timeline.orgsAnnouncedOn()
raise AccessViolation(DEF_PAGE_INACTIVE_BEFORE % period)
def acceptedStudentsAnnounced(self):
"""Checks if the accepted students have been announced.
"""
self.isProgramVisible()
if self.data.timeline.studentsAnnounced():
return
period = self.data.timeline.studentsAnnouncedOn()
raise AccessViolation(DEF_PAGE_INACTIVE_BEFORE % period)
def canApplyNonStudent(self, role, edit_url):
"""Checks if the user can apply as a mentor or org admin.
"""
self.isLoggedIn()
if self.data.profile and not self.data.profile.student_info:
raise RedirectRequest(edit_url)
if role == 'org_admin' and self.data.timeline.beforeOrgSignupStart():
period = self.data.timeline.orgSignupStart()
raise AccessViolation(DEF_PAGE_INACTIVE_BEFORE % period)
if role == 'mentor' and not self.data.timeline.orgsAnnounced():
period = self.data.timeline.orgsAnnouncedOn()
raise AccessViolation(DEF_PAGE_INACTIVE_BEFORE % period)
if not self.data.profile:
return
raise AccessViolation(DEF_ALREADY_PARTICIPATING_AS_STUDENT % (
role, self.data.program.name))
def isActiveStudent(self):
"""Checks if the user is an active student.
"""
self.isProfileActive()
if self.data.student_info:
return
raise AccessViolation(DEF_IS_NOT_STUDENT)
def isStudentWithProject(self):
self.isActiveStudent()
if self.data.student_info.number_of_projects > 0:
return
raise AccessViolation(DEF_HAS_NO_PROJECT)
def notStudent(self):
"""Checks if the current user has a non-student profile.
"""
self.isProfileActive()
if not self.data.student_info:
return
raise AccessViolation(DEF_IS_STUDENT)
def notOrgAdmin(self):
"""Checks if the user is not an admin.
"""
self.isProfileActive()
assert isSet(self.data.organization)
if self.data.organization.key() not in self.data.profile.org_admin_for:
return
raise AccessViolation(DEF_ALREADY_ADMIN % self.data.organization.name)
def notMentor(self):
"""Checks if the user is not a mentor.
"""
self.isProfileActive()
assert isSet(self.data.organization)
if not self.data.mentorFor(self.data.organization):
return
raise AccessViolation(DEF_ALREADY_MENTOR % self.data.organization.name)
def isOrgAdmin(self):
"""Checks if the user is an org admin.
"""
assert isSet(self.data.organization)
self.isOrgAdminForOrganization(self.data.organization)
def isMentor(self):
"""Checks if the user is a mentor.
"""
assert isSet(self.data.organization)
self.isMentorForOrganization(self.data.organization)
def isOrgAdminForOrganization(self, org):
"""Checks if the user is an admin for the specified organiztaion.
"""
self.isProfileActive()
if self.data.orgAdminFor(org):
return
raise AccessViolation(DEF_NOT_ADMIN % org.name)
def isMentorForOrganization(self, org):
"""Checks if the user is a mentor for the specified organiztaion.
"""
self.isProfileActive()
if self.data.mentorFor(org):
return
raise AccessViolation(DEF_NOT_MENTOR % org.name)
def isOrganizationInURLActive(self):
"""Checks if the organization in URL exists and if its status is active.
"""
assert isSet(self.data.organization)
if not self.data.organization:
error_msg = DEF_ORG_DOES_NOT_EXISTS % {
'link_id': self.data.kwargs['organization'],
'program': self.data.program.name
}
raise AccessViolation(error_msg)
self.isOrganizationActive(self.data.organization)
def isOrganizationActive(self, organization):
"""Checks if the specified organization is active.
"""
if organization.status != 'active':
error_msg = DEF_ORG_NOT_ACTIVE % {
'name': organization.name,
'program': self.data.program.name
}
raise AccessViolation(error_msg)
def isProposalInURLValid(self):
"""Checks if the proposal in URL exists.
"""
assert isSet(self.data.proposal)
if not self.data.proposal:
error_msg = DEF_ID_BASED_ENTITY_NOT_EXISTS % {
'model': 'GSoCProposal',
'id': self.data.kwargs['id']
}
raise AccessViolation(error_msg)
if self.data.proposal.status == 'invalid':
error_msg = DEF_ID_BASED_ENTITY_INVALID % {
'model': 'GSoCProposal',
'id': self.data.kwargs['id'],
}
raise AccessViolation(error_msg)
def studentSignupActive(self):
"""Checks if the student signup period is active.
"""
self.isProgramVisible()
if self.data.timeline.studentSignup():
return
raise AccessViolation(DEF_PAGE_INACTIVE_OUTSIDE %
self.data.timeline.studentsSignupBetween())
def canStudentUpdateProposalPostSignup(self):
"""Checks if the student signup deadline has passed.
"""
self.isProgramVisible()
if (self.data.timeline.afterStudentSignupEnd() and
self.data.proposal.is_editable_post_deadline):
return
violation_message = '%s %s'% ((DEF_PAGE_INACTIVE_OUTSIDE %
self.data.timeline.studentsSignupBetween()),
DEF_PROPOSAL_MODIFICATION_REQUEST)
raise AccessViolation(violation_message)
def canRespondToInvite(self):
"""Checks if the current user can accept/reject the invitation.
"""
assert isSet(self.data.invite)
assert isSet(self.data.invited_user)
# check if the entity represents an invitation
if self.data.invite.type != 'Invitation':
raise AccessViolation(DEF_NOT_VALID_INVITATION)
# check if the entity can be responded
if self.data.invite.status not in ['pending', 'rejected']:
raise AccessViolation(DEF_NOT_VALID_INVITATION)
# check if the entity is addressed to the current user
if self.data.invited_user.key() != self.data.user.key():
error_msg = DEF_ENTITY_DOES_NOT_BELONG_TO_YOU % {
'name': 'request'
}
raise AccessViolation(error_msg)
# check if the user does not have this role
if self.data.invite.role == 'org_admin':
self.notOrgAdmin()
else:
self.notMentor()
def canResubmitInvite(self):
"""Checks if the current user can resubmit the invitation.
"""
assert isSet(self.data.invite)
# check if the entity represents an invitation
if self.data.invite.type != INVITATION_TYPE:
raise AccessViolation(DEF_INVITE_DOES_NOT_EXIST)
# only withdrawn requests may be resubmitted
if self.data.invite.status != 'withdrawn':
raise AccessViolation(DEF_NOT_VALID_REQUEST)
# check if the user is an admin for the organization
self.isOrgAdmin()
def canInviteBeResubmitted(self):
"""Checks if the invitation may be resubmitted.
"""
assert isSet(self.data.invite)
# check if the entity represents an invitation
if self.data.invite.type != INVITATION_TYPE:
raise AccessViolation(DEF_INVITE_DOES_NOT_EXIST)
# only withdrawn requests may be resubmitted
if self.data.invite.status != 'withdrawn':
raise AccessViolation(DEF_INVITE_CANNOT_BE_RESUBMITTED)
#TODO(dhans): actually it needs to be checked if the user has not accepted
# a request in the meantime.
def canInviteBeWithdrawn(self):
"""Checks if the invitation may be withdrawn.
"""
assert isSet(self.data.invite)
# check if the entity represents an invitation
if self.data.invite.type != INVITATION_TYPE:
raise AccessViolation(DEF_INVITE_DOES_NOT_EXIST)
# only pending requests may be withdrawn
if self.data.invite.status != 'pending':
raise AccessViolation(DEF_INVITE_CANNOT_BE_WITHDRAWN)
def canRespondInvite(self):
"""Checks if the current user may respond to invite entity.
"""
assert isSet(self.data.invite)
# check if the entity represents an invitation
if self.data.invite.type != INVITATION_TYPE:
raise AccessViolation(DEF_INVITE_DOES_NOT_EXIST)
# only the invited user may respond to the invitation
if self.data.user.key() != self.data.invite.user.key():
raise AccessViolation(DEF_INVITE_CANNOT_BE_ACCESSED)
def isInviteRespondable(self):
"""Checks if the invite may be responded at this moment.
"""
assert isSet(self.data.invite)
# only pending invites may be responded
if self.data.invite.status == 'accepted':
raise AccessViolation(DEF_INVITE_ACCEPTED)
if self.data.invite.status == 'rejected':
raise AccessViolation(DEF_INVITE_REJECTED)
if self.data.invite.status == 'withdrawn':
raise AccessViolation(DEF_INVITE_WITHDRAWN)
def canManageRequest(self):
"""Checks if the current user may manage the specified request.
"""
assert isSet(self.data.request_entity)
# only the author may manage their request
if self.data.user.key() != self.data.request_entity.user.key():
raise AccessViolation(DEF_REQUEST_CANNOT_BE_ACCESSED)
# accepted requests cannot be managed
if self.data.request_entity.status == 'accepted':
raise AccessViolation(DEF_ACCEPTED_REQUEST_CANNOT_BE_MANAGED)
def isRequestManageable(self):
"""Checks if the request may be managed with the specified action.
"""
assert isSet(self.data.request_entity)
current_status = self.data.request_entity.status
if 'withdraw' in self.data.POST and current_status != 'pending':
raise AccessViolation(DEF_REQUEST_CANNOT_BE_WITHDRAWN % current_status)
if 'resubmit' in self.data.POST and \
current_status not in ['rejected', 'withdrawn']:
raise AccessViolation(DEF_REQUEST_CANNOT_BE_RESUBMITTED % current_status)
def canRespondToRequest(self):
"""Checks if the current user can accept/reject the request.
"""
assert isSet(self.data.request_entity)
assert isSet(self.data.requester)
# check if the entity represents an invitation
if self.data.request_entity.type != 'Request':
raise AccessViolation(DEF_NOT_VALID_REQUEST)
# check if the entity can be responded
if self.data.request_entity.status not in ['pending', 'rejected']:
raise AccessViolation(DEF_NOT_VALID_REQUEST)
# check if the user is an admin for the organization
self.isOrgAdmin()
def canResubmitRequest(self):
"""Checks if the current user can resubmit the request.
"""
assert isSet(self.data.request_entity)
assert isSet(self.data.requester)
# check if the entity represents an invitation
if self.data.request_entity.type != 'Request':
raise AccessViolation(DEF_NOT_VALID_REQUEST)
# only withdrawn requests may be resubmitted
if self.data.request_entity.status != 'withdrawn':
raise AccessViolation(DEF_NOT_VALID_REQUEST)
# check if the request belongs to the current user
if self.data.requester.key() != self.data.user.key():
error_msg = DEF_ENTITY_DOES_NOT_BELONG_TO_YOU % {
'name': 'request'
}
raise AccessViolation(error_msg)
def canViewInvite(self):
"""Checks if the current user can see the invitation.
"""
assert isSet(self.data.organization)
assert isSet(self.data.invite)
assert isSet(self.data.invited_user)
self._canAccessRequestEntity(
self.data.invite, self.data.invited_user, self.data.organization)
def canViewRequest(self):
"""Checks if the current user can see the request.
"""
assert isSet(self.data.organization)
assert isSet(self.data.request_entity)
assert isSet(self.data.requester)
self._canAccessRequestEntity(
self.data.request_entity, self.data.requester, self.data.organization)
def _canAccessRequestEntity(self, entity, user, org):
"""Checks if the current user is allowed to access a Request entity.
Args:
entity: an entity which belongs to Request model
user: user entity that the Request refers to
org: organization entity that the Request refers to
"""
# check if the entity is addressed to the current user
if user.key() != self.data.user.key():
# check if the current user is an org admin for the organization
self.isOrgAdmin()
def canAccessProposalEntity(self):
"""Checks if the current user is allowed to access a Proposal entity.
"""
assert isSet(self.data.proposal)
assert isSet(self.data.proposal_org)
assert isSet(self.data.url_user)
# if the proposal is public, everyone may access it
if self.data.proposal.is_publicly_visible:
return
if not self.data.user:
raise AccessViolation(DEF_PROPOSAL_NOT_PUBLIC)
self.isProfileActive()
# if the current user is the proposer, he or she may access it
if self.data.user.key() == self.data.url_user.key():
return
# all the mentors and org admins from the organization may access it
if self.data.mentorFor(self.data.proposal_org):
return
raise AccessViolation(DEF_PROPOSAL_NOT_PUBLIC)
def canEditDocument(self):
self.isHost()
def canViewDocument(self):
"""Checks if the specified user can see the document.
"""
assert isSet(self.data.document)
if not self.data.document:
raise NotFound(DEF_NO_DOCUMENT)
self.isProgramVisible()
if self.data.document.read_access == 'public':
return
raise AccessViolation(DEF_NOT_PUBLIC_DOCUMENT)
def isProposer(self):
"""Checks if the current user is the author of the proposal.
"""
self.isProgramVisible()
self.isProfileActive()
assert isSet(self.data.proposer)
if self.data.proposer.key() == self.data.profile.key():
return
raise AccessViolation(DEF_NOT_PROPOSER)
def isSlotTransferActive(self):
"""Checks if the slot transfers are active at the time.
"""
assert isSet(self.data.program)
assert isSet(self.data.timeline)
if (self.data.program.allocations_visible and
self.data.timeline.beforeStudentsAnnounced()):
return
raise AccessViolation(DEF_NO_SLOT_TRANSFER % (
self.data.timeline.studentsAnnouncedOn()))
def isProjectInURLValid(self):
"""Checks if the project in URL exists.
"""
assert isSet(self.data.project)
if not self.data.project:
error_msg = DEF_ID_BASED_ENTITY_NOT_EXISTS % {
'model': 'GSoCProject',
'id': self.data.kwargs['id']
}
raise AccessViolation(error_msg)
if self.data.project.status == 'invalid':
error_msg = DEF_ID_BASED_ENTITY_INVALID % {
'model': 'GSoCProject',
'id': self.data.kwargs['id'],
}
raise AccessViolation(error_msg)
def canStudentUpdateProject(self):
"""Checks if the student can edit the project details.
"""
assert isSet(self.data.program)
assert isSet(self.data.timeline)
assert isSet(self.data.project)
assert isSet(self.data.project_owner)
self.isProjectInURLValid()
# check if the timeline allows updating project
self.isProgramVisible()
self.acceptedStudentsAnnounced()
# check if the project belongs to the current user
expected_profile_key = self.data.project.parent_key()
if expected_profile_key != self.data.profile.key():
error_msg = DEF_ENTITY_DOES_NOT_BELONG_TO_YOU % {
'name': 'project'
}
raise AccessViolation(error_msg)
# check if the status allows the project to be updated
if self.data.project.status in ['invalid', 'withdrawn', 'failed']:
raise AccessViolation(DEF_CANNOT_UPDATE_ENTITY % {
'name': 'project'
})
def isSurveyActive(self, survey, show_url=None):
"""Checks if the survey in the request data is active.
Args:
survey: the survey entity for which the access must be checked
show_url: The survey show page url to which the user must be
redirected to
"""
assert isSet(self.data.program)
assert isSet(self.data.timeline)
if self.data.timeline.surveyPeriod(survey):
return
if self.data.timeline.afterSurveyEnd(survey) and show_url:
raise RedirectRequest(show_url)
raise AccessViolation(DEF_PAGE_INACTIVE_OUTSIDE %
(survey.survey_start, survey.survey_end))
def canUserTakeSurvey(self, survey, taking_access='user'):
"""Checks if the user with the given profile can take the survey.
Args:
survey: the survey entity for which the access must be checked
"""
assert isSet(self.data.program)
assert isSet(self.data.timeline)
self.isProjectInURLValid()
if taking_access == 'student':
self.isActiveStudent()
return
elif taking_access == 'org':
assert isSet(self.data.organization)
self.isMentor()
return
elif taking_access == 'user':
self.isUser()
return
raise AccessViolation(DEF_NO_SURVEY_ACCESS)
def isStatisticValid(self):
"""Checks if the URL refers to an existing statistic.
"""
assert isSet(self.data.statistic)
# check if the statistic exist
if not self.data.statistic:
error_msg = DEF_STATISTIC_DOES_NOT_EXIST % {
'key_name': self.data.kwargs['id']
}
raise AccessViolation(error_msg)
def canRetakeOrgApp(self):
"""Checks if the user can edit the org app record.
"""
assert isSet(self.data.org_app_record)
self.isUser()
allowed_keys = [self.data.org_app_record.main_admin.key(),
self.data.org_app_record.backup_admin.key()]
if self.data.user.key() not in allowed_keys:
raise AccessViolation(DEF_CANNOT_ACCESS_ORG_APP)
def canViewOrgApp(self):
"""Checks if the user can view the org app record. Only the org admins and
hosts are allowed to view.
"""
assert isSet(self.data.org_app_record)
try:
self.canRetakeOrgApp()
return
except AccessViolation:
pass
self.isHost()