source: fedd/federation/legacy_access.py @ d2e86f6

compt_changesinfo-ops
Last change on this file since d2e86f6 was c7141dc, checked in by Ted Faber <faber@…>, 13 years ago

Single access works

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