Issue46 - Refactoring of authentication.
Service page now only shows services the user has access to.
Service page does not require authentication.
Controller loading now done in serviceregistry
1.1 --- a/confs/development.ini Sun Aug 09 23:52:10 2009 +0200
1.2 +++ b/confs/development.ini Sun Oct 18 09:56:42 2009 +0200
1.3 @@ -25,7 +25,7 @@
1.4 authkit.setup.method = wsse
1.5 authkit.wsse.realm = Steward
1.6 authkit.wsse.options.allow_basic = true
1.7 -authkit.wsse.authenticate.user.data = tester:123123 publisher
1.8 +authkit.wsse.authenticate.user.data = tester:123123 publisher admin
1.9
1.10 # If you'd like to fine-tune the individual locations of the cache data dirs
1.11 # for the Cache data, or the Session saves, un-comment the desired settings
1.12 @@ -42,6 +42,9 @@
1.13 store_title = Development store
1.14 store_description = Project test
1.15
1.16 +## Should the steward service page be password protected [True | False] (default: False)
1.17 +#protected_service_page = False
1.18 +
1.19 # WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
1.20 # Debug mode will enable the interactive debugging tool, allowing ANYONE to
1.21 # execute malicious code after an exception is raised.
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/docs/configuration.rst Sun Oct 18 09:56:42 2009 +0200
2.3 @@ -0,0 +1,28 @@
2.4 +*********************
2.5 +Steward Configuration
2.6 +*********************
2.7 +
2.8 +Steward's configuration is done in a ini file. This is a simple
2.9 +Key-value format.
2.10 +
2.11 +------------------
2.12 +Mandatory Settings
2.13 +------------------
2.14 +
2.15 +The following are obligatory for having a functional Steward
2.16 +instalation.
2.17 +
2.18 + * port (Default: 5000)
2.19 + * base_url (Default: localhost:
2.20 + * file_store_base_path (Default: %(here)s/content)
2.21 + * sqlalchemy.url = (Default: sqlite:///%(here)s/steward.sqlite)
2.22 +
2.23 +-----------------
2.24 +Optional Settings
2.25 +-----------------
2.26 +
2.27 +The following options change the standard behavior of the Steward
2.28 +Server, but can be left alone.
2.29 +
2.30 + * protected_service_page (default: False)
2.31 +
3.1 --- a/docs/index.rst Sun Aug 09 23:52:10 2009 +0200
3.2 +++ b/docs/index.rst Sun Oct 18 09:56:42 2009 +0200
3.3 @@ -60,6 +60,7 @@
3.4 :maxdepth: 1
3.5
3.6 install
3.7 + configuration
3.8 api
3.9 datastructure
3.10 buildinfrastructure
4.1 --- a/docs/install.rst Sun Aug 09 23:52:10 2009 +0200
4.2 +++ b/docs/install.rst Sun Oct 18 09:56:42 2009 +0200
4.3 @@ -29,7 +29,8 @@
4.4 other configuration files.
4.5
4.6 The configuration file is auto-commented and by default uses sqlite as
4.7 -a database. Once the file has been edited prepare the environment by
4.8 +a database. The Configuration document has details on the configuration
4.9 +file it self. Once the file has been edited prepare the environment by
4.10 doing::
4.11
4.12 $ paster setup-app my_configuration.conf
5.1 --- a/steward.server.egg-info/paste_deploy_config.ini_tmpl Sun Aug 09 23:52:10 2009 +0200
5.2 +++ b/steward.server.egg-info/paste_deploy_config.ini_tmpl Sun Oct 18 09:56:42 2009 +0200
5.3 @@ -71,6 +71,9 @@
5.4 # Where do the files get stored. (/tmp) is NOT a good place
5.5 file_store_base_path = %(here)s/content
5.6
5.7 +## Should the steward service page be password protected [True | False] (default: False)
5.8 +#protected_service_page = False
5.9 +
5.10 # Buffer sizes for read and write loops.
5.11 file_read_buffer_size = 4096
5.12 file_write_buffer_size= 4096
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/steward/server/config/authorize.py Sun Oct 18 09:56:42 2009 +0200
6.3 @@ -0,0 +1,45 @@
6.4 +# -*- coding: utf-8 -*-
6.5 +## Copyright (C) 2008, 2009 Marijn Vriens
6.6 +##
6.7 +## This file is part of Steward
6.8 +##
6.9 +## Steward is free software: you can redistribute it and/or modify
6.10 +## it under the terms of the GNU General Public License as published by
6.11 +## the Free Software Foundation, either version 3 of the License, or
6.12 +## (at your option) any later version.
6.13 +##
6.14 +## This program is distributed in the hope that it will be useful,
6.15 +## but WITHOUT ANY WARRANTY; without even the implied warranty of
6.16 +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
6.17 +## GNU General Public License for more details.
6.18 +##
6.19 +## You should have received a copy of the GNU General Public License
6.20 +## along with this program. If not, see <http://www.gnu.org/licenses/>.
6.21 +##
6.22 +
6.23 +import logging
6.24 +
6.25 +from decorator import decorator
6.26 +from pylons import request
6.27 +
6.28 +from authkit.permissions import HasAuthKitRole, ValidAuthKitUser
6.29 +from authkit.authorize.pylons_adaptors import authorized
6.30 +
6.31 +log = logging.getLogger(__name__)
6.32 +
6.33 +def authorize():
6.34 + def called(func, self, environ, start_response, **kwargs):
6.35 + routeName = environ['routes.route'].name
6.36 + permission = self.DescribeUsages().getByRouteName(routeName).permission
6.37 + if permission and not hasattr(self, "checked"):
6.38 + self.checked = True;
6.39 + return permission.check(self, environ, start_response)
6.40 + return func(self, environ, start_response, **kwargs)
6.41 + return decorator(called)
6.42 +
6.43 +ValidUser = ValidAuthKitUser
6.44 +'''User is a valid user'''
6.45 +
6.46 +HasRole = HasAuthKitRole
6.47 +'''User has to have these roles to get access to the service.'''
6.48 +
7.1 --- a/steward/server/config/middleware.py Sun Aug 09 23:52:10 2009 +0200
7.2 +++ b/steward/server/config/middleware.py Sun Oct 18 09:56:42 2009 +0200
7.3 @@ -17,9 +17,7 @@
7.4 from beaker.middleware import CacheMiddleware, SessionMiddleware
7.5 from routes.middleware import RoutesMiddleware
7.6
7.7 -import authkit.authorize
7.8 -import authkit.authenticate
7.9 -from authkit.permissions import ValidAuthKitUser
7.10 +from authkit import authenticate
7.11
7.12 from steward.server.config.environment import load_environment
7.13
7.14 @@ -74,9 +72,7 @@
7.15
7.16 app = CacheMiddleware(app, config)
7.17
7.18 - permission = ValidAuthKitUser()
7.19 - app = authkit.authorize.middleware(app, permission)
7.20 - app = authkit.authenticate.middleware(app, app_conf)
7.21 + app = authenticate.middleware(app, app_conf)
7.22
7.23 # Display error documents for 401, 403, 404 status codes (and
7.24 # 500 when debug is disabled)
8.1 --- a/steward/server/config/routing.py Sun Aug 09 23:52:10 2009 +0200
8.2 +++ b/steward/server/config/routing.py Sun Oct 18 09:56:42 2009 +0200
8.3 @@ -26,6 +26,8 @@
8.4 from pylons import config
8.5 from routes import Mapper
8.6
8.7 +from steward.server.lib.serviceregistry import ServiceRegistry
8.8 +
8.9 def make_map():
8.10 """Create, configure and return the routes Mapper"""
8.11 map = Mapper(directory=config['pylons.paths']['controllers'],
8.12 @@ -36,49 +38,10 @@
8.13 if basePrefix != '/':
8.14 map.prefix = basePrefix
8.15
8.16 -
8.17 - # The ErrorController route (handles 404/500 error pages); it should
8.18 - # likely stay at the top, ensuring it can always be resolved
8.19 map.connect('error/:action/:id', controller='error')
8.20
8.21 - # Report access.
8.22 - map.connect('report_events', 'report/events', controller='reportselector', action='getEvents')
8.23 - map.connect('report_changes', 'report/changes', controller='reportselector', action='getChanges')
8.24 -
8.25 - # Main service page.
8.26 - map.connect('service_page', '', controller='serviceselector', action='index')
8.27 - map.connect('admin_purge', 'admin/purge', controller='adminselector', action="purge")
8.28 -
8.29 - # Aliases interface.
8.30 - map.connect('alias_get', 'file/*name', controller='aliasselector', action='get',
8.31 - conditions=dict(method=['GET']))
8.32 - map.connect('alias_ui', 'ui/file', controller='aliasselector', action='ui',
8.33 - conditions=dict(method=['POST']))
8.34 - map.connect('alias_del', 'file/*name', controller='aliasselector', action='delete',
8.35 - conditions=dict(method=['DELETE']))
8.36 -
8.37 - # Atom Interface
8.38 - map.connect('atom_service', 'atom', controller='atomselector', action='atomServiceDocument',
8.39 - conditions=dict(method=['GET']))
8.40 - map.connect('atom_publish', 'atom', controller='atomselector', action='publishEntry',
8.41 - conditions=dict(method=['POST']))
8.42 - map.connect('atom_feed', 'atom/feed', controller='atomselector', action='getFeed',
8.43 - conditions=dict(method=['GET']))
8.44 - map.connect('atom_entry', 'atom/:id', controller='atomselector', action='getEntry',
8.45 - conditions=dict(method=['GET']))
8.46 -
8.47 - # Main standard Asset interface.
8.48 - map.connect('asset_post', 'new', controller='assetselector', action='post',
8.49 - conditions=dict(method=['POST']))
8.50 - map.connect('asset_ui', 'ui/storage', controller='assetselector', action='ui',
8.51 - conditions=dict(method=['POST']))
8.52 - map.connect('alias_post', ':id/alias', controller='aliasselector', action='post',
8.53 - conditions=dict(method=['POST']))
8.54 - map.connect('asset_get', ':id', controller='assetselector', action='get',
8.55 - conditions=dict(method=['GET']))
8.56 - map.connect('asset_head', ':id', controller='assetselector', action='head',
8.57 - conditions=dict(method=['HEAD']))
8.58 - map.connect('asset_del', ':id', controller='assetselector', action='delete',
8.59 - conditions=dict(method=['DELETE']))
8.60 + for service in ServiceRegistry():
8.61 + service.addMapConnections(map)
8.62
8.63 return map
8.64 +
9.1 --- a/steward/server/controllers/adminselector.py Sun Aug 09 23:52:10 2009 +0200
9.2 +++ b/steward/server/controllers/adminselector.py Sun Oct 18 09:56:42 2009 +0200
9.3 @@ -20,13 +20,12 @@
9.4 import logging
9.5 from lxml import etree
9.6
9.7 -from authkit.authorize.pylons_adaptors import authorize
9.8 -from authkit.permissions import RemoteUser, ValidAuthKitUser
9.9 +from steward.server.lib.base import BaseController, request, meta
9.10
9.11 from steward.server.lib.assetcollection import AssetCollection
9.12 from steward.server.lib.eventcollection import EventCollection
9.13 from steward.server.lib.service import Service
9.14 -from steward.server.lib.base import *
9.15 +from steward.server.config.authorize import HasRole
9.16
9.17 log = logging.getLogger(__name__)
9.18
9.19 @@ -40,17 +39,15 @@
9.20 '''
9.21 Registers the use-cases of this Controller
9.22 '''
9.23 - sla = Service('admin', "Administrative interface")
9.24 - sla.addUsage('purge', 'admin_purge', 'get',
9.25 - "Eliminate assets that are marked deleted.")
9.26 + sla = Service('admin', "Administrative interface", controller="adminselector", permission=HasRole("admin"))
9.27 + sla.addUsage('purge', "Eliminate assets that are marked deleted.", 'admin/purge', method="post")
9.28 return sla
9.29
9.30 def __init__(self):
9.31 self.ecol = AssetCollection()
9.32 self.events = EventCollection()
9.33
9.34 -# @authorize(HasRole('admin'))
9.35 - def purge(self):
9.36 + def purgePost(self):
9.37 '''
9.38 Remove the objects that were marked deleted. This will reclame their space in the store.
9.39 '''
10.1 --- a/steward/server/controllers/aliasselector.py Sun Aug 09 23:52:10 2009 +0200
10.2 +++ b/steward/server/controllers/aliasselector.py Sun Oct 18 09:56:42 2009 +0200
10.3 @@ -22,10 +22,12 @@
10.4 from routes import url_for
10.5 from sqlalchemy.exceptions import InvalidRequestError
10.6
10.7 -from steward.server.lib.base import *
10.8 +from steward.server.lib.base import BaseControllerWithUi, response, h, request, abort
10.9 from steward.server.model import meta, Alias, Registry
10.10 from steward.server.lib.service import Service
10.11
10.12 +from steward.server.config.authorize import HasRole
10.13 +
10.14 log = logging.getLogger(__name__)
10.15
10.16 class AliasselectorController(BaseControllerWithUi):
10.17 @@ -38,21 +40,22 @@
10.18 '''
10.19 Registers the use-cases of this Controller
10.20 '''
10.21 - sl = Service('aliases', "Asociate names with objects in the store.")
10.22 - sl.addUsage('save', 'alias_post', 'post', "Create a new alias for an asset")
10.23 - sl.addUsage('get', 'alias_get', 'get', "Using the alias get the related asset url")
10.24 - sl.addUsage('remove', 'alias_del', 'delete', "Remove the alias")
10.25 - sl.addFormInterface("alias_ui")
10.26 + sl = Service('alias', "Associate names with objects in the store.", controller="aliasselector")
10.27 + sl.addUsage('save', "Create a new alias for an asset", ':id/alias', method='post', permission=HasRole("publisher"))
10.28 + sl.addUsage('get', "Using the alias get the related asset url", 'file/*name')
10.29 + sl.addUsage('remove', "Remove the alias", 'file/*name', method='delete', permission=HasRole("publisher"))
10.30 + sl.addFormInterface("ui/file")
10.31 +# import pdb; pdb.set_trace()
10.32 return sl
10.33
10.34 def __init__(self, *args, **kwargs):
10.35 super(AliasselectorController, self).__init__(*args, **kwargs)
10.36 self.uses = {
10.37 'save': (self.create, 201, "Alias '%(name)s' was associated with content '%(id)s'."),
10.38 - 'remove': (self.delete, 410, "Alias '%(name)s' was removed.")
10.39 + 'remove': (self.removeDelete, 410, "Alias '%(name)s' was removed.")
10.40 }
10.41
10.42 - def get(self, name):
10.43 + def getGet(self, name):
10.44 '''
10.45 Given the alias returns a reference to the object associated it.
10.46 '''
10.47 @@ -65,8 +68,7 @@
10.48 response.headers["Content-Length"] = len(asset_url)
10.49 return asset_url
10.50
10.51 - @authorize(HasRole('publisher'))
10.52 - def post(self, id):
10.53 + def savePost(self, id):
10.54 '''
10.55 Create a new alias by posting the alias name to the object alias url.
10.56 '''
10.57 @@ -97,14 +99,12 @@
10.58 except InvalidRequestError, e:
10.59 abort(404, e.message)
10.60 r.aliases.append(a)
10.61 - meta.Session.save_or_update(r)
10.62 location = url_for('alias_get', name=name)
10.63 response.status_int = 201
10.64 response.headers['Location'] = h.AbsoluteHostUrl(request) + location
10.65 return None
10.66
10.67 - @authorize(HasRole('publisher'))
10.68 - def delete(self, name):
10.69 + def removeDelete(self, name):
10.70 '''
10.71 Remove an alias, deleting the association it has with an object.
10.72 '''
11.1 --- a/steward/server/controllers/assetselector.py Sun Aug 09 23:52:10 2009 +0200
11.2 +++ b/steward/server/controllers/assetselector.py Sun Oct 18 09:56:42 2009 +0200
11.3 @@ -42,20 +42,21 @@
11.4 '''
11.5 Registers the use-cases of this Controller
11.6 '''
11.7 - s = Service('storage', "Save and retreive and remove assets in storage")
11.8 - s.addUsage('save', 'asset_post', 'post', "Add a new asset to the storage")
11.9 - s.addUsage('get', 'asset_get', 'get', "Retrieve an asset from storage")
11.10 - s.addUsage('remove', 'asset_del', 'delete', "Delete an asset from the storage")
11.11 - s.addUsage('check', 'asset_head', 'head', "Check status of asset in storage")
11.12 - s.addFormInterface('asset_ui')
11.13 + s = Service('asset', "Save and retrieve and remove assets in storage", controller="assetselector")
11.14 + s.addUsage('save', "Add a new asset to the storage", 'new', method='post', permission=HasRole("publisher"))
11.15 + s.addUsage('get', "Retrieve an asset from storage", '{id}', requirements={'id':R"[A-Z\d]{32}"})
11.16 + s.addUsage('remove', "Delete an asset from the storage", '{id}', method='delete',
11.17 + permission=HasRole("publisher"), requirements={'id':R"[A-Z\d]{32}"})
11.18 + s.addUsage('check', "Check status of asset in storage", '{id}', method='head', requirements={'id':R"[A-Z\d]{32}"})
11.19 + s.addFormInterface('ui/storage')
11.20 return s
11.21
11.22 def __init__(self):
11.23 self.ecol = AssetCollection()
11.24 self.events = EventCollection()
11.25 - self.uses = {'remove': (self.delete, 410, "Asset with id %(id)s was removed from the store.")}
11.26 + self.uses = {'remove': (self.removeDelete, 410, "Asset with id %(id)s was removed from the store.")}
11.27
11.28 - def getHeaders(self, id):
11.29 + def _getHeaders(self, id):
11.30 '''
11.31 When given the id it returns relevant headers of the asset.
11.32 '''
11.33 @@ -68,24 +69,23 @@
11.34 response.headers['Last-Modified'] = e.date_stored.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
11.35 return e
11.36
11.37 - def get(self, id):
11.38 + def getGet(self, id):
11.39 '''
11.40 Given the Id returns the object
11.41 '''
11.42 - e = self.getHeaders(id)
11.43 + e = self._getHeaders(id)
11.44 self.events.markGet(id, request.environ.get('REMOTE_USER'))
11.45 return e.getBinaryStream()
11.46
11.47 - def head(self, id):
11.48 + def checkHead(self, id):
11.49 '''
11.50 Returns the headers of the object.
11.51 '''
11.52 - e = self.getHeaders(id)
11.53 + e = self._getHeaders(id)
11.54 self.events.markCheck(id, request.environ.get('REMOTE_USER'))
11.55 return None
11.56
11.57 - @authorize(HasRole('publisher'))
11.58 - def post(self):
11.59 + def savePost(self):
11.60 '''
11.61 I am the interface for storing the object.
11.62 '''
11.63 @@ -116,8 +116,7 @@
11.64 else:
11.65 return str(location)
11.66
11.67 - @authorize(HasRole('publisher'))
11.68 - def delete(self, id):
11.69 + def removeDelete(self, id):
11.70 '''
11.71 Through me an object with id can be made unavialable.
11.72 '''
12.1 --- a/steward/server/controllers/atomselector.py Sun Aug 09 23:52:10 2009 +0200
12.2 +++ b/steward/server/controllers/atomselector.py Sun Oct 18 09:56:42 2009 +0200
12.3 @@ -49,28 +49,31 @@
12.4 '''
12.5 Registers the use-cases of this Controller
12.6 '''
12.7 - atomServ = Service('atom', "Atom protocol support.")
12.8 - atomServ.addUsage('service', 'atom_service', 'get', "Atom service document.")
12.9 - atomServ.addUsage('save', 'atom_publish', 'post', "Post an asset into the store via the atom protocol.")
12.10 - atomServ.addUsage('feed', 'atom_feed', 'get', "Atom feed of elements in the storage.")
12.11 - atomServ.addUsage('get', 'atom_entry', 'get', "Get the atom entry of asset {id}")
12.12 + atomServ = Service('atom', "Atom protocol support.", controller="atomselector")
12.13 + atomServ.addUsage('feed', "Atom feed of elements in the storage.", 'atom/feed')
12.14 + atomServ.addUsage('entry', "Get the atom entry of asset {id}", 'atom/{id}',
12.15 + requirements={'id':R"[A-Z\d]{32}"})
12.16 + atomServ.addUsage('service', "Atom service document.", "atom/index")
12.17 + atomServ.addUsage('publish', "Post an asset into the store via the atom protocol.", "atom", method='post',
12.18 + permission=HasRole("publisher"))
12.19 return atomServ
12.20
12.21 def __init__(self):
12.22 self.ecol = AssetCollection()
12.23 self.events = EventCollection()
12.24
12.25 - def atomServiceDocument(self):
12.26 + def serviceGet(self):
12.27 '''
12.28 I generate the atom service document.
12.29 '''
12.30 +
12.31 srv = etree.Element(APP+"service", nsmap=NSMAP)
12.32 workspace = etree.SubElement(srv, APP+"workspace")
12.33 workspace.append( self._formatCollection() )
12.34 response.headers["Content-Type"] = "application/atomsvc+xml"
12.35 return etree.tostring(srv, xml_declaration=True, encoding="UTF-8", pretty_print=config['debug'])
12.36
12.37 - def getFeed(self):
12.38 + def feedGet(self):
12.39 '''I show the Atom feed of the last documents added to the repository and am also the entry point for adding more documents.'''
12.40 ec = EventCollection()
12.41 response.headers['Content-Type'] = 'application/atom+xml;type=feed;charset="utf-8"' # RFC4287 #7
12.42 @@ -122,7 +125,7 @@
12.43 etree.SubElement(entry, ATOM+"link", rel="alternate", type=mime, href=url)
12.44 return entry
12.45
12.46 - def publishEntry(self):
12.47 + def publishPost(self):
12.48 '''I will store a document for you and return you the resulting entry in a Atom Entry format.'''
12.49 response.status_int = 201
12.50 response.headers['Content-Type'] ='application/atom+xml;type=entry;charset="utf-8"'
12.51 @@ -161,10 +164,12 @@
12.52 tree = etree.parse(req.body_file)
12.53 return tree.xpath("/a:entry/a:link[@rel='related']/@href", namespaces=NOKIAMAP)[0]
12.54
12.55 - def getEntry(self, id):
12.56 + def entryGet(self, id):
12.57 '''
12.58 I will return you the information about one entry.
12.59 '''
12.60 + if not id:
12.61 + abort(404, "No ID given.")
12.62 try:
12.63 e = self.events.getCreationEventOfAsset(id)
12.64 except AssetNotAvailableException, e:
13.1 --- a/steward/server/controllers/reportselector.py Sun Aug 09 23:52:10 2009 +0200
13.2 +++ b/steward/server/controllers/reportselector.py Sun Oct 18 09:56:42 2009 +0200
13.3 @@ -23,12 +23,11 @@
13.4
13.5 from routes import url_for
13.6
13.7 -from steward.server.lib.base import *
13.8 +from steward.server.lib.base import BaseController, request, response, h
13.9 from steward.server.lib.eventcollection import EventCollection
13.10 from steward.server.lib.service import Service
13.11
13.12 -from authkit.authorize.pylons_adaptors import authorize
13.13 -from authkit.permissions import RemoteUser, ValidAuthKitUser
13.14 +from steward.server.config.authorize import ValidUser
13.15
13.16 log = logging.getLogger(__name__)
13.17
13.18 @@ -42,18 +41,18 @@
13.19 '''
13.20 Registers the use-cases of this Controller
13.21 '''
13.22 - sle = Service('reports', "Report events in the store")
13.23 - sle.addUsage('events', 'report_events', 'get', "List all events that have occured in the storage")
13.24 - sle.addUsage('changes', 'report_changes', 'get', "List all changes that have affected the storage")
13.25 + sle = Service('report', "Report events in the store", controller="reportselector", permission=ValidUser())
13.26 + sle.addUsage('events', "List all events that have occured in the storage", 'report/events', method='get')
13.27 + sle.addUsage('changes', "List all changes that have affected the storage", 'report/changes', method='get')
13.28 return sle
13.29
13.30 - def getEvents(self):
13.31 + def eventsGet(self):
13.32 '''
13.33 Get the events that happen to the store.
13.34 '''
13.35 return self.genListOfEvents("events")
13.36
13.37 - def getChanges(self):
13.38 + def changesGet(self):
13.39 '''
13.40 Get the changes that have occured in the store.
13.41 '''
14.1 --- a/steward/server/controllers/serviceselector.py Sun Aug 09 23:52:10 2009 +0200
14.2 +++ b/steward/server/controllers/serviceselector.py Sun Oct 18 09:56:42 2009 +0200
14.3 @@ -22,7 +22,7 @@
14.4 from pylons import config
14.5
14.6 from steward.server.lib.base import *
14.7 -from steward.server.lib.service import Services
14.8 +from steward.server.lib.service import Services, Service
14.9 from steward.server.lib.helpers import BaseUrlPath
14.10
14.11 from adminselector import AdminselectorController
14.12 @@ -37,16 +37,29 @@
14.13 '''
14.14 I generate a page that allows clients to detect what services are provided by the Steward instance.
14.15 '''
14.16 - def index(self):
14.17 + @staticmethod
14.18 + def DescribeUsages():
14.19 + permission = ValidAuthKitUser() if ServiceselectorController.IsProtected() else None
14.20 + serv = Service('service', "Steward Server support pages", controller="serviceselector", permission=permission)
14.21 + serv.addUsage('index', "Index page", '')
14.22 + return serv
14.23 +
14.24 + @staticmethod
14.25 + def IsProtected():
14.26 + return config.get('protected_service_page', 'false').lower() == "true" # default to false.
14.27 +
14.28 + def __init__(self):
14.29 + self.protected = ServiceselectorController.IsProtected()
14.30 +
14.31 + def indexGet(self):
14.32 '''
14.33 I generate the main index page.
14.34 '''
14.35 services = Services(config['store_title'])
14.36 - for controller in [AssetselectorController, AliasselectorController, AtomselectorController,
14.37 - ReportselectorController, AdminselectorController]:
14.38 + for controller in [ServiceselectorController, AssetselectorController, AliasselectorController,
14.39 + AtomselectorController, ReportselectorController, AdminselectorController]:
14.40 services.addService(controller.DescribeUsages())
14.41 response.headers['Content-Type'] = 'application/xml'
14.42 xslUrl = BaseUrlPath() + 'static/xsl/service.xsl'
14.43 -# return services.toXml()
14.44 return services.toXml(xslUrl=xslUrl)
14.45
15.1 --- a/steward/server/lib/assetcollection.py Sun Aug 09 23:52:10 2009 +0200
15.2 +++ b/steward/server/lib/assetcollection.py Sun Oct 18 09:56:42 2009 +0200
15.3 @@ -67,6 +67,7 @@
15.4 elif len(res) == 0:
15.5 r = Registry()
15.6 r.sha = id
15.7 + meta.Session.add(r)
15.8 else:
15.9 msg = "%s more then once in Registry!" % id
15.10 log.error(msg)
15.11 @@ -74,8 +75,7 @@
15.12 r.length = length
15.13 r.mimetype = mimetype
15.14 r.status = 'active'
15.15 - meta.Session.save_or_update(r)
15.16 - meta.Session.commit()
15.17 + #meta.Session.commit()
15.18 return r
15.19
15.20 def remove(self, id):
15.21 @@ -94,8 +94,7 @@
15.22 log.error(msg)
15.23 raise InternalInconsistencyException(msg)
15.24 r.status = 'deleted'
15.25 - meta.Session.update(r)
15.26 - meta.Session.commit()
15.27 + #meta.Session.commit()
15.28 return r
15.29
15.30 def listDeleted(self):
15.31 @@ -111,5 +110,4 @@
15.32 self._store.purge(id)
15.33 res = meta.Session.query(Registry).filter(Registry.sha==id).one()
15.34 res.status = 'purged'
15.35 - meta.Session.update(res)
15.36 - meta.Session.commit()
15.37 + #meta.Session.commit()
16.1 --- a/steward/server/lib/base.py Sun Aug 09 23:52:10 2009 +0200
16.2 +++ b/steward/server/lib/base.py Sun Oct 18 09:56:42 2009 +0200
16.3 @@ -21,22 +21,30 @@
16.4 Provides the BaseController class for subclassing, and other objects
16.5 utilized by Controllers.
16.6 """
16.7 +
16.8 +import logging
16.9 +
16.10 from pylons import c, cache, config, g, request, response, session
16.11 from pylons.controllers import WSGIController
16.12 from pylons.controllers.util import abort, etag_cache, redirect_to
16.13 -from pylons.decorators import jsonify, validate
16.14 +from pylons.decorators import jsonify
16.15 from pylons.i18n import _, ungettext, N_
16.16 from pylons.templating import render
16.17
16.18 -from authkit.authorize.pylons_adaptors import authorize
16.19 -from authkit.permissions import HasAuthKitRole as HasRole
16.20 +from authkit.permissions import HasAuthKitRole as HasRole, ValidAuthKitUser
16.21
16.22 +from steward.server.config.authorize import authorize
16.23 import steward.server.lib.helpers as h
16.24 import steward.server.model as model
16.25 from steward.server.model import meta
16.26
16.27 +log = logging.getLogger(__name__)
16.28 +
16.29 class BaseController(WSGIController):
16.30
16.31 + protected=True # By default all pages are protected.
16.32 +
16.33 + @authorize()
16.34 def __call__(self, environ, start_response):
16.35 """Invoke the Controller"""
16.36 # WSGIController.__call__ dispatches to the Controller method
16.37 @@ -50,7 +58,7 @@
16.38 meta.Session.remove()
16.39
16.40 class BaseControllerWithUi(BaseController):
16.41 - def ui(self):
16.42 + def uiGet(self):
16.43 '''I parse the form and call it on to the right function in the controller.
16.44 ``uses`` is a dictionary with the different uses with the name as the key and a tuple (funcion, success status, success message) as value.
16.45 '''
17.1 --- a/steward/server/lib/eventcollection.py Sun Aug 09 23:52:10 2009 +0200
17.2 +++ b/steward/server/lib/eventcollection.py Sun Oct 18 09:56:42 2009 +0200
17.3 @@ -22,10 +22,11 @@
17.4 from lxml import etree
17.5
17.6 from sqlalchemy.orm import eagerload
17.7 +from sqlalchemy.orm.exc import NoResultFound
17.8 from sqlalchemy.exceptions import InvalidRequestError
17.9
17.10 from steward.server.model import meta, Event, Registry
17.11 -from steward.server.lib.exceptions import InternalInconsistencyException
17.12 +from steward.server.lib.exceptions import InternalInconsistencyException, AssetNotAvailableException
17.13
17.14 log = logging.getLogger(__name__)
17.15
17.16 @@ -77,14 +78,17 @@
17.17 except InvalidRequestError, e:
17.18 raise InternalInconsistencyException("Asset %s not found in registry" % id)
17.19 r.events.append(e)
17.20 - meta.Session.save(e)
17.21 + meta.Session.add(e)
17.22 return e
17.23
17.24 def getCreationEventOfAsset(self, id):
17.25 '''
17.26 Return the creation event of a object with id.
17.27 '''
17.28 - return meta.Session.query(Event).filter_by(register_id=id, action='save').order_by(Event.id.desc())[0]
17.29 + try:
17.30 + return meta.Session.query(Event).filter_by(register_id=id, action='save').order_by(Event.id.desc()).one()
17.31 + except NoResultFound, ex:
17.32 + raise AssetNotAvailableException(id)
17.33
17.34 def getListOfEvents(self, id):
17.35 '''
18.1 --- a/steward/server/lib/exceptions.py Sun Aug 09 23:52:10 2009 +0200
18.2 +++ b/steward/server/lib/exceptions.py Sun Oct 18 09:56:42 2009 +0200
18.3 @@ -65,3 +65,8 @@
18.4 class StoreNeedsUpgradeException(UpgradeException):
18.5 '''I get raised when the store needs updating.'''
18.6 pass
18.7 +
18.8 +class InternalException(Exception):
18.9 + '''I Should be raised when the code internals do unexpected things. I should just fail the server.'''
18.10 + pass
18.11 +
19.1 --- a/steward/server/lib/service.py Sun Aug 09 23:52:10 2009 +0200
19.2 +++ b/steward/server/lib/service.py Sun Oct 18 09:56:42 2009 +0200
19.3 @@ -21,84 +21,150 @@
19.4 from routes import url_for
19.5 from distutils import version
19.6
19.7 -from pylons import config
19.8 +from pylons import config, request
19.9
19.10 from steward.server import __name__ as projName
19.11 from steward.server import __version__ as projVersion
19.12 from steward.server.lib.utils import BuildXml
19.13 +from steward.server.lib.exceptions import InternalException
19.14 +
19.15 +from steward.server.config.authorize import authorized
19.16
19.17 class Services(object):
19.18 '''
19.19 I maintain the list of services that steward clams to support. All other modules that have external interfaces should notify me so that I can update my service list document.
19.20 '''
19.21 def __init__(self, storeTitle):
19.22 - self.doc = etree.Element("services")
19.23 - self.doc.attrib['name'] = 'steward'
19.24 - self.doc.attrib['version'] = projVersion
19.25 -
19.26 - href=config.get('base_url', "/")
19.27 - if not href.endswith("/"):
19.28 - href += "/"
19.29 - self.doc.attrib["base"] = href
19.30 - etree.SubElement(self.doc, "store", title=storeTitle)
19.31 + self._services = {}
19.32 + self.storeTitle = storeTitle
19.33
19.34 def addService(self, serviceList):
19.35 '''
19.36 Add a new service to my service page. I take a serviceList as input. This should have all the details of the service.
19.37 '''
19.38 - if not isinstance(serviceList, tuple):
19.39 + if not isinstance(serviceList, (tuple, list)):
19.40 serviceList = [serviceList]
19.41 for service in serviceList:
19.42 - serv = etree.SubElement(self.doc, "service", name=service.name, desc=service.desc)
19.43 - if service.ui:
19.44 - serv.attrib['ui'] = self._generateRoute(service.ui)
19.45 - for u in service:
19.46 - href = self._generateRoute(u.routeName)
19.47 - etree.SubElement(serv, "use", name=u.name, method=u.meth.upper(),
19.48 - href=href, desc=u.desc)
19.49 + self._services[service.name] = service
19.50 +
19.51 + def _baseHref(self):
19.52 + href=config.get('base_url', "/")
19.53 + if not href.endswith("/"):
19.54 + href += "/"
19.55 + return href
19.56
19.57 - def _generateRoute(self, name):
19.58 - routes = config['routes.map']
19.59 - prefix = '/'
19.60 - return routes._routenames[name].routepath
19.61 + def _docHeader(self):
19.62 + doc = etree.Element("services")
19.63 + doc.attrib['name'] = 'steward'
19.64 + doc.attrib['version'] = projVersion
19.65 +
19.66 + doc.attrib["base"] = self._baseHref()
19.67 + etree.SubElement(doc, "store", title=self.storeTitle)
19.68 + return doc
19.69 +
19.70 + def _docService(self, doc, service):
19.71 + if service.permission is not None and authorized(service.permission) == False:
19.72 + return
19.73 + serv = etree.SubElement(doc, "service", name=service.name, desc=service.desc)
19.74 + for u in service:
19.75 + if u.permission is not None and authorized(u.permission) == False:
19.76 + continue
19.77 + if u.usageName == 'ui':
19.78 + serv.attrib['ui'] = u.url
19.79 + else:
19.80 + etree.SubElement(serv, "use", name=u.usageName, method=u.method.upper(),
19.81 + href=u.url, desc=u.usageDesc)
19.82
19.83 def toXml(self, xslUrl=None):
19.84 '''
19.85 Build a XML document for the service.
19.86 '''
19.87 - return BuildXml(self.doc, xslUrl)
19.88 + doc = self._docHeader()
19.89 + for service in self._services.itervalues():
19.90 + self._docService(doc, service)
19.91 + return BuildXml(doc, xslUrl)
19.92
19.93 -class Service(list):
19.94 +class Service(object):
19.95 '''
19.96 One service. Each service can have multiple use-cases.
19.97 '''
19.98 - def __init__(self, name, desc="Unknown"):
19.99 + def __init__(self, name, desc, controller=None, permission=None):
19.100 + self.uses = {}
19.101 + self.usesList = []
19.102 + self._usesRoute = {}
19.103 +
19.104 self.name = name
19.105 self.desc = desc
19.106 - self.ui = None
19.107 + if controller is None:
19.108 + raise InternalException
19.109 + self.controller = controller
19.110 + self.permission = permission
19.111
19.112 - def addUsage(self, name, routeName, meth, desc):
19.113 + def __iter__(self):
19.114 + return self.usesList.__iter__()
19.115 +
19.116 + def _routeName(self, usageName):
19.117 + return "%s_%s" % (self.name, usageName)
19.118 +
19.119 + def _defaultAction(self, usageName, method):
19.120 + return usageName + method.capitalize()
19.121 +
19.122 + def addUsage(self, usageName, usageDesc, url, method='get',
19.123 + action=None, permission=None, requirements=None):
19.124 '''
19.125 Through me you can add a new use case to the service.
19.126 + @param usageName: name of the service usage.
19.127 + @param usageDesc: Description of the service usage.
19.128 + @param url: The URL that the usage is available under.
19.129 + @param method: The HTTP method for this ['get'].
19.130 + @param action: The method to be invoked [Null].
19.131 + @param permission: The method to check that permissions are
19.132 + granted.
19.133 + @param requirements: Dictionary of regular expressions that
19.134 + the parameters should comply to to match
19.135 '''
19.136 - self.append(ServiceUsage(name, routeName, meth, desc))
19.137 + routeName = self._routeName(usageName)
19.138 + if action is None:
19.139 + action = self._defaultAction(usageName, method)
19.140 + if permission is None:
19.141 + # If no usage permission is set, use the controller's permission as the usage permission.
19.142 + permission = self.permission
19.143 + if requirements is None:
19.144 + requirements = {}
19.145
19.146 - def addFormInterface(self, serviceUrl):
19.147 + d = locals()
19.148 + del d['self']
19.149 + use = ServiceUsage(d)
19.150 + self.uses[usageName] = use
19.151 + self._usesRoute[routeName] = use
19.152 + self.usesList.append(use)
19.153 +
19.154 + def getByRouteName(self, routeName):
19.155 + return self._usesRoute[routeName]
19.156 +
19.157 + def addFormInterface(self, interfaceUrl, permission=None):
19.158 '''
19.159 If the service has a interface for posting forms, the URL of that service should be added here.
19.160 '''
19.161 - self.ui = serviceUrl
19.162 + self.addUsage('ui', 'User Interface', interfaceUrl, method=['get', 'post'], action="uiGet", permission=permission)
19.163
19.164 + def _buildCondition(self, methods):
19.165 + if not isinstance(methods, (list, tuple)):
19.166 + methods = [methods]
19.167 + methods = [i.upper() for i in methods]
19.168 + return {'method':methods}
19.169
19.170 -class ServiceUsage(object):
19.171 - '''
19.172 - I am a simple container object for a service use case.
19.173 - '''
19.174 - def __init__(self, name, routeName, meth, desc):
19.175 - self.name = name
19.176 - self.routeName = routeName
19.177 - self.meth = meth
19.178 - self.desc = desc
19.179 + def addMapConnections(self, map):
19.180 + '''
19.181 + I create the connections for the route object
19.182 + @param map The Mapper object that maintains the valid routes in the server.
19.183 + '''
19.184 + for u in self:
19.185 + map.connect(u.routeName, u.url, controller=self.controller, action=u.action,
19.186 + conditions=self._buildCondition(u.method), requirements=u.requirements)
19.187
19.188 - def __repr__(self):
19.189 - return "<%s %s>" % (type(self), self.name)
19.190 +class ServiceUsage(dict):
19.191 + def __getattr__(self, name):
19.192 + if name in self:
19.193 + return self[name]
19.194 + raise AttributeError(name)
20.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
20.2 +++ b/steward/server/lib/serviceregistry.py Sun Oct 18 09:56:42 2009 +0200
20.3 @@ -0,0 +1,47 @@
20.4 +# -*- coding: utf-8 -*-
20.5 +## Copyright (C) 2008, 2009 Marijn Vriens
20.6 +##
20.7 +## This file is part of Steward
20.8 +##
20.9 +## Steward is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by
20.10 +## the Free Software Foundation, either version 3 of the License, or
20.11 +## (at your option) any later version.
20.12 +##
20.13 +## This program is distributed in the hope that it will be useful,
20.14 +## but WITHOUT ANY WARRANTY; without even the implied warranty of
20.15 +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20.16 +## GNU General Public License for more details.
20.17 +##
20.18 +## You should have received a copy of the GNU General Public License
20.19 +## along with this program. If not, see <http://www.gnu.org/licenses/>.
20.20 +##
20.21 +
20.22 +import logging
20.23 +
20.24 +log = logging.getLogger(__name__)
20.25 +
20.26 +from steward.server.controllers.reportselector import ReportselectorController
20.27 +from steward.server.controllers.serviceselector import ServiceselectorController
20.28 +from steward.server.controllers.assetselector import AssetselectorController
20.29 +from steward.server.controllers.atomselector import AtomselectorController
20.30 +from steward.server.controllers.aliasselector import AliasselectorController
20.31 +from steward.server.controllers.adminselector import AdminselectorController
20.32 +
20.33 +
20.34 +_registry = None
20.35 +
20.36 +def ServiceRegistry():
20.37 + global _registry
20.38 + if not _registry:
20.39 + _registry = _ServiceRegistry()
20.40 + return _registry
20.41 +
20.42 +class _ServiceRegistry(list):
20.43 + def __init__(self):
20.44 + self.append(ReportselectorController.DescribeUsages())
20.45 + self.append(ServiceselectorController.DescribeUsages())
20.46 + self.append(AssetselectorController.DescribeUsages())
20.47 + self.append(AliasselectorController.DescribeUsages())
20.48 + self.append(AdminselectorController.DescribeUsages())
20.49 + self.append(AtomselectorController.DescribeUsages())
20.50 +
21.1 --- a/steward/server/lib/users.py Sun Aug 09 23:52:10 2009 +0200
21.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
21.3 @@ -1,51 +0,0 @@
21.4 -# -*- coding: utf-8 -*-
21.5 -## Copyright (C) 2008, 2009 Marijn Vriens
21.6 -##
21.7 -## This file is part of Steward
21.8 -##
21.9 -## Steward is free software: you can redistribute it and/or modify
21.10 -## it under the terms of the GNU General Public License as published by
21.11 -## the Free Software Foundation, either version 3 of the License, or
21.12 -## (at your option) any later version.
21.13 -##
21.14 -## This program is distributed in the hope that it will be useful,
21.15 -## but WITHOUT ANY WARRANTY; without even the implied warranty of
21.16 -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21.17 -## GNU General Public License for more details.
21.18 -##
21.19 -## You should have received a copy of the GNU General Public License
21.20 -## along with this program. If not, see <http://www.gnu.org/licenses/>.
21.21 -##
21.22 -
21.23 -import logging
21.24 -
21.25 -from pylons import config
21.26 -
21.27 -log = logging.getLogger(__name__)
21.28 -
21.29 -class StewardUsers(object):
21.30 - '''I am the interface between the user-model and authkit'''
21.31 - def __init__(self, model, enc):
21.32 - if model == 'fake':
21.33 - import steward.server.tests.stubs
21.34 - modelCls = steward.server.tests.stubs.FakeUserModel
21.35 - else:
21.36 - raise Exception("Unknown user data model %s" % model)
21.37 - log.info("using %s as authentication source." % modelCls)
21.38 - modelConfKey = '%s_config' % model
21.39 - self._model = modelCls(eval(config[modelConfKey]))
21.40 -
21.41 - def user_exists(self, username):
21.42 - return True
21.43 -
21.44 - def user_has_password(self, username, password):
21.45 - return self._model.userHasPassword(username, password)
21.46 -
21.47 - def user_password(self, username):
21.48 - return self._model.getPassword(username)
21.49 -
21.50 - def role_exists(self, role):
21.51 - return True
21.52 -
21.53 - def user_has_role(self, username, role):
21.54 - return self._model.userHasRole(username, role)
22.1 --- a/steward/server/public/static/xsl/service.xsl Sun Aug 09 23:52:10 2009 +0200
22.2 +++ b/steward/server/public/static/xsl/service.xsl Sun Oct 18 09:56:42 2009 +0200
22.3 @@ -17,7 +17,7 @@
22.4 </div>
22.5 <div id="interfaces" class="section">
22.6 <div class="title">Simple browser based interfaces</div>
22.7 - <xsl:apply-templates select="/services/service[@name='storage']"/>
22.8 + <xsl:apply-templates select="/services/service[@name='assets']"/>
22.9 <xsl:apply-templates select="/services/service[@name='aliases']"/>
22.10 </div>
22.11 <div id="services" class="section">
22.12 @@ -51,14 +51,14 @@
22.13 </img>
22.14 </div>
22.15 <div class="footer">
22.16 - <a href="http://protocultura.cl/steward/">Steward <xsl:value-of select="/services/@version"/></a> - Simple storage and retrival of binary blobs.
22.17 + <a href="http://protocultura.cl/steward/">Steward <xsl:value-of select="/services/@version"/></a> - Simple storage and retrival of binary assets.
22.18 <br/> Copyright 2008, 2009 Marijn Vriens. This software is licensed under <a href="http://www.gnu.org/licenses/gpl-3.0.html">GPLv3</a>.<br/>
22.19 </div>
22.20 </body>
22.21 </html>
22.22 </xsl:template>
22.23
22.24 -<xsl:template match="/services/service[@name='storage']">
22.25 +<xsl:template match="/services/service[@name='assets']">
22.26 <div class="ui">
22.27 <table>
22.28 <thead>
23.1 --- a/steward/server/tests/__init__.py Sun Aug 09 23:52:10 2009 +0200
23.2 +++ b/steward/server/tests/__init__.py Sun Oct 18 09:56:42 2009 +0200
23.3 @@ -77,13 +77,12 @@
23.4
23.5 def delete(self, *args, **kwargs):
23.6 '''Use the DELETE http method'''
23.7 + #print locals()
23.8 return self._customizeRequest(super(CustomTestApp, self).delete, args, kwargs)
23.9
23.10 def head(self, *args, **kwargs):
23.11 '''Use the HEAD http method'''
23.12 - if 'headers' not in kwargs:
23.13 - kwargs['headers'] = {}
23.14 - AddWsseAuthorization(kwargs['headers'])
23.15 + AddWsseAuthorization(kwargs.setdefault('headers', {}))
23.16 res = self._gen_request('HEAD', *args, **kwargs)
23.17 self._customGlobalHttpAsserts(res)
23.18 return res
23.19 @@ -99,9 +98,7 @@
23.20
23.21 def _customizeRequest(self, meth, args, kwargs):
23.22 '''I pass the request to the TestApp, after adding some code to make testing easier.'''
23.23 - if 'headers' not in kwargs:
23.24 - kwargs['headers'] = {}
23.25 - AddWsseAuthorization(kwargs['headers'])
23.26 + AddWsseAuthorization(kwargs.setdefault('headers', {}))
23.27 res = meth(*args, **kwargs)
23.28 self._customGlobalHttpAsserts(res)
23.29 return res
24.1 --- a/steward/server/tests/fixtures.py Sun Aug 09 23:52:10 2009 +0200
24.2 +++ b/steward/server/tests/fixtures.py Sun Oct 18 09:56:42 2009 +0200
24.3 @@ -32,7 +32,7 @@
24.4 r.length = 42
24.5 r.mimetype = 'text/plain; charset=utf-8'
24.6 r.status = 'active'
24.7 - meta.Session.save(r)
24.8 + meta.Session.add(r)
24.9
24.10 def loadStoreFixtures(conf):
24.11 s = Pantry(conf['file_store_base_path'])
25.1 --- a/steward/server/tests/functional/test_adminselector.py Sun Aug 09 23:52:10 2009 +0200
25.2 +++ b/steward/server/tests/functional/test_adminselector.py Sun Oct 18 09:56:42 2009 +0200
25.3 @@ -28,14 +28,14 @@
25.4 self.app.delete(docId2, status=410)
25.5
25.6 self.assertEquals(0, self._nrOfPurges())
25.7 - resp = self.app.get(url_for('admin_purge'))
25.8 + resp = self.app.post(url_for('admin_purge'))
25.9 self.assertEquals(2, self._nrOfPurges())
25.10
25.11 tree = etree.fromstring( resp.body )
25.12 self.assertEquals('purged',tree.xpath('/result/text()')[0])
25.13
25.14 def _publish(self, data):
25.15 - resp = self.app.post(url_for('asset_post'), data,
25.16 + resp = self.app.post(url_for('asset_save'), data,
25.17 headers={'Content-Type': 'text/plain; charset=ascii'})
25.18 return resp.header('Location')
25.19
26.1 --- a/steward/server/tests/functional/test_aliasselector.py Sun Aug 09 23:52:10 2009 +0200
26.2 +++ b/steward/server/tests/functional/test_aliasselector.py Sun Oct 18 09:56:42 2009 +0200
26.3 @@ -21,9 +21,9 @@
26.4
26.5 class TestServicePage(TestController):
26.6 def test_serviceRegistered(self):
26.7 - response = self.app.get( url_for('service_page') )
26.8 + response = self.app.get( url_for('service_index') )
26.9 respTree = etree.fromstring(response.body)
26.10 - aliasParts = respTree.xpath('/services/service[@name="aliases"]')
26.11 + aliasParts = respTree.xpath('/services/service[@name="alias"]')
26.12 self.assertEquals(1, len(aliasParts) )
26.13 aliasDesc = aliasParts[0]
26.14 self.assertEquals(1, len(aliasDesc.xpath('use[@name="save"]')) )
26.15 @@ -100,15 +100,15 @@
26.16 self.app.get(aliasUrl, status=404)
26.17
26.18 def test_deleteAlias_noexiste(self): # Try to Delete an alias that doesn't exist.
26.19 - self.app.delete(url_for('alias_del', name="noexiste.txt"), status=410)
26.20 + self.app.delete(url_for('alias_remove', name="noexiste.txt"), status=410)
26.21
26.22 def _postAnAlias(self, assetId, name, status=201, mime='text/plain; charset=ascii'):
26.23 - return self.app.post(url_for('alias_post', id=assetId), name,
26.24 + return self.app.post(url_for('alias_save', id=assetId), name,
26.25 headers={'Content-Type': mime},
26.26 status=status)
26.27
26.28 def _postAnAsset(self, data):
26.29 - url = url_for('asset_post')
26.30 + url = url_for('asset_save')
26.31 response = self.app.post(url, data, status=201)
26.32 return response.header('location')
26.33
26.34 @@ -136,6 +136,6 @@
26.35 self.app.get(url_for('alias_get', name=aliasName), status=404)
26.36
26.37 def _postAnAsset(self, data):
26.38 - url = url_for('asset_post')
26.39 + url = url_for('asset_save')
26.40 response = self.app.post(url, data, status=201)
26.41 return response.header('location')
27.1 --- a/steward/server/tests/functional/test_assetselector.py Sun Aug 09 23:52:10 2009 +0200
27.2 +++ b/steward/server/tests/functional/test_assetselector.py Sun Oct 18 09:56:42 2009 +0200
27.3 @@ -47,7 +47,7 @@
27.4 self.assertEquals("", responseHead.body)
27.5
27.6 def test_post(self):
27.7 - url = url_for('asset_post')
27.8 + url = url_for('asset_save')
27.9 data = "Esto son los datos enviado via un post al servicio."
27.10 response = self.app.post(url, data, headers={'Content-Type': 'text/plain; charset=ascii'})
27.11 getUrl = 'http://test.example.com:5000' + url_for('asset_get', id="CYIFP555TZC2AAQXEGXP5EZQVIZVDEYO")
27.12 @@ -59,7 +59,7 @@
27.13 self.assertEquals(len(data), int(responseGet.header('content-length')))
27.14
27.15 def test_post_twice(self):
27.16 - url = url_for('asset_post')
27.17 + url = url_for('asset_save')
27.18 data = "Esto son los datos enviado via un post al servicio. Vamos a postearlo dos veces"
27.19 response = self.app.post(url, data, headers={'Content-Type': 'text/plain; charset=ascii'})
27.20 loc1 = response.header('Location')
27.21 @@ -69,12 +69,12 @@
27.22
27.23 def test_post_no_content(self):
27.24 # Test that posting without content is not accepted.
27.25 - url = url_for('asset_post')
27.26 + url = url_for('asset_save')
27.27 noData = ""
27.28 response = self.app.post(url, noData, headers={'Content-Type': 'text/plain; charset=ascii'}, status=400)
27.29
27.30 def test_delete(self):
27.31 - postUrl = url_for('asset_post')
27.32 + postUrl = url_for('asset_save')
27.33 data = "Documento para ser eleminado."
27.34 response = self.app.post(postUrl, data)
27.35 loc = response.header('Location')
27.36 @@ -82,7 +82,7 @@
27.37 response = self.app.get(loc, status=404)
27.38
27.39 def test_delete_twice(self):
27.40 - postUrl = url_for('asset_post')
27.41 + postUrl = url_for('asset_save')
27.42 data = "Documento para ser eleminado, dos veces!"
27.43 response = self.app.post(postUrl, data)
27.44 loc = response.header('Location')
27.45 @@ -90,7 +90,7 @@
27.46 self.app.delete(loc, status=410)
27.47
27.48 def test_delete_create(self):
27.49 - postUrl = url_for('asset_post')
27.50 + postUrl = url_for('asset_save')
27.51 data = "Document to be created, removed and re-created."
27.52 response = self.app.post(postUrl, data, headers={'Content-Type': 'text/plain; charset=ascii'})
27.53 locA = response.header('Location')
27.54 @@ -103,7 +103,7 @@
27.55
27.56 class TestWebInterface(TestController):
27.57 def test_post_via_form(self):
27.58 - url = url_for('asset_post') + "?ui=form"
27.59 + url = url_for('asset_save') + "?ui=form"
27.60 content = 'This is content uploaded via a form'
27.61 fileData = [('file', 'filename.txt', content)]
27.62 response = self.app.post(url, {'mimetype': 'text/plain'}, upload_files=fileData)
27.63 @@ -125,11 +125,11 @@
27.64 self.assertEquals(len(content), int(responseGet.header('content-length')))
27.65
27.66 def test_post_missing_content(self):
27.67 - url = url_for('asset_post') + "?ui=form"
27.68 + url = url_for('asset_save') + "?ui=form"
27.69 response = self.app.post(url, {'mimetype': 'text/plain'}, status=400)
27.70
27.71 def test_delete(self):
27.72 - postUrl = url_for('asset_post')
27.73 + postUrl = url_for('asset_save')
27.74 data = "Document to be remove via the ui"
27.75 response = self.app.post(postUrl, data)
27.76 objLoc = response.header('Location')
27.77 @@ -146,38 +146,21 @@
27.78 self.app.post(url_for('asset_ui'), {'use': 'remove', 'wrong': 'param'}, status=400)
27.79 self.app.post(url_for('asset_ui'), {'use': 'remove', 'id': '1231', 'extra': 'param'}, status=400)
27.80
27.81 -### Example filefox3 form posting.
27.82 -
27.83 -# POST /new?ui=form HTTP/1.1
27.84 -# Host: localhost:5000
27.85 -# User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.0.5) Gecko/2008120121 Firefox/3.0.5
27.86 -# Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
27.87 -# Accept-Language: en-us,en;q=0.5
27.88 -# Accept-Encoding: gzip,deflate
27.89 -# Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
27.90 -# Keep-Alive: 300
27.91 -# Connection: keep-alive
27.92 -# Referer: http://localhost:5000/
27.93 -# Authorization: Basic dGVzdGVyOjEyMzEyMw==
27.94 -# Content-Type: multipart/form-data; boundary=---------------------------935817076107365704609123648
27.95 -# Content-Length: 474
27.96 +class TestAssetSelectorController_anon(TestController):
27.97 + def test_get(self):
27.98 + url = url_for('asset_get', id="C7VNTJYDXUF2YBA7GH5MABLUMXG2YEXT")
27.99 + headers = {'Authorization':None}
27.100 + response = self.app.get(url, headers=headers)
27.101
27.102 -# -----------------------------935817076107365704609123648
27.103 -# Content-Disposition: form-data; name="file"; filename="airport-data.txt"
27.104 -# Content-Type: text/plain
27.105 + def test_post(self):
27.106 + url = url_for('asset_save')
27.107 + headers = {'Content-Type': 'text/plain; charset=ascii',
27.108 + 'Authorization':None,
27.109 + }
27.110 + data = "This is test data sent to the service."
27.111 + response = self.app.post(url, data, headers=headers, status=401)
27.112
27.113 -# Data goes here
27.114 -
27.115 -# -----------------------------935817076107365704609123648
27.116 -# Content-Disposition: form-data; name="mimetype"
27.117 -
27.118 -# text/plain
27.119 -# -----------------------------935817076107365704609123648--
27.120 -# HTTP/1.0 201 CREATED
27.121 -# Server: PasteWSGIServer/0.5 Python/2.5.2
27.122 -# Date: Sun, 11 Jan 2009 22:38:51 GMT
27.123 -# location: http://localhost:5000/3I42H3S6NNFQ2MSVX7XZKYAYSCX5QBYJ
27.124 -# pragma: no-cache
27.125 -# cache-control: no-cache
27.126 -# Connection: close
27.127 -
27.128 + def test_delete(self):
27.129 + url = url_for('asset_remove', id="C7VNTJYDXUF2YBA7GH5MABLUMXG2YEXT")
27.130 + headers = {'Authorization':None}
27.131 + response = self.app.delete(url, headers=headers, status=401)
28.1 --- a/steward/server/tests/functional/test_atomselector.py Sun Aug 09 23:52:10 2009 +0200
28.2 +++ b/steward/server/tests/functional/test_atomselector.py Sun Oct 18 09:56:42 2009 +0200
28.3 @@ -28,13 +28,13 @@
28.4
28.5 class TestServicePage(TestController):
28.6 def test_serviceRegistered(self):
28.7 - response = self.app.get( url_for('service_page') )
28.8 + response = self.app.get( url_for('service_index') )
28.9 respTree = etree.fromstring(response.body)
28.10 desc = respTree.xpath('/services/service[@name="atom"]')[0]
28.11 self.assertEquals(1, len(desc.xpath('use[@name="service"][@method="GET"]')) )
28.12 - self.assertEquals(1, len(desc.xpath('use[@name="save"][@method="POST"]')) )
28.13 + self.assertEquals(1, len(desc.xpath('use[@name="publish"][@method="POST"]')) )
28.14 self.assertEquals(1, len(desc.xpath('use[@name="feed"][@method="GET"]')) )
28.15 - self.assertEquals(1, len(desc.xpath('use[@name="get"][@method="GET"]')) )
28.16 + self.assertEquals(1, len(desc.xpath('use[@name="entry"][@method="GET"]')) )
28.17
28.18 class TestCollection(TestController):
28.19 def test_getAtomServiceDocument(self):
28.20 @@ -105,6 +105,13 @@
28.21 self.assertTrue( xml_compare(postEntry, getEntry,
28.22 reporter=self.fail, strip_whitespaces = True ) )
28.23
28.24 + def test_entryGet_missingId(self):
28.25 + self.app.get(url_for("atom_entry", id=""), status=404)
28.26 +
28.27 + def test_entryGet_notexisting(self):
28.28 + self.app.get(url_for("atom_entry", id="FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), status=404)
28.29 +
28.30 +
28.31 def test_publishDocument_via_entry(self):
28.32 xmlDoc = """<?xml version='1.0' encoding='UTF-8'?>
28.33 <entry xmlns="http://purl.org/atom/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
28.34 @@ -136,12 +143,12 @@
28.35 self.assertEquals(res, value)
28.36
28.37 def _stdEvents(self): # using the std interface, until atom publishing is implemented.
28.38 - self.app.post(url_for('asset_post'), "Data to store.",
28.39 + self.app.post(url_for('asset_save'), "Data to store.",
28.40 headers={'Content-Type': 'text/plain; charset=ascii'}).header('Location')
28.41 - loc = self.app.post(url_for('asset_post'), "Data in store to be removed",
28.42 + loc = self.app.post(url_for('asset_save'), "Data in store to be removed",
28.43 headers={'Content-Type': 'text/plain; charset=ascii'}).header('Location')
28.44 self.app.delete(loc, status=410)
28.45 - self.app.post(url_for('asset_post'), "Other data to save.",
28.46 + self.app.post(url_for('asset_save'), "Other data to save.",
28.47 headers={'Content-Type': 'text/plain; charset=ascii'})
28.48
28.49
29.1 --- a/steward/server/tests/functional/test_reportselector.py Sun Aug 09 23:52:10 2009 +0200
29.2 +++ b/steward/server/tests/functional/test_reportselector.py Sun Oct 18 09:56:42 2009 +0200
29.3 @@ -25,8 +25,8 @@
29.4
29.5 class TestServicePage(TestController):
29.6 def test_serviceRegistered_reports(self):
29.7 - serviceType='reports'
29.8 - response = self.app.get( url_for('service_page') )
29.9 + serviceType='report'
29.10 + response = self.app.get( url_for('service_index') )
29.11 respTree = etree.fromstring(response.body)
29.12 service = respTree.xpath('/services/service[@name="%s"]' % serviceType)
29.13 self.assertEquals(1, len(service) )
29.14 @@ -122,8 +122,8 @@
29.15 def test_getEvents_noExiste(self):
29.16 id = "NOEXISTENOEXISTENOEXISTENOEXISTE"
29.17 self.app.get(url_for('asset_get', id=id), status=404)
29.18 - self.app.get(url_for('asset_head', id=id), status=404)
29.19 - self.app.get(url_for('asset_del', id=id), status=404)
29.20 + self.app.get(url_for('asset_check', id=id), status=404)
29.21 + self.app.get(url_for('asset_remove', id=id), status=404)
29.22 response = self.app.get(url_for('report_events'))
29.23 self.assertTrue("<report/>", response)
29.24
29.25 @@ -149,10 +149,10 @@
29.26 del x.attrib['date']
29.27
29.28 def _stdEvents(self):
29.29 - loc = self.app.post(url_for('asset_post'), "datos para guardar.",
29.30 + loc = self.app.post(url_for('asset_save'), "datos para guardar.",
29.31 headers={'Content-Type': 'text/plain; charset=ascii'}).header('Location')
29.32 self.app.get(loc)
29.33 - self.app.post(url_for('asset_post'), "otro dato para guardar.",
29.34 + self.app.post(url_for('asset_save'), "otro dato para guardar.",
29.35 headers={'Content-Type': 'text/plain; charset=ascii'})
29.36 self.app.head(loc)
29.37 self.app.get(loc)
30.1 --- a/steward/server/tests/functional/test_serviceselector.py Sun Aug 09 23:52:10 2009 +0200
30.2 +++ b/steward/server/tests/functional/test_serviceselector.py Sun Oct 18 09:56:42 2009 +0200
30.3 @@ -24,27 +24,40 @@
30.4 from steward.server.lib.service import Service
30.5
30.6 from steward.server.tests import *
30.7 -from steward.server.tests import CreateTestApp
30.8 +from steward.server.tests import CreateTestApp, TestModel
30.9
30.10 class TestServiceselectorController(TestController):
30.11
30.12 - def test_index(self):
30.13 - response = self.app.get(url_for("service_page"))
30.14 + def test_user_index(self):
30.15 + response = self.app.get(url_for("service_index"))
30.16 servicesDoc = etree.fromstring(response.body)
30.17 -
30.18 self.assertEquals("Unittesting memory-only Store", servicesDoc.xpath('/services/store/@title')[0])
30.19
30.20 - self.assertEquals(5, len(servicesDoc.xpath('/services/service')), 'Unexpected number of services in document.')
30.21 + self.assertEquals(6, len(servicesDoc.xpath('/services/service')), 'Unexpected number of services in document.')
30.22 + self._checkService(servicesDoc, 'service', ['index'], None)
30.23 self._checkService(servicesDoc, 'admin', ['purge'], None)
30.24 - self._checkService(servicesDoc, 'aliases', ['save', 'get', 'remove'], url_for('alias_ui'))
30.25 - self._checkService(servicesDoc, 'atom', ['service', 'save', 'feed', 'get'], None)
30.26 - self._checkService(servicesDoc, 'reports', ['events', 'changes'], None)
30.27 - self._checkService(servicesDoc, 'storage', ['save', 'get', 'remove', 'check'], url_for('asset_ui'))
30.28 + self._checkService(servicesDoc, 'alias', ['save', 'get', 'remove'], url_for('alias_ui'))
30.29 + self._checkService(servicesDoc, 'atom', ['service', 'publish', 'feed', 'entry'], None)
30.30 + self._checkService(servicesDoc, 'report', ['events', 'changes'], None)
30.31 + self._checkService(servicesDoc, 'asset', ['save', 'get', 'remove', 'check'], url_for('asset_ui'))
30.32
30.33 - self.assertTrue(url_for("asset_post").endswith(servicesDoc.xpath("/services/service[@name='storage']/use[@name='save']/@href")[0]))
30.34 + self.assertTrue(url_for("asset_save").endswith(servicesDoc.xpath("/services/service[@name='asset']/use[@name='save']/@href")[0]))
30.35
30.36 self.assertEquals(len(response.body), int(response.header('Content-Length')))
30.37
30.38 + def test_anonymous_index(self):
30.39 + response = self.app.get(url_for("service_index"), headers={'Authorization': None})
30.40 + servicesDoc = etree.fromstring(response.body)
30.41 + self.assertEquals("Unittesting memory-only Store", servicesDoc.xpath('/services/store/@title')[0])
30.42 +
30.43 + self.assertEquals(4, len(servicesDoc.xpath('/services/service')), 'Unexpected number of services in document.')
30.44 + self._checkService(servicesDoc, 'service', ['index'], None)
30.45 + self._checkService(servicesDoc, 'alias', ['get'], url_for('alias_ui'))
30.46 + self._checkService(servicesDoc, 'atom', ['service', 'feed', 'entry'], None)
30.47 + self._checkService(servicesDoc, 'asset', ['get', 'check'], url_for('asset_ui'))
30.48 +
30.49 + self.assertEquals(len(response.body), int(response.header('Content-Length')))
30.50 +
30.51 def _checkService(self, servicesDoc, serviceName, usageNameList, uiUrl):
30.52 if uiUrl:
30.53 self.assertTrue(uiUrl.endswith(servicesDoc.xpath("/services/service[@name='%s']/@ui" % serviceName)[0]))
30.54 @@ -53,14 +66,14 @@
30.55 serviceUses = servicesDoc.xpath("/services/service[@name='%s']/use" % serviceName)
30.56 self.assertNotEquals([], serviceUses)
30.57 for u in serviceUses:
30.58 - self.assertTrue(u.attrib['name'] in usageNameList, 'missing use "%s" in service document' % u.attrib['name'])
30.59 + self.assertTrue(u.attrib['name'] in usageNameList, "missing use '%s' in section '%s' of document" % (u.attrib['name'], serviceName))
30.60 usageNameList.remove(u.attrib['name'])
30.61 self.assertEquals([], usageNameList, 'Not all expected services where found in document')
30.62
30.63 def test_index_xslInstruction(self, app=None):
30.64 if app is None:
30.65 app = self.app
30.66 - response = app.get(url_for("service_page"))
30.67 + response = app.get(url_for("service_index"))
30.68 serviceDoc = etree.fromstring(response.body)
30.69
30.70 xslPI = serviceDoc.getprevious()
30.71 @@ -70,22 +83,27 @@
30.72 def test_index_xslInstruction_altPath(self):
30.73 app = CreateTestApp("server/tests/test_urlpath.ini")
30.74 self.test_index_xslInstruction(app)
30.75 + app = CreateTestApp("server/tests/test.ini") # seems to be needed.
30.76
30.77 +class Test_authenticatedServicePage(TestModel):
30.78 + def setUp(self):
30.79 + super(Test_authenticatedServicePage, self).setUp()
30.80 + self.app = CreateTestApp("server/tests/test_auth_servicepage.ini")
30.81 +
30.82 def test_index_wrongWsseAuth(self):
30.83 headers = AddWsseAuthorization(user="tester", pw="Wrong Password")
30.84 - self.app.get(url_for(controller='serviceselector'), headers=headers, status=401)
30.85 + self.app.get(url_for('service_index'), headers=headers, status=401)
30.86
30.87 def test_index_basicAuth(self):
30.88 headers = AddBasicAuthorization(user="tester", pw="Wrong Password")
30.89 - self.app.get(url_for(controller='serviceselector'), headers=headers, status=401)
30.90 + self.app.get(url_for('service_index'), headers=headers, status=401)
30.91
30.92 headers = AddBasicAuthorization(user="tester", pw="123123")
30.93 - self.app.get(url_for(controller='serviceselector'), headers=headers, status=200)
30.94 + self.app.get(url_for('service_index'), headers=headers, status=200)
30.95
30.96 def test_auth_headers(self):
30.97 headers = AddBasicAuthorization(user="tester", pw="Wrong Password")
30.98 - response = self.app.get(url_for(controller='serviceselector'), headers=headers, status=401)
30.99 + response = self.app.get(url_for('service_index'), headers=headers, status=401)
30.100 for k,v in response.headers:
30.101 if k == 'WWW-Authenticate':
30.102 self.assertTrue(v.startswith('WSSE') or v.startswith('Basic'))
30.103 -
31.1 --- a/steward/server/tests/stubs.py Sun Aug 09 23:52:10 2009 +0200
31.2 +++ b/steward/server/tests/stubs.py Sun Oct 18 09:56:42 2009 +0200
31.3 @@ -27,24 +27,17 @@
31.4
31.5 from steward.server.lib.rfc3339 import rfc3339
31.6
31.7 -class FakeUserModel(object):
31.8 - def __init__(self, config):
31.9 - pass
31.10 -
31.11 - def userHasPassword(self, username, password):
31.12 - return username == 'tester' and password == '123123'
31.13 -
31.14 - def userHasRole(self, username, role):
31.15 - return username == 'tester' and role == 'publisher'
31.16 -
31.17 - def getPassword(self, username):
31.18 - return '123123'
31.19 -
31.20 def AddWsseAuthorization(headers=None, user='tester', pw='123123'):
31.21 - '''I add the WSSE authentization header to the dictionary but only if it's not already set.'''
31.22 + '''I add the WSSE authentization header to the dictionary but only if it's not already set.
31.23 +If the headers['Authentization'] has a value of None no authorization will be added and the header itself will be removed.
31.24 + '''
31.25 if headers is None:
31.26 headers = {}
31.27 - if 'Authorization' not in headers:
31.28 + if 'Authorization' in headers:
31.29 + if headers['Authorization'] is None:
31.30 + # Authorization == None means that no authorization should be added!
31.31 + del headers['Authorization']
31.32 + else:
31.33 nonce = hex(randint(0, 2**128))[2:-1]
31.34 now = rfc3339( time(), utc=True )
31.35 passDigest = b64encode( sha(nonce + now + pw).digest() )
32.1 --- a/steward/server/tests/test.ini Sun Aug 09 23:52:10 2009 +0200
32.2 +++ b/steward/server/tests/test.ini Sun Oct 18 09:56:42 2009 +0200
32.3 @@ -27,13 +27,3 @@
32.4
32.5 file_store_base_path = %(here)s/store
32.6
32.7 -# Set Authentication database for fake data.
32.8 -authkit.wsse.authenticate.user.type = steward.server.lib.users:StewardUsers
32.9 -authkit.wsse.authenticate.user.data = fake
32.10 -fake_config = {
32.11 - 'host' : 'ldap://localhost',
32.12 - 'basedn': 'dc=fake,dc=cl',
32.13 - 'people': 'ou=people',
32.14 - 'groups': 'ou=group',
32.15 - }
32.16 -
33.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
33.2 +++ b/steward/server/tests/test_auth_servicepage.ini Sun Oct 18 09:56:42 2009 +0200
33.3 @@ -0,0 +1,21 @@
33.4 +#
33.5 +# steward - Pylons testing environment configuration
33.6 +#
33.7 +# The %(here)s variable will be replaced with the parent directory of this file
33.8 +#
33.9 +[DEFAULT]
33.10 +debug = true
33.11 +# Uncomment and replace with the address which should receive any error reports
33.12 +#email_to = you@yourdomain.com
33.13 +smtp_server = localhost
33.14 +error_email_from = paste@localhost
33.15 +
33.16 +[server:main]
33.17 +use = egg:Paste#http
33.18 +host = 0.0.0.0
33.19 +port = 5000
33.20 +
33.21 +[app:main]
33.22 +use = config:test.ini
33.23 +
33.24 +protected_service_page = True
34.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
34.2 +++ b/steward/server/tests/test_service.py Sun Oct 18 09:56:42 2009 +0200
34.3 @@ -0,0 +1,157 @@
34.4 +# -*- coding: utf-8 -*-
34.5 +## Copyright (C) 2008, 2009 Marijn Vriens
34.6 +##
34.7 +## This file is part of Steward
34.8 +##
34.9 +## Steward is free software: you can redistribute it and/or modify
34.10 +## it under the terms of the GNU General Public License as published by
34.11 +## the Free Software Foundation, either version 3 of the License, or
34.12 +## (at your option) any later version.
34.13 +##
34.14 +## This program is distributed in the hope that it will be useful,
34.15 +## but WITHOUT ANY WARRANTY; without even the implied warranty of
34.16 +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34.17 +## GNU General Public License for more details.
34.18 +##
34.19 +## You should have received a copy of the GNU General Public License
34.20 +## along with this program. If not, see <http://www.gnu.org/licenses/>.
34.21 +##
34.22 +
34.23 +from steward.server.tests import *
34.24 +from xml_compare import xml_compare
34.25 +from lxml import etree
34.26 +from steward.server.lib.service import Service, Services
34.27 +from steward.server.lib.exceptions import InternalException
34.28 +
34.29 +class TestService(TestCase):
34.30 + def _permChecker(self):
34.31 + pass
34.32 +
34.33 + def setUp(self):
34.34 + self.s = Service("test", "a test service", controller="testController", permission=self._permChecker)
34.35 +
34.36 + def testCreateService(self):
34.37 + s = self.s
34.38 + self.assertEquals(s.name, "test")
34.39 + self.assertEquals(s.controller, "testController")
34.40 + self.assertEquals(s.permission, self._permChecker)
34.41 + self.assertEquals(s.desc, "a test service")
34.42 +
34.43 + def testCreateService_noController(self):
34.44 + try:
34.45 + Service("test", "a test service", permission=self._permChecker)
34.46 + except InternalException, ex:
34.47 + pass
34.48 + else:
34.49 + self.fail("Should have raised exception")
34.50 +
34.51 + def testAddUsage(self):
34.52 + self.s.addUsage("usage", "sample usage", "test/use")
34.53 + r = self.s.uses['usage']
34.54 + self.assertEquals(r.usageName, 'usage')
34.55 + self.assertEquals(r.usageDesc, "sample usage")
34.56 + self.assertEquals(r.url, "test/use")
34.57 + self.assertEquals(r.method, "get")
34.58 + self.assertEquals(r.action, "usageGet")
34.59 + self.assertEquals(r.routeName, "test_usage")
34.60 +
34.61 + def testAddUsage_method(self):
34.62 + self.s.addUsage("usage", "sample usage", "test/use", method="delete", action="fooBar")
34.63 + r = self.s.uses['usage']
34.64 + self.assertEquals(r.method, "delete")
34.65 + self.assertEquals(r.action, "fooBar")
34.66 +
34.67 + def testAddMapConnections(self):
34.68 + self.s.addUsage("usage", "sample usage", "test/use")
34.69 + self.s.addUsage("usage2", "sample usage2", "test/use2")
34.70 + mapper = FakeMapper()
34.71 + self.s.addMapConnections(mapper)
34.72 +
34.73 + r1 = self.s.uses['usage']
34.74 + self.assertEquals(r1.routeName, mapper.d[0]['routeName'])
34.75 + self.assertEquals(r1.url, mapper.d[0]['url'])
34.76 + self.assertEquals(self.s.controller, mapper.d[0]['controller'])
34.77 + self.assertEquals("usageGet", mapper.d[0]['action'])
34.78 +
34.79 + def testAddUsage_requrements(self):
34.80 + self.s.addUsage("usage", "requirement usage", "test/{id}", requirements={'id':R"\d{4}"})
34.81 + r = self.s.uses['usage']
34.82 + self.assertEquals(R'\d{4}', r.requirements['id'])
34.83 + mapper = FakeMapper()
34.84 + self.s.addMapConnections(mapper)
34.85 + self.assertEquals({'id': R'\d{4}'}, mapper.d[0]['requirements'])
34.86 +
34.87 + def testAddFormInterface(self):
34.88 + self.s.addFormInterface("test/ui")
34.89 + mapper = FakeMapper()
34.90 + self.s.addMapConnections(mapper)
34.91 + self.assertEquals('test_ui', mapper.d[0]['routeName'])
34.92 + self.assertEquals('test/ui', mapper.d[0]['url'])
34.93 + self.assertEquals('uiGet', mapper.d[0]['action'])
34.94 +
34.95 + def testGetByRouteName(self):
34.96 + self.s.addUsage("usage", "sample usage", "test/use")
34.97 + self.s.addUsage("usage2", "sample usage2", "test/use2")
34.98 + self.assertEquals('usageGet', self.s.getByRouteName('test_usage').action)
34.99 + self.assertEquals('usage2Get', self.s.getByRouteName('test_usage2').action)
34.100 +
34.101 +
34.102 +class TestService_permissions(TestCase):
34.103 + def _permChecker(self):
34.104 + pass
34.105 +
34.106 + def test_default(self):
34.107 + self.s = Service("test", "a test service", controller="testController")
34.108 + self.s.addUsage("default", "sample usage", "test/use")
34.109 + r = self.s.uses['default']
34.110 + self.assertEquals(None, r.permission)
34.111 +
34.112 + self.s.addUsage("checked", "sample usage", "test/use", permission=self._permChecker)
34.113 + r = self.s.uses['checked']
34.114 + self.assertEquals(self._permChecker, r.permission)
34.115 +
34.116 + def test_checkedController(self):
34.117 + self.s = Service("test", "a test service", controller="testController", permission=self._permChecker)
34.118 + self.s.addUsage("default", "sample usage", "test/use")
34.119 + r = self.s.uses['default']
34.120 + self.assertEquals(self._permChecker, r.permission)
34.121 +
34.122 +
34.123 +class TestServices(TestCase):
34.124 + def setUp(self):
34.125 + self.sList = Services("unittest store")
34.126 + def testAddService(self):
34.127 + s1 = Service("foo", "foo service", controller="testController")
34.128 + s2 = Service("bar", "bar service", controller="testController")
34.129 + self.sList.addService(s1)
34.130 + self.sList.addService([s2])
34.131 +
34.132 + def testToXml(self):
34.133 + s1 = Service("foo", "a foo service", controller="testController")
34.134 + s1.addUsage("use1", "some usage", "foo/use1", method="post")
34.135 + s1.addUsage("use2", "some usage2", "foo/use1", method="get")
34.136 + s2 = Service("bar", "a bar service", controller="testController")
34.137 + self.sList.addService([s1, s2])
34.138 + received = etree.fromstring(self.sList.toXml())
34.139 + self.assertTrue(xml_compare(self.expectedXml(), received))
34.140 +
34.141 + def expectedXml(self):
34.142 + from steward.server import __version__ as projVersion
34.143 + href = self.sList._baseHref()
34.144 + return etree.fromstring("""<?xml version='1.0' encoding='utf-8'?>
34.145 +<services name="steward" version="%s" base="%s">
34.146 + <store title="unittest store"/>
34.147 + <service name="foo" desc="a foo service">
34.148 + <use desc="some usage" href="foo/use1" method="POST" name="use1"/>
34.149 + <use desc="some usage2" href="foo/use1" method="GET" name="use2"/>
34.150 + </service>
34.151 + <service name="bar" desc="a bar service"/>
34.152 +</services>""" % (projVersion, href))
34.153 +
34.154 +class FakeMapper(object):
34.155 + def __init__(self):
34.156 + self.d = []
34.157 + def connect(self, routeName, url, controller=None, action=None, conditions=None, requirements=None):
34.158 + assert(conditions != None)
34.159 + self.d.append(locals())
34.160 +
35.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
35.2 +++ b/steward/server/tests/test_serviceregistry.py Sun Oct 18 09:56:42 2009 +0200
35.3 @@ -0,0 +1,26 @@
35.4 +# -*- coding: utf-8 -*-
35.5 +## Copyright (C) 2008, 2009 Marijn Vriens
35.6 +##
35.7 +## This file is part of Steward
35.8 +##
35.9 +## Steward is free software: you can redistribute it and/or modify
35.10 +## it under the terms of the GNU General Public License as published by
35.11 +## the Free Software Foundation, either version 3 of the License, or
35.12 +## (at your option) any later version.
35.13 +##
35.14 +## This program is distributed in the hope that it will be useful,
35.15 +## but WITHOUT ANY WARRANTY; without even the implied warranty of
35.16 +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35.17 +## GNU General Public License for more details.
35.18 +##
35.19 +## You should have received a copy of the GNU General Public License
35.20 +## along with this program. If not, see <http://www.gnu.org/licenses/>.
35.21 +##
35.22 +
35.23 +from steward.server.tests import *
35.24 +
35.25 +from steward.server.lib.serviceregistry import ServiceRegistry
35.26 +
35.27 +class TestService(TestCase):
35.28 + def test_create(self):
35.29 + sr = ServiceRegistry()
36.1 --- a/steward/server/tests/test_users.py Sun Aug 09 23:52:10 2009 +0200
36.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
36.3 @@ -1,43 +0,0 @@
36.4 -# -*- coding: utf-8 -*-
36.5 -## Copyright (C) 2008, 2009 Marijn Vriens
36.6 -##
36.7 -## This file is part of Steward
36.8 -##
36.9 -## Steward is free software: you can redistribute it and/or modify
36.10 -## it under the terms of the GNU General Public License as published by
36.11 -## the Free Software Foundation, either version 3 of the License, or
36.12 -## (at your option) any later version.
36.13 -##
36.14 -## This program is distributed in the hope that it will be useful,
36.15 -## but WITHOUT ANY WARRANTY; without even the implied warranty of
36.16 -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36.17 -## GNU General Public License for more details.
36.18 -##
36.19 -## You should have received a copy of the GNU General Public License
36.20 -## along with this program. If not, see <http://www.gnu.org/licenses/>.
36.21 -##
36.22 -
36.23 -from steward.server.tests import *
36.24 -
36.25 -from steward.server.lib.users import StewardUsers
36.26 -
36.27 -class TestStewardUsers(TestCase):
36.28 - def setUp(self):
36.29 - self.s = StewardUsers('fake', '')
36.30 -
36.31 - def test_user_exists(self):
36.32 - self.assertTrue( self.s.user_exists("tester") )
36.33 -
36.34 - def test_user_has_password(self):
36.35 - self.assertTrue( self.s.user_has_password("tester", "123123") )
36.36 - self.assertFalse( self.s.user_has_password("tester", "Wrong") )
36.37 - self.assertFalse( self.s.user_has_password("noexiste", "123123") )
36.38 - self.assertFalse( self.s.user_has_password("noexiste", "Wrong") )
36.39 -
36.40 - def test_user_has_role(self):
36.41 - self.assertTrue( self.s.user_has_role("tester", "publisher") )
36.42 - self.assertFalse( self.s.user_has_role("tester", "noexiste") )
36.43 - self.assertFalse( self.s.user_has_role("noexiste", "noexiste") )
36.44 -
36.45 - def test_creation_wrongType(self):
36.46 - self.assertRaises(Exception, StewardUsers, 'wrong', '')