#
source:
TI12-security/trunk/python/ndg.security.common/ndg/security/common/authz/xacml/cond/__init__.py
@
5395

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.common/ndg/security/common/authz/xacml/cond/__init__.py@5395
Revision 5395, 31.8 KB checked in by pjkersha, 11 years ago (diff) |
---|

Line | |
---|---|

1 | """XACML cond module contains condition function classes |

2 | |

3 | NERC DataGrid Project |

4 | |

5 | This code is adapted from the Sun Java XACML implementation ... |

6 | |

7 | Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. |

8 | |

9 | Redistribution and use in source and binary forms, with or without |

10 | modification, are permitted provided that the following conditions are met: |

11 | |

12 | 1. Redistribution of source code must retain the above copyright notice, |

13 | this list of conditions and the following disclaimer. |

14 | |

15 | 2. Redistribution in binary form must reproduce the above copyright |

16 | notice, this list of conditions and the following disclaimer in the |

17 | documentation and/or other materials provided with the distribution. |

18 | |

19 | Neither the name of Sun Microsystems, Inc. or the names of contributors may |

20 | be used to endorse or promote products derived from this software without |

21 | specific prior written permission. |

22 | |

23 | This software is provided "AS IS," without a warranty of any kind. ALL |

24 | EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING |

25 | ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE |

26 | OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") |

27 | AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE |

28 | AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS |

29 | DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST |

30 | REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, |

31 | INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY |

32 | OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, |

33 | EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. |

34 | |

35 | You acknowledge that this software is not designed or intended for use in |

36 | the design, construction, operation or maintenance of any nuclear facility. |

37 | """ |

38 | __author__ = "P J Kershaw" |

39 | __date__ = "02/04/09" |

40 | __copyright__ = "(C) 2009 Science and Technology Facilities Council" |

41 | __contact__ = "Philip.Kershaw@stfc.ac.uk" |

42 | __license__ = "BSD - see LICENSE file in top-level directory" |

43 | __contact__ = "Philip.Kershaw@stfc.ac.uk" |

44 | __revision__ = "$Id$" |

45 | import logging |

46 | log = logging.getLogger(__name__) |

47 | |

48 | from ndg.security.common.utils import getLocalName |

49 | from ndg.security.common.authz.xacml.exceptions import \ |

50 | UnknownIdentifierException, ParsingException |

51 | from ndg.security.common.authz.xacml.cond.eval import Evaluatable |

52 | from ndg.security.common.authz.xacml.attr import AnyURIAttribute, \ |

53 | Base64BinaryAttribute, BooleanAttribute, DateAttribute, DateTimeAttribute,\ |

54 | DayTimeDurationAttribute, DoubleAttribute, HexBinaryAttribute, \ |

55 | IntegerAttribute, RFC822NameAttribute, StringAttribute, TimeAttribute, \ |

56 | X500NameAttribute, YearMonthDurationAttribute, AttributeFactory, \ |

57 | AttributeDesignator |

58 | |

59 | |

60 | class Apply(Evaluatable): |

61 | '''Represents the XACML ApplyType and ConditionType XML types.''' |

62 | |

63 | def __init__(self, function, evals, bagFunction=None, isCondition=False): |

64 | '''Constructs an Apply object. Throws an |

65 | IllegalArgumentException if the given parameter list |

66 | isn't valid for the given function. |

67 | |

68 | @param function the Function to use in evaluating the elements in the |

69 | apply |

70 | @param evals the contents of the apply which will be the parameters |

71 | to the function, each of which is an Evaluatable |

72 | @param bagFunction the higher-order function to use |

73 | @param isCondition Rrue if this Apply is a Condition, False otherwise |

74 | ''' |

75 | |

76 | # check that the given inputs work for the function |

77 | inputs = evals |

78 | if bagFunction is not None: |

79 | inputs = [bagFunction] |

80 | inputs += evals |

81 | |

82 | function.checkInputs(inputs) |

83 | |

84 | # if everything checks out, then store the inputs |

85 | self._function = function |

86 | self._evals = tuple(evals) |

87 | self.bagFunction = bagFunction |

88 | self.isCondition = isCondition |

89 | |

90 | |

91 | @classmethod |

92 | def getConditionInstance(cls, root): |

93 | '''Returns an instance of an Apply based on the given DOM |

94 | root node. This will actually return a special kind of |

95 | Apply, namely an XML ConditionType, which is the root |

96 | of the condition logic in a RuleType. A ConditionType is the same |

97 | as an ApplyType except that it must use a FunctionId that returns |

98 | a boolean value. |

99 | |

100 | @param root the DOM root of a ConditionType XML type |

101 | ''' |

102 | from ndg.security.common.authz.xacml.cond.factory import \ |

103 | FunctionFactory |

104 | cls.__getInstance(root, FunctionFactory.getConditionInstance(), True) |

105 | |

106 | |

107 | @classmethod |

108 | def getInstance(cls, root): |

109 | '''Returns an instance of Apply based on the given root. |

110 | |

111 | @param root: the ElementTree.Element root of a ConditionType XML type |

112 | @raise ParsingException: if this is not a valid ApplyType |

113 | ''' |

114 | from ndg.security.common.authz.xacml.cond.factory import \ |

115 | FunctionFactory |

116 | cls.__getInstance(root, FunctionFactory.getGeneralInstance(), True) |

117 | |

118 | |

119 | @classmethod |

120 | def __getInstance(cls, root, factory, isCondition): |

121 | '''This is a helper method that is called by the two getInstance |

122 | methods. It takes a factory so we know that we're getting the right |

123 | kind of function.''' |

124 | |

125 | function = cls.__getFunction(root, factory) |

126 | bagFunction = None |

127 | evals = [] |

128 | |

129 | attrFactory = AttributeFactory.getInstance() |

130 | |

131 | for elem in root: |

132 | name = getLocalName(elem) |

133 | |

134 | if name == "Apply": |

135 | evals.append(Apply.getInstance(elem)) |

136 | |

137 | elif name == "AttributeValue": |

138 | try: |

139 | evals.append(attrFactory.createValue(elem)) |

140 | |

141 | except UnknownIdentifierException, e: |

142 | raise ParsingException("Unknown DataType: %s" % e) |

143 | |

144 | elif name == "SubjectAttributeDesignator": |

145 | evals.append(AttributeDesignator.getInstance(elem, |

146 | AttributeDesignator.SUBJECT_TARGET)) |

147 | |

148 | elif name =="ResourceAttributeDesignator": |

149 | evals.append(AttributeDesignator.getInstance(elem, |

150 | AttributeDesignator.RESOURCE_TARGET)) |

151 | |

152 | elif name == "ActionAttributeDesignator": |

153 | evals.append(AttributeDesignator.getInstance(elem, |

154 | AttributeDesignator.ACTION_TARGET)) |

155 | |

156 | elif name == "EnvironmentAttributeDesignator": |

157 | evals.append(AttributeDesignator.getInstance(elem, |

158 | AttributeDesignator.ENVIRONMENT_TARGET)) |

159 | |

160 | elif name == "AttributeSelector": |

161 | evals.append(AttributeSelector.getInstance(elem)) |

162 | |

163 | elif name == "Function": |

164 | # while the schema doesn't enforce this, it's illegal to |

165 | # have more than one FunctionType in a given ApplyType |

166 | if bagFunction != None: |

167 | raise ParsingException("Too many FunctionTypes") |

168 | |

169 | from ndg.security.common.authz.xacml.cond.factory import \ |

170 | FunctionFactory |

171 | bagFunction = cls.__getFunction(elem, |

172 | FunctionFactory.getGeneralInstance()) |

173 | |

174 | return Apply(function, evals, bagFunction, isCondition) |

175 | |

176 | |

177 | @classmethod |

178 | def __getFunction(cls, root, factory): |

179 | '''Helper method that tries to get a function instance''' |

180 | |

181 | functionName = root.attrib["FunctionId"] |

182 | try: |

183 | # try to get an instance of the given function |

184 | return factory.createFunction(functionName) |

185 | |

186 | except UnknownIdentifierException, e: |

187 | raise ParsingException("Unknown FunctionId in Apply: %s" % e) |

188 | |

189 | except FunctionTypeException, e: |

190 | # try creating as an abstract function, using a general factory |

191 | try: |

192 | from ndg.security.common.authz.xacml.cond.factory import \ |

193 | FunctionFactory |

194 | functionFactory = FunctionFactory.getGeneralInstance() |

195 | return functionFactory.createAbstractFunction(functionName, |

196 | root) |

197 | except Exception, e: |

198 | # any exception at this point is a failure |

199 | raise ParsingException("failed to create abstract function %s " |

200 | ": %s" % (functionName, e)) |

201 | |

202 | def getFunction(self): |

203 | '''Returns the Function used by this Apply. |

204 | |

205 | @return the Function''' |

206 | return self._function |

207 | |

208 | def getChildren(self): |

209 | '''Returns the List of children for this Apply. |

210 | The List contains Evaluatables. The list is |

211 | unmodifiable, and may be empty. |

212 | |

213 | @return a List of Evaluatables''' |

214 | return self._evals |

215 | |

216 | def getHigherOrderFunction(self): |

217 | '''Returns the higher order bag function used by this Apply |

218 | if it exists, or null if no higher order function is used. |

219 | |

220 | @return the higher order Function or null''' |

221 | return self.bagFunction |

222 | |

223 | def isCondition(self): |

224 | '''Returns whether or not this ApplyType is actually a ConditionType. |

225 | |

226 | @return whether or not this represents a ConditionType''' |

227 | return isCondition |

228 | |

229 | def evaluate(self, context): |

230 | '''Evaluates the apply object using the given function. This will in |

231 | turn call evaluate on all the given parameters, some of which may be |

232 | other Apply objects. |

233 | |

234 | @param context the representation of the request |

235 | |

236 | @return the result of trying to evaluate this apply object''' |

237 | parameters = self.evals |

238 | |

239 | # see if there is a higher-order function in here |

240 | if bagFunction != None: |

241 | # this is a special case, so we setup the parameters, starting |

242 | # with the function |

243 | parameters = [bagFunction] |

244 | |

245 | # now we evaluate all the parameters, returning INDETERMINATE |

246 | # if that's what any of them return, and otherwise tracking |

247 | # all the AttributeValues that get returned |

248 | for eval in self.evals: |

249 | result = eval.evaluate(context) |

250 | |

251 | # in a higher-order case, if anything is INDETERMINATE, then |

252 | # we stop right away |

253 | if result.indeterminate(): |

254 | return result |

255 | |

256 | parameters.add(result.getAttributeValue()) |

257 | |

258 | # now we can call the base function |

259 | return function.evaluate(parameters, context) |

260 | |

261 | def getType(self): |

262 | '''Returns the type of attribute that this object will return on a call |

263 | to evaluate. In practice, this will always be the same as |

264 | the result of calling getReturnType on the function used |

265 | by this object. |

266 | |

267 | @return the type returned by evaluate''' |

268 | return self.function.getReturnType() |

269 | |

270 | def evaluatesToBag(self): |

271 | '''Returns whether or not the Function will return a bag |

272 | of values on evaluation. |

273 | |

274 | @return true if evaluation will return a bag of values, false otherwise |

275 | ''' |

276 | return self.function.returnsBag() |

277 | |

278 | def encode(self, output, indenter): |

279 | '''Encodes this Apply into its XML representation and |

280 | writes this encoding to the given OutputStream with |

281 | indentation. |

282 | |

283 | @param output a stream into which the XML-encoded data is written |

284 | @param indenter an object that creates indentation strings''' |

285 | raise NotImplementedError() |

286 | |

287 | class Function(object): |

288 | '''Interface that all functions in the system must implement.''' |

289 | |

290 | def evaluate(self, inputs, context): |

291 | '''Evaluates the Function using the given inputs. |

292 | The List contains Evaluatables which are all |

293 | of the correct type if the Function has been created as |

294 | part of an Apply or TargetMatch, but which |

295 | may otherwise be invalid. Each parameter should be evaluated by the |

296 | Function, unless this is a higher-order function (in |

297 | which case the Apply has already evaluated the inputs |

298 | to check for any INDETERMINATE conditions), or the Function |

299 | doesn't need to evaluate all inputs to determine a result (as in the |

300 | case of the or function). The order of the List is |

301 | significant, so a Function should have a very good reason |

302 | if it wants to evaluate the inputs in a different order. |

303 | <p> |

304 | Note that if this is a higher-order function, like any-of, then |

305 | the first argument in the List will actually be a Function |

306 | object representing the function to apply to some bag. In this case, |

307 | the second and any subsequent entries in the list are |

308 | AttributeValue objects (no INDETERMINATE values are |

309 | allowed, so the function is not given the option of dealing with |

310 | attributes that cannot be resolved). A function needs to know if it's |

311 | a higher-order function, and therefore whether or not to look for |

312 | this case. Also, a higher-order function is responsible for checking |

313 | that the inputs that it will pass to the Function |

314 | provided as the first parameter are valid, ie. it must do a |

315 | checkInputs on its sub-function when |

316 | checkInputs is called on the higher-order function. |

317 | |

318 | @param inputs the List of inputs for the function |

319 | @param context the representation of the request |

320 | |

321 | @return a result containing the AttributeValue computed |

322 | when evaluating the function, or Status |

323 | specifying some error condition''' |

324 | raise NotImplementedError() |

325 | |

326 | |

327 | def getIdentifier(self): |

328 | '''Returns the identifier of this function as known by the factories. |

329 | In the case of the standard XACML functions, this will be one of the |

330 | URIs defined in the standard namespace. This function must always |

331 | return the complete namespace and identifier of this function. |

332 | |

333 | @return the function's identifier''' |

334 | raise NotImplementedError() |

335 | |

336 | def getReturnType(self): |

337 | '''Provides the type of AttributeValue that this function |

338 | returns from evaluate in a successful evaluation. |

339 | |

340 | @return the type returned by this function |

341 | ''' |

342 | raise NotImplementedError() |

343 | |

344 | def returnsBag(self): |

345 | '''Tells whether this function will return a bag of values or just a |

346 | single value. |

347 | |

348 | @return true if evaluation will return a bag, false otherwise''' |

349 | raise NotImplementedError() |

350 | |

351 | def checkInputs(self, inputs): |

352 | '''Checks that the given inputs are of the right types, in the right |

353 | order, and are the right number for this function to evaluate. If |

354 | the function cannot accept the inputs for evaluation, an |

355 | IllegalArgumentException is thrown. |

356 | |

357 | @param inputs a list of Evaluatables, with the first argument being a |

358 | Function if this is a higher-order function |

359 | |

360 | @throws TypeError if the inputs do match what the function accepts for |

361 | evaluation |

362 | ''' |

363 | raise NotImplementedError() |

364 | |

365 | def checkInputsNoBag(self, inputs): |

366 | '''Checks that the given inputs are of the right types, in the right |

367 | order, and are the right number for this function to evaluate. If |

368 | the function cannot accept the inputs for evaluation, an |

369 | IllegalArgumentException is thrown. Unlike the other |

370 | checkInput method in this interface, this assumes that |

371 | the parameters will never provide bags of values. This is useful if |

372 | you're considering a target function which has a designator or |

373 | selector in its input list, but which passes the values from the |

374 | derived bags one at a time to the function, so the function doesn't |

375 | have to deal with the bags that the selector or designator |

376 | generates. |

377 | |

378 | @param inputs a list of Evaluatables, with the first argument being a |

379 | Function if this is a higher-order function |

380 | |

381 | @throws TypeError if the inputs do match what the function accepts for |

382 | evaluation''' |

383 | raise NotImplementedError() |

384 | |

385 | |

386 | class FunctionBase(Function): |

387 | FUNCTION_NS = "urn:oasis:names:tc:xacml:1.0:function:" |

388 | supportedIdentifiers = () |

389 | |

390 | def __init__(self, |

391 | functionName, |

392 | functionId=None, |

393 | paramType=None, |

394 | paramIsBag=False, |

395 | numParams=-1, |

396 | minParams=0, |

397 | returnType='', |

398 | returnsBag=False): |

399 | ''' |

400 | @param functionName: the name of this function as used by the factory |

401 | and any XACML policies |

402 | @param functionId: an optional identifier that can be used by your |

403 | code for convenience |

404 | @param paramType: the type of each parameter, in order, required by |

405 | this function, as used by the factory and any XACML |

406 | documents |

407 | @param paramIsBag: whether or not each parameter is actually a bag |

408 | of values |

409 | @param numParams: the number of parameters required by this function, |

410 | or -1 if any number are allowed |

411 | @param minParams: the minimum number of parameters required if |

412 | numParams is -1 |

413 | @param returnType: the type returned by this function, as used by |

414 | the factory and any XACML documents |

415 | @param returnsBag: whether or not this function returns a bag of values |

416 | ''' |

417 | self.functionName = functionName |

418 | self.functionId = functionId |

419 | self.returnType = returnType |

420 | self.returnsBag = returnsBag |

421 | |

422 | self.paramType = paramType |

423 | |

424 | if isinstance(self.paramType, (list, tuple)): |

425 | if not self.paramType: |

426 | raise TypeError('"paramType" is set to an empty list or tuple') |

427 | self.singleType = False |

428 | |

429 | # Keep this test within the paramType is-a-list if-block otherwise |

430 | # it may fail checking the length of a bool |

431 | if len(paramIsBag) != len(self.paramType): |

432 | raise TypeError('"paramIsBag" and "paramType" inputs must ' |

433 | 'have the same length') |

434 | else: |

435 | self.singleType = True |

436 | |

437 | # These only apply if the input parameters are all of a single type |

438 | self.numParams = numParams |

439 | self.minParams = minParams |

440 | |

441 | self.paramIsBag = paramIsBag |

442 | |

443 | |

444 | def _setFunctionName(self, functionName): |

445 | if functionName not in self.__class__.supportedIdentifiers: |

446 | functionList = ', '.join(self.__class__.supportedIdentifiers) |

447 | raise TypeError("Function name [%s] is not on of the recognised " |

448 | "types: %s" % (functionName, functionList)) |

449 | self._functionName = functionName |

450 | |

451 | def _getFunctionName(self): |

452 | return getattr(self, '_functionName', None) |

453 | |

454 | functionName = property(fset=_setFunctionName, |

455 | fget=_getFunctionName) |

456 | |

457 | def checkInputs(self, inputs): |

458 | '''Checks that the given inputs are of the right types, in the right |

459 | order, and are the right number for this function to evaluate.''' |

460 | raise NotImplementedError() |

461 | |

462 | def checkInputsNoBag(self, inputs): |

463 | '''Default handling of input checking. This does some simple checking |

464 | based on the type of constructor used. If you need anything more |

465 | complex, or if you used the simple constructor, then you must |

466 | override this method. |

467 | |

468 | @param inputs: a list of Evaluatable instances |

469 | |

470 | @raise TypeError: if the inputs won't work |

471 | ''' |

472 | numInputs = len(inputs) |

473 | |

474 | if self.singleType: |

475 | # first check to see if we need bags |

476 | if sum(self.paramIsBag): |

477 | raise TypeError('"%s" needs bags on input' % self.functionName) |

478 | |

479 | # now check on the length |

480 | if self.numParams != -1: |

481 | if numInputs != self.numParams: |

482 | raise TypeError('wrong number of args to "%s"' % |

483 | self.functionName) |

484 | else: |

485 | if numInputs < self.minParams: |

486 | raise TypeError("not enough args to " % self.functionName) |

487 | |

488 | |

489 | # finally check param list |

490 | for eval in inputs: |

491 | if eval.getType().toString() != self.paramType: |

492 | raise TypeError("Illegal parameter: input type is %s but " |

493 | "%s type is %s" % |

494 | (eval.getType().toString(), |

495 | self.__class__.__name__, |

496 | self.paramType)) |

497 | |

498 | else: |

499 | # first, check the length of the inputs |

500 | if len(self.paramType) != numInputs: |

501 | raise TypeError('Wrong number of args to "%s"' % |

502 | self.functionName) |

503 | |

504 | # Ensure everything is of the same, correct type |

505 | it = zip(inputs, self.paramType, self.paramIsBag) |

506 | for eval, paramType, paramIsBag in it: |

507 | if eval.type != paramType or paramIsBag: |

508 | raise TypeError("Illegal parameter: input type is %s but " |

509 | "%s type is %s" % |

510 | (eval.type, |

511 | self.__class__.__name__, |

512 | paramType)) |

513 | |

514 | |

515 | def evaluate(self, inputs, context): |

516 | '''Evaluates the Function using the given inputs.''' |

517 | raise NotImplementedError() |

518 | |

519 | def evalArgs(self, params, context, args): |

520 | '''Evaluates each of the parameters, in order, filling in the argument |

521 | array with the resulting values. If any error occurs, this method |

522 | returns the error, otherwise null is returned, signalling that |

523 | evaluation was successful for all inputs, and the resulting argument |

524 | list can be used. |

525 | |

526 | @param params a list of Evaluatable objects representing the parameters |

527 | to evaluate |

528 | @param context the representation of the request |

529 | @param args an array as long as the params list that will, on return, |

530 | contain the AttributeValues generated from evaluating all parameters |

531 | |

532 | @return None if no errors were encountered, otherwise |

533 | an EvaluationResult representing the error |

534 | ''' |

535 | index = 0 |

536 | |

537 | for eval in params: |

538 | # get and evaluate the next parameter |

539 | result = eval.evaluate(context) |

540 | |

541 | # If there was an error, pass it back... |

542 | if result.indeterminate(): |

543 | return result |

544 | |

545 | # ...otherwise save it and keep going |

546 | args[index] = result.getAttributeValue() |

547 | index += 1 |

548 | |

549 | return None |

550 | |

551 | # TODO: Condition classes - minimal implementation until opportunity to fully |

552 | # implement |

553 | class BagFunction(FunctionBase): |

554 | def __init__(self, *arg, **kw): |

555 | raise NotImplementedError() |

556 | |

557 | class SetFunction(FunctionBase): |

558 | def __init__(self, *arg, **kw): |

559 | raise NotImplementedError() |

560 | |

561 | class ConditionBagFunction(BagFunction): |

562 | def __init__(self, *arg, **kw): |

563 | raise NotImplementedError() |

564 | |

565 | class ConditionSetFunction(FunctionBase): |

566 | def __init__(self, *arg, **kw): |

567 | raise NotImplementedError() |

568 | |

569 | class HigherOrderFunction(Function): |

570 | supportedIdentifiers = () |

571 | def __init__(self, *arg, **kw): |

572 | raise NotImplementedError() |

573 | |

574 | # TODO: Function classes - minimal implementation until opportunity to fully |

575 | # implement |

576 | class LogicalFunction(FunctionBase): |

577 | |

578 | def __init__(self, *arg, **kw): |

579 | raise NotImplementedError() |

580 | |

581 | class NOfFunction(FunctionBase): |

582 | |

583 | def __init__(self, *arg, **kw): |

584 | raise NotImplementedError() |

585 | |

586 | class NotFunction(FunctionBase): |

587 | |

588 | def __init__(self, *arg, **kw): |

589 | raise NotImplementedError() |

590 | |

591 | class ComparisonFunction(FunctionBase): |

592 | |

593 | def __init__(self, *arg, **kw): |

594 | raise NotImplementedError() |

595 | |

596 | class MatchFunction(FunctionBase): |

597 | NAME_REGEXP_STRING_MATCH = FunctionBase.FUNCTION_NS + "regexp-string-match" |

598 | NAME_RFC822NAME_MATCH = FunctionBase.FUNCTION_NS + "rfc822Name-match" |

599 | NAME_X500NAME_MATCH = FunctionBase.FUNCTION_NS + "x500Name-match" |

600 | |

601 | supportedIdentifiers = ( |

602 | NAME_REGEXP_STRING_MATCH, |

603 | NAME_RFC822NAME_MATCH, |

604 | NAME_X500NAME_MATCH) |

605 | |

606 | functionIds = range(3) |

607 | ID_REGEXP_STRING_MATCH, ID_X500NAME_MATCH, ID_RFC822NAME_MATCH=functionIds |

608 | getId = dict(zip(supportedIdentifiers, functionIds)) |

609 | argParams = ( |

610 | (StringAttribute.identifier,)*2, |

611 | (X500NameAttribute.identifier,)*2, |

612 | (StringAttribute.identifier, |

613 | RFC822NameAttribute.identifier) |

614 | ) |

615 | getArgumentTypes = dict(zip(supportedIdentifiers, argParams)) |

616 | |

617 | bagParams = (False, False) |

618 | |

619 | lut = { |

620 | NAME_REGEXP_STRING_MATCH: 'regexpStringMatch', |

621 | NAME_RFC822NAME_MATCH: 'rfc822NameMatch', |

622 | NAME_X500NAME_MATCH: 'x500NameMatch' |

623 | } |

624 | |

625 | def __init__(self, functionName, **kw): |

626 | super(MatchFunction, self).__init__(functionName, |

627 | functionId=MatchFunction.getId[functionName], |

628 | paramType=MatchFunction.getArgumentTypes[functionName], |

629 | paramIsBag=MatchFunction.bagParams, |

630 | returnType=BooleanAttribute.identifier, |

631 | returnsBag=False) |

632 | |

633 | |

634 | def regexpStringMatch(self, regex, val): |

635 | return re.match(regex, val) is not None |

636 | |

637 | def rfc822NameMatch(self, *inputs): |

638 | raise NotImplementedError() |

639 | |

640 | def x500NameMatch(self, *inputs): |

641 | raise NotImplementedError() |

642 | |

643 | def evaluate(self, inputs, context): |

644 | matchFunction = getattr(self, MatchFunction.lut[self.functionName]) |

645 | match = matchFunction(self, *inputs) |

646 | if match: |

647 | return EvaluationResult(status=Status.STATUS_OK) |

648 | |

649 | |

650 | class EqualFunction(FunctionBase): |

651 | supportedIdentifiers = ( |

652 | FunctionBase.FUNCTION_NS + "anyURI-equal", |

653 | FunctionBase.FUNCTION_NS + "base64Binary-equal", |

654 | FunctionBase.FUNCTION_NS + "boolean-equal", |

655 | FunctionBase.FUNCTION_NS + "date-equal", |

656 | FunctionBase.FUNCTION_NS + "dateTime-equal", |

657 | FunctionBase.FUNCTION_NS + "dayTimeDuration-equal", |

658 | FunctionBase.FUNCTION_NS + "double-equal", |

659 | FunctionBase.FUNCTION_NS + "hexBinary-equal", |

660 | FunctionBase.FUNCTION_NS + "integer-equal", |

661 | FunctionBase.FUNCTION_NS + "rfc822Name-equal", |

662 | FunctionBase.FUNCTION_NS + "string-equal", |

663 | FunctionBase.FUNCTION_NS + "time-equal", |

664 | FunctionBase.FUNCTION_NS + "x500Name-equal", |

665 | FunctionBase.FUNCTION_NS + "yearMonthDuration-equal" |

666 | ) |

667 | |

668 | (NAME_ANYURI_EQUAL, |

669 | NAME_BASE64BINARY_EQUAL, |

670 | NAME_BOOLEAN_EQUAL, |

671 | NAME_DATE_EQUAL, |

672 | NAME_DATETIME_EQUAL, |

673 | NAME_DAYTIME_DURATION_EQUAL, |

674 | NAME_DOUBLE_EQUAL, |

675 | NAME_HEXBINARY_EQUAL, |

676 | NAME_INTEGER_EQUAL, |

677 | NAME_RFC822NAME_EQUAL, |

678 | NAME_STRING_EQUAL, |

679 | NAME_TIME_EQUAL, |

680 | NAME_X500NAME_EQUAL, |

681 | NAME_YEARMONTH_DURATION_EQUAL) = supportedIdentifiers |

682 | |

683 | lut = { |

684 | NAME_STRING_EQUAL: 'stringEqual' |

685 | } |

686 | |

687 | _attrClasses = ( |

688 | AnyURIAttribute, |

689 | Base64BinaryAttribute, |

690 | BooleanAttribute, |

691 | DateAttribute, |

692 | DateTimeAttribute, |

693 | DayTimeDurationAttribute, |

694 | DoubleAttribute, |

695 | HexBinaryAttribute, |

696 | IntegerAttribute, |

697 | RFC822NameAttribute, |

698 | StringAttribute, |

699 | TimeAttribute, |

700 | X500NameAttribute, |

701 | YearMonthDurationAttribute |

702 | ) |

703 | |

704 | typeMap = dict([(i, j.identifier) for i,j in zip(supportedIdentifiers, |

705 | _attrClasses)]) |

706 | |

707 | def __init__(self, functionName, argumentType=None, **kw): |

708 | if kw.get('functionId') is None: |

709 | kw['functionId'] = functionName |

710 | |

711 | if kw.get('paramType') is None: |

712 | kw['paramType'] = EqualFunction._getArgumentType(functionName) |

713 | |

714 | super(EqualFunction, self).__init__(functionName, **kw) |

715 | |

716 | def evaluate(self, inputs, evaluationCtx): |

717 | function = EqualFunction.lut.get(self.functionName) |

718 | if function is None: |

719 | if self.functionName in supportedIdentifiers: |

720 | raise NotImplementedError("No implementation is available for " |

721 | "%s" % self.functionName) |

722 | else: |

723 | raise AttributeError('function name "%s" not recognised ' |

724 | 'for %s' % (self.functionName, |

725 | self.__class__.__name__)) |

726 | |

727 | return getattr(self, function)(inputs, evaluationCtx) |

728 | |

729 | def stringEqual(self, inputs, evaluationCtx): |

730 | result = self.evalArgs(inputs, context, argValues) |

731 | if result is not None: |

732 | return result |

733 | |

734 | return EvaluationResult(argValues[0] == argValues[1]) |

735 | |

736 | @classmethod |

737 | def _getArgumentType(cls, functionName): |

738 | argumentType = cls.typeMap.get(functionName) |

739 | if argumentType is None: |

740 | if functionName in cls.supportedIdentifiers: |

741 | raise NotImplementedError('No implementation is currently ' |

742 | 'available for "%s"' % functionName) |

743 | else: |

744 | raise TypeError("Not a standard function: %s" % functionName) |

745 | |

746 | return argumentType |

747 | |

748 | class AddFunction(FunctionBase): |

749 | |

750 | def __init__(self, *arg, **kw): |

751 | raise NotImplementedError() |

752 | |

753 | class SubtractFunction(FunctionBase): |

754 | |

755 | def __init__(self, *arg, **kw): |

756 | raise NotImplementedError() |

757 | |

758 | class MultiplyFunction(FunctionBase): |

759 | |

760 | def __init__(self, *arg, **kw): |

761 | raise NotImplementedError() |

762 | |

763 | class DivideFunction(FunctionBase): |

764 | |

765 | def __init__(self, *arg, **kw): |

766 | raise NotImplementedError() |

767 | |

768 | class ModFunction(FunctionBase): |

769 | |

770 | def __init__(self, *arg, **kw): |

771 | raise NotImplementedError() |

772 | |

773 | class AbsFunction(FunctionBase): |

774 | |

775 | def __init__(self, *arg, **kw): |

776 | raise NotImplementedError() |

777 | |

778 | class RoundFunction(FunctionBase): |

779 | |

780 | def __init__(self, *arg, **kw): |

781 | raise NotImplementedError() |

782 | |

783 | class FloorFunction(FunctionBase): |

784 | |

785 | def __init__(self, *arg, **kw): |

786 | raise NotImplementedError() |

787 | |

788 | class DateMathFunction(FunctionBase): |

789 | |

790 | def __init__(self, *arg, **kw): |

791 | raise NotImplementedError() |

792 | |

793 | class GeneralBagFunction(BagFunction): |

794 | |

795 | def __init__(self, *arg, **kw): |

796 | raise NotImplementedError() |

797 | |

798 | class NumericConvertFunction(FunctionBase): |

799 | |

800 | def __init__(self, *arg, **kw): |

801 | raise NotImplementedError() |

802 | |

803 | class StringNormalizeFunction(FunctionBase): |

804 | |

805 | def __init__(self, *arg, **kw): |

806 | raise NotImplementedError() |

807 | |

808 | class GeneralSetFunction(SetFunction): |

809 | |

810 | def __init__(self, *arg, **kw): |

811 | raise NotImplementedError() |

812 | |

813 | class MapFunction(Function): |

814 | supportedIdentifiers = () |

815 | NAME_MAP = FunctionBase.FUNCTION_NS + "map" |

816 | |

817 | def __init__(self, *arg, **kw): |

818 | raise NotImplementedError() |

819 | |

820 | @classmethod |

821 | def getInstance(cls, root): |

822 | raise NotImplementedError() |

823 | |

824 | class FunctionProxy(): |

825 | |

826 | def getInstance(self, root): |

827 | raise NotImplementedError() |

828 | |

829 | class MapFunctionProxy(FunctionProxy): |

830 | |

831 | def getInstance(self, root): |

832 | return MapFunction.getInstance(root) |

**Note:**See TracBrowser for help on using the repository browser.