blob: 40f9b8b9d835d638a1b5fddc9c98d39b14e09eaa [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
from pylons import tmpl_context as c
from nose.tools import assert_equal
from ming.odm import ThreadLocalODMSession
from allura.tests import decorators as td
from allura.tests import TestController
from allura.lib.security import Credentials, all_allowed, has_access
from allura import model as M
from forgewiki import model as WM
def _deny(obj, role, perm):
obj.acl.insert(0, M.ACE.deny(role._id, perm))
ThreadLocalODMSession.flush_all()
Credentials.get().clear()
def _add_to_group(user, role):
user.project_role().roles.append(role._id)
ThreadLocalODMSession.flush_all()
Credentials.get().clear()
class TestSecurity(TestController):
validate_skip = True
@td.with_wiki
def test_anon(self):
self.app.get('/security/*anonymous/forbidden', status=302)
self.app.get('/security/*anonymous/needs_auth', status=302)
self.app.get('/security/*anonymous/needs_project_access_fail', status=302)
self.app.get('/security/*anonymous/needs_artifact_access_fail', status=302)
@td.with_wiki
def test_auth(self):
self.app.get('/security/test-admin/forbidden', status=403)
self.app.get('/security/test-admin/needs_auth', status=200)
self.app.get('/security/test-admin/needs_project_access_fail', status=403)
self.app.get('/security/test-admin/needs_project_access_ok', status=200)
# This should fail b/c test-user doesn't have the permission
self.app.get('/security/test-user/needs_artifact_access_fail', extra_environ=dict(username='test-user'), status=403)
# This should succeed b/c users with the 'admin' permission on a
# project implicitly have all permissions to everything in the project
self.app.get('/security/test-admin/needs_artifact_access_fail', status=200)
self.app.get('/security/test-admin/needs_artifact_access_ok', status=200)
@td.with_wiki
def test_all_allowed(self):
wiki = c.project.app_instance('wiki')
page = WM.Page.query.get(app_config_id=wiki.config._id)
admin_role = M.ProjectRole.by_name('Admin')
dev_role = M.ProjectRole.by_name('Developer')
member_role = M.ProjectRole.by_name('Member')
auth_role = M.ProjectRole.by_name('*authenticated')
anon_role = M.ProjectRole.by_name('*anonymous')
test_user = M.User.by_username('test-user')
assert_equal(all_allowed(wiki, admin_role), set(['configure', 'read', 'create', 'edit', 'unmoderated_post', 'post', 'moderate', 'admin', 'delete']))
assert_equal(all_allowed(wiki, dev_role), set(['read', 'create', 'edit', 'unmoderated_post', 'post', 'moderate', 'delete']))
assert_equal(all_allowed(wiki, member_role), set(['read', 'create', 'edit', 'unmoderated_post', 'post']))
assert_equal(all_allowed(wiki, auth_role), set(['read', 'post', 'unmoderated_post']))
assert_equal(all_allowed(wiki, anon_role), set(['read']))
assert_equal(all_allowed(wiki, test_user), set(['read', 'post', 'unmoderated_post']))
_add_to_group(test_user, member_role)
assert_equal(all_allowed(wiki, test_user), set(['read', 'create', 'edit', 'unmoderated_post', 'post']))
_deny(wiki, auth_role, 'unmoderated_post')
assert_equal(all_allowed(wiki, member_role), set(['read', 'create', 'edit', 'post']))
assert_equal(all_allowed(wiki, test_user), set(['read', 'create', 'edit', 'post']))
@td.with_wiki
def test_weird_allow_vs_deny(self):
'''
Test weird interaction of DENYs and ALLOWs in has_access.
'''
wiki = c.project.app_instance('wiki')
page = WM.Page.query.get(app_config_id=wiki.config._id)
auth_role = M.ProjectRole.by_name('*authenticated')
test_user = M.User.by_username('test-user')
# DENY for auth_role on page prevents chaining of auth_role for 'read'
# but anon_role still chains so ALLOW read for anon_role on wiki applies
# and authed user can still read. 'post' and 'unmoderated_post' don't
# match DENY rule so they chain as normal.
#
# This behavior seems wrong and should probably be fixed at some point,
# but this test is here to confirm that all_allowed matches has_access.
assert has_access(page, 'read', test_user)()
assert has_access(page, 'post', test_user)()
assert has_access(page, 'unmoderated_post', test_user)()
assert_equal(all_allowed(page, test_user), set(['read', 'post', 'unmoderated_post']))
_deny(page, auth_role, 'read')
assert has_access(page, 'read', test_user)()
assert has_access(page, 'post', test_user)()
assert has_access(page, 'unmoderated_post', test_user)()
assert_equal(all_allowed(page, test_user), set(['read', 'post', 'unmoderated_post']))
# Same thing applies to ALLOW vs DENY on the same ACL;
# an ALLOW on any applicable role overrides a DENY on any other.
#
# In this case it's reasonable since you might want to DENY read for
# *anon but ALLOW it for *auth. *anon ALLOW overriding *auth DENY is
# just an unfortunate side-effect of not having a true heiarchy of roles.
assert has_access(wiki, 'read', test_user)()
assert has_access(wiki, 'post', test_user)()
assert has_access(wiki, 'unmoderated_post', test_user)()
assert_equal(all_allowed(wiki, test_user), set(['read', 'post', 'unmoderated_post']))
_deny(wiki, auth_role, 'read')
assert has_access(wiki, 'read', test_user)()
assert has_access(wiki, 'post', test_user)()
assert has_access(wiki, 'unmoderated_post', test_user)()
assert_equal(all_allowed(wiki, test_user), set(['read', 'post', 'unmoderated_post']))