1 """!Handles data restriction classes.
3 Implements access control mechanisms for NOAA data. Although this was
4 written for the NOAA Restricted Data (rstprod), it can be used for
5 general access control. It is also more general than NOAA, so long as
6 one correctly initializes the produtil.cluster module. The mechanism
7 used depends on the cluster, due to varying capabilities throughout.
8 Some do not implement access control mechanisms that are usable for
9 the restricted data (such as NOAA Jet). For those systems,
10 RstNoAccessControl is raised if one attempts to restrict a file."""
14 __all__= [
'RestrictionClass',
'tag_rstprod',
'rstprod_tagger',
15 'make_rstprod_tagger' ]
18 """!The base class of all exceptions specific to the rstprod module"""
20 """!Raised when the cluster has no access control mechanisms."""
22 """!Raised when a group's id or name could not be determined."""
27 from produtil.acl import ACL, ACL_TYPE_ACCESS, ACL_TYPE_DEFAULT
34 okay_mode = stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR | \
35 stat.S_IRGRP|stat.S_IWGRP|stat.S_IXGRP
38 """!Generates the access control list for the specified restriction
39 class (groupname) and nine bit access permissions (mode).
40 @param groupname the restricted file unix group
41 @param mode required access mode (world access will be removed even
42 if it is present in mode)"""
43 if not isinstance(mode,int):
45 'In acl_text_for_rstclass, the mode must be the integer access mode, not a %s %s'
46 %(type(groupname).__name__,repr(groupname)))
48 if not isinstance(groupname,basestring):
50 'In acl_text_for_rstclass, the groupname must be the string name of a unix group, not a %s %s'
51 %(type(groupname).__name__,repr(groupname)))
52 return "u::%c%c%c,g::---,g:%s:%c%c%c,o::---,m::rwx" % (
53 (
'r' if 0!=imode&stat.S_IRUSR else '-' ),
54 (
'w' if 0!=imode&stat.S_IWUSR
else '-' ),
55 (
'x' if 0!=imode&stat.S_IXUSR
else '-' ),
57 (
'r' if 0!=imode&stat.S_IRGRP else '-' ),
58 (
'w' if 0!=imode&stat.S_IWGRP
else '-' ),
59 (
'x' if 0!=imode&stat.S_IXGRP
else '-' ) )
62 """!This is a python class intended to be used to automate
63 restricting data to a specific restriction class using access
64 control lists or group ownership
68 rc=RestrictionClass("rstprod")
69 rc.restrict_file("/path/to/some/dangerous/file")
72 It can also set the Default Access Control List if supplied a directory:
74 rc.restrict_file("/path/to/some/dangerous/directory/")
76 def __init__(self,group,use_acl=None,logger=None):
77 """!Create a new RestrictionClass object for the specified
79 @param group The group may be the string group name, or the numeric
81 @param use_acl If use_acl is unspecified, then
82 produtil.cluster.use_acl_for_rstdata() is used to decide.
83 @param logger a logging.Logger for log messages"""
84 assert(use_acl
is None)
90 "This cluster cannot be used for NOAA restricted data. It "
91 "uses group quotas, so I cannot control access through "
92 "group IDs. It does not have a functional access control "
93 "list (ACL) mechanism, so I cannot use ACLs.")
96 if isinstance(group,basestring):
99 grent=grp.getgrnam(group)
101 except (EnvironmentError,KeyError)
as e:
102 raise RstBadGroup(
'%s: could not get group id for group: %s'
106 '%s: could not get group id for group. The grp.getgrnam'
107 '(...)[2] returned something that was not an int: a %s %s'
109 elif isinstance(group,int):
111 grent=grp.getgrgid(group)
114 except (EnvironmentError,KeyError)
as e:
115 raise RstBadGroup(
'%s: could not get group id for group: %s'
119 '%d: could not get group name for group. The grp.getgrgid'
120 '(...)[0] returned something that was not an int: a %s %s'
126 "In produtil.rstprod.RestrictionClass.__init__, the group parameter must be the string group name or integer group id. You provided a %s %s"
127 %(type(group).__name__,repr(group)))
128 self.
__allowed=stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR | \
129 stat.S_IRGRP|stat.S_IWGRP|stat.S_IXGRP
133 """!Internal function that generates the ACL dictionary.
136 This is part of the internal implementation of
137 RestrictionClass and should not be used directly. It returns
138 a dict() that maps from integer permission to an ACL object
139 that will set an access control list appropriate for that
140 permission. The user and restriction group will match the old
141 user and group permissions, but other groups will have no
142 permissions, and the "world" permissions will be 0."""
145 for IRUSR
in ( 0, stat.S_IRUSR ):
146 for IWUSR
in ( 0, stat.S_IWUSR ):
147 for IXUSR
in ( 0, stat.S_IXUSR ):
148 for IRGRP
in ( 0, stat.S_IRGRP ):
149 for IWGRP
in ( 0, stat.S_IWGRP ):
150 for IXGRP
in ( 0, stat.S_IXGRP ):
151 mode=IRUSR|IWUSR|IXUSR|IRGRP|IWGRP|IXGRP
159 """!The name of the group used for the restriction class"""
163 """!The numeric ID of the group used for the restriction class"""
167 """!True if ACLs are used for access permission, False if
168 setgid and chgrp are used."""
172 """!Returns an produtil.acl.ACL object for the specified access
173 mode. Will raise an exception if self.use_acl is False.
174 @param st_mode desired access mode"""
175 imode = stat.S_IMODE(st_mode)
180 """!Internal function that uses chgrp to restrict a file's access.
182 This is an internal implementation function that should not be
183 called directly. It handles the non-ACL (chgrp+setgid) case
184 of restrict_file and restrict_gid.
185 @param target the target file
186 @param st_mode the desired mode
187 @param chown chowning function
188 @param chmod chmodding function
189 @param logger a logging.Logger for log messages
191 if logger
is not None:
192 logger.info(
'%s: chgrp to %s'%(str(target),self.
__groupname))
194 if stat.S_ISDIR(st_mode):
195 smode = stat.S_ISGID | (stat.S_IMODE(st_mode)&okay_mode)
196 if logger
is not None:
197 logger.info(
'%s: set mode on directory to 0%o'
198 %(str(target),smode))
199 chmod( target, smode )
201 smode = stat.S_IMODE(st_mode)&okay_mode
202 if logger
is not None:
203 logger.info(
'%s: set mode on file to 0%o'%(str(target),smode))
204 chmod( target, smode )
207 """!Internal function that restricts files using ACLs
209 This is an internal implementation function that should not be
210 called directly. It handles the ACL case of restrict_file.
212 @param target the target file
213 @param st_mode the desired access
214 @param set_acl the acl-setting function
215 @param logger a logging.Logger for log messages """
216 if stat.S_ISDIR(st_mode):
217 if logger
is not None:
218 logger.info(
'%s: use acl to restrict dir to group %s'
220 set_acl(target,ACL_TYPE_ACCESS)
221 set_acl(target,ACL_TYPE_DEFAULT)
223 if logger
is not None:
224 logger.info(
'%s: use acl to restrict file to group %s'
226 set_acl(target,ACL_TYPE_ACCESS)
229 """!Adds the requested restrictions to the specified file or
230 directory. This routine needs to stat the opened file to get
232 @param st_mode To avoid a stat call, send st_mode into the
234 @param filename the target file
235 @param logger a logging.Logger for log messages"""
237 if logger
is not None:
238 logger.info(filename+
': stat file')
242 acl=self.
acl_for(stat.S_IMODE(st_mode))
248 """Adds the requested restrictions to an opened file. This
249 routine needs to stat the opened file to get the stat.st_mode.
250 @param st_mode To avoid a stat call, send st_mode into the optional argument.
251 @param fd the target file descriptor
252 @param logger a logging.Logger for log messages"""
253 if hasattr(fd,
'fileno'):
256 if logger
is not None:
257 logger.info(str(fd)+
': stat fileno')
261 acl=self.
acl_for(stat.S_IMODE(st_mode))
262 if logger
is not None:
263 logger.info(
'%s: set acl of fileno to restrict to group %s'
275 """!Creates the rstprod_tagger object for use by tag_rstprod"""
276 global rstprod_tagger
280 """!Places a file or directory under the rstprod restriction class.
281 This command will attempt to raise RstprodForbidden if it is run
282 on a cluster that is not supposed to have rstprod data (only
283 GAEA, Zeus and WCOSS are allowed).
285 This routine uses the approved rstprod protection mechanisms on
288 * Zeus --- place the file in the rstprod access control list, and
289 make it unreadable to anyone else.
291 * WCOSS --- place the file in group rstprod and remove permissions
294 * GAEA --- same as WCOSS
296 Note that the NOAA Jet cluster is not allowed to contain
297 restricted data, so this routine will raise RstprodForbidden on
299 if rstprod_tagger
is None:
301 if isinstance(target,basestring):
302 rstprod_tagger.restrict_file(target,logger=logger)
303 elif isinstance(target,file)
or isinstance(target,int):
304 rstprod_tagger.restrict_fd(target,logger=logger)
306 raise TypeError(
'The tag_rstprod target argument must be an int, a file '
307 'or a basestring. You supplied a %s %s'
308 %(type(target).__name__,repr(target)))
Manipulates Access Control Lists (ACL)
def groupid(self)
The numeric ID of the group used for the restriction class.
def no_access_control()
True if the cluster provides no means to control access to files.
def make_acl_dict(self)
Internal function that generates the ACL dictionary.
def use_acl(self)
True if ACLs are used for access permission, False if setgid and chgrp are used.
def use_acl_for_rstdata()
Synonym for here.use_acl_for_rstdata.
def acl_text_for_rstclass(groupname, mode)
Generates the access control list for the specified restriction class (groupname) and nine bit access...
def acl_for(self, st_mode)
Returns an produtil.acl.ACL object for the specified access mode.
Provides information about the cluster on which this job is running.
def restrict_file
Adds the requested restrictions to the specified file or directory.
def acl_restrict_file(self, target, st_mode, set_acl, logger)
Internal function that restricts files using ACLs.
The base class of all exceptions specific to the rstprod module.
def __init__
Create a new RestrictionClass object for the specified group.
This is a python class intended to be used to automate restricting data to a specific restriction cla...
Raised when a group's id or name could not be determined.
def groupname(self)
The name of the group used for the restriction class.
def chgrp_restrict(self, target, st_mode, chown, chmod, logger)
Internal function that uses chgrp to restrict a file's access.
Raised when the cluster has no access control mechanisms.
def tag_rstprod
Places a file or directory under the rstprod restriction class.
def make_rstprod_tagger
Creates the rstprod_tagger object for use by tag_rstprod.
ACL class wrapped around the libacl library: