Skip to content

Commit

Permalink
Add a stub policy that authenticates against a fixed password
Browse files Browse the repository at this point in the history
To use the stub policy, set "openstax_accounts.stub = true" in
development.ini

The default username is "test" and the default password is "password",
but can be overridden by setting "openstax_accounts.stub.username" and
"openstax_accounts.stub.password".  The profile can be set in
"openstax_accounts.stub.profile" in json format.

Close openstax#2
  • Loading branch information
karenc committed Jun 5, 2014
1 parent 35fe0a5 commit 1bf6710
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 7 deletions.
19 changes: 16 additions & 3 deletions development.ini.example
@@ -1,13 +1,26 @@
[app:main]
use = egg:openstax-accounts

# set openstax_accounts.stub to false to use an openstax/accounts instance with the settings below
openstax_accounts.stub = true
# Users for the stub authentication policy
# format: <username>,<password>,<optional json profile>
openstax_accounts.stub.users =
aaron,password,{"first_name": "Aaron", "last_name": "Andersen"}
babara,password
caitlin,password
dale,password
earl,password
fabian,password

openstax_accounts.login_path = /login
openstax_accounts.callback_path = /callback
openstax_accounts.logout_path = /logout

openstax_accounts.server_url = https://localhost:3000/
openstax_accounts.application_id = 940128529654aaaa8826654d3da1b992d815cd4bc2563e13dc66e6b18728dedf
openstax_accounts.application_secret = 226af66711708a044620199daf68a95a201e893c9f9cff2d46a5437520dda21e
openstax_accounts.application_url = http://localhost:8000/
openstax_accounts.login_path = /login
openstax_accounts.callback_path = /callback
openstax_accounts.logout_path = /logout

[server:main]
use = egg:waitress#main
Expand Down
14 changes: 14 additions & 0 deletions openstax_accounts/__init__.py
@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-

from pyramid.settings import asbool

def main(config):
settings = config.registry.settings

if asbool(settings.get('openstax_accounts.stub')):
# use the stub authentication policy
config.include('openstax_accounts.stub.main')
else:
# use the openstax accounts authentication policy
config.include('openstax_accounts.openstax_accounts.main')
config.include('openstax_accounts.authentication_policy.main')
6 changes: 2 additions & 4 deletions openstax_accounts/example.py
Expand Up @@ -100,11 +100,9 @@ def main(global_config, **settings):
config.add_route('callback', '/callback')
config.add_route('login', '/login')
config.add_route('logout', '/logout')
config.scan(ignore='openstax_accounts.tests')

# use the openstax accounts authentication policy
config.include('openstax_accounts.openstax_accounts.main')
config.include('openstax_accounts.authentication_policy.main')
config.scan(package='openstax_accounts.example')
config.include('openstax_accounts.main')

# authorization policy must be set if an authentication policy is set
config.set_authorization_policy(ACLAuthorizationPolicy())
Expand Down
129 changes: 129 additions & 0 deletions openstax_accounts/stub.py
@@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-

import copy
import json

from pyramid.httpexceptions import HTTPFound
from pyramid.interfaces import IAuthenticationPolicy
from pyramid.response import Response
from pyramid.security import Everyone, Authenticated
from pyramid.settings import aslist
from pyramid.view import view_config
from zope.interface import implementer

from .authentication_policy import get_user_from_session


DEFAULT_PROFILE = {
'username': 'test', # to be generated
'id': 1, # to be generated
'first_name': 'Test',
'last_name': 'User',
'contact_infos': [{
'type': 'EmailAddress',
'verified': True,
'id': 1,
'value': 'test@example.com',
}],
}


@implementer(IAuthenticationPolicy)
class StubAuthenticationPolicy(object):
def __init__(self, users):
self.users = {}
for i, user in enumerate(aslist(users, flatten=False)):
if user.count(',') > 1:
username, password, profile = user.split(',', 2)
profile = json.loads(profile)
else:
username, password = user.split(',')
profile = copy.deepcopy(DEFAULT_PROFILE)
profile['contact_infos'][0].update({
'id': i + 1,
'value': '{}@example.com'.format(username)
})
profile['id'] = i + 1
profile['username'] = username
self.users[username] = {
'profile': profile,
'password': password,
}

def authenticated_userid(self, request):
settings = request.registry.settings
login_path = settings['openstax_accounts.login_path']
callback_path = settings['openstax_accounts.callback_path']
if request.path == login_path:
raise HTTPFound(location=request.route_url('stub-login-form'))
if request.path == callback_path:
return self.unauthenticated_userid(request)

username = request.params.get('username')
password = request.params.get('password')
user = self.users.get(username)
if user and user['password'] == password:
self.remember(request, username, profile=user['profile'])
return username
return self.unauthenticated_userid(request)

def unauthenticated_userid(self, request):
return request.session.get('username')

def effective_principals(self, request):
principals = [Everyone]
userid = self.authenticated_userid(request)
if userid:
principals.append(Authenticated)
principals.append(userid)
return principals

def remember(self, request, principal, **kw):
request.session.update({
'username': principal,
'profile': kw.get('profile'),
})
request.session.changed()

def forget(self, request):
request.session.clear()


@view_config(route_name='stub-login-form', request_method=['GET', 'POST'])
def login_form(request):
error = ''
if request.method == 'POST':
if request.authenticated_userid:
raise HTTPFound(request.registry.settings[
'openstax_accounts.callback_path'])
error = 'Username or password incorrect'
return Response('''\
<html>
<body>
<div>{error}</div>
<form method="POST" action="">
<div>
<label for="username">Username:</label>
<input name="username" id="username" />
</div>
<div>
<label for="password">Password:</label>
<input name="password" type="password" id="password" />
</div>
<div>
<input type="submit" />
</div>
</form>
</body>
</html>
'''.format(error=error))


def main(config):
config.add_request_method(get_user_from_session, 'user', reify=True)
settings = config.registry.settings
config.set_authentication_policy(StubAuthenticationPolicy(
users=settings.get('openstax_accounts.stub.users'),
))
config.add_route('stub-login-form', '/stub-login-form')
config.scan(package='openstax_accounts.stub')
40 changes: 40 additions & 0 deletions openstax_accounts/tests.py
Expand Up @@ -24,6 +24,7 @@ def wrapper(self, *args, **kwargs):
self.driver.get_screenshot_as_file('error.png')
with open('error.html', 'w') as f:
f.write(self.driver.page_source.encode('utf-8'))
print(self.driver.page_source)
raise
return wrapper

Expand Down Expand Up @@ -105,6 +106,7 @@ def follow_link(self, link_text, exact=True):
self.driver.find_element_by_link_text(link_text).click()
else:
self.driver.find_element_by_partial_link_text(link_text).click()
import time; time.sleep(5)

def generate_username(self, prefix='user'):
length = 5
Expand All @@ -114,6 +116,44 @@ def generate_username(self, prefix='user'):
def page_text(self):
return re.sub('<[^>]*>', '', self.driver.page_source)

@screenshot_on_error
def test_stub(self):
# check that we are not logged in
self.driver.get(self.app_url)
self.assertTrue('You are currently not logged in' in self.driver.page_source)
self.follow_link('Log in')
# stub login form
self.fill_in('Username:', 'test')
self.fill_in('Password:', 'password')
self.driver.find_element_by_xpath('//input[@type="submit"]').click()
self.assertTrue('Username or password incorrect' in self.page_text())

self.fill_in('Username:', 'aaron')
self.fill_in('Password:', 'password')
self.driver.find_element_by_xpath('//input[@type="submit"]').click()
self.assertTrue('You are currently logged in.' in self.page_text())
# check profile data
self.follow_link('Profile')
self.assertTrue('username: aaron' in self.page_text())
self.assertTrue('last_name: Andersen' in self.page_text())
# logout
self.follow_link('Log out')
self.assertTrue('You are currently not logged in' in self.page_text())

# login as someone else
self.follow_link('Log in')
self.fill_in('Username:', 'babara')
self.fill_in('Password:', 'password')
self.driver.find_element_by_xpath('//input[@type="submit"]').click()
self.assertTrue('You are currently logged in.' in self.page_text())
# check profile data
self.follow_link('Profile')
self.assertTrue('username: babara' in self.page_text())
self.assertTrue('babara@example.com' in self.page_text())
# logout
self.follow_link('Log out')
self.assertTrue('You are currently not logged in' in self.page_text())

@screenshot_on_error
def test_local(self):
self._test_signup()
Expand Down

0 comments on commit 1bf6710

Please sign in to comment.