Index: fedd/fedd_access.py
===================================================================
--- fedd/fedd_access.py	(revision 72ed6e42af52f04cdebc38933c098f9cbfba144a)
+++ fedd/fedd_access.py	(revision 4ed10aefdf7652c7ea1c9a8488706ceb7c9436b8)
@@ -38,4 +38,6 @@
     dynamically.  This implements both direct requests and proxies.
     """
+
+    class parse_error(RuntimeError): pass
 
     bool_attrs = ("dynamic_projects", "project_priority")
@@ -82,5 +84,5 @@
 		    self.fedid_category[fedid(hexstr=m.string)] = cat
 		else:
-		    raise parse_error(\
+		    raise self.parse_error(\
 			    "Bad fedid in trust file (%s) line: %d" % \
 			    (trust, lineno))
@@ -93,5 +95,5 @@
 	    # Nothing matched - bad line, raise exception
 	    f.close()
-	    raise parse_error(\
+	    raise self.parse_error(\
 		    "Unparsable line in trustfile %s line %d" % (trust, lineno))
 	f.close()
@@ -121,5 +123,5 @@
 	with a # are ignored.
 
-	Parsing errors result in a parse_error exception being raised.
+	Parsing errors result in a self.parse_error exception being raised.
 	"""
 	lineno=0
@@ -138,5 +140,5 @@
 	access_re = re.compile('\('+key_name+'\s*,\s*'+key_name+'\s*,\s*'+
 		key_name+'\s*\)\s*->\s*\('+access_proj + '\s*,\s*' + 
-		access_name + '\s*\)', re.IGNORECASE)
+		access_name + '\s*,\s*' + access_name + '\s*\)', re.IGNORECASE)
 
 	def parse_name(n):
@@ -165,5 +167,5 @@
 		continue
 
-	    # Access line (t, p, u) -> (ap, au) line
+	    # Access line (t, p, u) -> (ap, cu, su) line
 	    m = access_re.match(line)
 	    if m != None:
@@ -174,9 +176,9 @@
 		    aps[0] = fedid(hexstr=aps[0])
 
-		au = m.group(5)
-		if au.startswith("fedid:"):
-		    au = fedid(hexstr=aus[len("fedid:"):])
-
-		access_val = (access_project(aps[0], aps[1:]), au)
+		cu = parse_name(m.group(5))
+		su = parse_name(m.group(6))
+
+		access_val = (access_project(aps[0], aps[1:]),
+			parse_name(m.group(5)), parse_name(m.group(6)))
 
 		self.access[access_key] = access_val
@@ -185,5 +187,5 @@
 	    # Nothing matched to here: unknown line - raise exception
 	    f.close()
-	    raise parse_error("Unknown statement at line %d of %s" % \
+	    raise self.parse_error("Unknown statement at line %d of %s" % \
 		    (lineno, config))
 	f.close()
@@ -287,11 +289,8 @@
 	if not config.has_option("access", "dynamic_projects_url"):
 	    self.allocate_project = \
-		fedd_allocate_project_local(self.dynamic_projects, 
-			None, proj_certs)
+		fedd_allocate_project_local(config)
 	else:
 	    self.allocate_project = \
-		fedd_allocate_project_remote(self.dynamic_projects, 
-			config.get("access", "dynamic_projects_url"),
-			proj_certs)
+		fedd_allocate_project_remote(config)
 
 	# If the project allocator exports services, put them in this object's
@@ -328,17 +327,4 @@
 	    return None
 
-    def strip_unicode(self, obj):
-	"""Loosly de-unicode an object"""
-	if isinstance(obj, dict):
-	    for k in obj.keys():
-		obj[k] = self.strip_unicode(obj[k])
-	    return obj
-	elif isinstance(obj, basestring):
-	    return str(obj)
-	elif getattr(obj, "__iter__", None):
-	    return [ self.strip_unicode(x) for x in obj]
-	else:
-	    return obj
-
     def proxy_xmlrpc_request(self, dt, req):
 	"""Send an XMLRPC proxy request.  Called if the SOAP RPC fails"""
@@ -360,6 +346,5 @@
 	else: url = str(dt)
 
-	r = copy.deepcopy(req)
-	self.strip_unicode(r)
+	r = strip_unicode(copy.deepcopy(req))
 	
 	transport = SSL_Transport(ctx)
@@ -517,5 +502,6 @@
 	# resolve <dynamic> and <same> in found
 	dyn_proj = False
-	dyn_user = False
+	dyn_create_user = False
+	dyn_service_user = False
 
 	if found[0].name == "<same>":
@@ -535,15 +521,27 @@
 	if found[1] == "<same>":
 	    if user_match == "<any>":
-		if user != None: ru = user[0]
+		if user != None: rcu = user[0]
 		else: raise service_error(\
 			service_error.server_config,
 			"Matched <same> on anonymous request")
 	    else:
-		ru = user_match
+		rcu = user_match
 	elif found[1] == "<dynamic>":
-	    ru = None
-	    dyn_user = True
+	    rcu = None
+	    dyn_create_user = True
 	
-	return (rp, ru), (dyn_user, dyn_proj)
+	if found[2] == "<same>":
+	    if user_match == "<any>":
+		if user != None: rsu = user[0]
+		else: raise service_error(\
+			service_error.server_config,
+			"Matched <same> on anonymous request")
+	    else:
+		rsu = user_match
+	elif found[2] == "<dynamic>":
+	    rsu = None
+	    dyn_service_user = True
+
+	return (rp, rcu, rsu), (dyn_create_user, dyn_service_user, dyn_proj)
 
     def build_response(self, alloc_id, ap):
@@ -612,14 +610,16 @@
 			    "Access denied (nodetypes %s)" % \
 			    str(', ').join(inaccessible))
-	    # XXX: This allocates a single user for both service and creation.
-	    # This needs to be made more nuanced.
-	    tmp_ssh = [ x['sshPubkey'] \
-		    for x in req['serviceAccess'] + req['createAccess'] \
-			if x.has_key('sshPubkey')]
-
-	    # Converting to a set collapses duplicates
-	    ssh = set(tmp_ssh)
-
-	    if len(ssh) > 0: 
+	    # These collect the keys for teh two roles into single sets, one
+	    # for creation and one for service.  The sets are a simple way to
+	    # eliminate duplicates
+	    create_ssh = set([ x['sshPubkey'] \
+		    for x in req['createAccess'] \
+			if x.has_key('sshPubkey')])
+
+	    service_ssh = set([ x['sshPubkey'] \
+		    for x in req['serviceAccess'] \
+			if x.has_key('sshPubkey')])
+
+	    if len(create_ssh) > 0 and len(service_ssh) >0: 
 		if dyn[1]: 
 		    # Compose the dynamic project request 
@@ -628,6 +628,15 @@
 				{ 'project' : {\
 				    'user': [ \
-				    { 'access': [ { 'sshPubkey': s } ] } \
-					for s in ssh ] \
+				    { \
+					'access': [ { 'sshPubkey': s } \
+					    for s in service_ssh ], 
+					 'role': "serviceAccess",\
+				    }, \
+				    { \
+					'access': [ { 'sshPubkey': s } \
+					    for s in create_ssh ], 
+					 'role': "experimentCreation",\
+				    }, \
+				    ], \
 				    }\
 				}\
@@ -641,12 +650,28 @@
 		else:
 		    # XXX ssh key additions
-		    ap = { 'project': \
-			    { 'name' : { 'localname' : found[0].name },\
-			      'user' : [ {\
-				'userID': { 'localname' : found[1] }, \
-				'access': [ { 'sshPubkey': s } for s in ssh]}\
-				]\
+		    preq = {'StaticProjectRequestBody' : \
+			    { 'project': \
+				{ 'name' : { 'localname' : found[0].name },\
+				  'user' : [ \
+				    {\
+					'userID': { 'localname' : found[1] }, \
+					'access': [ { 'sshPubkey': s } 
+					    for s in create_ssh ],
+					'role': 'experimentCreation'\
+				    },\
+				    {\
+					'userID': { 'localname' : found[2] }, \
+					'access': [ { 'sshPubkey': s } 
+					    for s in service_ssh ],
+					'role': 'serviceAccess'\
+				    },\
+				]}\
 			    }\
 		    }
+		    if restricted != None and len(restricted) > 0:
+			preq['StaticProjectRequestBody']['resources'] = \
+			    [ {'node': { 'hardware' :  [ h ] } } \
+				    for h in restricted ]
+		    ap = self.allocate_project.static_project(preq)
 	    else:
 		raise service_error(service_error.req, 
