summaryrefslogtreecommitdiffstats
path: root/cobbler/acls.py
blob: 8e7ecea9998d6397268d88781409fe764c7abf30 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
"""
network acl engine for cobbler

Copyright 2008, Red Hat, Inc
Michael DeHaan <mdehaan@redhat.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301  USA
"""

import sys
import api
import os
import os.path
import commands
import cexceptions
import utils
import fnmatch
import yaml
from cexceptions import *
from utils import _

####################################################

class AclEngine:

    def __init__(self, verbose=False):
        yfh = open("/etc/cobbler/acls.conf")
        data = yfh.read()
        yfh.close()
        self.data = yaml.load(data).next()
        self.verbose = verbose
 
    def __match(self, needle, haystack):
        data = fnmatch.filter([needle], haystack)
        if len(data) == 0:
           return False
        #if self.verbose:
        #   print "matched %s with %s" % (haystack,needle)
        return True

    def can_access(self,found_group,user,resource,arg1,arg2):
        if self.verbose:
            msg = "can_access(%s,%s,%s,%s,%s)" % (found_group,user,resource,arg1,arg2)
        rc = self.__can_access(found_group,user,resource,arg1,arg2)
        if self.verbose:
            print "%s -> %s" % (msg, rc)
        return rc

    def __can_access(self,found_group,user,resource,arg1,arg2):
        
        # since processing is fnmatch based, make sure we are dealing
        # with predicatible strings
        
        # find the rules for the group.  if group is not listed
        # use "unmatched" for the group
        group = found_group
        if not self.data.has_key(found_group):
           group = "unmatched"
        acldata = self.data[group]

        # for all top level patterns 
        patterns = acldata.keys()
        for p in patterns:

            # skip this pattern if it's not matched
            if not self.__match(resource,p):
                continue

            # if matched, what rules do we have under this pattern?
            subpatterns = acldata[p]

            if subpatterns == {}:
                return False


            # if we have subrules, we must look through them
            subkeys = subpatterns.keys()

            # just to keep things happy
            for sk in subkeys:

                if arg1 is not None and self.__match(arg1,sk):

                    subkeys2 = acldata[p][sk]

                    if subkeys2 == {}:
                        # direct match, fail
                        return False

                    else:

                        # FIXME: there are two scenarios here.  Comparing to the basic
                        # values and also comparing based on whether it's "modify-interface"
                        # behavior in which case arg2 is going to be a hash, not a simple
                        # value.  We need to fundamentally rewrite this section.

                        if arg2 is not None and type(arg2) != type({}):
                            # the basic case of setting the value to a normal type
                            # like a string
                            for sk2 in subkeys2:
                                if self.__match(arg2, sk):
                                    # match is a reject, actual value of the key
                                    # is not dealt with.
                                    return False 

                        elif arg2 is not None:

                            # the more advanced case where we're passing in a hash,
                            # check all keys of the hash against the pattern
                            arg_keys = arg2.keys()
                            for sk2 in subkeys2:
                                for arg2 in arg_keys:
                                    if self.__match(arg2, sk2):
                                        return False
        return True                    

if __name__ == "__main__":
   engine = AclEngine(verbose=True)
   engine.can_access("jradmin","foo","sync",None,None)
   engine.can_access("jradmin","foo","save_system",None,None)
   engine.can_access("jradmin","foo","save_profile",None,None)
   engine.can_access("lesstrusted","foo","save_system",None,None)
   engine.can_access("lesstrusted","foo","modify_system","name",None)
   intf_hash = { "ip-address-intf0" : "192.168.1.1" }
   intf_hash1 = { "foosball" : "192.168.1.1" }
   print engine.can_access("lesstrusted","foo","modify_system","modify-interface",intf_hash)
   print engine.can_access("lesstrusted","foo","modify_system","modify-interface",intf_hash1)
   print engine.can_access("admin","foo","modify_system","modify-interface",intf_hash)
   print engine.can_access("jradmin","foo","modify_system","modify-interface",intf_hash)