source: fedd/federation/authorizer.py @ 891fcf1

version-3.02
Last change on this file since 891fcf1 was 358e0b8, checked in by Ted Faber <faber@…>, 15 years ago

Some debugging - make str work better on an authorizer

  • Property mode set to 100644
File size: 6.8 KB
Line 
1#/usr/local/bin/python
2
3from fedid import fedid
4from remote_service import service_caller
5from abac_remote_service import abac_service_caller
6from service_error import service_error
7
8import sys
9
10class authorizer_base:
11    """
12    Classes based on this one keep track of authorization attributes for the
13    various modules running.  This base class holds some utility functions that
14    they all potentially use.
15    """
16
17    # general error exception for badly formed names. 
18    class bad_name(RuntimeError): pass
19
20    @staticmethod
21    def auth_name(name):
22        """
23        Helper to convert a non-unicode local name to a unicode string.  Mixed
24        representations can needlessly confuse the authorizer.
25        """
26        if isinstance(name, basestring):
27            if not isinstance(name, unicode): return unicode(name)
28            else: return name
29        else: return name
30
31    @staticmethod
32    def valid_name(name):
33        """
34        Ensure that the given name is valid.  A valid name can either be a
35        triple of strings and fedids representing one of our generalized Emulab
36        names or a single fedid.  Compound names can include wildcards (None)
37        and must anchor to a fedid at their highest level (unless they're all
38        None)
39
40        This either returns True or throws an exception.  More an assertion
41        than a function.
42        """
43        if isinstance(name, tuple) and len(name) == 3:
44            for n in name:
45                if n: 
46                    if not (isinstance(n, basestring) or isinstance(n, fedid)):
47                        raise authorizer_base.bad_name(
48                                "names must be either a triple or a fedid")
49            for n in name: 
50                if n:
51                    if isinstance(n, fedid):
52                        return True
53                    else:
54                        raise authorizer_base.bad_name(
55                                "Compound names must be " + \
56                                "rooted in fedids: %s" % str(name))
57
58            return True
59        elif isinstance(name, fedid):
60            return True
61        else:
62            raise authorizer_base.bad_name(
63                    "Names must be a triple or a fedid (%s)" % name)
64
65
66class authorizer(authorizer_base):
67    """
68    This class keeps track of authorization attributes for the various modules
69    running.  When it gets smarter it will be the basis for a real
70    attribute-based authentication system.
71    """
72    def __init__(self, def_attr="testbed"):
73        self.attrs = { }
74        self.globals=set()
75
76    def set_attribute(self, name, attr):
77        """
78        Attach attr to name.  Multiple attrs can be attached.
79        """
80        self.valid_name(name)
81        if isinstance(name, tuple):
82            aname = tuple([ self.auth_name(n) for n in name])
83        else:
84            aname = self.auth_name(name)
85
86        if not self.attrs.has_key(aname):
87            self.attrs[aname] = set()
88        self.attrs[aname].add(attr)
89
90    def unset_attribute(self, name, attr):
91        """
92        Remove an attribute from name
93        """
94        self.valid_name(name)
95        if isinstance(name, tuple):
96            aname = tuple([ self.auth_name(n) for n in name])
97        else:
98            aname = self.auth_name(name)
99
100        attrs = self.attrs.get(aname, None)
101        if attrs: attrs.discard(attr)
102
103    def check_attribute(self, name, attr):
104        """
105        Return True if name has attr (or if attr is global).  Tuple names match
106        any tuple name that matches all names present and has None entries in
107        other fileds.  For tuple names True implies that there is a matching
108        tuple name with the attribute.
109        """
110        def tup(tup, i, p):
111            mask = 1 << i
112            if p & mask : return authorizer.auth_name(tup[i])
113            else: return None
114
115        self.valid_name(name)
116        if attr in self.globals:
117            return True
118
119        if isinstance(name, tuple):
120            for p in range(0,8):
121                lookup = ( tup(name, 0, p), tup(name,1, p), tup(name,2,p))
122                if self.attrs.has_key(lookup):
123                    if attr in self.attrs[lookup]:
124                        return True
125        else:
126            return  attr in self.attrs.get(self.auth_name(name), set())
127
128    def set_global_attribute(self, attr):
129        """
130        Set a global attribute.  All names, even those otherwise unknown to the
131        authorizer have this attribute.
132        """
133        self.globals.add(attr)
134
135    def unset_global_attribute(self, attr):
136        """
137        Remove a global attribute
138        """
139
140        self.globals.discard(attr)
141
142    def __str__(self):
143        rv = ""
144        rv += "attrs %s\n" % self.attrs
145        rv += "globals %s" % self.globals
146        return rv
147
148class abac_authorizer(authorizer_base):
149    """
150    Use the ABAC authorization system to make attribute decisions.
151    """
152
153    def __init__(self, url=None, cert_file=None, cert_pwd=None,
154            trusted_certs=None, me=None):
155        self.call_CreateContext = abac_service_caller('CreateContext')
156        self.call_CredentialUpdate = abac_service_caller('CredentialUpdate')
157        self.call_Access = abac_service_caller('Access')
158        self.globals = set()
159
160        self.url = url
161        self.cert_file = cert_file
162        self.cert_pwd = cert_pwd
163        self.trusted_certs = trusted_certs
164        self.me = me
165        req = { 'context': { 'self': me.get_hexstr()} }
166        # May throw a service error
167        resp = self.call_CreateContext(self.url, req, self.cert_file,
168                self.cert_pwd, self.trusted_certs, tracefile=sys.stderr)
169
170        if resp.has_key('CreateContextResponseBody'):
171            resp =  resp['CreateContextResponseBody']
172            if resp.has_key('contextID'):
173                self.contextID = resp['contextID']
174            else:
175                raise service_error(service_error.internal, 
176                        "No context ID for the new authorization context")
177        else:
178            raise service_error(service_error.internal, 
179                    "Bad response to creating service context")
180
181    def set_attribute(self, name, attr):
182        self.valid_name(name)
183        if isinstance(name, tuple):
184            if not isinstance(name[0], fedid):
185                raise service_error(service_error.internal, 
186                        "Triple not anchored in testbed")
187            if not name[1] and not name[2]:
188                raise service_error(service_error.internal,
189                        "Anonymous access not yet permitted")
190
191            preconditions = "^".join(
192                    [ "%s.%s" % (name[0], self.auth_name(n)) \
193                            for n in name[1:3] if n ])
194
195            req = { 
196                    'credential': [ '%s.%s<--%s' % \
197                            ( self.me, attr, preconditions) ],
198                    'context': { 'contextID': self.contextID  }
199                }
200        else:
201            req = { 
202                    'credential': [ '%s.%s<--%s' % \
203                            ( self.me, attr, self.auth_name(name)) ],
204                    'context': { 'contextID': self.contextID }
205                }
206        print req
207        self.call_CredentialUpdate(self.url, req, self.cert_file, 
208                self.cert_pwd, self.trusted_certs, tracefile=sys.stderr)
209
210    def check_attribute(self, name, attr):
211        self.valid_name(name)
212        if attr in self.globals: return True
213
214        if isinstance(name, tuple):
215            raise service_error(service_error.internal, 
216                    "Only fedids permitted here")
217        req = {
218                'goal': '%s:%s<<---%s' % (self.me, attr, name),
219                'context': { 'contextID': self.contextID }
220            }
221        resp = self.call_Access(self.url, req, self.cert_file, self.cert_pwd, 
222                self.trusted_certs)
223        if resp.has_key('AccessResponseBody'):
224            resp = resp['AccessResponseBody']
225
226        result = resp.get('result', None)
227        return (result and result.lower() == 'true')
228
229    def set_global_attribute(self, attr):
230        """
231        Set a global attribute.  All names, even those otherwise unknown to the
232        authorizer have this attribute.
233        """
234        self.globals.add(attr)
235
236    def unset_global_attribute(self, attr):
237        """
238        Remove a global attribute
239        """
240
241        self.globals.discard(attr)
242
Note: See TracBrowser for help on using the repository browser.