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

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

Line | |
---|---|

1 | """XACML cond module contains condition function classes and class factories |

2 | |

3 | NERC DataGrid Project |

4 | """ |

5 | __author__ = "P J Kershaw" |

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

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

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

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

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

11 | __revision__ = "$Id$" |

12 | class FunctionBase(XacmlBase): |

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

14 | |

15 | def __init__(self, |

16 | functionName, |

17 | functionId=None, |

18 | paramType=None, |

19 | paramIsBag=False, |

20 | numParams=0, |

21 | minParams=0, |

22 | returnType='', |

23 | returnsBag=False): |

24 | |

25 | self.functionName = functionName |

26 | self.functionId = functionId |

27 | self.returnType = None |

28 | self.returnsBag = False |

29 | |

30 | self.singleType = True; |

31 | |

32 | self.paramType = paramType |

33 | self.paramIsBag = paramIsBag |

34 | self.numParams = numParams |

35 | self.minParams = minParams |

36 | |

37 | |

38 | def _setFunctionName(self, functionName): |

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

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

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

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

43 | self._functionName = functionName |

44 | |

45 | def _getFunctionName(self): |

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

47 | |

48 | functionName = property(fset=_setFunctionName, |

49 | fget=_getFunctionName) |

50 | |

51 | def checkInputs(self, inputs): |

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

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

54 | raise NotImplementedError() |

55 | |

56 | def checkInputsNoBag(self, inputs): |

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

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

59 | raise NotImplementedError() |

60 | |

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

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

63 | raise NotImplementedError() |

64 | |

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

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

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

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

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

70 | list can be used. |

71 | |

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

73 | to evaluate |

74 | @param context the representation of the request |

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

76 | contain the AttributeValues generated from evaluating all parameters |

77 | |

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

79 | an EvaluationResult representing the error |

80 | ''' |

81 | index = 0 |

82 | |

83 | for eval in params: |

84 | # get and evaluate the next parameter |

85 | result = eval.evaluate(context) |

86 | |

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

88 | if result.indeterminate(): |

89 | return result |

90 | |

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

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

93 | index += 1 |

94 | |

95 | return None |

96 | |

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

98 | # implement |

99 | class Function(XacmlBase): |

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

101 | raise NotImplementedError() |

102 | |

103 | @classmethod |

104 | def getInstance(cls, root): |

105 | raise NotImplementedError() |

106 | |

107 | class BagFunction(FunctionBase): |

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

109 | raise NotImplementedError() |

110 | |

111 | class SetFunction(FunctionBase): |

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

113 | raise NotImplementedError() |

114 | |

115 | class ConditionBagFunction(BagFunction): |

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

117 | raise NotImplementedError() |

118 | |

119 | class ConditionSetFunction(FunctionBase): |

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

121 | raise NotImplementedError() |

122 | |

123 | class HigherOrderFunction(Function): |

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

125 | raise NotImplementedError() |

126 | |

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

128 | # implement |

129 | class LogicalFunction(FunctionBase): |

130 | |

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

132 | raise NotImplementedError() |

133 | |

134 | class NOfFunction(FunctionBase): |

135 | |

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

137 | raise NotImplementedError() |

138 | |

139 | class NotFunction(FunctionBase): |

140 | |

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

142 | raise NotImplementedError() |

143 | |

144 | class ComparisonFunction(FunctionBase): |

145 | |

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

147 | raise NotImplementedError() |

148 | |

149 | class MatchFunction(FunctionBase): |

150 | NAME_REGEXP_STRING_MATCH = \ |

151 | "urn:oasis:names:tc:xacml:1.0:function:regexp-string-match" |

152 | NAME_RFC822NAME_MATCH = \ |

153 | "urn:oasis:names:tc:xacml:1.0:function:rfc822Name-match" |

154 | NAME_X500NAME_MATCH = \ |

155 | "urn:oasis:names:tc:xacml:1.0:function:x500Name-match" |

156 | |

157 | supportedIdentifiers = (NAME_REGEXP_STRING_MATCH, |

158 | NAME_RFC822NAME_MATCH, |

159 | NAME_X500NAME_MATCH) |

160 | |

161 | lut = { |

162 | NAME_REGEXP_STRING_MATCH: 'regexpStringMatch', |

163 | NAME_RFC822NAME_MATCH: 'rfc822NameMatch', |

164 | NAME_X500NAME_MATCH: 'x500NameMatch' |

165 | } |

166 | |

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

168 | super(MatchFunction, self).__init__(functionName, **kw) |

169 | |

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

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

172 | |

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

174 | raise NotImplementedError() |

175 | |

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

177 | raise NotImplementedError() |

178 | |

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

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

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

182 | if match: |

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

184 | |

185 | |

186 | class EqualFunction(FunctionBase): |

187 | supportedIdentifiers = ( |

188 | "urn:oasis:names:tc:xacml:1.0:function:anyURI-equal", |

189 | "urn:oasis:names:tc:xacml:1.0:function:base64Binary-equal", |

190 | "urn:oasis:names:tc:xacml:1.0:function:boolean-equal", |

191 | "urn:oasis:names:tc:xacml:1.0:function:date-equal", |

192 | "urn:oasis:names:tc:xacml:1.0:function:dateTime-equal", |

193 | "urn:oasis:names:tc:xacml:1.0:function:dayTimeDuration-equal", |

194 | "urn:oasis:names:tc:xacml:1.0:function:double-equal", |

195 | "urn:oasis:names:tc:xacml:1.0:function:hexBinary-equal", |

196 | "urn:oasis:names:tc:xacml:1.0:function:integer-equal", |

197 | "urn:oasis:names:tc:xacml:1.0:function:rfc822Name-equal", |

198 | "urn:oasis:names:tc:xacml:1.0:function:string-equal", |

199 | "urn:oasis:names:tc:xacml:1.0:function:time-equal", |

200 | "urn:oasis:names:tc:xacml:1.0:function:x500Name-equal", |

201 | "urn:oasis:names:tc:xacml:1.0:function:yearMonthDuration-equal" |

202 | ) |

203 | |

204 | (NAME_ANYURI_EQUAL, |

205 | NAME_BASE64BINARY_EQUAL, |

206 | NAME_BOOLEAN_EQUAL, |

207 | NAME_DATE_EQUAL, |

208 | NAME_DATETIME_EQUAL, |

209 | NAME_DAYTIME_DURATION_EQUAL, |

210 | NAME_DOUBLE_EQUAL, |

211 | NAME_HEXBINARY_EQUAL, |

212 | NAME_INTEGER_EQUAL, |

213 | NAME_RFC822NAME_EQUAL, |

214 | NAME_STRING_EQUAL, |

215 | NAME_TIME_EQUAL, |

216 | NAME_X500NAME_EQUAL, |

217 | NAME_YEARMONTH_DURATION_EQUAL) = supportedIdentifiers |

218 | |

219 | lut = { |

220 | NAME_STRING_EQUAL: 'stringEqual' |

221 | } |

222 | |

223 | typeMap = {NAME_STRING_EQUAL: basestring} |

224 | |

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

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

227 | |

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

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

230 | if function is None: |

231 | if self.functionName in supportedIdentifiers: |

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

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

234 | else: |

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

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

237 | self.__class__.__name__)) |

238 | |

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

240 | |

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

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

243 | if result is not None: |

244 | return result |

245 | |

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

247 | |

248 | def getArgumentType(functionName): |

249 | datatype = EqualFunction.typeMap.get(functionName); |

250 | if datatype is None: |

251 | raise AttributeError("Not a standard function: %s" % functionName) |

252 | |

253 | return datatype |

254 | |

255 | class AddFunction(FunctionBase): |

256 | |

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

258 | raise NotImplementedError() |

259 | |

260 | class SubtractFunction(FunctionBase): |

261 | |

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

263 | raise NotImplementedError() |

264 | |

265 | class MultiplyFunction(FunctionBase): |

266 | |

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

268 | raise NotImplementedError() |

269 | |

270 | class DivideFunction(FunctionBase): |

271 | |

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

273 | raise NotImplementedError() |

274 | |

275 | class ModFunction(FunctionBase): |

276 | |

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

278 | raise NotImplementedError() |

279 | |

280 | class AbsFunction(FunctionBase): |

281 | |

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

283 | raise NotImplementedError() |

284 | |

285 | class RoundFunction(FunctionBase): |

286 | |

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

288 | raise NotImplementedError() |

289 | |

290 | class FloorFunction(FunctionBase): |

291 | |

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

293 | raise NotImplementedError() |

294 | |

295 | class DateMathFunction(FunctionBase): |

296 | |

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

298 | raise NotImplementedError() |

299 | |

300 | class GeneralBagFunction(BagFunction): |

301 | |

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

303 | raise NotImplementedError() |

304 | |

305 | class NumericConvertFunction(FunctionBase): |

306 | |

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

308 | raise NotImplementedError() |

309 | |

310 | class StringNormalizeFunction(FunctionBase): |

311 | |

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

313 | raise NotImplementedError() |

314 | |

315 | class GeneralSetFunction(SetFunction): |

316 | |

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

318 | raise NotImplementedError() |

319 | |

320 | class MapFunction(Function): |

321 | supportedIdentifiers = () |

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

323 | |

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

325 | raise NotImplementedError() |

326 | |

327 | @classmethod |

328 | def getInstance(cls, root): |

329 | raise NotImplementedError() |

330 | |

331 | class FunctionProxy(): |

332 | |

333 | def getInstance(self, root): |

334 | raise NotImplementedError() |

335 | |

336 | class MapFunctionProxy(FunctionProxy): |

337 | |

338 | def getInstance(self, root): |

339 | return MapFunction.getInstance(root) |

340 | |

341 | |

342 | class UnknownIdentifierException(Exception): |

343 | pass |

344 | |

345 | class FunctionTypeException(Exception): |

346 | pass |

347 | |

348 | class ParsingException(Exception): |

349 | pass |

350 | |

351 | class FunctionFactory(XacmlBase): |

352 | '''Factory used to create all functions. There are three kinds of factories: |

353 | general, condition, and target. These provide functions that can be used |

354 | anywhere, only in a condition's root and only in a target (respectively). |

355 | |

356 | Note that all functions, except for abstract functions, are singletons, so |

357 | any instance that is added to a factory will be the same one returned |

358 | from the create methods. This is done because most functions don't have |

359 | state, so there is no need to have more than one, or to spend the time |

360 | creating multiple instances that all do the same thing.''' |

361 | |

362 | defaultFactoryProxy = StandardFunctionFactory() |

363 | |

364 | @classmethod |

365 | def getTargetInstance(cls): |

366 | '''Returns the default FunctionFactory that will only provide those |

367 | functions that are usable in Target matching. |

368 | |

369 | @return a FunctionFactory for target functions''' |

370 | return cls.defaultFactoryProxy.getTargetFactory() |

371 | |

372 | @classmethod |

373 | def getConditionInstance(cls): |

374 | '''Returns the default FunctionFactory that provides access to all the |

375 | functions. These Functions are a superset of the Condition functions. |

376 | |

377 | @return a FunctionFactory for all functions |

378 | ''' |

379 | return cls.defaultFactoryProxy.getConditionFactory() |

380 | |

381 | @classmethod |

382 | def getGeneralInstance(cls): |

383 | '''Sets the default factory. Note that this is just a place holder for |

384 | now, and will be replaced with a more useful mechanism soon.''' |

385 | return cls.defaultFactoryProxy.getGeneralFactory() |

386 | |

387 | |

388 | def addFunction(self, function): |

389 | '''Adds the function to the factory. Most functions have no state, so |

390 | the singleton model used here is typically desirable. The factory will |

391 | not enforce the requirement that a Target or Condition matching |

392 | function must be boolean. |

393 | |

394 | @param function the Function to add to the factory |

395 | ''' |

396 | raise NotImplementedError() |

397 | |

398 | def addAbstractFunction(self, functionProxy, identity): |

399 | '''Adds the abstract function proxy to the factory. This is used for |

400 | those functions which have state, or change behaviour (for instance |

401 | the standard map function, which changes its return type based on |

402 | how it is used). |

403 | |

404 | @param proxy the FunctionProxy to add to the factory |

405 | @param identity the function's identifier |

406 | ''' |

407 | raise NotImplementedError() |

408 | |

409 | def getSupportedFunctions(self): |

410 | '''Returns the function identifiers supported by this factory. |

411 | |

412 | @return a Set of Strings''' |

413 | raise NotImplementedError() |

414 | |

415 | def createFunction(self, identity): |

416 | '''Tries to get an instance of the specified function. |

417 | |

418 | @param identity the name of the function |

419 | ''' |

420 | raise NotImplementedError() |

421 | |

422 | def createAbstractFunction(self, identity, root): |

423 | '''Tries to get an instance of the specified abstract function. |

424 | |

425 | @param identity the name of the function |

426 | @param root the DOM root containing info used to create the function |

427 | ''' |

428 | raise NotImplementedError() |

429 | |

430 | |

431 | class FunctionFactoryProxy(XacmlBase): |

432 | '''A simple proxy interface used to install new FunctionFactorys. |

433 | The three kinds of factory (Target, Condition, and General) are tied |

434 | together in this interface because implementors writing new factories |

435 | should always implement all three types and provide them together''' |

436 | def getTargetFactory(): |

437 | raise NotImplementedError() |

438 | |

439 | def getConditionFactory(): |

440 | raise NotImplementedError() |

441 | |

442 | def getGeneralFactory(): |

443 | raise NotImplementedError() |

444 | |

445 | |

446 | class BasicFunctionFactoryProxy(FunctionFactoryProxy): |

447 | '''A simple utility class that manages triples of function factories.''' |

448 | |

449 | # the triple of factories |

450 | targetFactory = None |

451 | conditionFactory = None |

452 | generalFactory = None |

453 | |

454 | def __init__(targetFactory, conditionFactory, generalFactory): |

455 | '''Creates a new proxy. |

456 | |

457 | @param targetFactory the target factory provided by this proxy |

458 | @param conditionFactory the target condition provided by this proxy |

459 | @param generalFactory the general factory provided by this proxy |

460 | ''' |

461 | self.targetFactory = targetFactory |

462 | self.conditionFactory = conditionFactory |

463 | self.generalFactory = generalFactory |

464 | |

465 | def getTargetFactory(): |

466 | return targetFactory |

467 | |

468 | def getConditionFactory(): |

469 | return conditionFactory |

470 | |

471 | def getGeneralFactory(): |

472 | return generalFactory |

473 | |

474 | |

475 | class BaseFunctionFactory(FunctionFactory): |

476 | '''This is a basic implementation of <code>FunctionFactory</code>. It |

477 | implements the insertion and retrieval methods, but it doesn't actually |

478 | setup the factory with any functions. It also assumes a certain model |

479 | with regard to the different kinds of functions (Target, Condition, and |

480 | General). For this reason, you may want to re-use this class, or you |

481 | may want to extend FunctionFactory directly, if you're writing a new |

482 | factory implementation. |

483 | |

484 | Note that while this class is thread-safe on all creation methods, it |

485 | is not safe to add support for a new function while creating an instance |

486 | of a function. This follows from the assumption that most people will |

487 | initialize these factories up-front, and then start processing without |

488 | ever modifying the factories. If you need these mutual operations to |

489 | be thread-safe, then you should write a wrapper class that implements |

490 | the right synchronization. |

491 | ''' |

492 | |

493 | def __init__(self, |

494 | superset=None, |

495 | supportedFunctions=[], |

496 | supportedAbstractFunctions={}): |

497 | '''Sets a "superset factory". This is useful since |

498 | the different function factories (Target, Condition, and General) |

499 | have a superset relationship (Condition functions are a superset |

500 | of Target functions, etc.). Adding a function to this factory will |

501 | automatically add the same function to the superset factory. |

502 | |

503 | Constructor that defines the initial functions supported by this |

504 | factory but doesn't use a superset factory. |

505 | |

506 | Constructor that defines the initial functions supported by this |

507 | factory but doesn't use a superset factory. |

508 | |

509 | Constructor that defines the initial functions supported by this |

510 | factory and uses a superset factory. Note that the functions |

511 | supplied here are not propagated up to the superset factory, so |

512 | you must either make sure the superset factory is correctly |

513 | initialized or use BaseFunctionFactory(FunctionFactory) |

514 | and then manually add each function. |

515 | |

516 | @param supportedFunctions a Set of Functions |

517 | @param supportedAbstractFunctions a mapping from URI to |

518 | FunctionProxy |

519 | |

520 | @param supportedFunctions a Set of Functions |

521 | @param supportedAbstractFunctions a mapping from URI to FunctionProxy |

522 | |

523 | @param superset the superset factory or None''' |

524 | |

525 | # the backing maps for the Function objects |

526 | self.functionMap = {} |

527 | |

528 | # the superset factory chained to this factory |

529 | self.superset = superset |

530 | |

531 | for function in supportedFunctions: |

532 | self.functionMap[function.functionId] = function |

533 | |

534 | for id in supportedAbstractFunctions.keys(): |

535 | proxy = supportedAbstractFunctions.get(id) |

536 | self.functionMap[id] = proxy |

537 | |

538 | def addFunction(self, function): |

539 | '''Adds the function to the factory. Most functions have no state, so |

540 | the singleton model used here is typically desirable. The factory will |

541 | not enforce the requirement that a Target or Condition matching |

542 | function must be boolean. |

543 | |

544 | @param function the Function to add to the factory |

545 | @raise TypeError if the function's identifier is already used or if the |

546 | function is non-boolean (when this is a Target or Condition factory) |

547 | ''' |

548 | id = function.functionId |

549 | |

550 | # make sure this doesn't already exist |

551 | if id in self.functionMap: |

552 | raise TypeError("function %s already exists" % id) |

553 | |

554 | # add to the superset factory |

555 | if self.superset != None: |

556 | self.superset.addFunction(function) |

557 | |

558 | # Add to this factory |

559 | self.functionMap[id] = function |

560 | |

561 | |

562 | def addAbstractFunction(self, proxy, id): |

563 | '''Adds the abstract function proxy to the factory. This is used for |

564 | those functions which have state, or change behaviour (for instance |

565 | the standard map function, which changes its return type based on |

566 | how it is used). |

567 | |

568 | @param proxy: the FunctionProxy to add to the factory |

569 | @param id: the function's identifier |

570 | |

571 | @raise TypeError if the function's identifier is already used''' |

572 | |

573 | # make sure this doesn't already exist |

574 | if id in self.functionMap: |

575 | raise TypeError("function already exists") |

576 | |

577 | # add to the superset factory |

578 | if self.superset != None: |

579 | self.superset.addAbstractFunction(proxy, id) |

580 | |

581 | # finally, add to this factory |

582 | functionMap[id] = proxy |

583 | |

584 | |

585 | def getSupportedFunctions(self): |

586 | '''Returns the function identifiers supported by this factory. |

587 | |

588 | @return a list of strings''' |

589 | |

590 | functions = self.functionMap.keys() |

591 | |

592 | if self.superset != None: |

593 | functions += self.superset.getSupportedFunctions() |

594 | |

595 | return functions |

596 | |

597 | |

598 | def createFunction(self, identity): |

599 | '''Tries to get an instance of the specified function. |

600 | |

601 | @param identity the name of the function |

602 | |

603 | @throws UnknownIdentifierException if the name isn't known |

604 | @throws FunctionTypeException if the name is known to map to an |

605 | abstract function, and should therefore |

606 | be created through createAbstractFunction |

607 | ''' |

608 | entry = self.functionMap.get(identity) |

609 | if entry is not None: |

610 | if isinstance(entry, Function): |

611 | return entry |

612 | else: |

613 | # this is actually a proxy, which means the other create |

614 | # method should have been called |

615 | raise FunctionTypeException("function is abstract") |

616 | else: |

617 | # we couldn't find a match |

618 | raise UnknownIdentifierException("functions of type %s are not " |

619 | "supported by this factory" % |

620 | identity) |

621 | |

622 | |

623 | def createAbstractFunction(identity, root): |

624 | '''Tries to get an instance of the specified abstract function. |

625 | |

626 | @param identity the name of the function |

627 | @param root the DOM root containing info used to create the function |

628 | @param xpathVersion the version specified in the containing policy, or |

629 | None if no version was specified |

630 | |

631 | @throws UnknownIdentifierException if the name isn't known |

632 | @throws FunctionTypeException if the name is known to map to a |

633 | concrete function, and should therefore |

634 | be created through createFunction |

635 | @throws ParsingException if the function can't be created with the |

636 | given inputs''' |

637 | |

638 | entry = self.functionMap.get(identity) |

639 | if entry is not None: |

640 | if isinstance(entry, FunctionProxy): |

641 | try: |

642 | return entry.getInstance(root) |

643 | |

644 | except Exception, e: |

645 | raise ParsingException("Couldn't create abstract function " |

646 | "%s: %s" % identity, e) |

647 | else: |

648 | # this is actually a concrete function, which means that |

649 | # the other create method should have been called |

650 | raise FunctionTypeException("function is concrete") |

651 | |

652 | else: |

653 | raise UnknownIdentifierException("Abstract functions of type %s " |

654 | "are not supported by this " |

655 | "factory" % identity) |

656 | |

657 | |

658 | class StandardFunctionFactory(BaseFunctionFactory): |

659 | '''This factory supports the standard set of functions specified in XACML |

660 | 1.0 and 1.1. It is the default factory used by the system, and imposes |

661 | a singleton pattern insuring that there is only ever one instance of |

662 | this class. |

663 | <p> |

664 | Note that because this supports only the standard functions, this |

665 | factory does not allow the addition of any other functions. If you call |

666 | addFunction on an instance of this class, an exception |

667 | will be thrown. If you need a standard factory that is modifiable, |

668 | you can either create a new BaseFunctionFactory (or some |

669 | other implementation of FunctionFactory) populated with |

670 | the standard functions from getStandardFunctions or |

671 | you can use getNewFactoryProxy to get a proxy containing |

672 | a new, modifiable set of factories.''' |

673 | |

674 | |

675 | # the three singleton instances |

676 | targetFactory = None |

677 | conditionFactory = None |

678 | generalFactory = None |

679 | |

680 | # the three function sets/maps that we use internally |

681 | targetFunctions = None |

682 | conditionFunctions = None |

683 | generalFunctions = None |

684 | |

685 | targetAbstractFunctions = None |

686 | conditionAbstractFunctions = None |

687 | generalAbstractFunctions = None |

688 | |

689 | # the set/map used by each singleton factory instance |

690 | supportedFunctions = None |

691 | supportedAbstractFunctions = None |

692 | |

693 | |

694 | def __init__(self, supportedFunctions, supportedAbstractFunctions): |

695 | '''Creates a new StandardFunctionFactory, making sure that the default |

696 | maps are initialized correctly. Standard factories can't be modified, |

697 | so there is no notion of supersetting since that's only used for |

698 | correctly propagating new functions.''' |

699 | super(StandardFunctionFactory, self).__init__(supportedFunctions, |

700 | supportedAbstractFunctions) |

701 | |

702 | self.supportedFunctions = supportedFunctions |

703 | self.supportedAbstractFunctions = supportedAbstractFunctions |

704 | |

705 | |

706 | |

707 | |

708 | def _initTargetFunctions(self): |

709 | '''Private initializer for the target functions. This is only ever |

710 | called once.''' |

711 | log.info("Initializing standard Target functions") |

712 | |

713 | # Emulate a list with unique items using a dict with only the keys set |

714 | StandardFunctionFactory.targetFunctions = {} |

715 | |

716 | # add EqualFunction |

717 | StandardFunctionFactory.targetFunctions.fromkeys( |

718 | EqualFunction.supportedIdentifiers) |

719 | |

720 | # add LogicalFunction |

721 | StandardFunctionFactory.targetFunctions.fromkeys( |

722 | LogicalFunction.supportedIdentifiers) |

723 | |

724 | # add NOfFunction |

725 | StandardFunctionFactory.targetFunctions.fromkeys( |

726 | NOfFunction.supportedIdentifiers) |

727 | |

728 | # add NotFunction |

729 | StandardFunctionFactory.targetFunctions.fromkeys( |

730 | NotFunction.supportedIdentifiers) |

731 | |

732 | # add ComparisonFunction |

733 | StandardFunctionFactory.targetFunctions.fromkeys( |

734 | ComparisonFunction.supportedIdentifiers) |

735 | |

736 | # add MatchFunction |

737 | StandardFunctionFactory.targetFunctions.fromkeys( |

738 | MatchFunction.supportedIdentifiers) |

739 | |

740 | StandardFunctionFactory.targetAbstractFunctions = {} |

741 | |

742 | |

743 | def _initConditionFunctions(self): |

744 | '''Private initializer for the condition functions. This is only ever |

745 | called once.''' |

746 | log.info("Initializing standard Condition functions") |

747 | |

748 | if StandardFunctionFactory.targetFunctions is None: |

749 | self._initTargetFunctions() |

750 | |

751 | StandardFunctionFactory.conditionFunctions = \ |

752 | StandardFunctionFactory.targetFunctions.copy() |

753 | |

754 | # add condition functions from BagFunction |

755 | conditionFunctions.fromkeys(ConditionBagFunction.supportedIdentifiers) |

756 | |

757 | # add condition functions from SetFunction |

758 | conditionFunctions.fromkeys(ConditionSetFunction.supportedIdentifiers) |

759 | |

760 | # add condition functions from HigherOrderFunction |

761 | conditionFunctions.fromkeys(HigherOrderFunction.supportedIdentifiers) |

762 | |

763 | StandardFunctionFactory.conditionAbstractFunctions = \ |

764 | StandardFunctionFactory.targetAbstractFunctions.copy() |

765 | |

766 | |

767 | def _initGeneralFunctions(self): |

768 | '''Private initializer for the general functions. This is only ever |

769 | called once.''' |

770 | |

771 | log.info("Initializing standard General functions") |

772 | |

773 | if StandardFunctionFactory.conditionFunctions is None: |

774 | self._initConditionFunctions() |

775 | |

776 | StandardFunctionFactory.generalFunctions = \ |

777 | StandardFunctionFactory.conditionFunctions.copy() |

778 | |

779 | # add AddFunction |

780 | StandardFunctionFactory.generalFunctions.fromkeys( |

781 | AddFunction.supportedIdentifiers) |

782 | |

783 | # add SubtractFunction |

784 | StandardFunctionFactory.generalFunctions.fromkeys( |

785 | SubtractFunction.supportedIdentifiers) |

786 | |

787 | # add MultiplyFunction |

788 | StandardFunctionFactory.generalFunctions.fromkeys( |

789 | MultiplyFunction.supportedIdentifiers) |

790 | |

791 | # add DivideFunction |

792 | StandardFunctionFactory.generalFunctions.fromkeys( |

793 | DivideFunction.supportedIdentifiers) |

794 | |

795 | # add ModFunction |

796 | StandardFunctionFactory.generalFunctions.fromkeys( |

797 | ModFunction.supportedIdentifiers) |

798 | |

799 | # add AbsFunction |

800 | StandardFunctionFactory.generalFunctions.fromkeys( |

801 | AbsFunction.supportedIdentifiers) |

802 | |

803 | # add RoundFunction |

804 | StandardFunctionFactory.generalFunctions.fromkeys( |

805 | RoundFunction.supportedIdentifiers) |

806 | |

807 | # add FloorFunction |

808 | StandardFunctionFactory.generalFunctions.fromkeys( |

809 | FloorFunction.supportedIdentifiers) |

810 | |

811 | # add DateMathFunction |

812 | StandardFunctionFactory.generalFunctions.fromkeys( |

813 | DateMathFunction.supportedIdentifiers) |

814 | |

815 | # add general functions from BagFunction |

816 | StandardFunctionFactory.generalFunctions.fromkeys( |

817 | GeneralBagFunction.supportedIdentifiers) |

818 | |

819 | # add NumericConvertFunction |

820 | StandardFunctionFactory.generalFunctions.fromkeys( |

821 | NumericConvertFunction.supportedIdentifiers) |

822 | |

823 | # add StringNormalizeFunction |

824 | StandardFunctionFactory.generalFunctions.fromkeys( |

825 | StringNormalizeFunction.supportedIdentifiers) |

826 | |

827 | # add general functions from SetFunction |

828 | StandardFunctionFactory.generalFunctions.fromkeys( |

829 | GeneralSetFunction.supportedIdentifiers) |

830 | |

831 | StandardFunctionFactory.generalAbstractFunctions = \ |

832 | StandardFunctionFactory.conditionAbstractFunctions.copy() |

833 | |

834 | # Add the map function's proxy |

835 | StandardFunctionFactory.generalAbstractFunctions[ |

836 | MapFunction.NAME_MAP] = MapFunctionProxy() |

837 | |

838 | @classmethod |

839 | def getTargetFactory(cls): |

840 | '''Returns a FunctionFactory that will only provide those functions |

841 | that are usable in Target matching. This method enforces a singleton |

842 | model, meaning that this always returns the same instance, creating |

843 | the factory if it hasn't been requested before. This is the default |

844 | model used by the FunctionFactory, ensuring quick |

845 | access to this factory. |

846 | |

847 | @return a FunctionFactory for target functions''' |

848 | if StandardFunctionFactory.targetFactory is None: |

849 | if StandardFunctionFactory.targetFunctions is None: |

850 | StandardFunctionFactory._initTargetFunctions() |

851 | |

852 | if StandardFunctionFactory.targetFactory is None: |

853 | StandardFunctionFactory.targetFactory=StandardFunctionFactory( |

854 | StandardFunctionFactory.targetFunctions, |

855 | StandardFunctionFactory.targetAbstractFunctions) |

856 | |

857 | return StandardFunctionFactory.targetFactory |

858 | |

859 | |

860 | @classmethod |

861 | def getConditionFactory(cls): |

862 | '''Returns a FuntionFactory that will only provide those functions that |

863 | are usable in the root of the Condition. These Functions are a |

864 | superset of the Target functions. This method enforces a singleton |

865 | model, meaning that this always returns the same instance, creating |

866 | the factory if it hasn't been requested before. This is the default |

867 | model used by the FunctionFactory, ensuring quick |

868 | access to this factory. |

869 | |

870 | @return a FunctionFactory for condition functions |

871 | ''' |

872 | if StandardFunctionFactory.conditionFactory is None: |

873 | if StandardFunctionFactory.conditionFunctions is None: |

874 | StandardFunctionFactory._initConditionFunctions() |

875 | |

876 | if StandardFunctionFactory.conditionFactory is None: |

877 | StandardFunctionFactory.conditionFactory = \ |

878 | StandardFunctionFactory( |

879 | StandardFunctionFactory.conditionFunctions, |

880 | StandardFunctionFactory.conditionAbstractFunctions) |

881 | |

882 | return StandardFunctionFactory.conditionFactory |

883 | |

884 | |

885 | @classmethod |

886 | def getGeneralFactory(cls): |

887 | '''Returns a FunctionFactory that provides access to all the functions. |

888 | These Functions are a superset of the Condition functions. This method |

889 | enforces a singleton model, meaning that this always returns the same |

890 | instance, creating the factory if it hasn't been requested before. |

891 | This is the default model used by the FunctionFactory, |

892 | ensuring quick access to this factory. |

893 | |

894 | @return a FunctionFactory for all functions''' |

895 | |

896 | if StandardFunctionFactory.generalFactory is None: |

897 | if StandardFunctionFactory.generalFunctions is None: |

898 | StandardFunctionFactory._initGeneralFunctions() |

899 | |

900 | StandardFunctionFactory.generalFactory = \ |

901 | StandardFunctionFactory( |

902 | StandardFunctionFactory.generalFunctions, |

903 | StandardFunctionFactory.generalAbstractFunctions) |

904 | |

905 | return StandardFunctionFactory.generalFactory |

906 | |

907 | |

908 | def getStandardFunctions(self): |

909 | '''Returns the set of functions that this standard factory supports. |

910 | |

911 | @return a Set of Functions''' |

912 | return tuple(self.supportedFunctions.keys()) |

913 | |

914 | def getStandardAbstractFunctions(self): |

915 | '''Returns the set of abstract functions that this standard factory |

916 | supports as a mapping of identifier to proxy. |

917 | |

918 | @return a Map mapping URIs to FunctionProxys''' |

919 | return tuple(self.supportedAbstractFunctions.keys()) |

920 | |

921 | |

922 | @classmethod |

923 | def getNewFactoryProxy(cls): |

924 | '''A convenience method that returns a proxy containing newly created |

925 | instances of BaseFunctionFactorys that are correctly |

926 | supersetted and contain the standard functions and abstract functions. |

927 | These factories allow adding support for new functions. |

928 | |

929 | @return a new proxy containing new factories supporting the standard |

930 | functions''' |

931 | |

932 | general = StandardFunctionFactory.getGeneralFactory() |

933 | |

934 | newGeneral=BaseFunctionFactory(general.getStandardFunctions(), |

935 | general.getStandardAbstractFunctions()) |

936 | |

937 | condition = StandardFunctionFactory.getConditionFactory() |

938 | |

939 | newCondition = BaseFunctionFactory(newGeneral, |

940 | condition.getStandardFunctions(), |

941 | condition.getStandardAbstractFunctions()) |

942 | |

943 | target = StandardFunctionFactory.getTargetFactory() |

944 | newTarget = BaseFunctionFactory(newCondition, |

945 | target.getStandardFunctions(), |

946 | target.getStandardAbstractFunctions()) |

947 | |

948 | return BasicFunctionFactoryProxy(newTarget, newCondition, newGeneral) |

949 | |

950 | |

951 | |

952 | |

953 | def addFunction(self, function): |

954 | '''Always throws an exception, since support for new functions may not |

955 | be added to a standard factory. |

956 | |

957 | @param function the Function to add to the factory |

958 | @raise NotImplementedError''' |

959 | |

960 | raise NotImplementedError("a standard factory cannot support new " |

961 | "functions") |

962 | |

963 | |

964 | def addAbstractFunction(self, proxy, identity): |

965 | '''Always throws an exception, since support for new functions may not |

966 | be added to a standard factory. |

967 | |

968 | @param proxy the FunctionProxy to add to the factory |

969 | @param identity the function's identifier |

970 | |

971 | @raise NotImplementedError always''' |

972 | raise NotImplementedError("a standard factory cannot support new " |

973 | "functions") |

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