#!/usr/local/bin/python import os,sys import stat # for chmod constants import re import random import string import copy import pickle import logging import subprocess from threading import * from M2Crypto.SSL import SSLError from util import * from allocate_project import allocate_project_local, allocate_project_remote from fedid import fedid, generate_fedid from authorizer import authorizer from service_error import service_error from remote_service import xmlrpc_handler, soap_handler, service_caller import httplib import tempfile from urlparse import urlparse import topdl import list_log # Make log messages disappear if noone configures a fedd logger class nullHandler(logging.Handler): def emit(self, record): pass fl = logging.getLogger("fedd.access") fl.addHandler(nullHandler()) class legacy_access: """ This collects the legacy access control helpers in one place. """ class parse_error(RuntimeError): pass def __init__(self): pass def legacy_read_access(self, config, access_obj=None): """ Read an access DB with filename config of the form: (id, id, id) -> attribute, something where the ids can be fedids, strings, or or , attribute is the attribute to assign , and something is any set of charcters. The hash self.access is populated with mappings from those triples to the results of access_obj being called on the remainder of the line (if present). If access_obj is not given, the string itself is entered in the hash. Additionally, a triple with and mapped to None is entered in self.auth with the attribute given. Parsing errors result in a self.parse_error exception being raised. access_obj should throw that as well. """ lineno=0 name_expr = "["+string.ascii_letters + string.digits + "\.\-_]+" fedid_expr = "fedid:[" + string.hexdigits + "]+" key_name = "(||"+fedid_expr + "|"+ name_expr + ")" access_re = re.compile('\('+key_name+'\s*,\s*'+key_name+'\s*,\s*'+ key_name+'\s*\)\s*->\s*([^,]+)\s*(.*)', re.IGNORECASE) def parse_name(n): if n.startswith('fedid:'): return fedid(hexstr=n[len('fedid:'):]) else: return n def auth_name(n): if isinstance(n, basestring): if n =='' or n =='': return None else: return unicode(n) else: return n def strip_comma(s): s = s.strip() if s.startswith(','): s = s[1:].strip() return s if access_obj is None: access_obj = lambda(x): "%s" % x f = open(config, "r"); try: for line in f: lineno += 1 line = line.strip(); if len(line) == 0 or line.startswith('#'): continue # Access line (t, p, u) -> anything m = access_re.match(line) if m != None: access_key = tuple([ parse_name(x) \ for x in m.group(1,2,3)]) attribute = m.group(4) auth_key = tuple([ auth_name(x) for x in access_key]) self.auth.set_attribute(auth_key, attribute) if len(m.group(5)) > 0: access_val = access_obj(strip_comma(m.group(5))) self.access[access_key] = access_val continue # Nothing matched to here: unknown line - raise exception f.close() raise self.parse_error( "Unknown statement at line %d of %s" % \ (lineno, config)) finally: if f: f.close() @staticmethod def permute_wildcards(a, p): """Return a copy of a with various fields wildcarded. The bits of p control the wildcards. A set bit is a wildcard replacement with the lowest bit being user then project then testbed. """ if p & 1: user = [""] else: user = a[2] if p & 2: proj = "" else: proj = a[1] if p & 4: tb = "" else: tb = a[0] return (tb, proj, user) def legacy_find_access(self, search): """ Search the access DB for a match on this tuple. Return the matching access tuple and the user that matched. NB, if the initial tuple fails to match we start inserting wildcards in an order determined by self.project_priority. Try the list of users in order (when wildcarded, there's only one user in the list). """ if self.project_priority: perm = (0, 1, 2, 3, 4, 5, 6, 7) else: perm = (0, 2, 1, 3, 4, 6, 5, 7) for p in perm: s = self.permute_wildcards(search, p) # s[2] is None on an anonymous, unwildcarded request if s[2] != None: for u in s[2]: if self.access.has_key((s[0], s[1], u)): return (self.access[(s[0], s[1], u)], u) else: if self.access.has_key(s): return (self.access[s], None) return None, None def legacy_lookup_access_base(self, req, fid): """ Determine the allowed access for this request. Return the access and which fields are dynamic. The fedid is needed to construct the request """ user_re = re.compile("user:\s(.*)") project_re = re.compile("project:\s(.*)") # Search keys tb = fid user = [ user_re.findall(x)[0] for x in req.get('credential', []) \ if user_re.match(x)] project = [ project_re.findall(x)[0] \ for x in req.get('credential', []) \ if project_re.match(x)] if len(project) == 1: project = project[0] elif len(project) == 0: project = None else: raise service_error(service_error.req, "More than one project credential") # Confirm authorization for u in user: self.log.debug("[lookup_access] Checking access for %s" % \ ((tb, project, u),)) if self.auth.check_attribute((tb, project, u), 'access'): self.log.debug("[lookup_access] Access granted") break else: self.log.debug("[lookup_access] Access Denied") else: raise service_error(service_error.access, "Access denied") # This maps a valid user to the Emulab projects and users to use found, user_match = self.legacy_find_access((tb, project, user)) return (found, (tb, project, user_match))