├── .gitignore ├── .travis.yml ├── composer.json ├── composer.lock ├── docs ├── .gitkeep └── install.md ├── license.md ├── phpunit.xml ├── readme.md ├── src └── Authority │ ├── Authority.php │ ├── Events │ ├── Dispatcher.php │ └── Event.php │ ├── Rule.php │ ├── RuleAlias.php │ └── RuleRepository.php └── tests ├── AuthorityTest.php ├── DispatcherTest.php ├── RuleAliasTest.php ├── RuleRepositoryTest.php └── RuleTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | - 7.0 8 | - hhvm 9 | 10 | matrix: 11 | allow_failures: 12 | - php: 7.0 13 | 14 | sudo: false 15 | 16 | install: travis_retry composer install --no-interaction --prefer-source 17 | 18 | script: vendor/bin/phpunit 19 | 20 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "machuga/authority", 3 | "description": "A simple and flexible authorization system for PHP", 4 | "license": "mit", 5 | "authors": [ 6 | { 7 | "name": "Matthew Machuga", 8 | "email": "machuga@gmail.com" 9 | }, 10 | { 11 | "name": "Koen Schmeets", 12 | "email": "hello@koenschmeets.nl" 13 | }, 14 | { 15 | "name": "Jesse O'Brien", 16 | "email": "jesse@jesse-obrien.ca" 17 | } 18 | ], 19 | "require": { 20 | "php": ">=5.4.0", 21 | "illuminate/events": "~5" 22 | }, 23 | "require-dev": { 24 | "phpunit/phpunit": "3.7.*", 25 | "mockery/mockery": "0.7.*" 26 | }, 27 | "minimum-stability": "stable", 28 | "autoload": { 29 | "psr-0": { 30 | "Authority": "src/" 31 | } 32 | }, 33 | "extra": { 34 | "branch-alias": { 35 | "dev-master": "2.1.x-dev" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "2a210d828a4fcfdce67fea38d230643e", 8 | "packages": [ 9 | { 10 | "name": "danielstjules/stringy", 11 | "version": "1.9.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/danielstjules/Stringy.git", 15 | "reference": "3cf18e9e424a6dedc38b7eb7ef580edb0929461b" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/danielstjules/Stringy/zipball/3cf18e9e424a6dedc38b7eb7ef580edb0929461b", 20 | "reference": "3cf18e9e424a6dedc38b7eb7ef580edb0929461b", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "ext-mbstring": "*", 25 | "php": ">=5.3.0" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "~4.0" 29 | }, 30 | "type": "library", 31 | "autoload": { 32 | "psr-4": { 33 | "Stringy\\": "src/" 34 | }, 35 | "files": [ 36 | "src/Create.php" 37 | ] 38 | }, 39 | "notification-url": "https://packagist.org/downloads/", 40 | "license": [ 41 | "MIT" 42 | ], 43 | "authors": [ 44 | { 45 | "name": "Daniel St. Jules", 46 | "email": "danielst.jules@gmail.com", 47 | "homepage": "http://www.danielstjules.com" 48 | } 49 | ], 50 | "description": "A string manipulation library with multibyte support", 51 | "homepage": "https://github.com/danielstjules/Stringy", 52 | "keywords": [ 53 | "UTF", 54 | "helpers", 55 | "manipulation", 56 | "methods", 57 | "multibyte", 58 | "string", 59 | "utf-8", 60 | "utility", 61 | "utils" 62 | ], 63 | "time": "2015-02-10 06:19:18" 64 | }, 65 | { 66 | "name": "doctrine/inflector", 67 | "version": "v1.0.1", 68 | "source": { 69 | "type": "git", 70 | "url": "https://github.com/doctrine/inflector.git", 71 | "reference": "0bcb2e79d8571787f18b7eb036ed3d004908e604" 72 | }, 73 | "dist": { 74 | "type": "zip", 75 | "url": "https://api.github.com/repos/doctrine/inflector/zipball/0bcb2e79d8571787f18b7eb036ed3d004908e604", 76 | "reference": "0bcb2e79d8571787f18b7eb036ed3d004908e604", 77 | "shasum": "" 78 | }, 79 | "require": { 80 | "php": ">=5.3.2" 81 | }, 82 | "require-dev": { 83 | "phpunit/phpunit": "4.*" 84 | }, 85 | "type": "library", 86 | "extra": { 87 | "branch-alias": { 88 | "dev-master": "1.0.x-dev" 89 | } 90 | }, 91 | "autoload": { 92 | "psr-0": { 93 | "Doctrine\\Common\\Inflector\\": "lib/" 94 | } 95 | }, 96 | "notification-url": "https://packagist.org/downloads/", 97 | "license": [ 98 | "MIT" 99 | ], 100 | "authors": [ 101 | { 102 | "name": "Roman Borschel", 103 | "email": "roman@code-factory.org" 104 | }, 105 | { 106 | "name": "Benjamin Eberlei", 107 | "email": "kontakt@beberlei.de" 108 | }, 109 | { 110 | "name": "Guilherme Blanco", 111 | "email": "guilhermeblanco@gmail.com" 112 | }, 113 | { 114 | "name": "Jonathan Wage", 115 | "email": "jonwage@gmail.com" 116 | }, 117 | { 118 | "name": "Johannes Schmitt", 119 | "email": "schmittjoh@gmail.com" 120 | } 121 | ], 122 | "description": "Common String Manipulations with regard to casing and singular/plural rules.", 123 | "homepage": "http://www.doctrine-project.org", 124 | "keywords": [ 125 | "inflection", 126 | "pluralize", 127 | "singularize", 128 | "string" 129 | ], 130 | "time": "2014-12-20 21:24:13" 131 | }, 132 | { 133 | "name": "illuminate/container", 134 | "version": "v5.0.4", 135 | "source": { 136 | "type": "git", 137 | "url": "https://github.com/illuminate/container.git", 138 | "reference": "7ffdad0a2b2c600445deb57f5f7e93092e44ca2a" 139 | }, 140 | "dist": { 141 | "type": "zip", 142 | "url": "https://api.github.com/repos/illuminate/container/zipball/7ffdad0a2b2c600445deb57f5f7e93092e44ca2a", 143 | "reference": "7ffdad0a2b2c600445deb57f5f7e93092e44ca2a", 144 | "shasum": "" 145 | }, 146 | "require": { 147 | "illuminate/contracts": "5.0.*", 148 | "php": ">=5.4.0" 149 | }, 150 | "type": "library", 151 | "extra": { 152 | "branch-alias": { 153 | "dev-master": "5.0-dev" 154 | } 155 | }, 156 | "autoload": { 157 | "psr-4": { 158 | "Illuminate\\Container\\": "" 159 | } 160 | }, 161 | "notification-url": "https://packagist.org/downloads/", 162 | "license": [ 163 | "MIT" 164 | ], 165 | "authors": [ 166 | { 167 | "name": "Taylor Otwell", 168 | "email": "taylorotwell@gmail.com" 169 | } 170 | ], 171 | "description": "The Illuminate Container package.", 172 | "time": "2015-02-11 16:00:31" 173 | }, 174 | { 175 | "name": "illuminate/contracts", 176 | "version": "v5.0.0", 177 | "source": { 178 | "type": "git", 179 | "url": "https://github.com/illuminate/contracts.git", 180 | "reference": "78f1dba092d5fcb6d3a19537662abe31c4d128fd" 181 | }, 182 | "dist": { 183 | "type": "zip", 184 | "url": "https://api.github.com/repos/illuminate/contracts/zipball/78f1dba092d5fcb6d3a19537662abe31c4d128fd", 185 | "reference": "78f1dba092d5fcb6d3a19537662abe31c4d128fd", 186 | "shasum": "" 187 | }, 188 | "require": { 189 | "php": ">=5.4.0" 190 | }, 191 | "type": "library", 192 | "extra": { 193 | "branch-alias": { 194 | "dev-master": "5.0-dev" 195 | } 196 | }, 197 | "autoload": { 198 | "psr-4": { 199 | "Illuminate\\Contracts\\": "" 200 | } 201 | }, 202 | "notification-url": "https://packagist.org/downloads/", 203 | "license": [ 204 | "MIT" 205 | ], 206 | "authors": [ 207 | { 208 | "name": "Taylor Otwell", 209 | "email": "taylorotwell@gmail.com" 210 | } 211 | ], 212 | "description": "The Illuminate Contracts package.", 213 | "time": "2015-01-30 16:27:08" 214 | }, 215 | { 216 | "name": "illuminate/events", 217 | "version": "v5.0.4", 218 | "source": { 219 | "type": "git", 220 | "url": "https://github.com/illuminate/events.git", 221 | "reference": "ff8c302142a9f59dc1eeb5cc3e54a8e99d3e5785" 222 | }, 223 | "dist": { 224 | "type": "zip", 225 | "url": "https://api.github.com/repos/illuminate/events/zipball/ff8c302142a9f59dc1eeb5cc3e54a8e99d3e5785", 226 | "reference": "ff8c302142a9f59dc1eeb5cc3e54a8e99d3e5785", 227 | "shasum": "" 228 | }, 229 | "require": { 230 | "illuminate/container": "5.0.*", 231 | "illuminate/contracts": "5.0.*", 232 | "illuminate/support": "5.0.*", 233 | "php": ">=5.4.0" 234 | }, 235 | "type": "library", 236 | "extra": { 237 | "branch-alias": { 238 | "dev-master": "5.0-dev" 239 | } 240 | }, 241 | "autoload": { 242 | "psr-4": { 243 | "Illuminate\\Events\\": "" 244 | } 245 | }, 246 | "notification-url": "https://packagist.org/downloads/", 247 | "license": [ 248 | "MIT" 249 | ], 250 | "authors": [ 251 | { 252 | "name": "Taylor Otwell", 253 | "email": "taylorotwell@gmail.com" 254 | } 255 | ], 256 | "description": "The Illuminate Events package.", 257 | "time": "2015-01-28 22:17:45" 258 | }, 259 | { 260 | "name": "illuminate/support", 261 | "version": "v5.0.4", 262 | "source": { 263 | "type": "git", 264 | "url": "https://github.com/illuminate/support.git", 265 | "reference": "405a2241fefa49cfc39b7fba8cc54f69fce2f101" 266 | }, 267 | "dist": { 268 | "type": "zip", 269 | "url": "https://api.github.com/repos/illuminate/support/zipball/405a2241fefa49cfc39b7fba8cc54f69fce2f101", 270 | "reference": "405a2241fefa49cfc39b7fba8cc54f69fce2f101", 271 | "shasum": "" 272 | }, 273 | "require": { 274 | "danielstjules/stringy": "~1.8", 275 | "doctrine/inflector": "~1.0", 276 | "ext-mbstring": "*", 277 | "illuminate/contracts": "5.0.*", 278 | "php": ">=5.4.0" 279 | }, 280 | "suggest": { 281 | "jeremeamia/superclosure": "Required to be able to serialize closures (~2.0)." 282 | }, 283 | "type": "library", 284 | "extra": { 285 | "branch-alias": { 286 | "dev-master": "5.0-dev" 287 | } 288 | }, 289 | "autoload": { 290 | "psr-4": { 291 | "Illuminate\\Support\\": "" 292 | }, 293 | "files": [ 294 | "helpers.php" 295 | ] 296 | }, 297 | "notification-url": "https://packagist.org/downloads/", 298 | "license": [ 299 | "MIT" 300 | ], 301 | "authors": [ 302 | { 303 | "name": "Taylor Otwell", 304 | "email": "taylorotwell@gmail.com" 305 | } 306 | ], 307 | "description": "The Illuminate Support package.", 308 | "time": "2015-02-11 11:08:03" 309 | } 310 | ], 311 | "packages-dev": [ 312 | { 313 | "name": "mockery/mockery", 314 | "version": "0.7.2", 315 | "source": { 316 | "type": "git", 317 | "url": "https://github.com/padraic/mockery.git", 318 | "reference": "10ef0f8a63392f244e5b19de261b6a08eb8e4109" 319 | }, 320 | "dist": { 321 | "type": "zip", 322 | "url": "https://api.github.com/repos/padraic/mockery/zipball/10ef0f8a63392f244e5b19de261b6a08eb8e4109", 323 | "reference": "10ef0f8a63392f244e5b19de261b6a08eb8e4109", 324 | "shasum": "" 325 | }, 326 | "require": { 327 | "php": ">=5.3.2" 328 | }, 329 | "suggest": { 330 | "Hamcrest": "1.0.0" 331 | }, 332 | "type": "library", 333 | "autoload": { 334 | "psr-0": { 335 | "Mockery": "library/" 336 | } 337 | }, 338 | "notification-url": "https://packagist.org/downloads/", 339 | "license": [ 340 | "New BSD" 341 | ], 342 | "authors": [ 343 | { 344 | "name": "Pádraic Brady", 345 | "email": "padraic.brady@gmail.com", 346 | "homepage": "http://blog.astrumfutura.com" 347 | } 348 | ], 349 | "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succint API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.", 350 | "homepage": "http://github.com/padraic/mockery", 351 | "keywords": [ 352 | "BDD", 353 | "TDD", 354 | "library", 355 | "mock", 356 | "mock objects", 357 | "mockery", 358 | "stub", 359 | "test", 360 | "test double", 361 | "testing" 362 | ], 363 | "time": "2012-01-24 20:22:39" 364 | }, 365 | { 366 | "name": "phpunit/php-code-coverage", 367 | "version": "1.2.18", 368 | "source": { 369 | "type": "git", 370 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 371 | "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b" 372 | }, 373 | "dist": { 374 | "type": "zip", 375 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b", 376 | "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b", 377 | "shasum": "" 378 | }, 379 | "require": { 380 | "php": ">=5.3.3", 381 | "phpunit/php-file-iterator": ">=1.3.0@stable", 382 | "phpunit/php-text-template": ">=1.2.0@stable", 383 | "phpunit/php-token-stream": ">=1.1.3,<1.3.0" 384 | }, 385 | "require-dev": { 386 | "phpunit/phpunit": "3.7.*@dev" 387 | }, 388 | "suggest": { 389 | "ext-dom": "*", 390 | "ext-xdebug": ">=2.0.5" 391 | }, 392 | "type": "library", 393 | "extra": { 394 | "branch-alias": { 395 | "dev-master": "1.2.x-dev" 396 | } 397 | }, 398 | "autoload": { 399 | "classmap": [ 400 | "PHP/" 401 | ] 402 | }, 403 | "notification-url": "https://packagist.org/downloads/", 404 | "include-path": [ 405 | "" 406 | ], 407 | "license": [ 408 | "BSD-3-Clause" 409 | ], 410 | "authors": [ 411 | { 412 | "name": "Sebastian Bergmann", 413 | "email": "sb@sebastian-bergmann.de", 414 | "role": "lead" 415 | } 416 | ], 417 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 418 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 419 | "keywords": [ 420 | "coverage", 421 | "testing", 422 | "xunit" 423 | ], 424 | "time": "2014-09-02 10:13:14" 425 | }, 426 | { 427 | "name": "phpunit/php-file-iterator", 428 | "version": "1.3.4", 429 | "source": { 430 | "type": "git", 431 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 432 | "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" 433 | }, 434 | "dist": { 435 | "type": "zip", 436 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", 437 | "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", 438 | "shasum": "" 439 | }, 440 | "require": { 441 | "php": ">=5.3.3" 442 | }, 443 | "type": "library", 444 | "autoload": { 445 | "classmap": [ 446 | "File/" 447 | ] 448 | }, 449 | "notification-url": "https://packagist.org/downloads/", 450 | "include-path": [ 451 | "" 452 | ], 453 | "license": [ 454 | "BSD-3-Clause" 455 | ], 456 | "authors": [ 457 | { 458 | "name": "Sebastian Bergmann", 459 | "email": "sb@sebastian-bergmann.de", 460 | "role": "lead" 461 | } 462 | ], 463 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 464 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 465 | "keywords": [ 466 | "filesystem", 467 | "iterator" 468 | ], 469 | "time": "2013-10-10 15:34:57" 470 | }, 471 | { 472 | "name": "phpunit/php-text-template", 473 | "version": "1.2.0", 474 | "source": { 475 | "type": "git", 476 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 477 | "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" 478 | }, 479 | "dist": { 480 | "type": "zip", 481 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", 482 | "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", 483 | "shasum": "" 484 | }, 485 | "require": { 486 | "php": ">=5.3.3" 487 | }, 488 | "type": "library", 489 | "autoload": { 490 | "classmap": [ 491 | "Text/" 492 | ] 493 | }, 494 | "notification-url": "https://packagist.org/downloads/", 495 | "include-path": [ 496 | "" 497 | ], 498 | "license": [ 499 | "BSD-3-Clause" 500 | ], 501 | "authors": [ 502 | { 503 | "name": "Sebastian Bergmann", 504 | "email": "sb@sebastian-bergmann.de", 505 | "role": "lead" 506 | } 507 | ], 508 | "description": "Simple template engine.", 509 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 510 | "keywords": [ 511 | "template" 512 | ], 513 | "time": "2014-01-30 17:20:04" 514 | }, 515 | { 516 | "name": "phpunit/php-timer", 517 | "version": "1.0.5", 518 | "source": { 519 | "type": "git", 520 | "url": "https://github.com/sebastianbergmann/php-timer.git", 521 | "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" 522 | }, 523 | "dist": { 524 | "type": "zip", 525 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", 526 | "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", 527 | "shasum": "" 528 | }, 529 | "require": { 530 | "php": ">=5.3.3" 531 | }, 532 | "type": "library", 533 | "autoload": { 534 | "classmap": [ 535 | "PHP/" 536 | ] 537 | }, 538 | "notification-url": "https://packagist.org/downloads/", 539 | "include-path": [ 540 | "" 541 | ], 542 | "license": [ 543 | "BSD-3-Clause" 544 | ], 545 | "authors": [ 546 | { 547 | "name": "Sebastian Bergmann", 548 | "email": "sb@sebastian-bergmann.de", 549 | "role": "lead" 550 | } 551 | ], 552 | "description": "Utility class for timing", 553 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 554 | "keywords": [ 555 | "timer" 556 | ], 557 | "time": "2013-08-02 07:42:54" 558 | }, 559 | { 560 | "name": "phpunit/php-token-stream", 561 | "version": "1.2.2", 562 | "source": { 563 | "type": "git", 564 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 565 | "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32" 566 | }, 567 | "dist": { 568 | "type": "zip", 569 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32", 570 | "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32", 571 | "shasum": "" 572 | }, 573 | "require": { 574 | "ext-tokenizer": "*", 575 | "php": ">=5.3.3" 576 | }, 577 | "type": "library", 578 | "extra": { 579 | "branch-alias": { 580 | "dev-master": "1.2-dev" 581 | } 582 | }, 583 | "autoload": { 584 | "classmap": [ 585 | "PHP/" 586 | ] 587 | }, 588 | "notification-url": "https://packagist.org/downloads/", 589 | "include-path": [ 590 | "" 591 | ], 592 | "license": [ 593 | "BSD-3-Clause" 594 | ], 595 | "authors": [ 596 | { 597 | "name": "Sebastian Bergmann", 598 | "email": "sb@sebastian-bergmann.de", 599 | "role": "lead" 600 | } 601 | ], 602 | "description": "Wrapper around PHP's tokenizer extension.", 603 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 604 | "keywords": [ 605 | "tokenizer" 606 | ], 607 | "time": "2014-03-03 05:10:30" 608 | }, 609 | { 610 | "name": "phpunit/phpunit", 611 | "version": "3.7.38", 612 | "source": { 613 | "type": "git", 614 | "url": "https://github.com/sebastianbergmann/phpunit.git", 615 | "reference": "38709dc22d519a3d1be46849868aa2ddf822bcf6" 616 | }, 617 | "dist": { 618 | "type": "zip", 619 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/38709dc22d519a3d1be46849868aa2ddf822bcf6", 620 | "reference": "38709dc22d519a3d1be46849868aa2ddf822bcf6", 621 | "shasum": "" 622 | }, 623 | "require": { 624 | "ext-ctype": "*", 625 | "ext-dom": "*", 626 | "ext-json": "*", 627 | "ext-pcre": "*", 628 | "ext-reflection": "*", 629 | "ext-spl": "*", 630 | "php": ">=5.3.3", 631 | "phpunit/php-code-coverage": "~1.2", 632 | "phpunit/php-file-iterator": "~1.3", 633 | "phpunit/php-text-template": "~1.1", 634 | "phpunit/php-timer": "~1.0", 635 | "phpunit/phpunit-mock-objects": "~1.2", 636 | "symfony/yaml": "~2.0" 637 | }, 638 | "require-dev": { 639 | "pear-pear.php.net/pear": "1.9.4" 640 | }, 641 | "suggest": { 642 | "phpunit/php-invoker": "~1.1" 643 | }, 644 | "bin": [ 645 | "composer/bin/phpunit" 646 | ], 647 | "type": "library", 648 | "extra": { 649 | "branch-alias": { 650 | "dev-master": "3.7.x-dev" 651 | } 652 | }, 653 | "autoload": { 654 | "classmap": [ 655 | "PHPUnit/" 656 | ] 657 | }, 658 | "notification-url": "https://packagist.org/downloads/", 659 | "include-path": [ 660 | "", 661 | "../../symfony/yaml/" 662 | ], 663 | "license": [ 664 | "BSD-3-Clause" 665 | ], 666 | "authors": [ 667 | { 668 | "name": "Sebastian Bergmann", 669 | "email": "sebastian@phpunit.de", 670 | "role": "lead" 671 | } 672 | ], 673 | "description": "The PHP Unit Testing framework.", 674 | "homepage": "http://www.phpunit.de/", 675 | "keywords": [ 676 | "phpunit", 677 | "testing", 678 | "xunit" 679 | ], 680 | "time": "2014-10-17 09:04:17" 681 | }, 682 | { 683 | "name": "phpunit/phpunit-mock-objects", 684 | "version": "1.2.3", 685 | "source": { 686 | "type": "git", 687 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 688 | "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875" 689 | }, 690 | "dist": { 691 | "type": "zip", 692 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875", 693 | "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875", 694 | "shasum": "" 695 | }, 696 | "require": { 697 | "php": ">=5.3.3", 698 | "phpunit/php-text-template": ">=1.1.1@stable" 699 | }, 700 | "suggest": { 701 | "ext-soap": "*" 702 | }, 703 | "type": "library", 704 | "autoload": { 705 | "classmap": [ 706 | "PHPUnit/" 707 | ] 708 | }, 709 | "notification-url": "https://packagist.org/downloads/", 710 | "include-path": [ 711 | "" 712 | ], 713 | "license": [ 714 | "BSD-3-Clause" 715 | ], 716 | "authors": [ 717 | { 718 | "name": "Sebastian Bergmann", 719 | "email": "sb@sebastian-bergmann.de", 720 | "role": "lead" 721 | } 722 | ], 723 | "description": "Mock Object library for PHPUnit", 724 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 725 | "keywords": [ 726 | "mock", 727 | "xunit" 728 | ], 729 | "time": "2013-01-13 10:24:48" 730 | }, 731 | { 732 | "name": "symfony/yaml", 733 | "version": "v2.6.5", 734 | "target-dir": "Symfony/Component/Yaml", 735 | "source": { 736 | "type": "git", 737 | "url": "https://github.com/symfony/Yaml.git", 738 | "reference": "0cd8e72071e46e15fc072270ae39ea1b66b10a9d" 739 | }, 740 | "dist": { 741 | "type": "zip", 742 | "url": "https://api.github.com/repos/symfony/Yaml/zipball/0cd8e72071e46e15fc072270ae39ea1b66b10a9d", 743 | "reference": "0cd8e72071e46e15fc072270ae39ea1b66b10a9d", 744 | "shasum": "" 745 | }, 746 | "require": { 747 | "php": ">=5.3.3" 748 | }, 749 | "require-dev": { 750 | "symfony/phpunit-bridge": "~2.7" 751 | }, 752 | "type": "library", 753 | "extra": { 754 | "branch-alias": { 755 | "dev-master": "2.6-dev" 756 | } 757 | }, 758 | "autoload": { 759 | "psr-0": { 760 | "Symfony\\Component\\Yaml\\": "" 761 | } 762 | }, 763 | "notification-url": "https://packagist.org/downloads/", 764 | "license": [ 765 | "MIT" 766 | ], 767 | "authors": [ 768 | { 769 | "name": "Symfony Community", 770 | "homepage": "http://symfony.com/contributors" 771 | }, 772 | { 773 | "name": "Fabien Potencier", 774 | "email": "fabien@symfony.com" 775 | } 776 | ], 777 | "description": "Symfony Yaml Component", 778 | "homepage": "http://symfony.com", 779 | "time": "2015-03-12 10:28:44" 780 | } 781 | ], 782 | "aliases": [], 783 | "minimum-stability": "stable", 784 | "stability-flags": [], 785 | "prefer-stable": false, 786 | "prefer-lowest": false, 787 | "platform": { 788 | "php": ">=5.4.0" 789 | }, 790 | "platform-dev": [] 791 | } 792 | -------------------------------------------------------------------------------- /docs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/machuga/authority/4e28c42e90ecaf2b091d71d2f179726eaa196193/docs/.gitkeep -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | # Install and Setup 2 | 3 | [Composer](http://www.getcomposer.org) is required to install Authority into your project. 4 | 5 | **Quick Install Composer** 6 | 7 | ```$ curl -s https://getcomposer.org/installer | php``` 8 | 9 | ### Install Authority 10 | Open or create a ``composer.json`` file in your project directory and add an entry for authority to the required section. 11 | 12 | ```php 13 | File: /myproject/composer.json 14 | { 15 | require : { 16 | 'machuga/authority' : 'dev-develop' 17 | } 18 | } 19 | ``` 20 | 21 | If this if the first time you're running composer: 22 | 23 | ```$ composer install``` 24 | 25 | Otherwise: 26 | 27 | ```$ composer update``` 28 | 29 | ### Include Composer 30 | 31 | If you're using a modern framework, composer is likely being included for you already. If not, you can add the autoloader yourself by finding a bootstrap script for your framework, and requiring the the vendor/autoload.php file as demonstrated below: 32 | 33 | ```require 'path/to/vendor/autoloader.php';``` 34 | 35 | 36 | Aaaaand that's it! You should be ready to use authority and any other packages you've included in composer. 37 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2013 Matthew Machuga, machuga@gmail.com, 4 | Jesse O'Brien, jesse@jesse-obrien.ca 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | Developer’s Certificate of Origin 1.1 24 | 25 | By making a contribution to this project, I certify that: 26 | 27 | (a) The contribution was created in whole or in part by me and I 28 | have the right to submit it under the open source license 29 | indicated in the file; or 30 | 31 | (b) The contribution is based upon previous work that, to the best 32 | of my knowledge, is covered under an appropriate open source 33 | license and I have the right under that license to submit that 34 | work with modifications, whether created in whole or in part 35 | by me, under the same open source license (unless I am 36 | permitted to submit under a different license), as indicated 37 | in the file; or 38 | 39 | (c) The contribution was provided directly to me by some other 40 | person who certified (a), (b) or (c) and I have not modified 41 | it. 42 | 43 | (d) I understand and agree that this project and the contribution 44 | are public and that a record of the contribution (including all 45 | personal information I submit with it, including my sign-off) is 46 | maintained indefinitely and may be redistributed consistent with 47 | this project or the open source license(s) involved. 48 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # THIS REPO IS NOW DEPRECATED / EOL'ED 2 | It's been a fun few years, but I no longer use PHP in the capacity I find necessary to maintain Authority. It's been this way for a bit, but I'll simply never get a chance to release Authority 3. If someone else would like to take over this project ping me (@machuga) in an Issue. Otherwise I highly recommend checking out beatswitch/lock or sentry/cartalyst as your alternative ACL library. 3 | 4 | Thanks for your support! 5 | 6 | # Authority 7 | 8 | A simple and flexible activity/resource based authorization system for PHP 9 | 10 | [![Build Status](https://travis-ci.org/machuga/authority.png?branch=develop)](https://travis-ci.org/machuga/authority) 11 | 12 | 13 | ## Installation via Composer 14 | 15 | Add Authority to your composer.json file to require Authority 16 | 17 | ``` 18 | "require" : { 19 | "machuga/authority" : "dev-master" 20 | } 21 | ``` 22 | 23 | And install via composer 24 | 25 | `composer install` 26 | 27 | Further installation information is available in `docs/install.md` 28 | 29 | ## Introduction 30 | 31 | Authority is an authorization system for PHP that focuses more on the concept of activities and resources rather than roles. Using different user roles is still completely possible and often needed, but rather than determining functionality based on roles throughout your app, Authority allows you to simply check if a user is allowed to perform an action on a given resource or activity. 32 | 33 | Let's take an example of editing a Post `$post`. 34 | 35 | First we'll use standard role-based authorization checks for roles that may be able to delete a post 36 | 37 | ```php 38 | if ($user->hasRole('admin') || $user->hasRole('moderator') || $user->hasRole('editor')) { 39 | // Can perform actions on resource 40 | $post->delete(); 41 | } 42 | ``` 43 | While this certainly works, it is highly prone to needing changes, and could get quite large as roles increase. 44 | 45 | Let's instead see how simply checking against an activity on a resourse looks. 46 | 47 | ```php 48 | if ($authority->can('edit', $post)) { 49 | // Can perform actions on resource 50 | $post->delete(); 51 | } 52 | ``` 53 | 54 | Instead of littering the codebase with several conditionals about user roles, we only need 55 | to write out a conditional that reads like "if the current user can edit this post". 56 | 57 | ## Default behavior 58 | 59 | Two important default behaviors of Authority to keep in mind 60 | 61 | 1. **Unspecified rules are denied** - if you check a rule that has not been set, Authority will deny the activity. 62 | 2. **Rules are evaluated in order of declaration** - last rule takes precedence. 63 | 64 | ## Basic usage 65 | 66 | Authority is intended to be instantiated once per application (though supports multiple instances). It works well with an IoC (Inversion of Control) container that supports singleton access, like [Laravel's IoC](https://github.com/illuminate/container), or by using standard dependency injection. You may assign rules prior to your app authorizing resources, or add at any time. 67 | 68 | The Authority constructor requires at least one argument - the object that represents the current user. We'll cover the second optional argument later. 69 | 70 | ```php 71 | addAlias('manage', array('create', 'update', 'index', 'read', 'delete')); 84 | 85 | // Let's allow a User to see all other User resources 86 | $authority->allow('read', 'User'); 87 | 88 | /* 89 | * Now let's restrict a User to managing only hiself or herself through 90 | * the use of a conditional callback. 91 | * 92 | * Callback Parameters: 93 | * $self is always the current instance of Authority so that we always 94 | * have access to the user or other functions within the scope of the callback. 95 | * $user here will represent the User object we'll pass into the can() method later 96 | */ 97 | $authority->allow('manage', 'User', function($self, $user) { 98 | // Here we'll compare id's of the user objects - if they match, permission will 99 | // be granted, else it will be denied. 100 | return $self->user()->id === $user->id; 101 | }); 102 | 103 | // Now we can check to see if our rules are configured properly 104 | 105 | $otherUser = (object) array('id' => 2); 106 | if ($authority->can('read', 'User')) { 107 | echo 'I can read about any user based on class!'; 108 | } 109 | 110 | if ($authority->can('read', $otherUser)) { 111 | echo 'I can read about another user!'; 112 | } 113 | 114 | if ($authority->can('delete', $otherUser)) { 115 | echo 'I cannot edit this user so you will not see me :('; 116 | } 117 | 118 | if ($authority->can('delete', $user)) { 119 | echo 'I can delete my own user, so you see me :)'; 120 | } 121 | ``` 122 | 123 | If we run the above script, we will see: 124 | 125 | I can read about any user based on class! 126 | I can read about another user! 127 | I can delete my own user, so you see me :) 128 | 129 | 130 | ## Intermediate Usage 131 | 132 | Coming soon 133 | 134 | ## Advanced Usage 135 | 136 | Coming soon 137 | -------------------------------------------------------------------------------- /src/Authority/Authority.php: -------------------------------------------------------------------------------- 1 | rules = new RuleRepository; 45 | $this->setDispatcher($dispatcher); 46 | $this->setCurrentUser($currentUser); 47 | 48 | $this->dispatch('authority.initialized', array( 49 | 'user' => $this->getCurrentUser(), 50 | )); 51 | } 52 | 53 | /** 54 | * Fires event from current dispatcher 55 | * 56 | * @param string $eventName 57 | * @param mixed $payload 58 | * @return mixed|null 59 | */ 60 | public function dispatch($eventName, $payload = array()) 61 | { 62 | if ($this->dispatcher) { 63 | return $this->dispatcher->fire($eventName, $payload); 64 | } 65 | } 66 | 67 | /** 68 | * Determine if current user can access the given action and resource 69 | * 70 | * @return boolean 71 | */ 72 | public function can($action, $resource, $resourceValue = null) 73 | { 74 | $self = $this; 75 | if ( ! is_string($resource)) { 76 | $resourceValue = $resource; 77 | $resource = get_class($resourceValue); 78 | } 79 | 80 | $rules = $this->getRulesFor($action, $resource); 81 | 82 | if (! $rules->isEmpty()) { 83 | $allowed = array_reduce($rules->all(), function($result, $rule) use ($self, $resourceValue) { 84 | return $result && $rule->isAllowed($self, $resourceValue); 85 | }, true); 86 | 87 | $myRules = $rules->all(); 88 | $last = end($myRules); 89 | 90 | $allowed = $allowed || $last->isAllowed($self, $resourceValue); 91 | } else { 92 | $allowed = false; 93 | } 94 | return $allowed; 95 | } 96 | 97 | /** 98 | * Determine if current user cannot access the given action and resource 99 | * Returns negation of can() 100 | * 101 | * @return boolean 102 | */ 103 | public function cannot($action, $resource, $resourceValue = null) 104 | { 105 | return ! $this->can($action, $resource, $resourceValue); 106 | } 107 | 108 | /** 109 | * Define privilege for a given action and resource 110 | * 111 | * @param array|string $actions Action for the rule 112 | * @param mixed $resource Resource for the rule 113 | * @param Closure|null $condition Optional condition for the rule 114 | * @return array|Rule 115 | */ 116 | public function allow($actions, $resource, $condition = null) 117 | { 118 | return $this->addRule(true, $actions, $resource, $condition); 119 | } 120 | 121 | /** 122 | * Define restriction for a given action and resource 123 | * 124 | * @param array|string $actions Action for the rule 125 | * @param mixed $resource Resource for the rule 126 | * @param Closure|null $condition Optional condition for the rule 127 | * @return array|Rule 128 | */ 129 | public function deny($actions, $resource, $condition = null) 130 | { 131 | return $this->addRule(false, $actions, $resource, $condition); 132 | } 133 | 134 | /** 135 | * Define rule for a given action and resource 136 | * 137 | * @param boolean $allow True if privilege, false if restriction 138 | * @param array|string $actions Action for the rule 139 | * @param mixed $resource Resource for the rule 140 | * @param Closure|null $condition Optional condition for the rule 141 | * @return array|Rule 142 | */ 143 | public function addRule($allow, $actions, $resource, $condition = null) 144 | { 145 | $rules = array(); 146 | 147 | $actions = (array) $actions; 148 | 149 | foreach ($actions as $action) { 150 | $rule = new Rule($allow, $action, $resource, $condition); 151 | $this->rules->add($rule); 152 | $rules[] = $rule; 153 | } 154 | 155 | return count($rules) === 1 ? $rules[0] : $rules; 156 | } 157 | 158 | /** 159 | * Define new alias for an action 160 | * 161 | * @param string $name Name of action 162 | * @param array $actions Actions that $name aliases 163 | * @return RuleAlias 164 | */ 165 | public function addAlias($name, $actions) 166 | { 167 | $alias = new RuleAlias($name, $actions); 168 | $this->aliases[$name] = $alias; 169 | return $alias; 170 | } 171 | 172 | /** 173 | * Set current user 174 | * 175 | * @param mixed $currentUser Current user in the application 176 | * @return void 177 | */ 178 | public function setCurrentUser($currentUser) 179 | { 180 | $this->currentUser = $currentUser; 181 | } 182 | 183 | /** 184 | * Set dispatcher 185 | * 186 | * @param mixed $dispatcher Dispatcher to fire events 187 | * @return void 188 | */ 189 | public function setDispatcher($dispatcher) 190 | { 191 | $this->dispatcher = $dispatcher; 192 | } 193 | 194 | /** 195 | * Returns all rules relevant to the given action and resource 196 | * 197 | * @return RuleRepository 198 | */ 199 | public function getRulesFor($action, $resource) 200 | { 201 | $aliases = $this->getAliasesForAction($action); 202 | return $this->rules->getRelevantRules($aliases, $resource); 203 | } 204 | 205 | /** 206 | * Returns the current rule set 207 | * 208 | * @return RuleRepository 209 | */ 210 | public function getRules() 211 | { 212 | return $this->rules; 213 | } 214 | 215 | /** 216 | * Returns all actions a given action applies to 217 | * 218 | * @return array 219 | */ 220 | public function getAliasesForAction($action) 221 | { 222 | $actions = array($action); 223 | 224 | foreach ($this->aliases as $key => $alias) { 225 | if ($alias->includes($action)) { 226 | $actions[] = $key; 227 | } 228 | } 229 | 230 | return $actions; 231 | } 232 | 233 | /** 234 | * Returns all aliases 235 | * 236 | * @return array 237 | */ 238 | public function getAliases() 239 | { 240 | return $this->aliases; 241 | } 242 | 243 | /** 244 | * Returns a RuleAlias for a given action name 245 | * 246 | * @return RuleAlias|null 247 | */ 248 | public function getAlias($name) 249 | { 250 | return $this->aliases[$name]; 251 | } 252 | 253 | /** 254 | * Returns current user 255 | * 256 | * @return mixed 257 | */ 258 | public function getCurrentUser() 259 | { 260 | return $this->currentUser; 261 | } 262 | 263 | /** 264 | * Returns current user - alias of getCurrentUser() 265 | * 266 | * @return mixed 267 | */ 268 | public function user() 269 | { 270 | return $this->getCurrentUser(); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /src/Authority/Events/Dispatcher.php: -------------------------------------------------------------------------------- 1 | payload = $payload; 8 | } 9 | 10 | public function __get($key) { 11 | return $this->payload[$key]; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Authority/Rule.php: -------------------------------------------------------------------------------- 1 | setBehavior($behavior); 42 | $this->setAction($action); 43 | $this->setResource($resource); 44 | $this->addCondition($condition); 45 | } 46 | 47 | /** 48 | * Determine if current rule allows access 49 | * 50 | * @return boolean 51 | */ 52 | public function isAllowed() 53 | { 54 | $args = func_get_args(); 55 | 56 | if ($this->isPrivilege()) { 57 | $allow = array_reduce($this->conditions, function($results, $condition) use ($args) { 58 | return $results && call_user_func_array($condition, $args); 59 | }, true); 60 | } else { 61 | $allow = false; 62 | if ($this->conditions) { 63 | $allow = ! array_reduce($this->conditions, function($results, $condition) use ($args) { 64 | return $results || call_user_func_array($condition, $args); 65 | }, false); 66 | } 67 | } 68 | return $allow; 69 | } 70 | 71 | /** 72 | * Determine if current rule is relevant based on an action and resource 73 | * 74 | * @param string $action Action in question 75 | * @param string|mixed $resource Name of resource or instance of object 76 | * @return boolean 77 | */ 78 | public function isRelevant($action, $resource) 79 | { 80 | return $this->matchesAction($action) && $this->matchesResource($resource); 81 | } 82 | 83 | /** 84 | * Determine if the instance's action matches the one passed in 85 | * 86 | * @param string $action Action in question 87 | * @return boolean 88 | */ 89 | public function matchesAction($action) 90 | { 91 | $action = (array) $action; 92 | return in_array($this->action,$action); 93 | } 94 | 95 | /** 96 | * Determine if the instance's resource matches the one passed in 97 | * 98 | * @param string|mixed $resource Name of resource or instance of object 99 | * @return boolean 100 | */ 101 | public function matchesResource($resource) 102 | { 103 | $resource = is_object($resource) ? get_class($resource) : $resource; 104 | return $this->resource === $resource || $this->resource === 'all'; 105 | } 106 | 107 | /** 108 | * API friendly alias for addCondition 109 | * 110 | * @return void 111 | */ 112 | public function when($condition) 113 | { 114 | return $this->addCondition($condition); 115 | } 116 | 117 | /** 118 | * Add a condition for the rule to check against 119 | * 120 | * @param Closure $condition Condition callback for rule to utilize 121 | * @return void 122 | */ 123 | public function addCondition($condition) 124 | { 125 | if ($condition !== null) { 126 | $this->conditions[] = $condition; 127 | } 128 | } 129 | 130 | /** 131 | * Determine if rule is a privilege 132 | * 133 | * @return boolean 134 | */ 135 | public function isPrivilege() 136 | { 137 | return $this->getBehavior(); 138 | } 139 | 140 | /** 141 | * Determine if rule is a restriction 142 | * 143 | * @return boolean 144 | */ 145 | public function isRestriction() 146 | { 147 | return ! $this->getBehavior(); 148 | } 149 | 150 | /** 151 | * Set instance action 152 | * 153 | * @param string $action Action for rule to use 154 | * @return void 155 | */ 156 | public function setAction($action) 157 | { 158 | $this->action = $action; 159 | } 160 | 161 | /** 162 | * Set instance behavior 163 | * 164 | * @param boolean $behavior True for privilege, false for restriction 165 | * @return void 166 | */ 167 | public function setBehavior($behavior) 168 | { 169 | $this->behavior = $behavior; 170 | } 171 | 172 | /** 173 | * Set instance resource 174 | * 175 | * @param string|mixed $resource Set resource for rule to be checked against 176 | * @return void 177 | */ 178 | public function setResource($resource) 179 | { 180 | $this->resource = is_object($resource) ? get_class($resource) : $resource; 181 | } 182 | 183 | /** 184 | * Returns action this rule represents 185 | * 186 | * @return string 187 | */ 188 | public function getAction() 189 | { 190 | return $this->action; 191 | } 192 | 193 | /** 194 | * Returns whether rule is a privilege or a restriction 195 | * 196 | * @return boolean 197 | */ 198 | public function getBehavior() 199 | { 200 | return $this->behavior; 201 | } 202 | 203 | /** 204 | * Returns resource this rule represents 205 | * 206 | * @return mixed 207 | */ 208 | public function getResource() 209 | { 210 | return $this->resource; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/Authority/RuleAlias.php: -------------------------------------------------------------------------------- 1 | alias = $alias; 35 | $this->actions = (array) $actions; 36 | } 37 | 38 | /** 39 | * Determine if the alias represents the given action 40 | * 41 | * @param string $action Action in question 42 | * @return boolean 43 | */ 44 | public function includes($action) 45 | { 46 | return in_array($action, $this->actions); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Authority/RuleRepository.php: -------------------------------------------------------------------------------- 1 | rules = $rules; 35 | } 36 | 37 | /** 38 | * Add a rule to the collection 39 | * 40 | * @return void 41 | */ 42 | public function add(Rule $rule) 43 | { 44 | $this->rules[] = $rule; 45 | } 46 | 47 | /** 48 | * Runs a reduce callback on the collection 49 | * 50 | * @param Closure $callback Callback to use for the reduce algorithm 51 | * @param mixed $initialValue Initial value for the reduce set 52 | * @return RuleRepository 53 | */ 54 | public function reduce(Closure $callback, $initialValue = array()) 55 | { 56 | $rules = array_reduce($this->rules, $callback, $initialValue); 57 | return new static($rules); 58 | } 59 | 60 | /** 61 | * Get all rules only relevant to the given action and resource 62 | * 63 | * @param string $action Action to check against 64 | * @param string $resource Resource to check against 65 | * @return RuleRepository 66 | */ 67 | public function getRelevantRules($action, $resource) 68 | { 69 | $rules = array_reduce($this->rules, function($rules, $currentRule) use ($action, $resource) { 70 | if ($currentRule->isRelevant($action, $resource)) { 71 | $rules[] = $currentRule; 72 | } 73 | return $rules; 74 | }, array()); 75 | 76 | return new static($rules); 77 | } 78 | 79 | /** 80 | * Return the first element in the array or null if empty 81 | * 82 | * @return Rule|null 83 | */ 84 | public function first() 85 | { 86 | return count($this->rules) > 0 ? reset($this->rules) : null; 87 | } 88 | 89 | /** 90 | * Return the last element in the array or null if empty 91 | * 92 | * @return Rule|null 93 | */ 94 | public function last() 95 | { 96 | return count($this->rules) > 0 ? end($this->rules) : null; 97 | } 98 | 99 | /** 100 | * Return a raw array of all rules 101 | * 102 | * @return array 103 | */ 104 | public function all() 105 | { 106 | return $this->rules; 107 | } 108 | 109 | /** 110 | * Determine if empty 111 | * 112 | * @return boolean 113 | */ 114 | public function isEmpty() 115 | { 116 | return empty($this->rules); 117 | } 118 | 119 | /** 120 | * Returns an iterator for the internal array 121 | * 122 | * @return ArrayIterator 123 | */ 124 | public function getIterator() 125 | { 126 | return new ArrayIterator($this->rules); 127 | } 128 | 129 | /** 130 | * Returns the number of rules 131 | * 132 | * @return int 133 | */ 134 | public function count() 135 | { 136 | return count($this->rules); 137 | } 138 | 139 | /** 140 | * Determine if the rule exists by key 141 | * 142 | * @return boolean 143 | */ 144 | public function offsetExists($key) 145 | { 146 | return array_key_exists($key, $this->rules); 147 | } 148 | 149 | /** 150 | * Returns the requested Rule 151 | * 152 | * @return Rule 153 | */ 154 | public function offsetGet($key) 155 | { 156 | return $this->rules[$key]; 157 | } 158 | 159 | /** 160 | * Sets the rule at the given key 161 | * 162 | * @return void 163 | */ 164 | public function offsetSet($key, $value) 165 | { 166 | $this->rules[$key] = $value; 167 | } 168 | 169 | /** 170 | * Unsets the rule at the given key 171 | * 172 | * @return void 173 | */ 174 | public function offsetUnset($key) 175 | { 176 | unset($this->rules[$key]); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /tests/AuthorityTest.php: -------------------------------------------------------------------------------- 1 | user = new stdClass; 11 | $this->user->id = 1; 12 | $this->user->name = 'TestUser'; 13 | 14 | $this->auth = new Authority($this->user); 15 | } 16 | 17 | public function tearDown() 18 | { 19 | m::close(); 20 | } 21 | 22 | public function testCanStoreCurrentUser() 23 | { 24 | $this->assertSame($this->user, $this->auth->getCurrentUser()); 25 | 26 | $user = new stdClass; 27 | $this->auth->setCurrentUser($user); 28 | $this->assertSame($user, $this->auth->getCurrentUser()); 29 | } 30 | 31 | public function testCanStoreNewPrivilege() 32 | { 33 | $rule = $this->auth->allow('read', 'User'); 34 | $this->assertCount(1, $this->auth->getRules()); 35 | $this->assertContains($rule, $this->auth->getRules()); 36 | $this->assertTrue($rule->getBehavior()); 37 | } 38 | 39 | public function testCanStoreMultiplePrivileges() 40 | { 41 | $rules = $this->auth->allow(array('read', 'create'), 'User'); 42 | $this->assertCount(2, $this->auth->getRules()); 43 | $this->assertCount(2, $rules); 44 | 45 | foreach ($rules as $rule) { 46 | $this->assertContains($rule, $this->auth->getRules()); 47 | $this->assertTrue($rule->getBehavior()); 48 | } 49 | } 50 | 51 | public function testCanStoreNewRestriction() 52 | { 53 | $rule = $this->auth->deny('read', 'User'); 54 | $this->assertCount(1, $this->auth->getRules()); 55 | $this->assertContains($rule, $this->auth->getRules()); 56 | $this->assertFalse($rule->getBehavior()); 57 | } 58 | 59 | public function testCanStoreMultipleRestrictions() 60 | { 61 | $rules = $this->auth->deny(array('read', 'create'), 'User'); 62 | $this->assertCount(2, $this->auth->getRules()); 63 | $this->assertCount(2, $rules); 64 | 65 | foreach ($rules as $rule) { 66 | $this->assertContains($rule, $this->auth->getRules()); 67 | $this->assertFalse($rule->getBehavior()); 68 | } 69 | } 70 | 71 | public function testCanStoreNewAlias() 72 | { 73 | $alias = $this->auth->addAlias('manage', array('create', 'read', 'update', 'delete')); 74 | $this->assertContains($alias, $this->auth->getAliases()); 75 | $this->assertSame($alias, $this->auth->getAlias('manage')); 76 | } 77 | 78 | public function testCanFetchAliasedActions() 79 | { 80 | $this->auth->addAlias('manage', array('create', 'read', 'update', 'delete')); 81 | $this->auth->addAlias('comment', array('read', 'comment')); 82 | 83 | $this->assertCount(3, $this->auth->getAliasesForAction('read')); 84 | } 85 | 86 | public function testCanFetchAllRulesForAction() 87 | { 88 | $this->auth->addAlias('manage', array('create', 'read', 'update', 'delete')); 89 | $this->auth->addAlias('comment', array('read', 'comment')); 90 | 91 | $this->auth->allow('manage', 'User'); 92 | $this->auth->allow('comment', 'User'); 93 | $this->auth->deny('read', 'User'); 94 | 95 | $this->assertCount(3, $this->auth->getRulesFor('read', 'User')); 96 | } 97 | 98 | public function testCanEvaluateRulesForAction() 99 | { 100 | $this->auth->addAlias('manage', array('create', 'read', 'update', 'delete')); 101 | $this->auth->addAlias('comment', array('read', 'create')); 102 | 103 | $this->auth->allow('manage', 'User'); 104 | $this->auth->allow('comment', 'User'); 105 | $this->auth->deny('read', 'User'); 106 | 107 | $this->assertTrue($this->auth->can('manage', 'User')); 108 | $this->assertTrue($this->auth->can('create', 'User')); 109 | $this->assertFalse($this->auth->can('read', 'User')); 110 | $this->assertFalse($this->auth->can('explodeEverything', 'User')); 111 | $this->assertTrue($this->auth->cannot('explodeEverything', 'User')); 112 | } 113 | 114 | public function testCanEvaluateRulesOnObject() 115 | { 116 | $user = $this->user; 117 | $user2 = new stdClass; 118 | $user2->id = 2; 119 | 120 | $this->auth->allow('comment', 'User', function ($self, $a_user) { 121 | return $self->getCurrentUser()->id == $a_user->id; 122 | }); 123 | 124 | $this->auth->deny('read', 'User', function ($self, $a_user) { 125 | return $self->getCurrentUser()->id != $a_user->id; 126 | }); 127 | 128 | $this->assertFalse($this->auth->can('comment', $user)); 129 | $this->assertTrue($this->auth->can('comment', 'User', $user)); 130 | $this->assertFalse($this->auth->can('comment', $user2)); 131 | $this->assertFalse($this->auth->can('comment', 'User', $user2)); 132 | } 133 | 134 | public function testLastRuleOverridesPreviousRules() 135 | { 136 | $user = $this->user; 137 | 138 | $this->auth->allow('comment', 'User', function ($self, $a_user) { 139 | return $self->getCurrentUser()->id != $a_user->id; 140 | }); 141 | 142 | $this->auth->allow('comment', 'User'); 143 | 144 | $this->assertFalse($this->auth->can('comment', $user)); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /tests/DispatcherTest.php: -------------------------------------------------------------------------------- 1 | dispatcher = new Dispatcher(new Container); 13 | } 14 | 15 | public function tearDown() 16 | { 17 | m::close(); 18 | } 19 | 20 | public function testInitializeEvent() 21 | { 22 | $test = new stdClass; 23 | $user = new stdClass; 24 | $user->name = 'Tester'; 25 | 26 | // Need to decouple test from Authority class eventually 27 | $this->dispatcher->listen('authority.initialized', function($payload) use (&$test) { 28 | $test->user = $payload->user; 29 | $test->timestamp = $payload->timestamp; 30 | }); 31 | 32 | $authority = new Authority($user, $this->dispatcher); 33 | 34 | $this->assertSame($test->user, $user); 35 | $this->assertInstanceOf('DateTime', $test->timestamp); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/RuleAliasTest.php: -------------------------------------------------------------------------------- 1 | actions(); 22 | //$alias->includes('read'); 23 | $this->assertTrue($alias->includes('read')); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/RuleRepositoryTest.php: -------------------------------------------------------------------------------- 1 | repo = new RuleRepository; 12 | 13 | $this->rules = array( 14 | new Rule(true, 'read', 'Obj'), 15 | new Rule(false, 'write', 'Obj') 16 | ); 17 | } 18 | 19 | public function tearDown() 20 | { 21 | m::close(); 22 | } 23 | 24 | public function testCanStoreRules() 25 | { 26 | $repo = new RuleRepository($this->rules); 27 | $repo->add(new Rule(true, 'delete', 'Obj')); 28 | $this->assertCount(3, $repo); 29 | } 30 | 31 | public function testCanReturnEndRules() 32 | { 33 | $repo = new RuleRepository($this->rules); 34 | $this->assertSame($this->rules[0], $repo->first()); 35 | $this->assertSame($this->rules[1], $repo->last()); 36 | } 37 | 38 | public function testCanNarrowRulesByReduce() 39 | { 40 | $repo = new RuleRepository($this->rules); 41 | $rules = $repo->reduce(function($rules, $currentRule) { 42 | if ($currentRule->isPrivilege()) { 43 | $rules[] = $currentRule; 44 | } 45 | return $rules; 46 | }); 47 | $this->assertCount(1, $rules); 48 | } 49 | 50 | public function testCanFetchRelevantRules() 51 | { 52 | $repo = new RuleRepository($this->rules); 53 | $this->assertCount(1, $repo->getRelevantRules('read', 'Obj')); 54 | 55 | $repo->add(new Rule(true, 'read', 'Obj')); 56 | $this->assertCount(2, $repo->getRelevantRules('read', 'Obj')); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/RuleTest.php: -------------------------------------------------------------------------------- 1 | rule = new Rule(true, 'read', m::mock('Obj')); 11 | } 12 | 13 | public function tearDown() 14 | { 15 | m::close(); 16 | } 17 | 18 | public function testCanBeSetToAllowOrDeny() 19 | { 20 | $allowed_rule = new Rule(true, 'read', m::mock('Obj')); 21 | $denied_rule = new Rule(false, 'write', m::mock('Obj')); 22 | $this->assertTrue($allowed_rule->getBehavior()); 23 | $this->assertTrue($allowed_rule->isPrivilege()); 24 | 25 | $this->assertFalse($denied_rule->getBehavior()); 26 | $this->assertTrue($denied_rule->isRestriction()); 27 | } 28 | 29 | public function testCanMatchAction() 30 | { 31 | $this->assertTrue($this->rule->matchesAction('read')); 32 | $this->assertFalse($this->rule->matchesAction('write')); 33 | } 34 | 35 | public function testCanMatchResource() 36 | { 37 | $this->assertTrue($this->rule->matchesResource(m::mock('Obj'))); 38 | $this->assertTrue($this->rule->matchesResource('Mockery\\Mock')); 39 | $this->assertFalse($this->rule->matchesResource('Duck')); 40 | } 41 | 42 | public function testCanDetermineRelevance() 43 | { 44 | $this->assertTrue($this->rule->isRelevant('read', 'Mockery\\Mock')); 45 | $this->assertTrue($this->rule->isRelevant(array('read', 'write'), 'Mockery\\Mock')); 46 | $this->assertFalse($this->rule->isRelevant('write', 'Mockery\\Mock')); 47 | } 48 | 49 | public function testCanSetAndCheckIfAllowed() 50 | { 51 | $rule = new Rule(true, 'read', 'stdClass'); 52 | $this->assertTrue($rule->isAllowed()); 53 | 54 | $rule2 = new Rule(false, 'read', 'stdClass'); 55 | $this->assertFalse($rule2->isAllowed()); 56 | } 57 | 58 | public function testCanSetAndCheckPrivilegeAgainstConditions() 59 | { 60 | $object1 = new stdClass; 61 | $object1->id = 1; 62 | 63 | $object2 = new stdClass; 64 | $object2->id = 2; 65 | 66 | $rule = new Rule(true, 'read', 'stdClass', function($obj) { return $obj->id == 1; }); 67 | $this->assertTrue($rule->isAllowed($object1)); 68 | $this->assertFalse($rule->isAllowed($object2)); 69 | 70 | $rule->when(function($obj) { return 1 == 2; }); 71 | 72 | $this->assertFalse($rule->isAllowed($object1)); 73 | $this->assertFalse($rule->isAllowed($object2)); 74 | } 75 | 76 | public function testCanSetAndCheckRestrictionAgainstConditions() 77 | { 78 | $object1 = new stdClass; 79 | $object1->id = 1; 80 | 81 | $object2 = new stdClass; 82 | $object2->id = 2; 83 | 84 | $rule = new Rule(false, 'read', 'stdClass', function($obj) { 85 | return $obj->id == 1; 86 | }); 87 | $this->assertFalse($rule->isAllowed($object1)); 88 | $this->assertTrue($rule->isAllowed($object2)); 89 | 90 | $rule->when(function($obj) { return 1 == 2; }); 91 | 92 | $this->assertFalse($rule->isAllowed($object1)); 93 | $this->assertTrue($rule->isAllowed($object2)); 94 | 95 | $rule->when(function($obj) { return 1 == 1; }); 96 | 97 | $this->assertFalse($rule->isAllowed($object1)); 98 | $this->assertFalse($rule->isAllowed($object2)); 99 | } 100 | } 101 | --------------------------------------------------------------------------------