source: fedd/federation/legacy_access.py @ 1ae1aa2

compt_changesinfo-ops
Last change on this file since 1ae1aa2 was 78f2668, checked in by Ted Faber <faber@…>, 14 years ago

Move some functions from access to legacy_access. Rename functions so abac is the default

  • Property mode set to 100644
File size: 6.2 KB
Line 
1#!/usr/local/bin/python
2
3import os,sys
4import stat # for chmod constants
5import re
6import random
7import string
8import copy
9import pickle
10import logging
11import subprocess
12
13from threading import *
14from M2Crypto.SSL import SSLError
15
16from util import *
17from allocate_project import allocate_project_local, allocate_project_remote
18from fedid import fedid, generate_fedid
19from authorizer import authorizer
20from service_error import service_error
21from remote_service import xmlrpc_handler, soap_handler, service_caller
22
23import httplib
24import tempfile
25from urlparse import urlparse
26
27import topdl
28import list_log
29import proxy_emulab_segment
30import local_emulab_segment
31
32
33# Make log messages disappear if noone configures a fedd logger
34class nullHandler(logging.Handler):
35    def emit(self, record): pass
36
37fl = logging.getLogger("fedd.access")
38fl.addHandler(nullHandler())
39
40class legacy_access:
41    """
42    This collects the legacy access control helpers in one place.
43    """
44
45    class parse_error(RuntimeError): pass
46
47    def __init__(self): pass
48
49    def legacy_read_access(self, config, access_obj=None):
50        """
51        Read an access DB with filename config  of the form:
52            (id, id, id) -> attribute, something
53        where the ids can be fedids, strings, or <any> or <none>, attribute is
54        the attribute to assign , and something is any set of charcters.  The
55        hash self.access is populated with mappings from those triples to the
56        results of access_obj being called on the remainder of the line (if
57        present).  If access_obj is not given, the string itself is entered in
58        the hash.  Additionally, a triple with <any> and <none> mapped to None
59        is entered in self.auth with the attribute given.
60
61        Parsing errors result in a self.parse_error exception being raised.
62        access_obj should throw that as well.
63        """
64        lineno=0
65        name_expr = "["+string.ascii_letters + string.digits + "\.\-_]+"
66        fedid_expr = "fedid:[" + string.hexdigits + "]+"
67        key_name = "(<ANY>|<NONE>|"+fedid_expr + "|"+ name_expr + ")"
68        access_re = re.compile('\('+key_name+'\s*,\s*'+key_name+'\s*,\s*'+
69                key_name+'\s*\)\s*->\s*([^,]+)\s*(.*)', re.IGNORECASE)
70
71        def parse_name(n):
72            if n.startswith('fedid:'): return fedid(hexstr=n[len('fedid:'):])
73            else: return n
74       
75        def auth_name(n):
76            if isinstance(n, basestring):
77                if n =='<any>' or n =='<none>': return None
78                else: return unicode(n)
79            else:
80                return n
81        def strip_comma(s):
82            s = s.strip()
83            if s.startswith(','):
84                s = s[1:].strip()
85            return s
86
87        if access_obj is None:
88            access_obj = lambda(x): "%s" % x
89
90        f = open(config, "r");
91        try:
92            for line in f:
93                lineno += 1
94                line = line.strip();
95                if len(line) == 0 or line.startswith('#'):
96                    continue
97
98                # Access line (t, p, u) -> anything
99                m = access_re.match(line)
100                if m != None:
101                    access_key = tuple([ parse_name(x) \
102                            for x in m.group(1,2,3)])
103                    attribute = m.group(4)
104                    auth_key = tuple([ auth_name(x) for x in access_key])
105                    self.auth.set_attribute(auth_key, attribute)
106                    if len(m.group(5)) > 0:
107                        access_val = access_obj(strip_comma(m.group(5)))
108                        self.access[access_key] = access_val
109                    continue
110
111                # Nothing matched to here: unknown line - raise exception
112                f.close()
113                raise self.parse_error(
114                        "Unknown statement at line %d of %s" % \
115                        (lineno, config))
116        finally:
117            if f: f.close()
118
119    @staticmethod
120    def permute_wildcards(a, p):
121        """Return a copy of a with various fields wildcarded.
122
123        The bits of p control the wildcards.  A set bit is a wildcard
124        replacement with the lowest bit being user then project then testbed.
125        """
126        if p & 1: user = ["<any>"]
127        else: user = a[2]
128        if p & 2: proj = "<any>"
129        else: proj = a[1]
130        if p & 4: tb = "<any>"
131        else: tb = a[0]
132
133        return (tb, proj, user)
134
135    def legacy_find_access(self, search):
136        """
137        Search the access DB for a match on this tuple.  Return the matching
138        access tuple and the user that matched.
139       
140        NB, if the initial tuple fails to match we start inserting wildcards in
141        an order determined by self.project_priority.  Try the list of users in
142        order (when wildcarded, there's only one user in the list).
143        """
144        if self.project_priority: perm = (0, 1, 2, 3, 4, 5, 6, 7)
145        else: perm = (0, 2, 1, 3, 4, 6, 5, 7)
146
147        for p in perm: 
148            s = self.permute_wildcards(search, p)
149            # s[2] is None on an anonymous, unwildcarded request
150            if s[2] != None:
151                for u in s[2]:
152                    if self.access.has_key((s[0], s[1], u)):
153                        return (self.access[(s[0], s[1], u)], u)
154            else:
155                if self.access.has_key(s):
156                    return (self.access[s], None)
157        return None, None
158
159    def legacy_lookup_access_base(self, req, fid):
160        """
161        Determine the allowed access for this request.  Return the access and
162        which fields are dynamic.
163
164        The fedid is needed to construct the request
165        """
166        user_re = re.compile("user:\s(.*)")
167        project_re = re.compile("project:\s(.*)")
168
169        # Search keys
170        tb = fid
171        user = [ user_re.findall(x)[0] for x in req.get('credential', []) \
172                if user_re.match(x)]
173        project = [ project_re.findall(x)[0] \
174                for x in req.get('credential', []) \
175                    if project_re.match(x)]
176
177        if len(project) == 1: project = project[0]
178        elif len(project) == 0: project = None
179        else: 
180            raise service_error(service_error.req, 
181                    "More than one project credential")
182
183        # Confirm authorization
184        for u in user:
185            self.log.debug("[lookup_access] Checking access for %s" % \
186                    ((tb, project, u),))
187            if self.auth.check_attribute((tb, project, u), 'access'):
188                self.log.debug("[lookup_access] Access granted")
189                break
190            else:
191                self.log.debug("[lookup_access] Access Denied")
192        else:
193            raise service_error(service_error.access, "Access denied")
194
195        # This maps a valid user to the Emulab projects and users to use
196        found, user_match = self.legacy_find_access((tb, project, user))
197
198        return (found, (tb, project, user_match))
Note: See TracBrowser for help on using the repository browser.