pax_global_header00006660000000000000000000000064145637645240014532gustar00rootroot0000000000000052 comment=b1f4a76c605cdf7bae437d4af7d68706160bb78b golang-github-casbin-casbin-2.82.0/000077500000000000000000000000001456376452400170445ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/.github/000077500000000000000000000000001456376452400204045ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001456376452400225675ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000020261456376452400252610ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: "[Bug]" labels: bug assignees: hsluoyz --- **Want to prioritize this issue? Try:** [![issuehunt-to-marktext](https://github.com/BoostIO/issuehunt-materials/raw/master/v1/issuehunt-button-v1.svg)](https://issuehunt.io/r/casbin/casbin) ------ **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] **Smartphone (please complete the following information):** - Device: [e.g. iPhone6] - OS: [e.g. iOS8.1] - Browser [e.g. stock browser, safari] - Version [e.g. 22] **Additional context** Add any other context about the problem here. golang-github-casbin-casbin-2.82.0/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000014631456376452400263200ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: "[Feature]" labels: enhancement assignees: hsluoyz --- **Want to prioritize this issue? Try:** [![issuehunt-to-marktext](https://github.com/BoostIO/issuehunt-materials/raw/master/v1/issuehunt-button-v1.svg)](https://issuehunt.io/r/casbin/casbin) ------ **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. golang-github-casbin-casbin-2.82.0/.github/ISSUE_TEMPLATE/question.md000066400000000000000000000016151456376452400247630ustar00rootroot00000000000000--- name: Question about: Questions like "Why this model and policy don't work as expected?" title: "[Question]" labels: question assignees: hsluoyz --- **Want to prioritize this issue? Try:** [![issuehunt-to-marktext](https://github.com/BoostIO/issuehunt-materials/raw/master/v1/issuehunt-button-v1.svg)](https://issuehunt.io/r/casbin/casbin) ------ **What's your scenario? What do you want to achieve?** Your answer here **Your model:** ```ini [request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act ``` **Your policy:** ``` p, alice, data1, read p, bob, data2, write p, data2_admin, data2, read p, data2_admin, data2, write g, alice, data2_admin ``` **Your request(s):** ``` alice, data2, read ---> false (expected: true) ``` golang-github-casbin-casbin-2.82.0/.github/semantic.yml000066400000000000000000000001101456376452400227220ustar00rootroot00000000000000# Always validate the PR title AND all the commits titleAndCommits: truegolang-github-casbin-casbin-2.82.0/.github/workflows/000077500000000000000000000000001456376452400224415ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/.github/workflows/PerformancePush.yml000066400000000000000000000024161456376452400262700ustar00rootroot00000000000000name: Performance for Push on: push: branches: [ master ] permissions: # deployments permission to deploy GitHub pages website deployments: write # contents permission to update benchmark contents in gh-pages branch contents: write jobs: benchmark: name: Performance regression check runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 # Run benchmark with `go test -bench` and stores the output to a file - name: Run benchmark run: go test -bench '.' -benchmem ./... | tee output.txt # Run `github-action-benchmark` action - name: Store benchmark result uses: benchmark-action/github-action-benchmark@v1 with: tool: 'go' output-file-path: output.txt benchmark-data-dir-path: benchmark-monitoring save-data-file: false fail-on-alert: true alert-threshold: '110%' fail-threshold: '200%' # GitHub API token to make a commit comment github-token: ${{ secrets.GITHUB_TOKEN }} # Enable alert commit comment comment-on-alert: true comment-always: true gh-pages-branch: gh-pages # alert-comment-cc-users: '@XXX' auto-push: truegolang-github-casbin-casbin-2.82.0/.github/workflows/default.yml000066400000000000000000000024241456376452400246120ustar00rootroot00000000000000name: Build on: [push, pull_request] jobs: test: runs-on: ubuntu-latest strategy: matrix: go: ['1.18', '1.19', '1.20'] steps: - uses: actions/checkout@v2 - name: Set up Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go }} - name: Run go test run: make test benchmark: runs-on: ubuntu-latest strategy: matrix: go: ['1.18', '1.19', '1.20'] steps: - uses: actions/checkout@v2 - name: Set up Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go }} - name: Run go test bench run: make benchmark lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Go uses: actions/setup-go@v2 with: go-version: '1.20' - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: version: v1.51 semantic-release: needs: [test, lint] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Run semantic-release if: github.repository == 'casbin/casbin' && github.event_name == 'push' run: make release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} golang-github-casbin-casbin-2.82.0/.github/workflows/staticcheck.yaml000066400000000000000000000005651456376452400256200ustar00rootroot00000000000000--- name: "staticcheck" on: push: branches: - main paths: - '**.go' pull_request: paths: - '**.go' jobs: ci: name: "staticcheck" runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 with: fetch-depth: 1 - uses: dominikh/staticcheck-action@v1.2.0 with: version: "2022.1.1" golang-github-casbin-casbin-2.82.0/.gitignore000066400000000000000000000004571456376452400210420ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof .idea/ *.iml # vendor files vendor golang-github-casbin-casbin-2.82.0/.releaserc.json000066400000000000000000000004411456376452400217610ustar00rootroot00000000000000{ "debug": true, "branches": [ "+([0-9])?(.{+([0-9]),x}).x", "master", { "name": "beta", "prerelease": true } ], "plugins": [ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", "@semantic-release/github" ] } golang-github-casbin-casbin-2.82.0/.travis.yml000066400000000000000000000002151456376452400211530ustar00rootroot00000000000000language: go sudo: false env: - GO111MODULE=on go: - "1.11.13" - "1.12" - "1.13" - "1.14" script: - make test golang-github-casbin-casbin-2.82.0/CONTRIBUTING.md000066400000000000000000000044251456376452400213020ustar00rootroot00000000000000# How to contribute The following is a set of guidelines for contributing to casbin and its libraries, which are hosted at [casbin organization at Github](https://github.com/casbin). This project adheres to the [Contributor Covenant 1.2.](https://www.contributor-covenant.org/version/1/2/0/code-of-conduct.html) By participating, you are expected to uphold this code. Please report unacceptable behavior to info@casbin.com. ## Questions - We do our best to have an [up-to-date documentation](https://casbin.org/docs/overview) - [Stack Overflow](https://stackoverflow.com) is the best place to start if you have a question. Please use the [casbin tag](https://stackoverflow.com/tags/casbin/info) we are actively monitoring. We encourage you to use Stack Overflow specially for Modeling Access Control Problems, in order to build a shared knowledge base. - You can also join our [Discord](https://discord.gg/S5UjpzGZjN). ## Reporting issues Reporting issues are a great way to contribute to the project. We are perpetually grateful about a well-written, through bug report. Before raising a new issue, check our [issue list](https://github.com/casbin/casbin/issues) to determine if it already contains the problem that you are facing. A good bug report shouldn't leave others needing to chase you for more information. Please be as detailed as possible. The following questions might serve as a template for writing a detailed report: What were you trying to achieve? What are the expected results? What are the received results? What are the steps to reproduce the issue? In what environment did you encounter the issue? Feature requests can also be submitted as issues. ## Pull requests Good pull requests (e.g. patches, improvements, new features) are a fantastic help. They should remain focused in scope and avoid unrelated commits. Please ask first before embarking on any significant pull request (e.g. implementing new features, refactoring code etc.), otherwise you risk spending a lot of time working on something that the maintainers might not want to merge into the project. First add an issue to the project to discuss the improvement. Please adhere to the coding conventions used throughout the project. If in doubt, consult the [Effective Go style guide](https://golang.org/doc/effective_go.html). golang-github-casbin-casbin-2.82.0/LICENSE000066400000000000000000000261351456376452400200600ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. golang-github-casbin-casbin-2.82.0/Makefile000066400000000000000000000003751456376452400205110ustar00rootroot00000000000000SHELL = /bin/bash export PATH := $(shell yarn global bin):$(PATH) default: lint test test: go test -race -v ./... benchmark: go test -bench=. lint: golangci-lint run --verbose release: yarn global add semantic-release@17.2.4 semantic-release golang-github-casbin-casbin-2.82.0/README.md000066400000000000000000000445461456376452400203400ustar00rootroot00000000000000Casbin ==== [![Go Report Card](https://goreportcard.com/badge/github.com/casbin/casbin)](https://goreportcard.com/report/github.com/casbin/casbin) [![Build](https://github.com/casbin/casbin/actions/workflows/default.yml/badge.svg)](https://github.com/casbin/casbin/actions/workflows/default.yml) [![Coverage Status](https://coveralls.io/repos/github/casbin/casbin/badge.svg?branch=master)](https://coveralls.io/github/casbin/casbin?branch=master) [![Godoc](https://godoc.org/github.com/casbin/casbin?status.svg)](https://pkg.go.dev/github.com/casbin/casbin/v2) [![Release](https://img.shields.io/github/release/casbin/casbin.svg)](https://github.com/casbin/casbin/releases/latest) [![Discord](https://img.shields.io/discord/1022748306096537660?logo=discord&label=discord&color=5865F2)](https://discord.gg/S5UjpzGZjN) [![Sourcegraph](https://sourcegraph.com/github.com/casbin/casbin/-/badge.svg)](https://sourcegraph.com/github.com/casbin/casbin?badge) **News**: still worry about how to write the correct Casbin policy? ``Casbin online editor`` is coming to help! Try it at: https://casbin.org/editor/ ![casbin Logo](casbin-logo.png) Casbin is a powerful and efficient open-source access control library for Golang projects. It provides support for enforcing authorization based on various [access control models](https://en.wikipedia.org/wiki/Computer_security_model).

Sponsored by

Build auth with fraud prevention, faster.
Try Stytch for API-first authentication, user & org management, multi-tenant SSO, MFA, device fingerprinting, and more.

## All the languages supported by Casbin: | [![golang](https://casbin.org/img/langs/golang.png)](https://github.com/casbin/casbin) | [![java](https://casbin.org/img/langs/java.png)](https://github.com/casbin/jcasbin) | [![nodejs](https://casbin.org/img/langs/nodejs.png)](https://github.com/casbin/node-casbin) | [![php](https://casbin.org/img/langs/php.png)](https://github.com/php-casbin/php-casbin) | |----------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| | [Casbin](https://github.com/casbin/casbin) | [jCasbin](https://github.com/casbin/jcasbin) | [node-Casbin](https://github.com/casbin/node-casbin) | [PHP-Casbin](https://github.com/php-casbin/php-casbin) | | production-ready | production-ready | production-ready | production-ready | | [![python](https://casbin.org/img/langs/python.png)](https://github.com/casbin/pycasbin) | [![dotnet](https://casbin.org/img/langs/dotnet.png)](https://github.com/casbin-net/Casbin.NET) | [![c++](https://casbin.org/img/langs/cpp.png)](https://github.com/casbin/casbin-cpp) | [![rust](https://casbin.org/img/langs/rust.png)](https://github.com/casbin/casbin-rs) | |------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------| | [PyCasbin](https://github.com/casbin/pycasbin) | [Casbin.NET](https://github.com/casbin-net/Casbin.NET) | [Casbin-CPP](https://github.com/casbin/casbin-cpp) | [Casbin-RS](https://github.com/casbin/casbin-rs) | | production-ready | production-ready | production-ready | production-ready | ## Table of contents - [Supported models](#supported-models) - [How it works?](#how-it-works) - [Features](#features) - [Installation](#installation) - [Documentation](#documentation) - [Online editor](#online-editor) - [Tutorials](#tutorials) - [Get started](#get-started) - [Policy management](#policy-management) - [Policy persistence](#policy-persistence) - [Policy consistence between multiple nodes](#policy-consistence-between-multiple-nodes) - [Role manager](#role-manager) - [Benchmarks](#benchmarks) - [Examples](#examples) - [Middlewares](#middlewares) - [Our adopters](#our-adopters) ## Supported models 1. [**ACL (Access Control List)**](https://en.wikipedia.org/wiki/Access_control_list) 2. **ACL with [superuser](https://en.wikipedia.org/wiki/Superuser)** 3. **ACL without users**: especially useful for systems that don't have authentication or user log-ins. 3. **ACL without resources**: some scenarios may target for a type of resources instead of an individual resource by using permissions like ``write-article``, ``read-log``. It doesn't control the access to a specific article or log. 4. **[RBAC (Role-Based Access Control)](https://en.wikipedia.org/wiki/Role-based_access_control)** 5. **RBAC with resource roles**: both users and resources can have roles (or groups) at the same time. 6. **RBAC with domains/tenants**: users can have different role sets for different domains/tenants. 7. **[ABAC (Attribute-Based Access Control)](https://en.wikipedia.org/wiki/Attribute-Based_Access_Control)**: syntax sugar like ``resource.Owner`` can be used to get the attribute for a resource. 8. **[RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer)**: supports paths like ``/res/*``, ``/res/:id`` and HTTP methods like ``GET``, ``POST``, ``PUT``, ``DELETE``. 9. **Deny-override**: both allow and deny authorizations are supported, deny overrides the allow. 10. **Priority**: the policy rules can be prioritized like firewall rules. ## How it works? In Casbin, an access control model is abstracted into a CONF file based on the **PERM metamodel (Policy, Effect, Request, Matchers)**. So switching or upgrading the authorization mechanism for a project is just as simple as modifying a configuration. You can customize your own access control model by combining the available models. For example, you can get RBAC roles and ABAC attributes together inside one model and share one set of policy rules. The most basic and simplest model in Casbin is ACL. ACL's model CONF is: ```ini # Request definition [request_definition] r = sub, obj, act # Policy definition [policy_definition] p = sub, obj, act # Policy effect [policy_effect] e = some(where (p.eft == allow)) # Matchers [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.act ``` An example policy for ACL model is like: ``` p, alice, data1, read p, bob, data2, write ``` It means: - alice can read data1 - bob can write data2 We also support multi-line mode by appending '\\' in the end: ```ini # Matchers [matchers] m = r.sub == p.sub && r.obj == p.obj \ && r.act == p.act ``` Further more, if you are using ABAC, you can try operator `in` like following in Casbin **golang** edition (jCasbin and Node-Casbin are not supported yet): ```ini # Matchers [matchers] m = r.obj == p.obj && r.act == p.act || r.obj in ('data2', 'data3') ``` But you **SHOULD** make sure that the length of the array is **MORE** than **1**, otherwise there will cause it to panic. For more operators, you may take a look at [govaluate](https://github.com/casbin/govaluate) ## Features What Casbin does: 1. enforce the policy in the classic ``{subject, object, action}`` form or a customized form as you defined, both allow and deny authorizations are supported. 2. handle the storage of the access control model and its policy. 3. manage the role-user mappings and role-role mappings (aka role hierarchy in RBAC). 4. support built-in superuser like ``root`` or ``administrator``. A superuser can do anything without explicit permissions. 5. multiple built-in operators to support the rule matching. For example, ``keyMatch`` can map a resource key ``/foo/bar`` to the pattern ``/foo*``. What Casbin does NOT do: 1. authentication (aka verify ``username`` and ``password`` when a user logs in) 2. manage the list of users or roles. I believe it's more convenient for the project itself to manage these entities. Users usually have their passwords, and Casbin is not designed as a password container. However, Casbin stores the user-role mapping for the RBAC scenario. ## Installation ``` go get github.com/casbin/casbin/v2 ``` ## Documentation https://casbin.org/docs/overview ## Online editor You can also use the online editor (https://casbin.org/editor/) to write your Casbin model and policy in your web browser. It provides functionality such as ``syntax highlighting`` and ``code completion``, just like an IDE for a programming language. ## Tutorials https://casbin.org/docs/tutorials ## Get started 1. New a Casbin enforcer with a model file and a policy file: ```go e, _ := casbin.NewEnforcer("path/to/model.conf", "path/to/policy.csv") ``` Note: you can also initialize an enforcer with policy in DB instead of file, see [Policy-persistence](#policy-persistence) section for details. 2. Add an enforcement hook into your code right before the access happens: ```go sub := "alice" // the user that wants to access a resource. obj := "data1" // the resource that is going to be accessed. act := "read" // the operation that the user performs on the resource. if res, _ := e.Enforce(sub, obj, act); res { // permit alice to read data1 } else { // deny the request, show an error } ``` 3. Besides the static policy file, Casbin also provides API for permission management at run-time. For example, You can get all the roles assigned to a user as below: ```go roles, _ := e.GetImplicitRolesForUser(sub) ``` See [Policy management APIs](#policy-management) for more usage. ## Policy management Casbin provides two sets of APIs to manage permissions: - [Management API](https://casbin.org/docs/management-api): the primitive API that provides full support for Casbin policy management. - [RBAC API](https://casbin.org/docs/rbac-api): a more friendly API for RBAC. This API is a subset of Management API. The RBAC users could use this API to simplify the code. We also provide a [web-based UI](https://casbin.org/docs/admin-portal) for model management and policy management: ![model editor](https://hsluoyz.github.io/casbin/ui_model_editor.png) ![policy editor](https://hsluoyz.github.io/casbin/ui_policy_editor.png) ## Policy persistence https://casbin.org/docs/adapters ## Policy consistence between multiple nodes https://casbin.org/docs/watchers ## Role manager https://casbin.org/docs/role-managers ## Benchmarks https://casbin.org/docs/benchmark ## Examples | Model | Model file | Policy file | |---------------------------|----------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------| | ACL | [basic_model.conf](https://github.com/casbin/casbin/blob/master/examples/basic_model.conf) | [basic_policy.csv](https://github.com/casbin/casbin/blob/master/examples/basic_policy.csv) | | ACL with superuser | [basic_model_with_root.conf](https://github.com/casbin/casbin/blob/master/examples/basic_with_root_model.conf) | [basic_policy.csv](https://github.com/casbin/casbin/blob/master/examples/basic_policy.csv) | | ACL without users | [basic_model_without_users.conf](https://github.com/casbin/casbin/blob/master/examples/basic_without_users_model.conf) | [basic_policy_without_users.csv](https://github.com/casbin/casbin/blob/master/examples/basic_without_users_policy.csv) | | ACL without resources | [basic_model_without_resources.conf](https://github.com/casbin/casbin/blob/master/examples/basic_without_resources_model.conf) | [basic_policy_without_resources.csv](https://github.com/casbin/casbin/blob/master/examples/basic_without_resources_policy.csv) | | RBAC | [rbac_model.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_model.conf) | [rbac_policy.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_policy.csv) | | RBAC with resource roles | [rbac_model_with_resource_roles.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_with_resource_roles_model.conf) | [rbac_policy_with_resource_roles.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_with_resource_roles_policy.csv) | | RBAC with domains/tenants | [rbac_model_with_domains.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_with_domains_model.conf) | [rbac_policy_with_domains.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_with_domains_policy.csv) | | ABAC | [abac_model.conf](https://github.com/casbin/casbin/blob/master/examples/abac_model.conf) | N/A | | RESTful | [keymatch_model.conf](https://github.com/casbin/casbin/blob/master/examples/keymatch_model.conf) | [keymatch_policy.csv](https://github.com/casbin/casbin/blob/master/examples/keymatch_policy.csv) | | Deny-override | [rbac_model_with_deny.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_with_deny_model.conf) | [rbac_policy_with_deny.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_with_deny_policy.csv) | | Priority | [priority_model.conf](https://github.com/casbin/casbin/blob/master/examples/priority_model.conf) | [priority_policy.csv](https://github.com/casbin/casbin/blob/master/examples/priority_policy.csv) | ## Middlewares Authz middlewares for web frameworks: https://casbin.org/docs/middlewares ## Our adopters https://casbin.org/docs/adopters ## How to Contribute Please read the [contributing guide](CONTRIBUTING.md). ## Contributors This project exists thanks to all the people who contribute. ## Backers Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/casbin#backer)] ## Sponsors Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/casbin#sponsor)] ## Star History [![Star History Chart](https://api.star-history.com/svg?repos=casbin/casbin&type=Date)](https://star-history.com/#casbin/casbin&Date) ## License This project is licensed under the [Apache 2.0 license](LICENSE). ## Contact If you have any issues or feature requests, please contact us. PR is welcomed. - https://github.com/casbin/casbin/issues - hsluoyz@gmail.com - Tencent QQ group: [546057381](//shang.qq.com/wpa/qunwpa?idkey=8ac8b91fc97ace3d383d0035f7aa06f7d670fd8e8d4837347354a31c18fac885) golang-github-casbin-casbin-2.82.0/casbin-logo.png000066400000000000000000001027331456376452400217550ustar00rootroot00000000000000PNG  IHDR` pHYs**ˆ>GiTXtXML:com.adobe.xmp Adobe Photoshop CC (Windows) 2017-05-04T22:30:16+08:00 2017-05-04T23:32:11+08:00 2017-05-04T23:32:11+08:00 image/png 3 xmp.iid:1a8e04b2-ef8f-8241-bd11-ca81ebee7fbb xmp.did:4deef3c0-781f-9444-b7a9-e02f920a69fd xmp.did:fe29720f-6920-634f-bdff-2d8839ddc21a created xmp.iid:fe29720f-6920-634f-bdff-2d8839ddc21a 2017-05-04T22:30:16+08:00 Adobe Photoshop CC (Windows) saved xmp.iid:7ce46333-e622-6542-a7ef-62b430afcdd6 2017-05-04T22:46:43+08:00 Adobe Photoshop CC (Windows) / saved xmp.iid:d372582d-e33b-6b4f-967c-ba0bafe95c67 2017-05-04T22:51:39+08:00 Adobe Photoshop CC (Windows) / converted from image/png to application/vnd.adobe.photoshop derived converted from image/png to application/vnd.adobe.photoshop saved xmp.iid:4deef3c0-781f-9444-b7a9-e02f920a69fd 2017-05-04T22:51:39+08:00 Adobe Photoshop CC (Windows) / saved xmp.iid:524450fd-9afd-6b42-8671-70842d58d3a8 2017-05-04T23:27:59+08:00 Adobe Photoshop CC (Windows) / converted from application/vnd.adobe.photoshop to image/png derived converted from application/vnd.adobe.photoshop to image/png saved xmp.iid:e5deca8b-4984-3944-aff3-88f2c02e2524 2017-05-04T23:27:59+08:00 Adobe Photoshop CC (Windows) / saved xmp.iid:1a8e04b2-ef8f-8241-bd11-ca81ebee7fbb 2017-05-04T23:32:11+08:00 Adobe Photoshop CC (Windows) / xmp.iid:524450fd-9afd-6b42-8671-70842d58d3a8 xmp.did:4deef3c0-781f-9444-b7a9-e02f920a69fd xmp.did:fe29720f-6920-634f-bdff-2d8839ddc21a 1 1636270/10000 1636270/10000 2 65535 384 96 7 cHRMz%u0`:o_F=sIDATxwU?g۶f[z#!!@@4 * p{rkAtQrRB{udv}eK}v33zRJB"~pFkXRn10o</^L27)%3=9 f{gc=uuuuYR)q֮]?>jk8p-\!B), Rʫm'd$hI`P |t lV;dB7Bdz<D=;L.(++Y=L&K1Y"`>0|v`S lDC3g?|+**"- ^s}̙CO!c3Dh@}Y}[ Gu9Cx׺/^|F{{s=;uQG?}n"Cqpb890~\ b4H$… q &|yĉL63f0~x1y䫪q… D"0 }%@>v p9|p'K%DqLBM͞2ܢE.(**Z<|"HOiiGq ]ϭaʔ)?F@ p#3És{%"D~²,9s&0i$555va؀ v|ƏQ__1sL;#B!pZ  0G}F "D~@AKK s%0{ ء$ ǐ^&pO5kɠΝKKKK4"{F*{K'DAJiL>RO2_tܿ7>p2u***bƌhFX`$ۑ9kHL0 t!D (H.B9fZiokD[[[=;dʕu2&4ʊ#>ղܩd ƈ 00ϗ#$&ӑp=pMRJt*IBewż(m}7iZǵhL+4y'<4]w8Y/'fƘ<jlnnk!Lv444<.e:pSAQ䭍M+9LjNJ(ljhgņ&ז1oZ-?EeoiN6e+:;p؊+8X4^L6 fRjժ6n^zo}[3 Iw}mog[niD@ 㫿9l+Ζ-\ mN*㩧bݺuZfV,2E_JRYbNY8vW+|A+hu4ZO@OBShq]p/ $!ɬO`֤*ߓ:+*"H\$҄nX@;Yr孭\}մcΠ?=8>R㫿Q"%$ŗ݉Y1+c=Ν;Ynݟ4ad7$TSk8vdʋ)-4dk=6W8bF%)f.Lhu !XTnkTXL{d=Ayg rnݺH)wĿaCe^N4s67h<|dbK:04YJOy)vdRdhsIWz#YOr\>%ɼCj4Ͷ21UE|ԧp|4E s_0oiIχ?Ĩ 4ͮ[vw/;P#'Yz>zޮ=́V9뭷t:oR h#cԉׁ4! hB\|.XĊJ콷5hE60m{g,Ӹq5[x$qG}NnOjY a#Ǐ8Q]w|j^M?AO&s-uؒ5u+S2aLuhBy{msT>{h 㘊M+/P__i?~D7U+sRu!r+n***H$@*$CA`49[y)0$S5g;EdqE r9Jjذn+WŋaV;:FhRmkRű" t 'c;6&H+}i<~獴7lWi1˲j(6pct*47@TL!"53"k H44J ޠT+E-Զ؞0l%%TTK.;v;cnx<+B_dHbB )Y&Mg>Yn6yQ>:::]wsg {B(S҄KB14h:^&/O 1 b!$1d3z).,Y8+իW<G"X,y3 `dO^Fۂ%]"",PJ7}}]vc"]w3QSpʻO;՗S[[H ^I[R Sֲc6nFfϞɓ>}:f$B4guUfQfH#LὭ7@q_/GZoWhxźQ {%B4(qӵ 1 0l2~_۶BDO}D?Ęra}Yx 5M/{ׂjdӆ꟡|O«444[֜h4a=;'ľc]T5,O=BwqP|Oz=n.y* ^!vT͘1'6n4MLMȮ8i%gGH3$;ڸs9|u$4?}7G,:kV+ % պu1uF?!*Ҟu]}?TFokBYp^,4!(1vY9J*/rmcFLaxa""T?0vur1 , @e noĻ- =<.(A*(C 'dãq_eZ`ڼ&\ >wZ} GWlhHٻeV躎 L{<cE:BmŲ}xR|%L:6lȘ<ڔ,[fVZ[[uUk}4M&<^f*?݃B7GD%MLTbZUͳզr0!$T\$N{Vp#ÁF1 T; +#u`*GgR@DXw PN =n7V bjBP0T?apf^6k;Y,sO LḲ%ԙShu=Gb$Аt̐<ؔ*3eO'#PE)7}iPS2a 값9vn\R34iDfds&9*G+p}hBtFiڮ0U]Ƕ_v$(6/D0/ `"b)܀ `>j$;U~N|0?k_`n' A}X9=OG뀗_y~/x{ѡOv\pw6TׯD|Qw P=wLn{zkegVh^GT#gZ8OgZ`ʌ?tzfCh#is5| է9V]UYcL)57tD)'Ey끿˟%(f֢cyY'-id;Mae.դ)}FWD24HZ1")4{m &0PBjF#[| pׂ@.>HMEf->@J"Ib049#~^ Y_{9 4'p)v_O A̺1aH@W+-Tv,w_l+v=u:暱PuF,,9~1I0E/c@9ˀc2EU{듃|+Cp2ch"J9)drEWOժȸPq)2$/뼙4pxDEflL"SYbj:|E֛,Α}OMj'IhH39Ril MƦAӨ0}b2P <{C:?C }I`߯χ%H~Cߢ"Y"k`aT<ǝ&%:ݬQvœ`} FF T *MjWx9EqOd5u<]O *(3ʄ)ں"M@h忦fl~8~][7q3eEtthEo;Qo#괹+w Q!Bpnat%dS} f J&a<@Z]es_R`!&|6sQvF.yy@!z| btk09(P`w|p,gGc@dtCz 62ۑ:rNKX*ERG)5׋14CD𥇇[ؾi\,9Zw,OC[9S`զH)9`R=Gz llIU3m('0 ]|I>?> Cj.kw5<HTf )!agC bߣdp p>Ow,3P;3變 m0uh8QC~A:.ZeHYT)n"i!mu¨BE<*wnt&s(m܌+ekc;% ;iJ 㱹 vaʐ<QVnGoo"84T,3@MN%u~} F mb0d~`Voإ@9 1)<:^bќJ7=0k U?WЀ Z؂  ozWh >hKf{[h?= @KБ jtvaBCGP=}]s|*7 J"J8Hz.@!":‘ͪ<슃Ì**;6M`HzH!JD R:B2ŵD- u/eD oE GyP-Xn=0\, ŨX@zx `~=ͅƯD.~vQzZ TXgw*4`g{ntPQTC2MM R ܀ ezOZzO.C\ =:J4D/cFmg$=iwv $t::&:`8[#/i#$&츯SA2s!RRv_{l.Eez RJ&VW7 ~^ൟ |ray?*flOҤ֋ꁀ.*yT/_M?0s|=pb0o}>ggN@ֿ> 灩E[7h|88jod!@>J&9 oe[^tc}$] /a$().3z |M#=|JRPT4D re)e"G"#Ơkjqv%(,`Q9&𕽎 jr,]Vm1~y@]AE|oR޸G8`&# wp;;;/H:`  oOw^XRӈ3iT N{V{3V2MJ 3D<ӌb(#JC]X@j̇gs3N*OF'^'$>~z`xyI[Acy 7_ N0G_ GBU̇Y?'̰`]#džv/(@)džv""5}>]Qֻ pm2ֹZBjĘ_wRxϓ~ pCs%,F"V+02K)ل2=31L~~ UkTF<2ӧ6ƙtew_21 0ucKݧVߡ"9Pq# PjQ*q0 2߯SXFrQaO +멧$Cc]fِk2(?F @q0\9!TcUwFLaNs(\SaЈ2 ; @"j&9fej3;VfiWQ(=RbP:.0lo%31TqTHG]HQ Ѹ yqeQa,Vw9;O7n!s;}4-XO 0#5y˴T?.}W @젰r&[ r !|ڪ3>l @"67ŪUA JVeqzҍm ;x/cϭ[#!,Z-hF e_0jhg[HCtѕ{;W"h2:}9_ud cTEG+ү~9N\: x7kf) 6LQ$CtQu/܉AMz1zQ[ @I&YbՄKYNY-.ATDx9Vu}+gՑ%d<OőxR"Y/ Sc1f\6oB"tSDELM@c |E7p֒Ө(BdTWPaȗ!Z3TLrI.? *n+29h2!i&n;?sYJ99!_~-sKēIk&ɲj&yvrjD-PyQN IfMTЙ(V!&@*<'|)&|]n嚩 gG\3HNkve>+͸D%2"tSR~:= +VΌU0}FDa]LyˇpŶQNE:s%˨"hп~˳g(XA! o&utP'"ƾ.44EzK޽%&м9 ^ǿSHKo>72Cǭ1 `>0xXr[f[a+~E‰-LXǼ6d!^WEĿ41⨎YϢj*5FhF!!%K @ؽ ٞ02#<={L1+ysٗq%)Njy/]_?!e"=~n>0<=BYxctّr謩sAcuM]u{}|=ը,>G:H+ǞڒZ$ٱBe` + n>ƴ<(qlц~@e]ZB ،D5Ѻ'Ͷ0p3wxk8| ^2$dҷc^AHda?B"]Oz]aIAY较:B DzoI6P쎰IP9`9*afsbL*,@Oك4s|B!TȓN[ˌ ;Lv *gW.Sp=3 $NO^_Sb!(1na3M2aS%-^.^;T۲YjɅ3僉97`=1$gq_U.~Xs $U'c cR _B bBG+9rwY]υ|)ؕK<#TF=QLj:l3˩ʲPjK{!5%E<{Šop #h]8ga?RbSo|Y9`( wCExKhOǓM= YLU#sp_}J[|%˯;٧2TFc9H FC4?o2MxFtI.qmB#%HTS|[m | i|'ɧFưFfm:i{0UwAiZ?&xn$6-gn,$>} "㑱;VvUfY/6yE(O Sj QYzHY1L&~j, Q|5yigk& x?@.E/[l꣕?W-?iyn$IcD ^CQxb|!pTa|`]2dy&<QfynflSK8h/m)ڤ' -zfW!@x&MCNsRhtW`KVj '@=ugQe!a ib!$xMt|LweVOGh-J*[֞a~}z?_ 'G,)1&B7+36BDW"4 7eu,OYgFڜ:/+sPw eQ$6 sPaE/gf?#m"5@GL@0tik]ȊSqb߃h|=$&J/ Y#EG"z ~BYtb2r~VZ3M rHfR|3iwچ=cKh #7oZT1g~K~A+G0!w+i(=!I vkwI_fHV|ۚȪ1uByeUV"_;s=]o\$)< ,@%7}z& }}Cv+z!l>/@~^oGE? 7iۑ9Sr& LR>mm/@=Ezo (-gx2QMJ`TZ&ʆ;߫q=|'fSXy.PTW_F0+nَ J|+mBH!k"%tW-4($TIהkds)T-/^9KJ)*d6{e c ' N*ܳ;8(_E>O+7Neʱ$:ߙjhSHY+Gr SimD-@JD@4xUAFXs<rÏ)t̟RXmNMUpnsǣ"FB@\܋f{UߗQ54loԀ|@+L}6,g|=4# LVr،j0t7U`}\R;HEW4eYR43t,tOArUAMI# ʓ0*g<UWEY6x2 +11Nw9vֺhASPE/has7XmZ=D8^cO'tX|W1(c 5NvӘ¬-Ffɒ L t9ˎ:;*Obka} Rt&2y>|GN>*z/V* L#A5 iKNaUN7ZEdYk㱀!- i#*]Bxǡ|%"EaJ<Hp|Qf$Q{GZ@/g;DtDMsfJk9ogI68S`R;?OVzUP\*AzU}87ABp)_Z 󍨖k?(<bbv? ]$4iӼTr0;Ѣ5qɔoRnğQPZr&NL3f;ʉϿU~(ߨx}]lc;JmdQ?bf K)ɭiW墥cX ܼJ.EUölȌۂ']ȥ$C{Q;9T篆~V"X,)/VM0kdy #/]}HNn!} s!H>EW|# ?'b/~_"L 7& [>2ΩM&\i]@+|Uj~7NU@7@EO^RAs{zS+{}LGuL{˷_+{9s8N:C=%OWw(%3dB:] kXڨRŌeҗ64Q-n*'{&ZM fشu[53\^`}\Ϲ͇PQa=a)*h?Ƞ= [mi(4\=U4.-XtIC/cT%&B@%^2(? `5uб7"99Z-d 0{x4 }9ÌW?A=ӱ:յi&*$Krގ {3dhm" b8 u9.u2 WLcPddvw]fTvTPP ^NyV|"pP]h\<ϋ(f>K% t2zH7ړhhCY;5) 6F@ oyȌ AP?cZ(zkO~iO:11">f \#%b%ϕȭH:;tkKg=O6l?}>~5е]_;hs3OWzqԿ񑈓´DS{xA6QD3MT65U:O31e&IHCF"D$|P54)pi{3S8?.mDN~ɲ$RNpZ$BdX"5d6˶'[M-D>ۻ!BcPziVaUT*Hҷ>i&ag}mQ*7MH/fafQfDKq(B-—[;to$4c_vzp5b90t6+S#$c֩1x$L'C[tV8;V2Jph3l˖^gbf=U6H{do0E"T/fn!Bϰσm$e;5}c)Jl?/$GiO*Mxקn7T _|jQnQ*T.JĈ*>N-L5J'h,U5~VO=+ \). .,fMHYIqB1!\xqw1}}:V|+z!}Pk9ռ$T`~6`"&EJ!* r$Si$3Y7 "~T왊#iY>1tW3B\2C!BT?"DNA!B D!BGJGRIENDB`golang-github-casbin-casbin-2.82.0/config/000077500000000000000000000000001456376452400203115ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/config/config.go000066400000000000000000000147511456376452400221150ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package config import ( "bufio" "bytes" "errors" "fmt" "io" "os" "strconv" "strings" ) var ( // DEFAULT_SECTION specifies the name of a section if no name provided DEFAULT_SECTION = "default" // DEFAULT_COMMENT defines what character(s) indicate a comment `#` DEFAULT_COMMENT = []byte{'#'} // DEFAULT_COMMENT_SEM defines what alternate character(s) indicate a comment `;` DEFAULT_COMMENT_SEM = []byte{';'} // DEFAULT_MULTI_LINE_SEPARATOR defines what character indicates a multi-line content DEFAULT_MULTI_LINE_SEPARATOR = []byte{'\\'} ) // ConfigInterface defines the behavior of a Config implementation type ConfigInterface interface { String(key string) string Strings(key string) []string Bool(key string) (bool, error) Int(key string) (int, error) Int64(key string) (int64, error) Float64(key string) (float64, error) Set(key string, value string) error } // Config represents an implementation of the ConfigInterface type Config struct { // Section:key=value data map[string]map[string]string } // NewConfig create an empty configuration representation from file. func NewConfig(confName string) (ConfigInterface, error) { c := &Config{ data: make(map[string]map[string]string), } err := c.parse(confName) return c, err } // NewConfigFromText create an empty configuration representation from text. func NewConfigFromText(text string) (ConfigInterface, error) { c := &Config{ data: make(map[string]map[string]string), } err := c.parseBuffer(bufio.NewReader(strings.NewReader(text))) return c, err } // AddConfig adds a new section->key:value to the configuration. func (c *Config) AddConfig(section string, option string, value string) bool { if section == "" { section = DEFAULT_SECTION } if _, ok := c.data[section]; !ok { c.data[section] = make(map[string]string) } _, ok := c.data[section][option] c.data[section][option] = value return !ok } func (c *Config) parse(fname string) (err error) { f, err := os.Open(fname) if err != nil { return err } defer f.Close() buf := bufio.NewReader(f) return c.parseBuffer(buf) } func (c *Config) parseBuffer(buf *bufio.Reader) error { var section string var lineNum int var buffer bytes.Buffer var canWrite bool for { if canWrite { if err := c.write(section, lineNum, &buffer); err != nil { return err } else { canWrite = false } } lineNum++ line, _, err := buf.ReadLine() if err == io.EOF { // force write when buffer is not flushed yet if buffer.Len() > 0 { if err := c.write(section, lineNum, &buffer); err != nil { return err } } break } else if err != nil { return err } line = bytes.TrimSpace(line) switch { case bytes.Equal(line, []byte{}), bytes.HasPrefix(line, DEFAULT_COMMENT_SEM), bytes.HasPrefix(line, DEFAULT_COMMENT): canWrite = true continue case bytes.HasPrefix(line, []byte{'['}) && bytes.HasSuffix(line, []byte{']'}): // force write when buffer is not flushed yet if buffer.Len() > 0 { if err := c.write(section, lineNum, &buffer); err != nil { return err } canWrite = false } section = string(line[1 : len(line)-1]) default: var p []byte if bytes.HasSuffix(line, DEFAULT_MULTI_LINE_SEPARATOR) { p = bytes.TrimSpace(line[:len(line)-1]) p = append(p, " "...) } else { p = line canWrite = true } end := len(p) for i, value := range p { if value == DEFAULT_COMMENT[0] || value == DEFAULT_COMMENT_SEM[0] { end = i break } } if _, err := buffer.Write(p[:end]); err != nil { return err } } } return nil } func (c *Config) write(section string, lineNum int, b *bytes.Buffer) error { if b.Len() <= 0 { return nil } optionVal := bytes.SplitN(b.Bytes(), []byte{'='}, 2) if len(optionVal) != 2 { return fmt.Errorf("parse the content error : line %d , %s = ? ", lineNum, optionVal[0]) } option := bytes.TrimSpace(optionVal[0]) value := bytes.TrimSpace(optionVal[1]) c.AddConfig(section, string(option), string(value)) // flush buffer after adding b.Reset() return nil } // Bool lookups up the value using the provided key and converts the value to a bool func (c *Config) Bool(key string) (bool, error) { return strconv.ParseBool(c.get(key)) } // Int lookups up the value using the provided key and converts the value to a int func (c *Config) Int(key string) (int, error) { return strconv.Atoi(c.get(key)) } // Int64 lookups up the value using the provided key and converts the value to a int64 func (c *Config) Int64(key string) (int64, error) { return strconv.ParseInt(c.get(key), 10, 64) } // Float64 lookups up the value using the provided key and converts the value to a float64 func (c *Config) Float64(key string) (float64, error) { return strconv.ParseFloat(c.get(key), 64) } // String lookups up the value using the provided key and converts the value to a string func (c *Config) String(key string) string { return c.get(key) } // Strings lookups up the value using the provided key and converts the value to an array of string // by splitting the string by comma func (c *Config) Strings(key string) []string { v := c.get(key) if v == "" { return nil } return strings.Split(v, ",") } // Set sets the value for the specific key in the Config func (c *Config) Set(key string, value string) error { if len(key) == 0 { return errors.New("key is empty") } var ( section string option string ) keys := strings.Split(strings.ToLower(key), "::") if len(keys) >= 2 { section = keys[0] option = keys[1] } else { option = keys[0] } c.AddConfig(section, option, value) return nil } // section.key or key func (c *Config) get(key string) string { var ( section string option string ) keys := strings.Split(strings.ToLower(key), "::") if len(keys) >= 2 { section = keys[0] option = keys[1] } else { section = DEFAULT_SECTION option = keys[0] } if value, ok := c.data[section][option]; ok { return value } return "" } golang-github-casbin-casbin-2.82.0/config/config_test.go000066400000000000000000000076111456376452400231510ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package config import ( "testing" ) func TestGet(t *testing.T) { config, cerr := NewConfig("testdata/testini.ini") if cerr != nil { t.Errorf("Configuration file loading failed, err:%v", cerr.Error()) t.Fatalf("err: %v", cerr) } // default::key test if v, err := config.Bool("debug"); err != nil || !v { t.Errorf("Get failure: expected different value for debug (expected: [%#v] got: [%#v])", true, v) t.Fatalf("err: %v", err) } if v := config.String("url"); v != "act.wiki" { t.Errorf("Get failure: expected different value for url (expected: [%#v] got: [%#v])", "act.wiki", v) } // redis::key test if v := config.Strings("redis::redis.key"); len(v) != 2 || v[0] != "push1" || v[1] != "push2" { t.Errorf("Get failure: expected different value for redis::redis.key (expected: [%#v] got: [%#v])", "[]string{push1,push2}", v) } if v := config.String("mysql::mysql.dev.host"); v != "127.0.0.1" { t.Errorf("Get failure: expected different value for mysql::mysql.dev.host (expected: [%#v] got: [%#v])", "127.0.0.1", v) } if v := config.String("mysql::mysql.master.host"); v != "10.0.0.1" { t.Errorf("Get failure: expected different value for mysql::mysql.master.host (expected: [%#v] got: [%#v])", "10.0.0.1", v) } if v := config.String("mysql::mysql.master.user"); v != "root" { t.Errorf("Get failure: expected different value for mysql::mysql.master.user (expected: [%#v] got: [%#v])", "root", v) } if v := config.String("mysql::mysql.master.pass"); v != "89dds)2$" { t.Errorf("Get failure: expected different value for mysql::mysql.master.pass (expected: [%#v] got: [%#v])", "89dds)2$", v) } // math::key test if v, err := config.Int64("math::math.i64"); err != nil || v != 64 { t.Errorf("Get failure: expected different value for math::math.i64 (expected: [%#v] got: [%#v])", 64, v) t.Fatalf("err: %v", err) } if v, err := config.Float64("math::math.f64"); err != nil || v != 64.1 { t.Errorf("Get failure: expected different value for math::math.f64 (expected: [%#v] got: [%#v])", 64.1, v) t.Fatalf("err: %v", err) } _ = config.Set("other::key1", "new test key") if v := config.String("other::key1"); v != "new test key" { t.Errorf("Get failure: expected different value for other::key1 (expected: [%#v] got: [%#v])", "new test key", v) } _ = config.Set("other::key1", "test key") if v := config.String("multi1::name"); v != "r.sub==p.sub && r.obj==p.obj" { t.Errorf("Get failure: expected different value for multi1::name (expected: [%#v] got: [%#v])", "r.sub==p.sub&&r.obj==p.obj", v) } if v := config.String("multi2::name"); v != "r.sub==p.sub && r.obj==p.obj" { t.Errorf("Get failure: expected different value for multi2::name (expected: [%#v] got: [%#v])", "r.sub==p.sub&&r.obj==p.obj", v) } if v := config.String("multi3::name"); v != "r.sub==p.sub && r.obj==p.obj" { t.Errorf("Get failure: expected different value for multi3::name (expected: [%#v] got: [%#v])", "r.sub==p.sub&&r.obj==p.obj", v) } if v := config.String("multi4::name"); v != "" { t.Errorf("Get failure: expected different value for multi4::name (expected: [%#v] got: [%#v])", "", v) } if v := config.String("multi5::name"); v != "r.sub==p.sub && r.obj==p.obj" { t.Errorf("Get failure: expected different value for multi5::name (expected: [%#v] got: [%#v])", "r.sub==p.sub&&r.obj==p.obj", v) } } golang-github-casbin-casbin-2.82.0/config/testdata/000077500000000000000000000000001456376452400221225ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/config/testdata/testini.ini000066400000000000000000000012161456376452400243020ustar00rootroot00000000000000# test config debug = true url = act.wiki ; redis config [redis] redis.key = push1,push2 ; mysql config [mysql] mysql.dev.host = 127.0.0.1 mysql.dev.user = root mysql.dev.pass = 123456 mysql.dev.db = test mysql.master.host = 10.0.0.1 # host # 10.0.0.1 mysql.master.user = root ; user name mysql.master.pass = 89dds)2$#d mysql.master.db = act ; math config [math] math.i64 = 64 math.f64 = 64.1 # multi-line test [multi1] name = r.sub==p.sub \ && r.obj==p.obj\ \ [multi2] name = r.sub==p.sub \ && r.obj==p.obj [multi3] name = r.sub==p.sub \ && r.obj==p.obj [multi4] name = \ \ \ [multi5] name = r.sub==p.sub \ && r.obj==p.obj\ \ golang-github-casbin-casbin-2.82.0/constant/000077500000000000000000000000001456376452400206755ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/constant/constants.go000066400000000000000000000020501456376452400232350ustar00rootroot00000000000000// Copyright 2022 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package constant const ( DomainIndex = "dom" SubjectIndex = "sub" ObjectIndex = "obj" PriorityIndex = "priority" ) const ( AllowOverrideEffect = "some(where (p_eft == allow))" DenyOverrideEffect = "!some(where (p_eft == deny))" AllowAndDenyEffect = "some(where (p_eft == allow)) && !some(where (p_eft == deny))" PriorityEffect = "priority(p_eft) || deny" SubjectPriorityEffect = "subjectPriority(p_eft) || deny" ) golang-github-casbin-casbin-2.82.0/effector/000077500000000000000000000000001456376452400206415ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/effector/default_effector.go000066400000000000000000000054671456376452400245050ustar00rootroot00000000000000// Copyright 2018 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package effector import ( "errors" "github.com/casbin/casbin/v2/constant" ) // DefaultEffector is default effector for Casbin. type DefaultEffector struct { } // NewDefaultEffector is the constructor for DefaultEffector. func NewDefaultEffector() *DefaultEffector { e := DefaultEffector{} return &e } // MergeEffects merges all matching results collected by the enforcer into a single decision. func (e *DefaultEffector) MergeEffects(expr string, effects []Effect, matches []float64, policyIndex int, policyLength int) (Effect, int, error) { result := Indeterminate explainIndex := -1 switch expr { case constant.AllowOverrideEffect: if matches[policyIndex] == 0 { break } // only check the current policyIndex if effects[policyIndex] == Allow { result = Allow explainIndex = policyIndex break } case constant.DenyOverrideEffect: // only check the current policyIndex if matches[policyIndex] != 0 && effects[policyIndex] == Deny { result = Deny explainIndex = policyIndex break } // if no deny rules are matched at last, then allow if policyIndex == policyLength-1 { result = Allow } case constant.AllowAndDenyEffect: // short-circuit if matched deny rule if matches[policyIndex] != 0 && effects[policyIndex] == Deny { result = Deny // set hit rule to the (first) matched deny rule explainIndex = policyIndex break } // short-circuit some effects in the middle if policyIndex < policyLength-1 { // choose not to short-circuit return result, explainIndex, nil } // merge all effects at last for i, eft := range effects { if matches[i] == 0 { continue } if eft == Allow { result = Allow // set hit rule to first matched allow rule explainIndex = i break } } case constant.PriorityEffect, constant.SubjectPriorityEffect: // reverse merge, short-circuit may be earlier for i := len(effects) - 1; i >= 0; i-- { if matches[i] == 0 { continue } if effects[i] != Indeterminate { if effects[i] == Allow { result = Allow } else { result = Deny } explainIndex = i break } } default: return Deny, -1, errors.New("unsupported effect") } return result, explainIndex, nil } golang-github-casbin-casbin-2.82.0/effector/effector.go000066400000000000000000000020611456376452400227640ustar00rootroot00000000000000// Copyright 2018 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package effector // Effect is the result for a policy rule. type Effect int // Values for policy effect. const ( Allow Effect = iota Indeterminate Deny ) // Effector is the interface for Casbin effectors. type Effector interface { // MergeEffects merges all matching results collected by the enforcer into a single decision. MergeEffects(expr string, effects []Effect, matches []float64, policyIndex int, policyLength int) (Effect, int, error) } golang-github-casbin-casbin-2.82.0/enforcer.go000066400000000000000000000642071456376452400212070ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "errors" "fmt" "runtime/debug" "strings" "sync" "github.com/casbin/casbin/v2/effector" "github.com/casbin/casbin/v2/log" "github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/persist" fileadapter "github.com/casbin/casbin/v2/persist/file-adapter" "github.com/casbin/casbin/v2/rbac" defaultrolemanager "github.com/casbin/casbin/v2/rbac/default-role-manager" "github.com/casbin/casbin/v2/util" "github.com/casbin/govaluate" ) // Enforcer is the main interface for authorization enforcement and policy management. type Enforcer struct { modelPath string model model.Model fm model.FunctionMap eft effector.Effector adapter persist.Adapter watcher persist.Watcher dispatcher persist.Dispatcher rmMap map[string]rbac.RoleManager condRmMap map[string]rbac.ConditionalRoleManager matcherMap sync.Map enabled bool autoSave bool autoBuildRoleLinks bool autoNotifyWatcher bool autoNotifyDispatcher bool acceptJsonRequest bool logger log.Logger } // EnforceContext is used as the first element of the parameter "rvals" in method "enforce" type EnforceContext struct { RType string PType string EType string MType string } func (e EnforceContext) GetCacheKey() string { return "EnforceContext{" + e.RType + "-" + e.PType + "-" + e.EType + "-" + e.MType + "}" } // NewEnforcer creates an enforcer via file or DB. // // File: // // e := casbin.NewEnforcer("path/to/basic_model.conf", "path/to/basic_policy.csv") // // MySQL DB: // // a := mysqladapter.NewDBAdapter("mysql", "mysql_username:mysql_password@tcp(127.0.0.1:3306)/") // e := casbin.NewEnforcer("path/to/basic_model.conf", a) func NewEnforcer(params ...interface{}) (*Enforcer, error) { e := &Enforcer{logger: &log.DefaultLogger{}} parsedParamLen := 0 paramLen := len(params) if paramLen >= 1 { enableLog, ok := params[paramLen-1].(bool) if ok { e.EnableLog(enableLog) parsedParamLen++ } } if paramLen-parsedParamLen >= 1 { logger, ok := params[paramLen-parsedParamLen-1].(log.Logger) if ok { e.logger = logger parsedParamLen++ } } if paramLen-parsedParamLen == 2 { switch p0 := params[0].(type) { case string: switch p1 := params[1].(type) { case string: err := e.InitWithFile(p0, p1) if err != nil { return nil, err } default: err := e.InitWithAdapter(p0, p1.(persist.Adapter)) if err != nil { return nil, err } } default: switch params[1].(type) { case string: return nil, errors.New("invalid parameters for enforcer") default: err := e.InitWithModelAndAdapter(p0.(model.Model), params[1].(persist.Adapter)) if err != nil { return nil, err } } } } else if paramLen-parsedParamLen == 1 { switch p0 := params[0].(type) { case string: err := e.InitWithFile(p0, "") if err != nil { return nil, err } default: err := e.InitWithModelAndAdapter(p0.(model.Model), nil) if err != nil { return nil, err } } } else if paramLen-parsedParamLen == 0 { return e, nil } else { return nil, errors.New("invalid parameters for enforcer") } return e, nil } // InitWithFile initializes an enforcer with a model file and a policy file. func (e *Enforcer) InitWithFile(modelPath string, policyPath string) error { a := fileadapter.NewAdapter(policyPath) return e.InitWithAdapter(modelPath, a) } // InitWithAdapter initializes an enforcer with a database adapter. func (e *Enforcer) InitWithAdapter(modelPath string, adapter persist.Adapter) error { m, err := model.NewModelFromFile(modelPath) if err != nil { return err } err = e.InitWithModelAndAdapter(m, adapter) if err != nil { return err } e.modelPath = modelPath return nil } // InitWithModelAndAdapter initializes an enforcer with a model and a database adapter. func (e *Enforcer) InitWithModelAndAdapter(m model.Model, adapter persist.Adapter) error { e.adapter = adapter e.model = m m.SetLogger(e.logger) e.model.PrintModel() e.fm = model.LoadFunctionMap() e.initialize() // Do not initialize the full policy when using a filtered adapter fa, ok := e.adapter.(persist.FilteredAdapter) if e.adapter != nil && (!ok || ok && !fa.IsFiltered()) { err := e.LoadPolicy() if err != nil { return err } } return nil } // SetLogger changes the current enforcer's logger. func (e *Enforcer) SetLogger(logger log.Logger) { e.logger = logger e.model.SetLogger(e.logger) for k := range e.rmMap { e.rmMap[k].SetLogger(e.logger) } } func (e *Enforcer) initialize() { e.rmMap = map[string]rbac.RoleManager{} e.condRmMap = map[string]rbac.ConditionalRoleManager{} e.eft = effector.NewDefaultEffector() e.watcher = nil e.matcherMap = sync.Map{} e.enabled = true e.autoSave = true e.autoBuildRoleLinks = true e.autoNotifyWatcher = true e.autoNotifyDispatcher = true e.initRmMap() } // LoadModel reloads the model from the model CONF file. // Because the policy is attached to a model, so the policy is invalidated and needs to be reloaded by calling LoadPolicy(). func (e *Enforcer) LoadModel() error { var err error e.model, err = model.NewModelFromFile(e.modelPath) if err != nil { return err } e.model.SetLogger(e.logger) e.model.PrintModel() e.fm = model.LoadFunctionMap() e.initialize() return nil } // GetModel gets the current model. func (e *Enforcer) GetModel() model.Model { return e.model } // SetModel sets the current model. func (e *Enforcer) SetModel(m model.Model) { e.model = m e.fm = model.LoadFunctionMap() e.model.SetLogger(e.logger) e.initialize() } // GetAdapter gets the current adapter. func (e *Enforcer) GetAdapter() persist.Adapter { return e.adapter } // SetAdapter sets the current adapter. func (e *Enforcer) SetAdapter(adapter persist.Adapter) { e.adapter = adapter } // SetWatcher sets the current watcher. func (e *Enforcer) SetWatcher(watcher persist.Watcher) error { e.watcher = watcher if _, ok := e.watcher.(persist.WatcherEx); ok { // The callback of WatcherEx has no generic implementation. return nil } else { // In case the Watcher wants to use a customized callback function, call `SetUpdateCallback` after `SetWatcher`. return watcher.SetUpdateCallback(func(string) { _ = e.LoadPolicy() }) } } // GetRoleManager gets the current role manager. func (e *Enforcer) GetRoleManager() rbac.RoleManager { return e.rmMap["g"] } // GetNamedRoleManager gets the role manager for the named policy. func (e *Enforcer) GetNamedRoleManager(ptype string) rbac.RoleManager { return e.rmMap[ptype] } // SetRoleManager sets the current role manager. func (e *Enforcer) SetRoleManager(rm rbac.RoleManager) { e.invalidateMatcherMap() e.rmMap["g"] = rm } // SetNamedRoleManager sets the role manager for the named policy. func (e *Enforcer) SetNamedRoleManager(ptype string, rm rbac.RoleManager) { e.invalidateMatcherMap() e.rmMap[ptype] = rm } // SetEffector sets the current effector. func (e *Enforcer) SetEffector(eft effector.Effector) { e.eft = eft } // ClearPolicy clears all policy. func (e *Enforcer) ClearPolicy() { e.invalidateMatcherMap() if e.dispatcher != nil && e.autoNotifyDispatcher { _ = e.dispatcher.ClearPolicy() return } e.model.ClearPolicy() } // LoadPolicy reloads the policy from file/database. func (e *Enforcer) LoadPolicy() error { e.invalidateMatcherMap() needToRebuild := false newModel := e.model.Copy() newModel.ClearPolicy() var err error defer func() { if err != nil { if e.autoBuildRoleLinks && needToRebuild { _ = e.BuildRoleLinks() } } }() if err = e.adapter.LoadPolicy(newModel); err != nil && err.Error() != "invalid file path, file path cannot be empty" { return err } if err = newModel.SortPoliciesBySubjectHierarchy(); err != nil { return err } if err = newModel.SortPoliciesByPriority(); err != nil { return err } if e.autoBuildRoleLinks { needToRebuild = true if err := e.rebuildRoleLinks(newModel); err != nil { return err } if err := e.rebuildConditionalRoleLinks(newModel); err != nil { return err } } e.model = newModel return nil } func (e *Enforcer) rebuildRoleLinks(newModel model.Model) error { if len(e.rmMap) != 0 { for _, rm := range e.rmMap { err := rm.Clear() if err != nil { return err } } err := newModel.BuildRoleLinks(e.rmMap) if err != nil { return err } } return nil } func (e *Enforcer) rebuildConditionalRoleLinks(newModel model.Model) error { if len(e.condRmMap) != 0 { for _, crm := range e.condRmMap { err := crm.Clear() if err != nil { return err } } err := newModel.BuildConditionalRoleLinks(e.condRmMap) if err != nil { return err } } return nil } func (e *Enforcer) loadFilteredPolicy(filter interface{}) error { e.invalidateMatcherMap() var filteredAdapter persist.FilteredAdapter // Attempt to cast the Adapter as a FilteredAdapter switch adapter := e.adapter.(type) { case persist.FilteredAdapter: filteredAdapter = adapter default: return errors.New("filtered policies are not supported by this adapter") } if err := filteredAdapter.LoadFilteredPolicy(e.model, filter); err != nil && err.Error() != "invalid file path, file path cannot be empty" { return err } if err := e.model.SortPoliciesBySubjectHierarchy(); err != nil { return err } if err := e.model.SortPoliciesByPriority(); err != nil { return err } e.initRmMap() e.model.PrintPolicy() if e.autoBuildRoleLinks { err := e.BuildRoleLinks() if err != nil { return err } } return nil } // LoadFilteredPolicy reloads a filtered policy from file/database. func (e *Enforcer) LoadFilteredPolicy(filter interface{}) error { e.model.ClearPolicy() return e.loadFilteredPolicy(filter) } // LoadIncrementalFilteredPolicy append a filtered policy from file/database. func (e *Enforcer) LoadIncrementalFilteredPolicy(filter interface{}) error { return e.loadFilteredPolicy(filter) } // IsFiltered returns true if the loaded policy has been filtered. func (e *Enforcer) IsFiltered() bool { filteredAdapter, ok := e.adapter.(persist.FilteredAdapter) if !ok { return false } return filteredAdapter.IsFiltered() } // SavePolicy saves the current policy (usually after changed with Casbin API) back to file/database. func (e *Enforcer) SavePolicy() error { if e.IsFiltered() { return errors.New("cannot save a filtered policy") } if err := e.adapter.SavePolicy(e.model); err != nil { return err } if e.watcher != nil { var err error if watcher, ok := e.watcher.(persist.WatcherEx); ok { err = watcher.UpdateForSavePolicy(e.model) } else { err = e.watcher.Update() } return err } return nil } func (e *Enforcer) initRmMap() { for ptype, assertion := range e.model["g"] { if rm, ok := e.rmMap[ptype]; ok { _ = rm.Clear() continue } if len(assertion.Tokens) <= 2 && len(assertion.ParamsTokens) == 0 { assertion.RM = defaultrolemanager.NewRoleManagerImpl(10) e.rmMap[ptype] = assertion.RM } if len(assertion.Tokens) <= 2 && len(assertion.ParamsTokens) != 0 { assertion.CondRM = defaultrolemanager.NewConditionalRoleManager(10) e.condRmMap[ptype] = assertion.CondRM } if len(assertion.Tokens) > 2 { if len(assertion.ParamsTokens) == 0 { assertion.RM = defaultrolemanager.NewRoleManager(10) e.rmMap[ptype] = assertion.RM } else { assertion.CondRM = defaultrolemanager.NewConditionalDomainManager(10) e.condRmMap[ptype] = assertion.CondRM } matchFun := "keyMatch(r_dom, p_dom)" if strings.Contains(e.model["m"]["m"].Value, matchFun) { e.AddNamedDomainMatchingFunc(ptype, "g", util.KeyMatch) } } } } // EnableEnforce changes the enforcing state of Casbin, when Casbin is disabled, all access will be allowed by the Enforce() function. func (e *Enforcer) EnableEnforce(enable bool) { e.enabled = enable } // EnableLog changes whether Casbin will log messages to the Logger. func (e *Enforcer) EnableLog(enable bool) { e.logger.EnableLog(enable) } // IsLogEnabled returns the current logger's enabled status. func (e *Enforcer) IsLogEnabled() bool { return e.logger.IsEnabled() } // EnableAutoNotifyWatcher controls whether to save a policy rule automatically notify the Watcher when it is added or removed. func (e *Enforcer) EnableAutoNotifyWatcher(enable bool) { e.autoNotifyWatcher = enable } // EnableAutoNotifyDispatcher controls whether to save a policy rule automatically notify the Dispatcher when it is added or removed. func (e *Enforcer) EnableAutoNotifyDispatcher(enable bool) { e.autoNotifyDispatcher = enable } // EnableAutoSave controls whether to save a policy rule automatically to the adapter when it is added or removed. func (e *Enforcer) EnableAutoSave(autoSave bool) { e.autoSave = autoSave } // EnableAutoBuildRoleLinks controls whether to rebuild the role inheritance relations when a role is added or deleted. func (e *Enforcer) EnableAutoBuildRoleLinks(autoBuildRoleLinks bool) { e.autoBuildRoleLinks = autoBuildRoleLinks } // EnableAcceptJsonRequest controls whether to accept json as a request parameter func (e *Enforcer) EnableAcceptJsonRequest(acceptJsonRequest bool) { e.acceptJsonRequest = acceptJsonRequest } // BuildRoleLinks manually rebuild the role inheritance relations. func (e *Enforcer) BuildRoleLinks() error { for _, rm := range e.rmMap { err := rm.Clear() if err != nil { return err } } return e.model.BuildRoleLinks(e.rmMap) } // BuildIncrementalRoleLinks provides incremental build the role inheritance relations. func (e *Enforcer) BuildIncrementalRoleLinks(op model.PolicyOp, ptype string, rules [][]string) error { e.invalidateMatcherMap() return e.model.BuildIncrementalRoleLinks(e.rmMap, op, "g", ptype, rules) } // BuildIncrementalConditionalRoleLinks provides incremental build the role inheritance relations with conditions. func (e *Enforcer) BuildIncrementalConditionalRoleLinks(op model.PolicyOp, ptype string, rules [][]string) error { e.invalidateMatcherMap() return e.model.BuildIncrementalConditionalRoleLinks(e.condRmMap, op, "g", ptype, rules) } // NewEnforceContext Create a default structure based on the suffix func NewEnforceContext(suffix string) EnforceContext { return EnforceContext{ RType: "r" + suffix, PType: "p" + suffix, EType: "e" + suffix, MType: "m" + suffix, } } func (e *Enforcer) invalidateMatcherMap() { e.matcherMap = sync.Map{} } // enforce use a custom matcher to decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "". func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interface{}) (ok bool, err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("panic: %v\n%s", r, debug.Stack()) } }() if !e.enabled { return true, nil } functions := e.fm.GetFunctions() if _, ok := e.model["g"]; ok { for key, ast := range e.model["g"] { // g must be a normal role definition (ast.RM != nil) // or a conditional role definition (ast.CondRM != nil) // ast.RM and ast.CondRM shouldn't be nil at the same time if ast.RM != nil { functions[key] = util.GenerateGFunction(ast.RM) } if ast.CondRM != nil { functions[key] = util.GenerateConditionalGFunction(ast.CondRM) } } } var ( rType = "r" pType = "p" eType = "e" mType = "m" ) if len(rvals) != 0 { switch rvals[0].(type) { case EnforceContext: enforceContext := rvals[0].(EnforceContext) rType = enforceContext.RType pType = enforceContext.PType eType = enforceContext.EType mType = enforceContext.MType rvals = rvals[1:] default: break } } var expString string if matcher == "" { expString = e.model["m"][mType].Value } else { expString = util.RemoveComments(util.EscapeAssertion(matcher)) } rTokens := make(map[string]int, len(e.model["r"][rType].Tokens)) for i, token := range e.model["r"][rType].Tokens { rTokens[token] = i } pTokens := make(map[string]int, len(e.model["p"][pType].Tokens)) for i, token := range e.model["p"][pType].Tokens { pTokens[token] = i } if e.acceptJsonRequest { // try to parse all request values from json to map[string]interface{} // skip if there is an error for i, rval := range rvals { switch rval := rval.(type) { case string: mapValue, err := util.JsonToMap(rval) if err == nil { rvals[i] = mapValue } } } } parameters := enforceParameters{ rTokens: rTokens, rVals: rvals, pTokens: pTokens, } hasEval := util.HasEval(expString) if hasEval { functions["eval"] = generateEvalFunction(functions, ¶meters) } var expression *govaluate.EvaluableExpression expression, err = e.getAndStoreMatcherExpression(hasEval, expString, functions) if err != nil { return false, err } if len(e.model["r"][rType].Tokens) != len(rvals) { return false, fmt.Errorf( "invalid request size: expected %d, got %d, rvals: %v", len(e.model["r"][rType].Tokens), len(rvals), rvals) } var policyEffects []effector.Effect var matcherResults []float64 var effect effector.Effect var explainIndex int if policyLen := len(e.model["p"][pType].Policy); policyLen != 0 && strings.Contains(expString, pType+"_") { policyEffects = make([]effector.Effect, policyLen) matcherResults = make([]float64, policyLen) for policyIndex, pvals := range e.model["p"][pType].Policy { // log.LogPrint("Policy Rule: ", pvals) if len(e.model["p"][pType].Tokens) != len(pvals) { return false, fmt.Errorf( "invalid policy size: expected %d, got %d, pvals: %v", len(e.model["p"][pType].Tokens), len(pvals), pvals) } parameters.pVals = pvals result, err := expression.Eval(parameters) // log.LogPrint("Result: ", result) if err != nil { return false, err } // set to no-match at first matcherResults[policyIndex] = 0 switch result := result.(type) { case bool: if result { matcherResults[policyIndex] = 1 } case float64: if result != 0 { matcherResults[policyIndex] = 1 } default: return false, errors.New("matcher result should be bool, int or float") } if j, ok := parameters.pTokens[pType+"_eft"]; ok { eft := parameters.pVals[j] if eft == "allow" { policyEffects[policyIndex] = effector.Allow } else if eft == "deny" { policyEffects[policyIndex] = effector.Deny } else { policyEffects[policyIndex] = effector.Indeterminate } } else { policyEffects[policyIndex] = effector.Allow } // if e.model["e"]["e"].Value == "priority(p_eft) || deny" { // break // } effect, explainIndex, err = e.eft.MergeEffects(e.model["e"][eType].Value, policyEffects, matcherResults, policyIndex, policyLen) if err != nil { return false, err } if effect != effector.Indeterminate { break } } } else { if hasEval && len(e.model["p"][pType].Policy) == 0 { return false, errors.New("please make sure rule exists in policy when using eval() in matcher") } policyEffects = make([]effector.Effect, 1) matcherResults = make([]float64, 1) matcherResults[0] = 1 parameters.pVals = make([]string, len(parameters.pTokens)) result, err := expression.Eval(parameters) if err != nil { return false, err } if result.(bool) { policyEffects[0] = effector.Allow } else { policyEffects[0] = effector.Indeterminate } effect, explainIndex, err = e.eft.MergeEffects(e.model["e"][eType].Value, policyEffects, matcherResults, 0, 1) if err != nil { return false, err } } var logExplains [][]string if explains != nil { if len(*explains) > 0 { logExplains = append(logExplains, *explains) } if explainIndex != -1 && len(e.model["p"][pType].Policy) > explainIndex { *explains = e.model["p"][pType].Policy[explainIndex] logExplains = append(logExplains, *explains) } } // effect -> result result := false if effect == effector.Allow { result = true } e.logger.LogEnforce(expString, rvals, result, logExplains) return result, nil } func (e *Enforcer) getAndStoreMatcherExpression(hasEval bool, expString string, functions map[string]govaluate.ExpressionFunction) (*govaluate.EvaluableExpression, error) { var expression *govaluate.EvaluableExpression var err error var cachedExpression, isPresent = e.matcherMap.Load(expString) if !hasEval && isPresent { expression = cachedExpression.(*govaluate.EvaluableExpression) } else { expression, err = govaluate.NewEvaluableExpressionWithFunctions(expString, functions) if err != nil { return nil, err } e.matcherMap.Store(expString, expression) } return expression, nil } // Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). func (e *Enforcer) Enforce(rvals ...interface{}) (bool, error) { return e.enforce("", nil, rvals...) } // EnforceWithMatcher use a custom matcher to decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "". func (e *Enforcer) EnforceWithMatcher(matcher string, rvals ...interface{}) (bool, error) { return e.enforce(matcher, nil, rvals...) } // EnforceEx explain enforcement by informing matched rules func (e *Enforcer) EnforceEx(rvals ...interface{}) (bool, []string, error) { explain := []string{} result, err := e.enforce("", &explain, rvals...) return result, explain, err } // EnforceExWithMatcher use a custom matcher and explain enforcement by informing matched rules func (e *Enforcer) EnforceExWithMatcher(matcher string, rvals ...interface{}) (bool, []string, error) { explain := []string{} result, err := e.enforce(matcher, &explain, rvals...) return result, explain, err } // BatchEnforce enforce in batches func (e *Enforcer) BatchEnforce(requests [][]interface{}) ([]bool, error) { var results []bool for _, request := range requests { result, err := e.enforce("", nil, request...) if err != nil { return results, err } results = append(results, result) } return results, nil } // BatchEnforceWithMatcher enforce with matcher in batches func (e *Enforcer) BatchEnforceWithMatcher(matcher string, requests [][]interface{}) ([]bool, error) { var results []bool for _, request := range requests { result, err := e.enforce(matcher, nil, request...) if err != nil { return results, err } results = append(results, result) } return results, nil } // AddNamedMatchingFunc add MatchingFunc by ptype RoleManager func (e *Enforcer) AddNamedMatchingFunc(ptype, name string, fn rbac.MatchingFunc) bool { if rm, ok := e.rmMap[ptype]; ok { rm.AddMatchingFunc(name, fn) return true } return false } // AddNamedDomainMatchingFunc add MatchingFunc by ptype to RoleManager func (e *Enforcer) AddNamedDomainMatchingFunc(ptype, name string, fn rbac.MatchingFunc) bool { if rm, ok := e.rmMap[ptype]; ok { rm.AddDomainMatchingFunc(name, fn) return true } return false } // AddNamedLinkConditionFunc Add condition function fn for Link userName->roleName, // when fn returns true, Link is valid, otherwise invalid func (e *Enforcer) AddNamedLinkConditionFunc(ptype, user, role string, fn rbac.LinkConditionFunc) bool { if rm, ok := e.condRmMap[ptype]; ok { rm.AddLinkConditionFunc(user, role, fn) return true } return false } // AddNamedDomainLinkConditionFunc Add condition function fn for Link userName-> {roleName, domain}, // when fn returns true, Link is valid, otherwise invalid func (e *Enforcer) AddNamedDomainLinkConditionFunc(ptype, user, role string, domain string, fn rbac.LinkConditionFunc) bool { if rm, ok := e.condRmMap[ptype]; ok { rm.AddDomainLinkConditionFunc(user, role, domain, fn) return true } return false } // SetNamedLinkConditionFuncParams Sets the parameters of the condition function fn for Link userName->roleName func (e *Enforcer) SetNamedLinkConditionFuncParams(ptype, user, role string, params ...string) bool { if rm, ok := e.condRmMap[ptype]; ok { rm.SetLinkConditionFuncParams(user, role, params...) return true } return false } // SetNamedDomainLinkConditionFuncParams Sets the parameters of the condition function fn // for Link userName->{roleName, domain} func (e *Enforcer) SetNamedDomainLinkConditionFuncParams(ptype, user, role, domain string, params ...string) bool { if rm, ok := e.condRmMap[ptype]; ok { rm.SetDomainLinkConditionFuncParams(user, role, domain, params...) return true } return false } // assumes bounds have already been checked type enforceParameters struct { rTokens map[string]int rVals []interface{} pTokens map[string]int pVals []string } // implements govaluate.Parameters func (p enforceParameters) Get(name string) (interface{}, error) { if name == "" { return nil, nil } switch name[0] { case 'p': i, ok := p.pTokens[name] if !ok { return nil, errors.New("No parameter '" + name + "' found.") } return p.pVals[i], nil case 'r': i, ok := p.rTokens[name] if !ok { return nil, errors.New("No parameter '" + name + "' found.") } return p.rVals[i], nil default: return nil, errors.New("No parameter '" + name + "' found.") } } func generateEvalFunction(functions map[string]govaluate.ExpressionFunction, parameters *enforceParameters) govaluate.ExpressionFunction { return func(args ...interface{}) (interface{}, error) { if len(args) != 1 { return nil, fmt.Errorf("function eval(subrule string) expected %d arguments, but got %d", 1, len(args)) } expression, ok := args[0].(string) if !ok { return nil, errors.New("argument of eval(subrule string) must be a string") } expression = util.EscapeAssertion(expression) expr, err := govaluate.NewEvaluableExpressionWithFunctions(expression, functions) if err != nil { return nil, fmt.Errorf("error while parsing eval parameter: %s, %s", expression, err.Error()) } return expr.Eval(parameters) } } golang-github-casbin-casbin-2.82.0/enforcer_cached.go000066400000000000000000000106251456376452400224710ustar00rootroot00000000000000// Copyright 2018 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "strings" "sync" "sync/atomic" "time" "github.com/casbin/casbin/v2/persist/cache" ) // CachedEnforcer wraps Enforcer and provides decision cache type CachedEnforcer struct { *Enforcer expireTime time.Duration cache cache.Cache enableCache int32 locker *sync.RWMutex } type CacheableParam interface { GetCacheKey() string } // NewCachedEnforcer creates a cached enforcer via file or DB. func NewCachedEnforcer(params ...interface{}) (*CachedEnforcer, error) { e := &CachedEnforcer{} var err error e.Enforcer, err = NewEnforcer(params...) if err != nil { return nil, err } e.enableCache = 1 e.cache, _ = cache.NewDefaultCache() e.locker = new(sync.RWMutex) return e, nil } // EnableCache determines whether to enable cache on Enforce(). When enableCache is enabled, cached result (true | false) will be returned for previous decisions. func (e *CachedEnforcer) EnableCache(enableCache bool) { var enabled int32 if enableCache { enabled = 1 } atomic.StoreInt32(&e.enableCache, enabled) } // Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). // if rvals is not string , ingore the cache func (e *CachedEnforcer) Enforce(rvals ...interface{}) (bool, error) { if atomic.LoadInt32(&e.enableCache) == 0 { return e.Enforcer.Enforce(rvals...) } key, ok := e.getKey(rvals...) if !ok { return e.Enforcer.Enforce(rvals...) } if res, err := e.getCachedResult(key); err == nil { return res, nil } else if err != cache.ErrNoSuchKey { return res, err } res, err := e.Enforcer.Enforce(rvals...) if err != nil { return false, err } err = e.setCachedResult(key, res, e.expireTime) return res, err } func (e *CachedEnforcer) LoadPolicy() error { if atomic.LoadInt32(&e.enableCache) != 0 { if err := e.cache.Clear(); err != nil { return err } } return e.Enforcer.LoadPolicy() } func (e *CachedEnforcer) RemovePolicy(params ...interface{}) (bool, error) { if atomic.LoadInt32(&e.enableCache) != 0 { key, ok := e.getKey(params...) if ok { if err := e.cache.Delete(key); err != nil && err != cache.ErrNoSuchKey { return false, err } } } return e.Enforcer.RemovePolicy(params...) } func (e *CachedEnforcer) RemovePolicies(rules [][]string) (bool, error) { if len(rules) != 0 { if atomic.LoadInt32(&e.enableCache) != 0 { irule := make([]interface{}, len(rules[0])) for _, rule := range rules { for i, param := range rule { irule[i] = param } key, _ := e.getKey(irule...) if err := e.cache.Delete(key); err != nil && err != cache.ErrNoSuchKey { return false, err } } } } return e.Enforcer.RemovePolicies(rules) } func (e *CachedEnforcer) getCachedResult(key string) (res bool, err error) { e.locker.Lock() defer e.locker.Unlock() return e.cache.Get(key) } func (e *CachedEnforcer) SetExpireTime(expireTime time.Duration) { e.expireTime = expireTime } func (e *CachedEnforcer) SetCache(c cache.Cache) { e.cache = c } func (e *CachedEnforcer) setCachedResult(key string, res bool, extra ...interface{}) error { e.locker.Lock() defer e.locker.Unlock() return e.cache.Set(key, res, extra...) } func (e *CachedEnforcer) getKey(params ...interface{}) (string, bool) { return GetCacheKey(params...) } // InvalidateCache deletes all the existing cached decisions. func (e *CachedEnforcer) InvalidateCache() error { e.locker.Lock() defer e.locker.Unlock() return e.cache.Clear() } func GetCacheKey(params ...interface{}) (string, bool) { key := strings.Builder{} for _, param := range params { switch typedParam := param.(type) { case string: key.WriteString(typedParam) case CacheableParam: key.WriteString(typedParam.GetCacheKey()) default: return "", false } key.WriteString("$$") } return key.String(), true } golang-github-casbin-casbin-2.82.0/enforcer_cached_b_test.go000066400000000000000000000136271456376452400240360ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "fmt" "testing" ) func BenchmarkCachedRaw(b *testing.B) { for i := 0; i < b.N; i++ { rawEnforce("alice", "data1", "read") } } func BenchmarkCachedBasicModel(b *testing.B) { e, _ := NewCachedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv", false) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", "data1", "read") } } func BenchmarkCachedRBACModel(b *testing.B) { e, _ := NewCachedEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv", false) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", "data2", "read") } } func BenchmarkCachedRBACModelSmall(b *testing.B) { e, _ := NewCachedEnforcer("examples/rbac_model.conf", false) // 100 roles, 10 resources. for i := 0; i < 100; i++ { _, err := e.AddPolicy(fmt.Sprintf("group%d", i), fmt.Sprintf("data%d", i/10), "read") if err != nil { b.Fatal(err) } } // 1000 users. for i := 0; i < 1000; i++ { _, err := e.AddGroupingPolicy(fmt.Sprintf("user%d", i), fmt.Sprintf("group%d", i/10)) if err != nil { b.Fatal(err) } } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("user501", "data9", "read") } } func BenchmarkCachedRBACModelMedium(b *testing.B) { e, _ := NewCachedEnforcer("examples/rbac_model.conf", false) // 1000 roles, 100 resources. pPolicies := make([][]string, 0) for i := 0; i < 1000; i++ { pPolicies = append(pPolicies, []string{fmt.Sprintf("group%d", i), fmt.Sprintf("data%d", i/10), "read"}) } _, err := e.AddPolicies(pPolicies) if err != nil { b.Fatal(err) } // 10000 users. gPolicies := make([][]string, 0) for i := 0; i < 10000; i++ { gPolicies = append(gPolicies, []string{fmt.Sprintf("user%d", i), fmt.Sprintf("group%d", i/10)}) } _, err = e.AddGroupingPolicies(gPolicies) if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("user5001", "data150", "read") } } func BenchmarkCachedRBACModelLarge(b *testing.B) { e, _ := NewCachedEnforcer("examples/rbac_model.conf", false) // 10000 roles, 1000 resources. pPolicies := make([][]string, 0) for i := 0; i < 10000; i++ { pPolicies = append(pPolicies, []string{fmt.Sprintf("group%d", i), fmt.Sprintf("data%d", i/10), "read"}) } _, err := e.AddPolicies(pPolicies) if err != nil { b.Fatal(err) } // 100000 users. gPolicies := make([][]string, 0) for i := 0; i < 100000; i++ { gPolicies = append(gPolicies, []string{fmt.Sprintf("user%d", i), fmt.Sprintf("group%d", i/10)}) } _, err = e.AddGroupingPolicies(gPolicies) if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("user50001", "data1500", "read") } } func BenchmarkCachedRBACModelWithResourceRoles(b *testing.B) { e, _ := NewCachedEnforcer("examples/rbac_with_resource_roles_model.conf", "examples/rbac_with_resource_roles_policy.csv", false) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", "data1", "read") } } func BenchmarkCachedRBACModelWithDomains(b *testing.B) { e, _ := NewCachedEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv", false) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", "domain1", "data1", "read") } } func BenchmarkCachedABACModel(b *testing.B) { e, _ := NewCachedEnforcer("examples/abac_model.conf", false) data1 := newTestResource("data1", "alice") b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", data1, "read") } } func BenchmarkCachedKeyMatchModel(b *testing.B) { e, _ := NewCachedEnforcer("examples/keymatch_model.conf", "examples/keymatch_policy.csv", false) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", "/alice_data/resource1", "GET") } } func BenchmarkCachedRBACModelWithDeny(b *testing.B) { e, _ := NewCachedEnforcer("examples/rbac_with_deny_model.conf", "examples/rbac_with_deny_policy.csv", false) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", "data1", "read") } } func BenchmarkCachedPriorityModel(b *testing.B) { e, _ := NewCachedEnforcer("examples/priority_model.conf", "examples/priority_policy.csv", false) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", "data1", "read") } } func BenchmarkCachedWithEnforceContext(b *testing.B) { e, _ := NewCachedEnforcer("examples/priority_model_enforce_context.conf", "examples/priority_policy_enforce_context.csv", false) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce(EnforceContext{RType: "r2", PType: "p", EType: "e", MType: "m2"}, "alice", "data1") } } func BenchmarkCachedRBACModelMediumParallel(b *testing.B) { e, _ := NewCachedEnforcer("examples/rbac_model.conf", false) // 10000 roles, 1000 resources. pPolicies := make([][]string, 0) for i := 0; i < 10000; i++ { pPolicies = append(pPolicies, []string{fmt.Sprintf("group%d", i), fmt.Sprintf("data%d", i/10), "read"}) } _, err := e.AddPolicies(pPolicies) if err != nil { b.Fatal(err) } // 100000 users. gPolicies := make([][]string, 0) for i := 0; i < 100000; i++ { gPolicies = append(gPolicies, []string{fmt.Sprintf("user%d", i), fmt.Sprintf("group%d", i/10)}) } _, err = e.AddGroupingPolicies(gPolicies) if err != nil { b.Fatal(err) } b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { _, _ = e.Enforce("user5001", "data150", "read") } }) } golang-github-casbin-casbin-2.82.0/enforcer_cached_synced.go000066400000000000000000000116241456376452400240360ustar00rootroot00000000000000// Copyright 2018 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "sync" "sync/atomic" "time" "github.com/casbin/casbin/v2/persist/cache" ) // SyncedCachedEnforcer wraps Enforcer and provides decision sync cache type SyncedCachedEnforcer struct { *SyncedEnforcer expireTime time.Duration cache cache.Cache enableCache int32 locker *sync.RWMutex } // NewSyncedCachedEnforcer creates a sync cached enforcer via file or DB. func NewSyncedCachedEnforcer(params ...interface{}) (*SyncedCachedEnforcer, error) { e := &SyncedCachedEnforcer{} var err error e.SyncedEnforcer, err = NewSyncedEnforcer(params...) if err != nil { return nil, err } e.enableCache = 1 e.cache, _ = cache.NewSyncCache() e.locker = new(sync.RWMutex) return e, nil } // EnableCache determines whether to enable cache on Enforce(). When enableCache is enabled, cached result (true | false) will be returned for previous decisions. func (e *SyncedCachedEnforcer) EnableCache(enableCache bool) { var enabled int32 if enableCache { enabled = 1 } atomic.StoreInt32(&e.enableCache, enabled) } // Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). // if rvals is not string , ingore the cache func (e *SyncedCachedEnforcer) Enforce(rvals ...interface{}) (bool, error) { if atomic.LoadInt32(&e.enableCache) == 0 { return e.SyncedEnforcer.Enforce(rvals...) } key, ok := e.getKey(rvals...) if !ok { return e.SyncedEnforcer.Enforce(rvals...) } if res, err := e.getCachedResult(key); err == nil { return res, nil } else if err != cache.ErrNoSuchKey { return res, err } res, err := e.SyncedEnforcer.Enforce(rvals...) if err != nil { return false, err } err = e.setCachedResult(key, res, e.expireTime) return res, err } func (e *SyncedCachedEnforcer) LoadPolicy() error { if atomic.LoadInt32(&e.enableCache) != 0 { if err := e.cache.Clear(); err != nil { return err } } return e.SyncedEnforcer.LoadPolicy() } func (e *SyncedCachedEnforcer) AddPolicy(params ...interface{}) (bool, error) { if ok, err := e.checkOneAndRemoveCache(params...); !ok { return ok, err } return e.SyncedEnforcer.AddPolicy(params...) } func (e *SyncedCachedEnforcer) AddPolicies(rules [][]string) (bool, error) { if ok, err := e.checkManyAndRemoveCache(rules); !ok { return ok, err } return e.SyncedEnforcer.AddPolicies(rules) } func (e *SyncedCachedEnforcer) RemovePolicy(params ...interface{}) (bool, error) { if ok, err := e.checkOneAndRemoveCache(params...); !ok { return ok, err } return e.SyncedEnforcer.RemovePolicy(params...) } func (e *SyncedCachedEnforcer) RemovePolicies(rules [][]string) (bool, error) { if ok, err := e.checkManyAndRemoveCache(rules); !ok { return ok, err } return e.SyncedEnforcer.RemovePolicies(rules) } func (e *SyncedCachedEnforcer) getCachedResult(key string) (res bool, err error) { return e.cache.Get(key) } func (e *SyncedCachedEnforcer) SetExpireTime(expireTime time.Duration) { e.locker.Lock() defer e.locker.Unlock() e.expireTime = expireTime } // SetCache need to be sync cache func (e *SyncedCachedEnforcer) SetCache(c cache.Cache) { e.locker.Lock() defer e.locker.Unlock() e.cache = c } func (e *SyncedCachedEnforcer) setCachedResult(key string, res bool, extra ...interface{}) error { return e.cache.Set(key, res, extra...) } func (e *SyncedCachedEnforcer) getKey(params ...interface{}) (string, bool) { return GetCacheKey(params...) } // InvalidateCache deletes all the existing cached decisions. func (e *SyncedCachedEnforcer) InvalidateCache() error { return e.cache.Clear() } func (e *SyncedCachedEnforcer) checkOneAndRemoveCache(params ...interface{}) (bool, error) { if atomic.LoadInt32(&e.enableCache) != 0 { key, ok := e.getKey(params...) if ok { if err := e.cache.Delete(key); err != nil && err != cache.ErrNoSuchKey { return false, err } } } return true, nil } func (e *SyncedCachedEnforcer) checkManyAndRemoveCache(rules [][]string) (bool, error) { if len(rules) != 0 { if atomic.LoadInt32(&e.enableCache) != 0 { irule := make([]interface{}, len(rules[0])) for _, rule := range rules { for i, param := range rule { irule[i] = param } key, _ := e.getKey(irule...) if err := e.cache.Delete(key); err != nil && err != cache.ErrNoSuchKey { return false, err } } } } return true, nil } golang-github-casbin-casbin-2.82.0/enforcer_cached_synced_test.go000066400000000000000000000056331456376452400251000ustar00rootroot00000000000000// Copyright 2018 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "sync" "testing" "time" ) func testSyncEnforceCache(t *testing.T, e *SyncedCachedEnforcer, sub string, obj interface{}, act string, res bool) { t.Helper() if myRes, _ := e.Enforce(sub, obj, act); myRes != res { t.Errorf("%s, %v, %s: %t, supposed to be %t", sub, obj, act, myRes, res) } } func TestSyncCache(t *testing.T) { e, _ := NewSyncedCachedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") e.expireTime = time.Millisecond // The cache is enabled by default for NewCachedEnforcer. g := sync.WaitGroup{} goThread := 1000 g.Add(goThread) for i := 0; i < goThread; i++ { go func() { _, _ = e.AddPolicy("alice", "data2", "read") testSyncEnforceCache(t, e, "alice", "data2", "read", true) if e.InvalidateCache() != nil { panic("never reached") } g.Done() }() } g.Wait() _, _ = e.RemovePolicy("alice", "data2", "read") testSyncEnforceCache(t, e, "alice", "data1", "read", true) time.Sleep(time.Millisecond * 2) // coverage for expire testSyncEnforceCache(t, e, "alice", "data1", "read", true) testSyncEnforceCache(t, e, "alice", "data1", "write", false) testSyncEnforceCache(t, e, "alice", "data2", "read", false) testSyncEnforceCache(t, e, "alice", "data2", "write", false) // The cache is enabled, calling RemovePolicy, LoadPolicy or RemovePolicies will // also operate cached items. _, _ = e.RemovePolicy("alice", "data1", "read") testSyncEnforceCache(t, e, "alice", "data1", "read", false) testSyncEnforceCache(t, e, "alice", "data1", "write", false) testSyncEnforceCache(t, e, "alice", "data2", "read", false) testSyncEnforceCache(t, e, "alice", "data2", "write", false) e, _ = NewSyncedCachedEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") testSyncEnforceCache(t, e, "alice", "data1", "read", true) testSyncEnforceCache(t, e, "bob", "data2", "write", true) testSyncEnforceCache(t, e, "alice", "data2", "read", true) testSyncEnforceCache(t, e, "alice", "data2", "write", true) _, _ = e.RemovePolicies([][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, }) testSyncEnforceCache(t, e, "alice", "data1", "read", false) testSyncEnforceCache(t, e, "bob", "data2", "write", false) testSyncEnforceCache(t, e, "alice", "data2", "read", true) testSyncEnforceCache(t, e, "alice", "data2", "write", true) } golang-github-casbin-casbin-2.82.0/enforcer_cached_test.go000066400000000000000000000044541456376452400235330ustar00rootroot00000000000000// Copyright 2018 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import "testing" func testEnforceCache(t *testing.T, e *CachedEnforcer, sub string, obj interface{}, act string, res bool) { t.Helper() if myRes, _ := e.Enforce(sub, obj, act); myRes != res { t.Errorf("%s, %v, %s: %t, supposed to be %t", sub, obj, act, myRes, res) } } func TestCache(t *testing.T) { e, _ := NewCachedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") // The cache is enabled by default for NewCachedEnforcer. testEnforceCache(t, e, "alice", "data1", "read", true) testEnforceCache(t, e, "alice", "data1", "write", false) testEnforceCache(t, e, "alice", "data2", "read", false) testEnforceCache(t, e, "alice", "data2", "write", false) // The cache is enabled, calling RemovePolicy, LoadPolicy or RemovePolicies will // also operate cached items. _, _ = e.RemovePolicy("alice", "data1", "read") testEnforceCache(t, e, "alice", "data1", "read", false) testEnforceCache(t, e, "alice", "data1", "write", false) testEnforceCache(t, e, "alice", "data2", "read", false) testEnforceCache(t, e, "alice", "data2", "write", false) e, _ = NewCachedEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") testEnforceCache(t, e, "alice", "data1", "read", true) testEnforceCache(t, e, "bob", "data2", "write", true) testEnforceCache(t, e, "alice", "data2", "read", true) testEnforceCache(t, e, "alice", "data2", "write", true) _, _ = e.RemovePolicies([][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, }) testEnforceCache(t, e, "alice", "data1", "read", false) testEnforceCache(t, e, "bob", "data2", "write", false) testEnforceCache(t, e, "alice", "data2", "read", true) testEnforceCache(t, e, "alice", "data2", "write", true) } golang-github-casbin-casbin-2.82.0/enforcer_distributed.go000066400000000000000000000147661456376452400236160ustar00rootroot00000000000000package casbin import ( "github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/persist" ) // DistributedEnforcer wraps SyncedEnforcer for dispatcher. type DistributedEnforcer struct { *SyncedEnforcer } func NewDistributedEnforcer(params ...interface{}) (*DistributedEnforcer, error) { e := &DistributedEnforcer{} var err error e.SyncedEnforcer, err = NewSyncedEnforcer(params...) if err != nil { return nil, err } return e, nil } // SetDispatcher sets the current dispatcher. func (e *DistributedEnforcer) SetDispatcher(dispatcher persist.Dispatcher) { e.dispatcher = dispatcher } // AddPoliciesSelf provides a method for dispatcher to add authorization rules to the current policy. // The function returns the rules affected and error. func (d *DistributedEnforcer) AddPoliciesSelf(shouldPersist func() bool, sec string, ptype string, rules [][]string) (affected [][]string, err error) { d.m.Lock() defer d.m.Unlock() if shouldPersist != nil && shouldPersist() { var noExistsPolicy [][]string for _, rule := range rules { if !d.model.HasPolicy(sec, ptype, rule) { noExistsPolicy = append(noExistsPolicy, rule) } } if err := d.adapter.(persist.BatchAdapter).AddPolicies(sec, ptype, noExistsPolicy); err != nil { if err.Error() != notImplemented { return nil, err } } } affected = d.model.AddPoliciesWithAffected(sec, ptype, rules) if sec == "g" { err := d.BuildIncrementalRoleLinks(model.PolicyAdd, ptype, affected) if err != nil { return affected, err } } return affected, nil } // RemovePoliciesSelf provides a method for dispatcher to remove a set of rules from current policy. // The function returns the rules affected and error. func (d *DistributedEnforcer) RemovePoliciesSelf(shouldPersist func() bool, sec string, ptype string, rules [][]string) (affected [][]string, err error) { d.m.Lock() defer d.m.Unlock() if shouldPersist != nil && shouldPersist() { if err := d.adapter.(persist.BatchAdapter).RemovePolicies(sec, ptype, rules); err != nil { if err.Error() != notImplemented { return nil, err } } } affected = d.model.RemovePoliciesWithAffected(sec, ptype, rules) if sec == "g" { err := d.BuildIncrementalRoleLinks(model.PolicyRemove, ptype, affected) if err != nil { return affected, err } } return affected, err } // RemoveFilteredPolicySelf provides a method for dispatcher to remove an authorization rule from the current policy, field filters can be specified. // The function returns the rules affected and error. func (d *DistributedEnforcer) RemoveFilteredPolicySelf(shouldPersist func() bool, sec string, ptype string, fieldIndex int, fieldValues ...string) (affected [][]string, err error) { d.m.Lock() defer d.m.Unlock() if shouldPersist != nil && shouldPersist() { if err := d.adapter.RemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues...); err != nil { if err.Error() != notImplemented { return nil, err } } } _, affected = d.model.RemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues...) if sec == "g" { err := d.BuildIncrementalRoleLinks(model.PolicyRemove, ptype, affected) if err != nil { return affected, err } } return affected, nil } // ClearPolicySelf provides a method for dispatcher to clear all rules from the current policy. func (d *DistributedEnforcer) ClearPolicySelf(shouldPersist func() bool) error { d.m.Lock() defer d.m.Unlock() if shouldPersist != nil && shouldPersist() { err := d.adapter.SavePolicy(nil) if err != nil { return err } } d.model.ClearPolicy() return nil } // UpdatePolicySelf provides a method for dispatcher to update an authorization rule from the current policy. func (d *DistributedEnforcer) UpdatePolicySelf(shouldPersist func() bool, sec string, ptype string, oldRule, newRule []string) (affected bool, err error) { d.m.Lock() defer d.m.Unlock() if shouldPersist != nil && shouldPersist() { err := d.adapter.(persist.UpdatableAdapter).UpdatePolicy(sec, ptype, oldRule, newRule) if err != nil { return false, err } } ruleUpdated := d.model.UpdatePolicy(sec, ptype, oldRule, newRule) if !ruleUpdated { return ruleUpdated, nil } if sec == "g" { err := d.BuildIncrementalRoleLinks(model.PolicyRemove, ptype, [][]string{oldRule}) // remove the old rule if err != nil { return ruleUpdated, err } err = d.BuildIncrementalRoleLinks(model.PolicyAdd, ptype, [][]string{newRule}) // add the new rule if err != nil { return ruleUpdated, err } } return ruleUpdated, nil } // UpdatePoliciesSelf provides a method for dispatcher to update a set of authorization rules from the current policy. func (d *DistributedEnforcer) UpdatePoliciesSelf(shouldPersist func() bool, sec string, ptype string, oldRules, newRules [][]string) (affected bool, err error) { d.m.Lock() defer d.m.Unlock() if shouldPersist != nil && shouldPersist() { err := d.adapter.(persist.UpdatableAdapter).UpdatePolicies(sec, ptype, oldRules, newRules) if err != nil { return false, err } } ruleUpdated := d.model.UpdatePolicies(sec, ptype, oldRules, newRules) if !ruleUpdated { return ruleUpdated, nil } if sec == "g" { err := d.BuildIncrementalRoleLinks(model.PolicyRemove, ptype, oldRules) // remove the old rule if err != nil { return ruleUpdated, err } err = d.BuildIncrementalRoleLinks(model.PolicyAdd, ptype, newRules) // add the new rule if err != nil { return ruleUpdated, err } } return ruleUpdated, nil } // UpdateFilteredPoliciesSelf provides a method for dispatcher to update a set of authorization rules from the current policy. func (d *DistributedEnforcer) UpdateFilteredPoliciesSelf(shouldPersist func() bool, sec string, ptype string, newRules [][]string, fieldIndex int, fieldValues ...string) (bool, error) { d.m.Lock() defer d.m.Unlock() var ( oldRules [][]string err error ) if shouldPersist != nil && shouldPersist() { oldRules, err = d.adapter.(persist.UpdatableAdapter).UpdateFilteredPolicies(sec, ptype, newRules, fieldIndex, fieldValues...) if err != nil { return false, err } } ruleChanged := !d.model.RemovePolicies(sec, ptype, oldRules) d.model.AddPolicies(sec, ptype, newRules) ruleChanged = ruleChanged && len(newRules) != 0 if !ruleChanged { return ruleChanged, nil } if sec == "g" { err := d.BuildIncrementalRoleLinks(model.PolicyRemove, ptype, oldRules) // remove the old rule if err != nil { return ruleChanged, err } err = d.BuildIncrementalRoleLinks(model.PolicyAdd, ptype, newRules) // add the new rule if err != nil { return ruleChanged, err } } return true, nil } golang-github-casbin-casbin-2.82.0/enforcer_interface.go000066400000000000000000000217571456376452400232320ustar00rootroot00000000000000// Copyright 2019 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "github.com/casbin/casbin/v2/effector" "github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/persist" "github.com/casbin/casbin/v2/rbac" "github.com/casbin/govaluate" ) var _ IEnforcer = &Enforcer{} var _ IEnforcer = &SyncedEnforcer{} var _ IEnforcer = &CachedEnforcer{} // IEnforcer is the API interface of Enforcer type IEnforcer interface { /* Enforcer API */ InitWithFile(modelPath string, policyPath string) error InitWithAdapter(modelPath string, adapter persist.Adapter) error InitWithModelAndAdapter(m model.Model, adapter persist.Adapter) error LoadModel() error GetModel() model.Model SetModel(m model.Model) GetAdapter() persist.Adapter SetAdapter(adapter persist.Adapter) SetWatcher(watcher persist.Watcher) error GetRoleManager() rbac.RoleManager SetRoleManager(rm rbac.RoleManager) SetEffector(eft effector.Effector) ClearPolicy() LoadPolicy() error LoadFilteredPolicy(filter interface{}) error LoadIncrementalFilteredPolicy(filter interface{}) error IsFiltered() bool SavePolicy() error EnableEnforce(enable bool) EnableLog(enable bool) EnableAutoNotifyWatcher(enable bool) EnableAutoSave(autoSave bool) EnableAutoBuildRoleLinks(autoBuildRoleLinks bool) BuildRoleLinks() error Enforce(rvals ...interface{}) (bool, error) EnforceWithMatcher(matcher string, rvals ...interface{}) (bool, error) EnforceEx(rvals ...interface{}) (bool, []string, error) EnforceExWithMatcher(matcher string, rvals ...interface{}) (bool, []string, error) BatchEnforce(requests [][]interface{}) ([]bool, error) BatchEnforceWithMatcher(matcher string, requests [][]interface{}) ([]bool, error) /* RBAC API */ GetRolesForUser(name string, domain ...string) ([]string, error) GetUsersForRole(name string, domain ...string) ([]string, error) HasRoleForUser(name string, role string, domain ...string) (bool, error) AddRoleForUser(user string, role string, domain ...string) (bool, error) AddPermissionForUser(user string, permission ...string) (bool, error) AddPermissionsForUser(user string, permissions ...[]string) (bool, error) DeletePermissionForUser(user string, permission ...string) (bool, error) DeletePermissionsForUser(user string) (bool, error) GetPermissionsForUser(user string, domain ...string) ([][]string, error) HasPermissionForUser(user string, permission ...string) bool GetImplicitRolesForUser(name string, domain ...string) ([]string, error) GetImplicitPermissionsForUser(user string, domain ...string) ([][]string, error) GetImplicitUsersForPermission(permission ...string) ([]string, error) DeleteRoleForUser(user string, role string, domain ...string) (bool, error) DeleteRolesForUser(user string, domain ...string) (bool, error) DeleteUser(user string) (bool, error) DeleteRole(role string) (bool, error) DeletePermission(permission ...string) (bool, error) /* RBAC API with domains*/ GetUsersForRoleInDomain(name string, domain string) []string GetRolesForUserInDomain(name string, domain string) []string GetPermissionsForUserInDomain(user string, domain string) [][]string AddRoleForUserInDomain(user string, role string, domain string) (bool, error) DeleteRoleForUserInDomain(user string, role string, domain string) (bool, error) GetAllUsersByDomain(domain string) []string DeleteRolesForUserInDomain(user string, domain string) (bool, error) DeleteAllUsersByDomain(domain string) (bool, error) DeleteDomains(domains ...string) (bool, error) GetAllDomains() ([]string, error) GetAllRolesByDomain(domain string) []string /* Management API */ GetAllSubjects() []string GetAllNamedSubjects(ptype string) []string GetAllObjects() []string GetAllNamedObjects(ptype string) []string GetAllActions() []string GetAllNamedActions(ptype string) []string GetAllRoles() []string GetAllNamedRoles(ptype string) []string GetPolicy() [][]string GetFilteredPolicy(fieldIndex int, fieldValues ...string) [][]string GetNamedPolicy(ptype string) [][]string GetFilteredNamedPolicy(ptype string, fieldIndex int, fieldValues ...string) [][]string GetGroupingPolicy() [][]string GetFilteredGroupingPolicy(fieldIndex int, fieldValues ...string) [][]string GetNamedGroupingPolicy(ptype string) [][]string GetFilteredNamedGroupingPolicy(ptype string, fieldIndex int, fieldValues ...string) [][]string HasPolicy(params ...interface{}) bool HasNamedPolicy(ptype string, params ...interface{}) bool AddPolicy(params ...interface{}) (bool, error) AddPolicies(rules [][]string) (bool, error) AddNamedPolicy(ptype string, params ...interface{}) (bool, error) AddNamedPolicies(ptype string, rules [][]string) (bool, error) AddPoliciesEx(rules [][]string) (bool, error) AddNamedPoliciesEx(ptype string, rules [][]string) (bool, error) RemovePolicy(params ...interface{}) (bool, error) RemovePolicies(rules [][]string) (bool, error) RemoveFilteredPolicy(fieldIndex int, fieldValues ...string) (bool, error) RemoveNamedPolicy(ptype string, params ...interface{}) (bool, error) RemoveNamedPolicies(ptype string, rules [][]string) (bool, error) RemoveFilteredNamedPolicy(ptype string, fieldIndex int, fieldValues ...string) (bool, error) HasGroupingPolicy(params ...interface{}) bool HasNamedGroupingPolicy(ptype string, params ...interface{}) bool AddGroupingPolicy(params ...interface{}) (bool, error) AddGroupingPolicies(rules [][]string) (bool, error) AddGroupingPoliciesEx(rules [][]string) (bool, error) AddNamedGroupingPolicy(ptype string, params ...interface{}) (bool, error) AddNamedGroupingPolicies(ptype string, rules [][]string) (bool, error) AddNamedGroupingPoliciesEx(ptype string, rules [][]string) (bool, error) RemoveGroupingPolicy(params ...interface{}) (bool, error) RemoveGroupingPolicies(rules [][]string) (bool, error) RemoveFilteredGroupingPolicy(fieldIndex int, fieldValues ...string) (bool, error) RemoveNamedGroupingPolicy(ptype string, params ...interface{}) (bool, error) RemoveNamedGroupingPolicies(ptype string, rules [][]string) (bool, error) RemoveFilteredNamedGroupingPolicy(ptype string, fieldIndex int, fieldValues ...string) (bool, error) AddFunction(name string, function govaluate.ExpressionFunction) UpdatePolicy(oldPolicy []string, newPolicy []string) (bool, error) UpdatePolicies(oldPolicies [][]string, newPolicies [][]string) (bool, error) UpdateFilteredPolicies(newPolicies [][]string, fieldIndex int, fieldValues ...string) (bool, error) UpdateGroupingPolicy(oldRule []string, newRule []string) (bool, error) UpdateGroupingPolicies(oldRules [][]string, newRules [][]string) (bool, error) UpdateNamedGroupingPolicy(ptype string, oldRule []string, newRule []string) (bool, error) UpdateNamedGroupingPolicies(ptype string, oldRules [][]string, newRules [][]string) (bool, error) /* Management API with autoNotifyWatcher disabled */ SelfAddPolicy(sec string, ptype string, rule []string) (bool, error) SelfAddPolicies(sec string, ptype string, rules [][]string) (bool, error) SelfAddPoliciesEx(sec string, ptype string, rules [][]string) (bool, error) SelfRemovePolicy(sec string, ptype string, rule []string) (bool, error) SelfRemovePolicies(sec string, ptype string, rules [][]string) (bool, error) SelfRemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) (bool, error) SelfUpdatePolicy(sec string, ptype string, oldRule, newRule []string) (bool, error) SelfUpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) (bool, error) } var _ IDistributedEnforcer = &DistributedEnforcer{} // IDistributedEnforcer defines dispatcher enforcer. type IDistributedEnforcer interface { IEnforcer SetDispatcher(dispatcher persist.Dispatcher) /* Management API for DistributedEnforcer*/ AddPoliciesSelf(shouldPersist func() bool, sec string, ptype string, rules [][]string) (affected [][]string, err error) RemovePoliciesSelf(shouldPersist func() bool, sec string, ptype string, rules [][]string) (affected [][]string, err error) RemoveFilteredPolicySelf(shouldPersist func() bool, sec string, ptype string, fieldIndex int, fieldValues ...string) (affected [][]string, err error) ClearPolicySelf(shouldPersist func() bool) error UpdatePolicySelf(shouldPersist func() bool, sec string, ptype string, oldRule, newRule []string) (affected bool, err error) UpdatePoliciesSelf(shouldPersist func() bool, sec string, ptype string, oldRules, newRules [][]string) (affected bool, err error) UpdateFilteredPoliciesSelf(shouldPersist func() bool, sec string, ptype string, newRules [][]string, fieldIndex int, fieldValues ...string) (bool, error) } golang-github-casbin-casbin-2.82.0/enforcer_synced.go000066400000000000000000000571461456376452400225600ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "sync" "sync/atomic" "time" "github.com/casbin/govaluate" "github.com/casbin/casbin/v2/persist" "github.com/casbin/casbin/v2/rbac" defaultrolemanager "github.com/casbin/casbin/v2/rbac/default-role-manager" ) // SyncedEnforcer wraps Enforcer and provides synchronized access type SyncedEnforcer struct { *Enforcer m sync.RWMutex stopAutoLoad chan struct{} autoLoadRunning int32 } // NewSyncedEnforcer creates a synchronized enforcer via file or DB. func NewSyncedEnforcer(params ...interface{}) (*SyncedEnforcer, error) { e := &SyncedEnforcer{} var err error e.Enforcer, err = NewEnforcer(params...) if err != nil { return nil, err } e.stopAutoLoad = make(chan struct{}, 1) e.autoLoadRunning = 0 return e, nil } // GetLock return the private RWMutex lock func (e *SyncedEnforcer) GetLock() *sync.RWMutex { return &e.m } // IsAutoLoadingRunning check if SyncedEnforcer is auto loading policies func (e *SyncedEnforcer) IsAutoLoadingRunning() bool { return atomic.LoadInt32(&(e.autoLoadRunning)) != 0 } // StartAutoLoadPolicy starts a go routine that will every specified duration call LoadPolicy func (e *SyncedEnforcer) StartAutoLoadPolicy(d time.Duration) { // Don't start another goroutine if there is already one running if !atomic.CompareAndSwapInt32(&e.autoLoadRunning, 0, 1) { return } ticker := time.NewTicker(d) go func() { defer func() { ticker.Stop() atomic.StoreInt32(&(e.autoLoadRunning), int32(0)) }() n := 1 for { select { case <-ticker.C: // error intentionally ignored _ = e.LoadPolicy() // Uncomment this line to see when the policy is loaded. // log.Print("Load policy for time: ", n) n++ case <-e.stopAutoLoad: return } } }() } // StopAutoLoadPolicy causes the go routine to exit. func (e *SyncedEnforcer) StopAutoLoadPolicy() { if e.IsAutoLoadingRunning() { e.stopAutoLoad <- struct{}{} } } // SetWatcher sets the current watcher. func (e *SyncedEnforcer) SetWatcher(watcher persist.Watcher) error { e.m.Lock() defer e.m.Unlock() return e.Enforcer.SetWatcher(watcher) } // LoadModel reloads the model from the model CONF file. func (e *SyncedEnforcer) LoadModel() error { e.m.Lock() defer e.m.Unlock() return e.Enforcer.LoadModel() } // ClearPolicy clears all policy. func (e *SyncedEnforcer) ClearPolicy() { e.m.Lock() defer e.m.Unlock() e.Enforcer.ClearPolicy() } // LoadPolicy reloads the policy from file/database. func (e *SyncedEnforcer) LoadPolicy() error { e.m.Lock() defer e.m.Unlock() return e.Enforcer.LoadPolicy() } // LoadPolicyFast is not blocked when adapter calls LoadPolicy. func (e *SyncedEnforcer) LoadPolicyFast() error { e.m.RLock() newModel := e.model.Copy() e.m.RUnlock() newModel.ClearPolicy() newRmMap := map[string]rbac.RoleManager{} var err error if err = e.adapter.LoadPolicy(newModel); err != nil && err.Error() != "invalid file path, file path cannot be empty" { return err } if err = newModel.SortPoliciesBySubjectHierarchy(); err != nil { return err } if err = newModel.SortPoliciesByPriority(); err != nil { return err } if e.autoBuildRoleLinks { for ptype := range newModel["g"] { newRmMap[ptype] = defaultrolemanager.NewRoleManager(10) } err = newModel.BuildRoleLinks(newRmMap) if err != nil { return err } } // reduce the lock range e.m.Lock() defer e.m.Unlock() e.model = newModel e.rmMap = newRmMap return nil } // LoadFilteredPolicy reloads a filtered policy from file/database. func (e *SyncedEnforcer) LoadFilteredPolicy(filter interface{}) error { e.m.Lock() defer e.m.Unlock() return e.Enforcer.LoadFilteredPolicy(filter) } // LoadIncrementalFilteredPolicy reloads a filtered policy from file/database. func (e *SyncedEnforcer) LoadIncrementalFilteredPolicy(filter interface{}) error { e.m.Lock() defer e.m.Unlock() return e.Enforcer.LoadIncrementalFilteredPolicy(filter) } // SavePolicy saves the current policy (usually after changed with Casbin API) back to file/database. func (e *SyncedEnforcer) SavePolicy() error { e.m.Lock() defer e.m.Unlock() return e.Enforcer.SavePolicy() } // BuildRoleLinks manually rebuild the role inheritance relations. func (e *SyncedEnforcer) BuildRoleLinks() error { e.m.Lock() defer e.m.Unlock() return e.Enforcer.BuildRoleLinks() } // Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). func (e *SyncedEnforcer) Enforce(rvals ...interface{}) (bool, error) { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.Enforce(rvals...) } // EnforceWithMatcher use a custom matcher to decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "". func (e *SyncedEnforcer) EnforceWithMatcher(matcher string, rvals ...interface{}) (bool, error) { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.EnforceWithMatcher(matcher, rvals...) } // EnforceEx explain enforcement by informing matched rules func (e *SyncedEnforcer) EnforceEx(rvals ...interface{}) (bool, []string, error) { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.EnforceEx(rvals...) } // EnforceExWithMatcher use a custom matcher and explain enforcement by informing matched rules func (e *SyncedEnforcer) EnforceExWithMatcher(matcher string, rvals ...interface{}) (bool, []string, error) { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.EnforceExWithMatcher(matcher, rvals...) } // BatchEnforce enforce in batches func (e *SyncedEnforcer) BatchEnforce(requests [][]interface{}) ([]bool, error) { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.BatchEnforce(requests) } // BatchEnforceWithMatcher enforce with matcher in batches func (e *SyncedEnforcer) BatchEnforceWithMatcher(matcher string, requests [][]interface{}) ([]bool, error) { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.BatchEnforceWithMatcher(matcher, requests) } // GetAllSubjects gets the list of subjects that show up in the current policy. func (e *SyncedEnforcer) GetAllSubjects() []string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetAllSubjects() } // GetAllNamedSubjects gets the list of subjects that show up in the current named policy. func (e *SyncedEnforcer) GetAllNamedSubjects(ptype string) []string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetAllNamedSubjects(ptype) } // GetAllObjects gets the list of objects that show up in the current policy. func (e *SyncedEnforcer) GetAllObjects() []string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetAllObjects() } // GetAllNamedObjects gets the list of objects that show up in the current named policy. func (e *SyncedEnforcer) GetAllNamedObjects(ptype string) []string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetAllNamedObjects(ptype) } // GetAllActions gets the list of actions that show up in the current policy. func (e *SyncedEnforcer) GetAllActions() []string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetAllActions() } // GetAllNamedActions gets the list of actions that show up in the current named policy. func (e *SyncedEnforcer) GetAllNamedActions(ptype string) []string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetAllNamedActions(ptype) } // GetAllRoles gets the list of roles that show up in the current policy. func (e *SyncedEnforcer) GetAllRoles() []string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetAllRoles() } // GetAllNamedRoles gets the list of roles that show up in the current named policy. func (e *SyncedEnforcer) GetAllNamedRoles(ptype string) []string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetAllNamedRoles(ptype) } // GetPolicy gets all the authorization rules in the policy. func (e *SyncedEnforcer) GetPolicy() [][]string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetPolicy() } // GetFilteredPolicy gets all the authorization rules in the policy, field filters can be specified. func (e *SyncedEnforcer) GetFilteredPolicy(fieldIndex int, fieldValues ...string) [][]string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetFilteredPolicy(fieldIndex, fieldValues...) } // GetNamedPolicy gets all the authorization rules in the named policy. func (e *SyncedEnforcer) GetNamedPolicy(ptype string) [][]string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetNamedPolicy(ptype) } // GetFilteredNamedPolicy gets all the authorization rules in the named policy, field filters can be specified. func (e *SyncedEnforcer) GetFilteredNamedPolicy(ptype string, fieldIndex int, fieldValues ...string) [][]string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetFilteredNamedPolicy(ptype, fieldIndex, fieldValues...) } // GetGroupingPolicy gets all the role inheritance rules in the policy. func (e *SyncedEnforcer) GetGroupingPolicy() [][]string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetGroupingPolicy() } // GetFilteredGroupingPolicy gets all the role inheritance rules in the policy, field filters can be specified. func (e *SyncedEnforcer) GetFilteredGroupingPolicy(fieldIndex int, fieldValues ...string) [][]string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetFilteredGroupingPolicy(fieldIndex, fieldValues...) } // GetNamedGroupingPolicy gets all the role inheritance rules in the policy. func (e *SyncedEnforcer) GetNamedGroupingPolicy(ptype string) [][]string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetNamedGroupingPolicy(ptype) } // GetFilteredNamedGroupingPolicy gets all the role inheritance rules in the policy, field filters can be specified. func (e *SyncedEnforcer) GetFilteredNamedGroupingPolicy(ptype string, fieldIndex int, fieldValues ...string) [][]string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetFilteredNamedGroupingPolicy(ptype, fieldIndex, fieldValues...) } // HasPolicy determines whether an authorization rule exists. func (e *SyncedEnforcer) HasPolicy(params ...interface{}) bool { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.HasPolicy(params...) } // HasNamedPolicy determines whether a named authorization rule exists. func (e *SyncedEnforcer) HasNamedPolicy(ptype string, params ...interface{}) bool { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.HasNamedPolicy(ptype, params...) } // AddPolicy adds an authorization rule to the current policy. // If the rule already exists, the function returns false and the rule will not be added. // Otherwise the function returns true by adding the new rule. func (e *SyncedEnforcer) AddPolicy(params ...interface{}) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddPolicy(params...) } // AddPolicies adds authorization rules to the current policy. // If the rule already exists, the function returns false for the corresponding rule and the rule will not be added. // Otherwise the function returns true for the corresponding rule by adding the new rule. func (e *SyncedEnforcer) AddPolicies(rules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddPolicies(rules) } // AddPoliciesEx adds authorization rules to the current policy. // If the rule already exists, the rule will not be added. // But unlike AddPolicies, other non-existent rules are added instead of returning false directly func (e *SyncedEnforcer) AddPoliciesEx(rules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddPoliciesEx(rules) } // AddNamedPolicy adds an authorization rule to the current named policy. // If the rule already exists, the function returns false and the rule will not be added. // Otherwise the function returns true by adding the new rule. func (e *SyncedEnforcer) AddNamedPolicy(ptype string, params ...interface{}) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddNamedPolicy(ptype, params...) } // AddNamedPolicies adds authorization rules to the current named policy. // If the rule already exists, the function returns false for the corresponding rule and the rule will not be added. // Otherwise the function returns true for the corresponding by adding the new rule. func (e *SyncedEnforcer) AddNamedPolicies(ptype string, rules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddNamedPolicies(ptype, rules) } // AddNamedPoliciesEx adds authorization rules to the current named policy. // If the rule already exists, the rule will not be added. // But unlike AddNamedPolicies, other non-existent rules are added instead of returning false directly func (e *SyncedEnforcer) AddNamedPoliciesEx(ptype string, rules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddNamedPoliciesEx(ptype, rules) } // RemovePolicy removes an authorization rule from the current policy. func (e *SyncedEnforcer) RemovePolicy(params ...interface{}) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.RemovePolicy(params...) } // UpdatePolicy updates an authorization rule from the current policy. func (e *SyncedEnforcer) UpdatePolicy(oldPolicy []string, newPolicy []string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.UpdatePolicy(oldPolicy, newPolicy) } func (e *SyncedEnforcer) UpdateNamedPolicy(ptype string, p1 []string, p2 []string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.UpdateNamedPolicy(ptype, p1, p2) } // UpdatePolicies updates authorization rules from the current policies. func (e *SyncedEnforcer) UpdatePolicies(oldPolices [][]string, newPolicies [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.UpdatePolicies(oldPolices, newPolicies) } func (e *SyncedEnforcer) UpdateNamedPolicies(ptype string, p1 [][]string, p2 [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.UpdateNamedPolicies(ptype, p1, p2) } func (e *SyncedEnforcer) UpdateFilteredPolicies(newPolicies [][]string, fieldIndex int, fieldValues ...string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.UpdateFilteredPolicies(newPolicies, fieldIndex, fieldValues...) } func (e *SyncedEnforcer) UpdateFilteredNamedPolicies(ptype string, newPolicies [][]string, fieldIndex int, fieldValues ...string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.UpdateFilteredNamedPolicies(ptype, newPolicies, fieldIndex, fieldValues...) } // RemovePolicies removes authorization rules from the current policy. func (e *SyncedEnforcer) RemovePolicies(rules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.RemovePolicies(rules) } // RemoveFilteredPolicy removes an authorization rule from the current policy, field filters can be specified. func (e *SyncedEnforcer) RemoveFilteredPolicy(fieldIndex int, fieldValues ...string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.RemoveFilteredPolicy(fieldIndex, fieldValues...) } // RemoveNamedPolicy removes an authorization rule from the current named policy. func (e *SyncedEnforcer) RemoveNamedPolicy(ptype string, params ...interface{}) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.RemoveNamedPolicy(ptype, params...) } // RemoveNamedPolicies removes authorization rules from the current named policy. func (e *SyncedEnforcer) RemoveNamedPolicies(ptype string, rules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.RemoveNamedPolicies(ptype, rules) } // RemoveFilteredNamedPolicy removes an authorization rule from the current named policy, field filters can be specified. func (e *SyncedEnforcer) RemoveFilteredNamedPolicy(ptype string, fieldIndex int, fieldValues ...string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.RemoveFilteredNamedPolicy(ptype, fieldIndex, fieldValues...) } // HasGroupingPolicy determines whether a role inheritance rule exists. func (e *SyncedEnforcer) HasGroupingPolicy(params ...interface{}) bool { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.HasGroupingPolicy(params...) } // HasNamedGroupingPolicy determines whether a named role inheritance rule exists. func (e *SyncedEnforcer) HasNamedGroupingPolicy(ptype string, params ...interface{}) bool { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.HasNamedGroupingPolicy(ptype, params...) } // AddGroupingPolicy adds a role inheritance rule to the current policy. // If the rule already exists, the function returns false and the rule will not be added. // Otherwise the function returns true by adding the new rule. func (e *SyncedEnforcer) AddGroupingPolicy(params ...interface{}) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddGroupingPolicy(params...) } // AddGroupingPolicies adds role inheritance rulea to the current policy. // If the rule already exists, the function returns false for the corresponding policy rule and the rule will not be added. // Otherwise the function returns true for the corresponding policy rule by adding the new rule. func (e *SyncedEnforcer) AddGroupingPolicies(rules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddGroupingPolicies(rules) } // AddGroupingPoliciesEx adds role inheritance rules to the current policy. // If the rule already exists, the rule will not be added. // But unlike AddGroupingPolicies, other non-existent rules are added instead of returning false directly func (e *SyncedEnforcer) AddGroupingPoliciesEx(rules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddGroupingPoliciesEx(rules) } // AddNamedGroupingPolicy adds a named role inheritance rule to the current policy. // If the rule already exists, the function returns false and the rule will not be added. // Otherwise the function returns true by adding the new rule. func (e *SyncedEnforcer) AddNamedGroupingPolicy(ptype string, params ...interface{}) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddNamedGroupingPolicy(ptype, params...) } // AddNamedGroupingPolicies adds named role inheritance rules to the current policy. // If the rule already exists, the function returns false for the corresponding policy rule and the rule will not be added. // Otherwise the function returns true for the corresponding policy rule by adding the new rule. func (e *SyncedEnforcer) AddNamedGroupingPolicies(ptype string, rules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddNamedGroupingPolicies(ptype, rules) } // AddNamedGroupingPoliciesEx adds named role inheritance rules to the current policy. // If the rule already exists, the rule will not be added. // But unlike AddNamedGroupingPolicies, other non-existent rules are added instead of returning false directly func (e *SyncedEnforcer) AddNamedGroupingPoliciesEx(ptype string, rules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddNamedGroupingPoliciesEx(ptype, rules) } // RemoveGroupingPolicy removes a role inheritance rule from the current policy. func (e *SyncedEnforcer) RemoveGroupingPolicy(params ...interface{}) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.RemoveGroupingPolicy(params...) } // RemoveGroupingPolicies removes role inheritance rules from the current policy. func (e *SyncedEnforcer) RemoveGroupingPolicies(rules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.RemoveGroupingPolicies(rules) } // RemoveFilteredGroupingPolicy removes a role inheritance rule from the current policy, field filters can be specified. func (e *SyncedEnforcer) RemoveFilteredGroupingPolicy(fieldIndex int, fieldValues ...string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.RemoveFilteredGroupingPolicy(fieldIndex, fieldValues...) } // RemoveNamedGroupingPolicy removes a role inheritance rule from the current named policy. func (e *SyncedEnforcer) RemoveNamedGroupingPolicy(ptype string, params ...interface{}) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.RemoveNamedGroupingPolicy(ptype, params...) } // RemoveNamedGroupingPolicies removes role inheritance rules from the current named policy. func (e *SyncedEnforcer) RemoveNamedGroupingPolicies(ptype string, rules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.RemoveNamedGroupingPolicies(ptype, rules) } func (e *SyncedEnforcer) UpdateGroupingPolicy(oldRule []string, newRule []string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.UpdateGroupingPolicy(oldRule, newRule) } func (e *SyncedEnforcer) UpdateGroupingPolicies(oldRules [][]string, newRules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.UpdateGroupingPolicies(oldRules, newRules) } func (e *SyncedEnforcer) UpdateNamedGroupingPolicy(ptype string, oldRule []string, newRule []string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.UpdateNamedGroupingPolicy(ptype, oldRule, newRule) } func (e *SyncedEnforcer) UpdateNamedGroupingPolicies(ptype string, oldRules [][]string, newRules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.UpdateNamedGroupingPolicies(ptype, oldRules, newRules) } // RemoveFilteredNamedGroupingPolicy removes a role inheritance rule from the current named policy, field filters can be specified. func (e *SyncedEnforcer) RemoveFilteredNamedGroupingPolicy(ptype string, fieldIndex int, fieldValues ...string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.RemoveFilteredNamedGroupingPolicy(ptype, fieldIndex, fieldValues...) } // AddFunction adds a customized function. func (e *SyncedEnforcer) AddFunction(name string, function govaluate.ExpressionFunction) { e.m.Lock() defer e.m.Unlock() e.Enforcer.AddFunction(name, function) } func (e *SyncedEnforcer) SelfAddPolicy(sec string, ptype string, rule []string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.SelfAddPolicy(sec, ptype, rule) } func (e *SyncedEnforcer) SelfAddPolicies(sec string, ptype string, rules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.SelfAddPolicies(sec, ptype, rules) } func (e *SyncedEnforcer) SelfAddPoliciesEx(sec string, ptype string, rules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.SelfAddPoliciesEx(sec, ptype, rules) } func (e *SyncedEnforcer) SelfRemovePolicy(sec string, ptype string, rule []string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.SelfRemovePolicy(sec, ptype, rule) } func (e *SyncedEnforcer) SelfRemovePolicies(sec string, ptype string, rules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.SelfRemovePolicies(sec, ptype, rules) } func (e *SyncedEnforcer) SelfRemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.SelfRemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues...) } func (e *SyncedEnforcer) SelfUpdatePolicy(sec string, ptype string, oldRule, newRule []string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.SelfUpdatePolicy(sec, ptype, oldRule, newRule) } func (e *SyncedEnforcer) SelfUpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.SelfUpdatePolicies(sec, ptype, oldRules, newRules) } golang-github-casbin-casbin-2.82.0/enforcer_synced_test.go000066400000000000000000000512471456376452400236130ustar00rootroot00000000000000// Copyright 2018 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "github.com/casbin/casbin/v2/errors" "github.com/casbin/casbin/v2/util" "sort" "testing" "time" ) func testEnforceSync(t *testing.T, e *SyncedEnforcer, sub string, obj interface{}, act string, res bool) { t.Helper() if myRes, _ := e.Enforce(sub, obj, act); myRes != res { t.Errorf("%s, %v, %s: %t, supposed to be %t", sub, obj, act, myRes, res) } } func TestSync(t *testing.T) { e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") // Start reloading the policy every 200 ms. e.StartAutoLoadPolicy(time.Millisecond * 200) testEnforceSync(t, e, "alice", "data1", "read", true) testEnforceSync(t, e, "alice", "data1", "write", false) testEnforceSync(t, e, "alice", "data2", "read", false) testEnforceSync(t, e, "alice", "data2", "write", false) testEnforceSync(t, e, "bob", "data1", "read", false) testEnforceSync(t, e, "bob", "data1", "write", false) testEnforceSync(t, e, "bob", "data2", "read", false) testEnforceSync(t, e, "bob", "data2", "write", true) // Simulate a policy change e.ClearPolicy() testEnforceSync(t, e, "bob", "data2", "write", false) // Wait for at least one sync time.Sleep(time.Millisecond * 300) testEnforceSync(t, e, "bob", "data2", "write", true) // Stop the reloading policy periodically. e.StopAutoLoadPolicy() } func TestStopAutoLoadPolicy(t *testing.T) { e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") e.StartAutoLoadPolicy(5 * time.Millisecond) if !e.IsAutoLoadingRunning() { t.Error("auto load is not running") } e.StopAutoLoadPolicy() // Need a moment, to exit goroutine time.Sleep(10 * time.Millisecond) if e.IsAutoLoadingRunning() { t.Error("auto load is still running") } } func testSyncedEnforcerGetPolicy(t *testing.T, e *SyncedEnforcer, res [][]string) { t.Helper() myRes := e.GetPolicy() if !util.SortedArray2DEquals(res, myRes) { t.Error("Policy: ", myRes, ", supposed to be ", res) } else { t.Log("Policy: ", myRes) } } func TestSyncedEnforcerSelfAddPolicy(t *testing.T) { for i := 0; i < 10; i++ { e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user1", "data1", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user2", "data2", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user3", "data3", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user4", "data4", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user5", "data5", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user6", "data6", "read"}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, {"user1", "data1", "read"}, {"user2", "data2", "read"}, {"user3", "data3", "read"}, {"user4", "data4", "read"}, {"user5", "data5", "read"}, {"user6", "data6", "read"}, }) } } func TestSyncedEnforcerSelfAddPolicies(t *testing.T) { for i := 0; i < 10; i++ { e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") go func() { _, _ = e.SelfAddPolicies("p", "p", [][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}}) }() go func() { _, _ = e.SelfAddPolicies("p", "p", [][]string{{"user3", "data3", "read"}, {"user4", "data4", "read"}}) }() go func() { _, _ = e.SelfAddPolicies("p", "p", [][]string{{"user5", "data5", "read"}, {"user6", "data6", "read"}}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, {"user1", "data1", "read"}, {"user2", "data2", "read"}, {"user3", "data3", "read"}, {"user4", "data4", "read"}, {"user5", "data5", "read"}, {"user6", "data6", "read"}, }) } } func TestSyncedEnforcerSelfAddPoliciesEx(t *testing.T) { for i := 0; i < 10; i++ { e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") go func() { _, _ = e.SelfAddPoliciesEx("p", "p", [][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}}) }() go func() { _, _ = e.SelfAddPoliciesEx("p", "p", [][]string{{"user2", "data2", "read"}, {"user3", "data3", "read"}}) }() go func() { _, _ = e.SelfAddPoliciesEx("p", "p", [][]string{{"user3", "data3", "read"}, {"user4", "data4", "read"}}) }() go func() { _, _ = e.SelfAddPoliciesEx("p", "p", [][]string{{"user4", "data4", "read"}, {"user5", "data5", "read"}}) }() go func() { _, _ = e.SelfAddPoliciesEx("p", "p", [][]string{{"user5", "data5", "read"}, {"user6", "data6", "read"}}) }() go func() { _, _ = e.SelfAddPoliciesEx("p", "p", [][]string{{"user6", "data6", "read"}, {"user1", "data1", "read"}}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, {"user1", "data1", "read"}, {"user2", "data2", "read"}, {"user3", "data3", "read"}, {"user4", "data4", "read"}, {"user5", "data5", "read"}, {"user6", "data6", "read"}, }) } } func TestSyncedEnforcerSelfRemovePolicy(t *testing.T) { for i := 0; i < 10; i++ { e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user1", "data1", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user2", "data2", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user3", "data3", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user4", "data4", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user5", "data5", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user6", "data6", "read"}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, {"user1", "data1", "read"}, {"user2", "data2", "read"}, {"user3", "data3", "read"}, {"user4", "data4", "read"}, {"user5", "data5", "read"}, {"user6", "data6", "read"}, }) go func() { _, _ = e.SelfRemovePolicy("p", "p", []string{"user1", "data1", "read"}) }() go func() { _, _ = e.SelfRemovePolicy("p", "p", []string{"user2", "data2", "read"}) }() go func() { _, _ = e.SelfRemovePolicy("p", "p", []string{"user3", "data3", "read"}) }() go func() { _, _ = e.SelfRemovePolicy("p", "p", []string{"user4", "data4", "read"}) }() go func() { _, _ = e.SelfRemovePolicy("p", "p", []string{"user5", "data5", "read"}) }() go func() { _, _ = e.SelfRemovePolicy("p", "p", []string{"user6", "data6", "read"}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, }) } } func TestSyncedEnforcerSelfRemovePolicies(t *testing.T) { for i := 0; i < 10; i++ { e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user1", "data1", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user2", "data2", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user3", "data3", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user4", "data4", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user5", "data5", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user6", "data6", "read"}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, {"user1", "data1", "read"}, {"user2", "data2", "read"}, {"user3", "data3", "read"}, {"user4", "data4", "read"}, {"user5", "data5", "read"}, {"user6", "data6", "read"}, }) go func() { _, _ = e.SelfRemovePolicies("p", "p", [][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}}) }() go func() { _, _ = e.SelfRemovePolicies("p", "p", [][]string{{"user3", "data3", "read"}, {"user4", "data4", "read"}}) }() go func() { _, _ = e.SelfRemovePolicies("p", "p", [][]string{{"user5", "data5", "read"}, {"user6", "data6", "read"}}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, }) } } func TestSyncedEnforcerSelfRemoveFilteredPolicy(t *testing.T) { for i := 0; i < 10; i++ { e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user1", "data1", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user2", "data2", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user3", "data3", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user4", "data4", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user5", "data5", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user6", "data6", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user7", "data7", "write"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user8", "data8", "write"}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, {"user1", "data1", "read"}, {"user2", "data2", "read"}, {"user3", "data3", "read"}, {"user4", "data4", "read"}, {"user5", "data5", "read"}, {"user6", "data6", "read"}, {"user7", "data7", "write"}, {"user8", "data8", "write"}, }) go func() { _, _ = e.SelfRemoveFilteredPolicy("p", "p", 0, "user1") }() go func() { _, _ = e.SelfRemoveFilteredPolicy("p", "p", 0, "user2") }() go func() { _, _ = e.SelfRemoveFilteredPolicy("p", "p", 1, "data3") }() go func() { _, _ = e.SelfRemoveFilteredPolicy("p", "p", 1, "data4") }() go func() { _, _ = e.SelfRemoveFilteredPolicy("p", "p", 0, "user5") }() go func() { _, _ = e.SelfRemoveFilteredPolicy("p", "p", 0, "user6") }() go func() { _, _ = e.SelfRemoveFilteredPolicy("p", "p", 2, "write") }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, }) } } func TestSyncedEnforcerSelfUpdatePolicy(t *testing.T) { for i := 0; i < 10; i++ { e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user1", "data1", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user2", "data2", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user3", "data3", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user4", "data4", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user5", "data5", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user6", "data6", "read"}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, {"user1", "data1", "read"}, {"user2", "data2", "read"}, {"user3", "data3", "read"}, {"user4", "data4", "read"}, {"user5", "data5", "read"}, {"user6", "data6", "read"}, }) go func() { _, _ = e.SelfUpdatePolicy("p", "p", []string{"user1", "data1", "read"}, []string{"user1", "data1", "write"}) }() go func() { _, _ = e.SelfUpdatePolicy("p", "p", []string{"user2", "data2", "read"}, []string{"user2", "data2", "write"}) }() go func() { _, _ = e.SelfUpdatePolicy("p", "p", []string{"user3", "data3", "read"}, []string{"user3", "data3", "write"}) }() go func() { _, _ = e.SelfUpdatePolicy("p", "p", []string{"user4", "data4", "read"}, []string{"user4", "data4", "write"}) }() go func() { _, _ = e.SelfUpdatePolicy("p", "p", []string{"user5", "data5", "read"}, []string{"user5", "data5", "write"}) }() go func() { _, _ = e.SelfUpdatePolicy("p", "p", []string{"user6", "data6", "read"}, []string{"user6", "data6", "write"}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, {"user1", "data1", "write"}, {"user2", "data2", "write"}, {"user3", "data3", "write"}, {"user4", "data4", "write"}, {"user5", "data5", "write"}, {"user6", "data6", "write"}, }) } } func TestSyncedEnforcerSelfUpdatePolicies(t *testing.T) { for i := 0; i < 10; i++ { e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user1", "data1", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user2", "data2", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user3", "data3", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user4", "data4", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user5", "data5", "read"}) }() go func() { _, _ = e.SelfAddPolicy("p", "p", []string{"user6", "data6", "read"}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, {"user1", "data1", "read"}, {"user2", "data2", "read"}, {"user3", "data3", "read"}, {"user4", "data4", "read"}, {"user5", "data5", "read"}, {"user6", "data6", "read"}, }) go func() { _, _ = e.SelfUpdatePolicies("p", "p", [][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}}, [][]string{{"user1", "data1", "write"}, {"user2", "data2", "write"}}) }() go func() { _, _ = e.SelfUpdatePolicies("p", "p", [][]string{{"user3", "data3", "read"}, {"user4", "data4", "read"}}, [][]string{{"user3", "data3", "write"}, {"user4", "data4", "write"}}) }() go func() { _, _ = e.SelfUpdatePolicies("p", "p", [][]string{{"user5", "data5", "read"}, {"user6", "data6", "read"}}, [][]string{{"user5", "data5", "write"}, {"user6", "data6", "write"}}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, {"user1", "data1", "write"}, {"user2", "data2", "write"}, {"user3", "data3", "write"}, {"user4", "data4", "write"}, {"user5", "data5", "write"}, {"user6", "data6", "write"}, }) } } func TestSyncedEnforcerAddPoliciesEx(t *testing.T) { for i := 0; i < 10; i++ { e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") go func() { _, _ = e.AddPoliciesEx([][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}}) }() go func() { _, _ = e.AddPoliciesEx([][]string{{"user2", "data2", "read"}, {"user3", "data3", "read"}}) }() go func() { _, _ = e.AddPoliciesEx([][]string{{"user4", "data4", "read"}, {"user5", "data5", "read"}}) }() go func() { _, _ = e.AddPoliciesEx([][]string{{"user5", "data5", "read"}, {"user6", "data6", "read"}}) }() go func() { _, _ = e.AddPoliciesEx([][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}}) }() go func() { _, _ = e.AddPoliciesEx([][]string{{"user2", "data2", "read"}, {"user3", "data3", "read"}}) }() go func() { _, _ = e.AddPoliciesEx([][]string{{"user4", "data4", "read"}, {"user5", "data5", "read"}}) }() go func() { _, _ = e.AddPoliciesEx([][]string{{"user5", "data5", "read"}, {"user6", "data6", "read"}}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, {"user1", "data1", "read"}, {"user2", "data2", "read"}, {"user3", "data3", "read"}, {"user4", "data4", "read"}, {"user5", "data5", "read"}, {"user6", "data6", "read"}, }) } } func TestSyncedEnforcerAddNamedPoliciesEx(t *testing.T) { for i := 0; i < 10; i++ { e, _ := NewSyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") go func() { _, _ = e.AddNamedPoliciesEx("p", [][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}}) }() go func() { _, _ = e.AddNamedPoliciesEx("p", [][]string{{"user2", "data2", "read"}, {"user3", "data3", "read"}}) }() go func() { _, _ = e.AddNamedPoliciesEx("p", [][]string{{"user4", "data4", "read"}, {"user5", "data5", "read"}}) }() go func() { _, _ = e.AddNamedPoliciesEx("p", [][]string{{"user5", "data5", "read"}, {"user6", "data6", "read"}}) }() go func() { _, _ = e.AddNamedPoliciesEx("p", [][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}}) }() go func() { _, _ = e.AddNamedPoliciesEx("p", [][]string{{"user2", "data2", "read"}, {"user3", "data3", "read"}}) }() go func() { _, _ = e.AddNamedPoliciesEx("p", [][]string{{"user4", "data4", "read"}, {"user5", "data5", "read"}}) }() go func() { _, _ = e.AddNamedPoliciesEx("p", [][]string{{"user5", "data5", "read"}, {"user6", "data6", "read"}}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, {"user1", "data1", "read"}, {"user2", "data2", "read"}, {"user3", "data3", "read"}, {"user4", "data4", "read"}, {"user5", "data5", "read"}, {"user6", "data6", "read"}, }) } } func testSyncedEnforcerGetUsers(t *testing.T, e *SyncedEnforcer, res []string, name string, domain ...string) { t.Helper() myRes, err := e.GetUsersForRole(name, domain...) myResCopy := make([]string, len(myRes)) copy(myResCopy, myRes) sort.Strings(myRes) sort.Strings(res) switch err { case nil: break case errors.ErrNameNotFound: t.Log("No name found") default: t.Error("Users for ", name, " could not be fetched: ", err.Error()) } t.Log("Users for ", name, ": ", myRes) if !util.SetEquals(res, myRes) { t.Error("Users for ", name, ": ", myRes, ", supposed to be ", res) } } func TestSyncedEnforcerAddGroupingPoliciesEx(t *testing.T) { for i := 0; i < 10; i++ { e, _ := NewSyncedEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") e.ClearPolicy() go func() { _, _ = e.AddGroupingPoliciesEx([][]string{{"user1", "member"}, {"user2", "member"}}) }() go func() { _, _ = e.AddGroupingPoliciesEx([][]string{{"user2", "member"}, {"user3", "member"}}) }() go func() { _, _ = e.AddGroupingPoliciesEx([][]string{{"user4", "member"}, {"user5", "member"}}) }() go func() { _, _ = e.AddGroupingPoliciesEx([][]string{{"user5", "member"}, {"user6", "member"}}) }() go func() { _, _ = e.AddGroupingPoliciesEx([][]string{{"user1", "member"}, {"user2", "member"}}) }() go func() { _, _ = e.AddGroupingPoliciesEx([][]string{{"user2", "member"}, {"user3", "member"}}) }() go func() { _, _ = e.AddGroupingPoliciesEx([][]string{{"user4", "member"}, {"user5", "member"}}) }() go func() { _, _ = e.AddGroupingPoliciesEx([][]string{{"user5", "member"}, {"user6", "member"}}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetUsers(t, e, []string{"user1", "user2", "user3", "user4", "user5", "user6"}, "member") } } func TestSyncedEnforcerAddNamedGroupingPoliciesEx(t *testing.T) { for i := 0; i < 10; i++ { e, _ := NewSyncedEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") e.ClearPolicy() go func() { _, _ = e.AddNamedGroupingPoliciesEx("g", [][]string{{"user1", "member"}, {"user2", "member"}}) }() go func() { _, _ = e.AddNamedGroupingPoliciesEx("g", [][]string{{"user2", "member"}, {"user3", "member"}}) }() go func() { _, _ = e.AddNamedGroupingPoliciesEx("g", [][]string{{"user4", "member"}, {"user5", "member"}}) }() go func() { _, _ = e.AddNamedGroupingPoliciesEx("g", [][]string{{"user5", "member"}, {"user6", "member"}}) }() go func() { _, _ = e.AddNamedGroupingPoliciesEx("g", [][]string{{"user1", "member"}, {"user2", "member"}}) }() go func() { _, _ = e.AddNamedGroupingPoliciesEx("g", [][]string{{"user2", "member"}, {"user3", "member"}}) }() go func() { _, _ = e.AddNamedGroupingPoliciesEx("g", [][]string{{"user4", "member"}, {"user5", "member"}}) }() go func() { _, _ = e.AddNamedGroupingPoliciesEx("g", [][]string{{"user5", "member"}, {"user6", "member"}}) }() time.Sleep(100 * time.Millisecond) testSyncedEnforcerGetUsers(t, e, []string{"user1", "user2", "user3", "user4", "user5", "user6"}, "member") } } golang-github-casbin-casbin-2.82.0/enforcer_test.go000066400000000000000000000665471456376452400222570ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "sync" "testing" "github.com/casbin/casbin/v2/model" fileadapter "github.com/casbin/casbin/v2/persist/file-adapter" "github.com/casbin/casbin/v2/util" ) func TestKeyMatchModelInMemory(t *testing.T) { m := model.NewModel() m.AddDef("r", "r", "sub, obj, act") m.AddDef("p", "p", "sub, obj, act") m.AddDef("e", "e", "some(where (p.eft == allow))") m.AddDef("m", "m", "r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)") a := fileadapter.NewAdapter("examples/keymatch_policy.csv") e, _ := NewEnforcer(m, a) testEnforce(t, e, "alice", "/alice_data/resource1", "GET", true) testEnforce(t, e, "alice", "/alice_data/resource1", "POST", true) testEnforce(t, e, "alice", "/alice_data/resource2", "GET", true) testEnforce(t, e, "alice", "/alice_data/resource2", "POST", false) testEnforce(t, e, "alice", "/bob_data/resource1", "GET", false) testEnforce(t, e, "alice", "/bob_data/resource1", "POST", false) testEnforce(t, e, "alice", "/bob_data/resource2", "GET", false) testEnforce(t, e, "alice", "/bob_data/resource2", "POST", false) testEnforce(t, e, "bob", "/alice_data/resource1", "GET", false) testEnforce(t, e, "bob", "/alice_data/resource1", "POST", false) testEnforce(t, e, "bob", "/alice_data/resource2", "GET", true) testEnforce(t, e, "bob", "/alice_data/resource2", "POST", false) testEnforce(t, e, "bob", "/bob_data/resource1", "GET", false) testEnforce(t, e, "bob", "/bob_data/resource1", "POST", true) testEnforce(t, e, "bob", "/bob_data/resource2", "GET", false) testEnforce(t, e, "bob", "/bob_data/resource2", "POST", true) testEnforce(t, e, "cathy", "/cathy_data", "GET", true) testEnforce(t, e, "cathy", "/cathy_data", "POST", true) testEnforce(t, e, "cathy", "/cathy_data", "DELETE", false) e, _ = NewEnforcer(m) _ = a.LoadPolicy(e.GetModel()) testEnforce(t, e, "alice", "/alice_data/resource1", "GET", true) testEnforce(t, e, "alice", "/alice_data/resource1", "POST", true) testEnforce(t, e, "alice", "/alice_data/resource2", "GET", true) testEnforce(t, e, "alice", "/alice_data/resource2", "POST", false) testEnforce(t, e, "alice", "/bob_data/resource1", "GET", false) testEnforce(t, e, "alice", "/bob_data/resource1", "POST", false) testEnforce(t, e, "alice", "/bob_data/resource2", "GET", false) testEnforce(t, e, "alice", "/bob_data/resource2", "POST", false) testEnforce(t, e, "bob", "/alice_data/resource1", "GET", false) testEnforce(t, e, "bob", "/alice_data/resource1", "POST", false) testEnforce(t, e, "bob", "/alice_data/resource2", "GET", true) testEnforce(t, e, "bob", "/alice_data/resource2", "POST", false) testEnforce(t, e, "bob", "/bob_data/resource1", "GET", false) testEnforce(t, e, "bob", "/bob_data/resource1", "POST", true) testEnforce(t, e, "bob", "/bob_data/resource2", "GET", false) testEnforce(t, e, "bob", "/bob_data/resource2", "POST", true) testEnforce(t, e, "cathy", "/cathy_data", "GET", true) testEnforce(t, e, "cathy", "/cathy_data", "POST", true) testEnforce(t, e, "cathy", "/cathy_data", "DELETE", false) } func TestKeyMatchWithRBACInDomain(t *testing.T) { e, _ := NewEnforcer("examples/keymatch_with_rbac_in_domain.conf", "examples/keymatch_with_rbac_in_domain.csv") testDomainEnforce(t, e, "Username==test2", "engines/engine1", "*", "attach", true) } func TestKeyMatchModelInMemoryDeny(t *testing.T) { m := model.NewModel() m.AddDef("r", "r", "sub, obj, act") m.AddDef("p", "p", "sub, obj, act") m.AddDef("e", "e", "!some(where (p.eft == deny))") m.AddDef("m", "m", "r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)") a := fileadapter.NewAdapter("examples/keymatch_policy.csv") e, _ := NewEnforcer(m, a) testEnforce(t, e, "alice", "/alice_data/resource2", "POST", true) } func TestRBACModelInMemoryIndeterminate(t *testing.T) { m := model.NewModel() m.AddDef("r", "r", "sub, obj, act") m.AddDef("p", "p", "sub, obj, act") m.AddDef("g", "g", "_, _") m.AddDef("e", "e", "some(where (p.eft == allow))") m.AddDef("m", "m", "g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act") e, _ := NewEnforcer(m) _, _ = e.AddPermissionForUser("alice", "data1", "invalid") testEnforce(t, e, "alice", "data1", "read", false) } func TestRBACModelInMemory(t *testing.T) { m := model.NewModel() m.AddDef("r", "r", "sub, obj, act") m.AddDef("p", "p", "sub, obj, act") m.AddDef("g", "g", "_, _") m.AddDef("e", "e", "some(where (p.eft == allow))") m.AddDef("m", "m", "g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act") e, _ := NewEnforcer(m) _, _ = e.AddPermissionForUser("alice", "data1", "read") _, _ = e.AddPermissionForUser("bob", "data2", "write") _, _ = e.AddPermissionForUser("data2_admin", "data2", "read") _, _ = e.AddPermissionForUser("data2_admin", "data2", "write") _, _ = e.AddRoleForUser("alice", "data2_admin") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", true) testEnforce(t, e, "alice", "data2", "write", true) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } func TestRBACModelInMemory2(t *testing.T) { text := ` [request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act ` m, _ := model.NewModelFromString(text) // The above is the same as: // m := NewModel() // m.LoadModelFromText(text) e, _ := NewEnforcer(m) _, _ = e.AddPermissionForUser("alice", "data1", "read") _, _ = e.AddPermissionForUser("bob", "data2", "write") _, _ = e.AddPermissionForUser("data2_admin", "data2", "read") _, _ = e.AddPermissionForUser("data2_admin", "data2", "write") _, _ = e.AddRoleForUser("alice", "data2_admin") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", true) testEnforce(t, e, "alice", "data2", "write", true) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } func TestNotUsedRBACModelInMemory(t *testing.T) { m := model.NewModel() m.AddDef("r", "r", "sub, obj, act") m.AddDef("p", "p", "sub, obj, act") m.AddDef("g", "g", "_, _") m.AddDef("e", "e", "some(where (p.eft == allow))") m.AddDef("m", "m", "g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act") e, _ := NewEnforcer(m) _, _ = e.AddPermissionForUser("alice", "data1", "read") _, _ = e.AddPermissionForUser("bob", "data2", "write") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } func TestMatcherUsingInOperator(t *testing.T) { // From file config e, _ := NewEnforcer("examples/rbac_model_matcher_using_in_op.conf") _, _ = e.AddPermissionForUser("alice", "data1", "read") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data2", "read", true) testEnforce(t, e, "alice", "data3", "read", true) testEnforce(t, e, "anyone", "data1", "read", false) testEnforce(t, e, "anyone", "data2", "read", true) testEnforce(t, e, "anyone", "data3", "read", true) } func TestMatcherUsingInOperatorBracket(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model_matcher_using_in_op_bracket.conf") _, _ = e.AddPermissionForUser("alice", "data1", "read") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data2", "read", true) testEnforce(t, e, "alice", "data3", "read", true) testEnforce(t, e, "anyone", "data1", "read", false) testEnforce(t, e, "anyone", "data2", "read", true) testEnforce(t, e, "anyone", "data3", "read", true) } func TestReloadPolicy(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") _ = e.LoadPolicy() testGetPolicy(t, e, [][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}) } func TestSavePolicy(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") _ = e.SavePolicy() } func TestClearPolicy(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") e.ClearPolicy() } func TestEnableEnforce(t *testing.T) { e, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") e.EnableEnforce(false) testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", true) testEnforce(t, e, "alice", "data2", "read", true) testEnforce(t, e, "alice", "data2", "write", true) testEnforce(t, e, "bob", "data1", "read", true) testEnforce(t, e, "bob", "data1", "write", true) testEnforce(t, e, "bob", "data2", "read", true) testEnforce(t, e, "bob", "data2", "write", true) e.EnableEnforce(true) testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } func TestEnableLog(t *testing.T) { e, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv", true) // The log is enabled by default, so the above is the same with: // e := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) // The log can also be enabled or disabled at run-time. e.EnableLog(false) testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } func TestEnableAutoSave(t *testing.T) { e, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") e.EnableAutoSave(false) // Because AutoSave is disabled, the policy change only affects the policy in Casbin enforcer, // it doesn't affect the policy in the storage. _, _ = e.RemovePolicy("alice", "data1", "read") // Reload the policy from the storage to see the effect. _ = e.LoadPolicy() testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) e.EnableAutoSave(true) // Because AutoSave is enabled, the policy change not only affects the policy in Casbin enforcer, // but also affects the policy in the storage. _, _ = e.RemovePolicy("alice", "data1", "read") // However, the file adapter doesn't implement the AutoSave feature, so enabling it has no effect at all here. // Reload the policy from the storage to see the effect. _ = e.LoadPolicy() testEnforce(t, e, "alice", "data1", "read", true) // Will not be false here. testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } func TestInitWithAdapter(t *testing.T) { adapter := fileadapter.NewAdapter("examples/basic_policy.csv") e, _ := NewEnforcer("examples/basic_model.conf", adapter) testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } func TestRoleLinks(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf") e.EnableAutoBuildRoleLinks(false) _ = e.BuildRoleLinks() _, _ = e.Enforce("user501", "data9", "read") } func TestEnforceConcurrency(t *testing.T) { defer func() { if r := recover(); r != nil { t.Errorf("Enforce is not concurrent") } }() e, _ := NewEnforcer("examples/rbac_model.conf") _ = e.LoadModel() var wg sync.WaitGroup // Simulate concurrency (maybe use a timer?) for i := 1; i <= 10000; i++ { wg.Add(1) go func() { _, _ = e.Enforce("user501", "data9", "read") wg.Done() }() } wg.Wait() } func TestGetAndSetModel(t *testing.T) { e, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") e2, _ := NewEnforcer("examples/basic_with_root_model.conf", "examples/basic_policy.csv") testEnforce(t, e, "root", "data1", "read", false) e.SetModel(e2.GetModel()) testEnforce(t, e, "root", "data1", "read", true) } func TestGetAndSetAdapterInMem(t *testing.T) { e, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") e2, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_inverse_policy.csv") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) a2 := e2.GetAdapter() e.SetAdapter(a2) _ = e.LoadPolicy() testEnforce(t, e, "alice", "data1", "read", false) testEnforce(t, e, "alice", "data1", "write", true) } func TestSetAdapterFromFile(t *testing.T) { e, _ := NewEnforcer("examples/basic_model.conf") testEnforce(t, e, "alice", "data1", "read", false) a := fileadapter.NewAdapter("examples/basic_policy.csv") e.SetAdapter(a) _ = e.LoadPolicy() testEnforce(t, e, "alice", "data1", "read", true) } func TestInitEmpty(t *testing.T) { e, _ := NewEnforcer() m := model.NewModel() m.AddDef("r", "r", "sub, obj, act") m.AddDef("p", "p", "sub, obj, act") m.AddDef("e", "e", "some(where (p.eft == allow))") m.AddDef("m", "m", "r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)") a := fileadapter.NewAdapter("examples/keymatch_policy.csv") e.SetModel(m) e.SetAdapter(a) _ = e.LoadPolicy() testEnforce(t, e, "alice", "/alice_data/resource1", "GET", true) } func testEnforceEx(t *testing.T, e *Enforcer, sub, obj, act interface{}, res []string) { t.Helper() _, myRes, _ := e.EnforceEx(sub, obj, act) if ok := util.ArrayEquals(res, myRes); !ok { t.Error("Key: ", myRes, ", supposed to be ", res) } } func TestEnforceEx(t *testing.T) { e, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") testEnforceEx(t, e, "alice", "data1", "read", []string{"alice", "data1", "read"}) testEnforceEx(t, e, "alice", "data1", "write", []string{}) testEnforceEx(t, e, "alice", "data2", "read", []string{}) testEnforceEx(t, e, "alice", "data2", "write", []string{}) testEnforceEx(t, e, "bob", "data1", "read", []string{}) testEnforceEx(t, e, "bob", "data1", "write", []string{}) testEnforceEx(t, e, "bob", "data2", "read", []string{}) testEnforceEx(t, e, "bob", "data2", "write", []string{"bob", "data2", "write"}) e, _ = NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") testEnforceEx(t, e, "alice", "data1", "read", []string{"alice", "data1", "read"}) testEnforceEx(t, e, "alice", "data1", "write", []string{}) testEnforceEx(t, e, "alice", "data2", "read", []string{"data2_admin", "data2", "read"}) testEnforceEx(t, e, "alice", "data2", "write", []string{"data2_admin", "data2", "write"}) testEnforceEx(t, e, "bob", "data1", "read", []string{}) testEnforceEx(t, e, "bob", "data1", "write", []string{}) testEnforceEx(t, e, "bob", "data2", "read", []string{}) testEnforceEx(t, e, "bob", "data2", "write", []string{"bob", "data2", "write"}) e, _ = NewEnforcer("examples/priority_model.conf", "examples/priority_policy.csv") testEnforceEx(t, e, "alice", "data1", "read", []string{"alice", "data1", "read", "allow"}) testEnforceEx(t, e, "alice", "data1", "write", []string{"data1_deny_group", "data1", "write", "deny"}) testEnforceEx(t, e, "alice", "data2", "read", []string{}) testEnforceEx(t, e, "alice", "data2", "write", []string{}) testEnforceEx(t, e, "bob", "data1", "write", []string{}) testEnforceEx(t, e, "bob", "data2", "read", []string{"data2_allow_group", "data2", "read", "allow"}) testEnforceEx(t, e, "bob", "data2", "write", []string{"bob", "data2", "write", "deny"}) e, _ = NewEnforcer("examples/abac_model.conf") obj := struct{ Owner string }{Owner: "alice"} testEnforceEx(t, e, "alice", obj, "write", []string{}) } func TestEnforceExLog(t *testing.T) { e, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv", true) testEnforceEx(t, e, "alice", "data1", "read", []string{"alice", "data1", "read"}) testEnforceEx(t, e, "alice", "data1", "write", []string{}) testEnforceEx(t, e, "alice", "data2", "read", []string{}) testEnforceEx(t, e, "alice", "data2", "write", []string{}) testEnforceEx(t, e, "bob", "data1", "read", []string{}) testEnforceEx(t, e, "bob", "data1", "write", []string{}) testEnforceEx(t, e, "bob", "data2", "read", []string{}) testEnforceEx(t, e, "bob", "data2", "write", []string{"bob", "data2", "write"}) } func testBatchEnforce(t *testing.T, e *Enforcer, requests [][]interface{}, results []bool) { t.Helper() myRes, _ := e.BatchEnforce(requests) if len(myRes) != len(results) { t.Errorf("%v supposed to be %v", myRes, results) } for i, v := range myRes { if v != results[i] { t.Errorf("%v supposed to be %v", myRes, results) } } } func TestBatchEnforce(t *testing.T) { e, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") results := []bool{true, true, false} testBatchEnforce(t, e, [][]interface{}{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"jack", "data3", "read"}}, results) } func TestSubjectPriority(t *testing.T) { e, _ := NewEnforcer("examples/subject_priority_model.conf", "examples/subject_priority_policy.csv") testBatchEnforce(t, e, [][]interface{}{ {"jane", "data1", "read"}, {"alice", "data1", "read"}, }, []bool{ true, true, }) } func TestSubjectPriorityWithDomain(t *testing.T) { e, _ := NewEnforcer("examples/subject_priority_model_with_domain.conf", "examples/subject_priority_policy_with_domain.csv") testBatchEnforce(t, e, [][]interface{}{ {"alice", "data1", "domain1", "write"}, {"bob", "data2", "domain2", "write"}, }, []bool{ true, true, }) } func TestSubjectPriorityInFilter(t *testing.T) { e, _ := NewEnforcer() adapter := fileadapter.NewFilteredAdapter("examples/subject_priority_policy_with_domain.csv") _ = e.InitWithAdapter("examples/subject_priority_model_with_domain.conf", adapter) if err := e.loadFilteredPolicy(&fileadapter.Filter{ P: []string{"", "", "domain1"}, }); err != nil { t.Errorf("unexpected error in LoadFilteredPolicy: %v", err) } testBatchEnforce(t, e, [][]interface{}{ {"alice", "data1", "domain1", "write"}, {"admin", "data1", "domain1", "write"}, }, []bool{ true, false, }) } func TestMultiplePolicyDefinitions(t *testing.T) { e, _ := NewEnforcer("examples/multiple_policy_definitions_model.conf", "examples/multiple_policy_definitions_policy.csv") enforceContext := NewEnforceContext("2") enforceContext.EType = "e" testBatchEnforce(t, e, [][]interface{}{ {"alice", "data2", "read"}, {enforceContext, struct{ Age int }{Age: 70}, "/data1", "read"}, {enforceContext, struct{ Age int }{Age: 30}, "/data1", "read"}, }, []bool{ true, false, true, }) } func TestPriorityExplicit(t *testing.T) { e, _ := NewEnforcer("examples/priority_model_explicit.conf", "examples/priority_policy_explicit.csv") testBatchEnforce(t, e, [][]interface{}{ {"alice", "data1", "write"}, {"alice", "data1", "read"}, {"bob", "data2", "read"}, {"bob", "data2", "write"}, {"data1_deny_group", "data1", "read"}, {"data1_deny_group", "data1", "write"}, {"data2_allow_group", "data2", "read"}, {"data2_allow_group", "data2", "write"}, }, []bool{ true, true, false, true, false, false, true, true, }) _, err := e.AddPolicy("1", "bob", "data2", "write", "deny") if err != nil { t.Fatalf("Add Policy: %v", err) } testBatchEnforce(t, e, [][]interface{}{ {"alice", "data1", "write"}, {"alice", "data1", "read"}, {"bob", "data2", "read"}, {"bob", "data2", "write"}, {"data1_deny_group", "data1", "read"}, {"data1_deny_group", "data1", "write"}, {"data2_allow_group", "data2", "read"}, {"data2_allow_group", "data2", "write"}, }, []bool{ true, true, false, false, false, false, true, true, }) } func TestFailedToLoadPolicy(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_pattern_model.conf", "examples/rbac_with_pattern_policy.csv") e.AddNamedMatchingFunc("g2", "matchingFunc", util.KeyMatch2) testEnforce(t, e, "alice", "/book/1", "GET", true) testEnforce(t, e, "bob", "/pen/3", "GET", true) e.SetAdapter(fileadapter.NewAdapter("not found")) _ = e.LoadPolicy() testEnforce(t, e, "alice", "/book/1", "GET", true) testEnforce(t, e, "bob", "/pen/3", "GET", true) } func TestReloadPolicyWithFunc(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_pattern_model.conf", "examples/rbac_with_pattern_policy.csv") e.AddNamedMatchingFunc("g2", "matchingFunc", util.KeyMatch2) testEnforce(t, e, "alice", "/book/1", "GET", true) testEnforce(t, e, "bob", "/pen/3", "GET", true) _ = e.LoadPolicy() testEnforce(t, e, "alice", "/book/1", "GET", true) testEnforce(t, e, "bob", "/pen/3", "GET", true) } func TestEvalPriority(t *testing.T) { e, _ := NewEnforcer("examples/eval_operator_model.conf", "examples/eval_operator_policy.csv") testEnforce(t, e, "admin", "users", "write", true) testEnforce(t, e, "admin", "none", "write", false) testEnforce(t, e, "user", "users", "write", false) } func TestLinkConditionFunc(t *testing.T) { TrueFunc := func(args ...string) (bool, error) { if len(args) != 0 { return args[0] == "_" || args[0] == "true", nil } return false, nil } FalseFunc := func(args ...string) (bool, error) { if len(args) != 0 { return args[0] == "_" || args[0] == "false", nil } return false, nil } m, _ := model.NewModelFromFile("examples/rbac_with_temporal_roles_model.conf") e, _ := NewEnforcer(m) _, _ = e.AddPolicies([][]string{ {"alice", "data1", "read"}, {"alice", "data1", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}, {"data3_admin", "data3", "read"}, {"data3_admin", "data3", "write"}, {"data4_admin", "data4", "read"}, {"data4_admin", "data4", "write"}, {"data5_admin", "data5", "read"}, {"data5_admin", "data5", "write"}, }) _, _ = e.AddGroupingPolicies([][]string{ {"alice", "data2_admin", "_", "_"}, {"alice", "data3_admin", "_", "_"}, {"alice", "data4_admin", "_", "_"}, {"alice", "data5_admin", "_", "_"}, }) e.AddNamedLinkConditionFunc("g", "alice", "data2_admin", TrueFunc) e.AddNamedLinkConditionFunc("g", "alice", "data3_admin", TrueFunc) e.AddNamedLinkConditionFunc("g", "alice", "data4_admin", FalseFunc) e.AddNamedLinkConditionFunc("g", "alice", "data5_admin", FalseFunc) e.SetNamedLinkConditionFuncParams("g", "alice", "data2_admin", "true") e.SetNamedLinkConditionFuncParams("g", "alice", "data3_admin", "not true") e.SetNamedLinkConditionFuncParams("g", "alice", "data4_admin", "false") e.SetNamedLinkConditionFuncParams("g", "alice", "data5_admin", "not false") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", true) testEnforce(t, e, "alice", "data2", "read", true) testEnforce(t, e, "alice", "data2", "write", true) testEnforce(t, e, "alice", "data3", "read", false) testEnforce(t, e, "alice", "data3", "write", false) testEnforce(t, e, "alice", "data4", "read", true) testEnforce(t, e, "alice", "data4", "write", true) testEnforce(t, e, "alice", "data5", "read", false) testEnforce(t, e, "alice", "data5", "write", false) m, _ = model.NewModelFromFile("examples/rbac_with_domain_temporal_roles_model.conf") e, _ = NewEnforcer(m) _, _ = e.AddPolicies([][]string{ {"alice", "domain1", "data1", "read"}, {"alice", "domain1", "data1", "write"}, {"data2_admin", "domain2", "data2", "read"}, {"data2_admin", "domain2", "data2", "write"}, {"data3_admin", "domain3", "data3", "read"}, {"data3_admin", "domain3", "data3", "write"}, {"data4_admin", "domain4", "data4", "read"}, {"data4_admin", "domain4", "data4", "write"}, {"data5_admin", "domain5", "data5", "read"}, {"data5_admin", "domain5", "data5", "write"}, }) _, _ = e.AddGroupingPolicies([][]string{ {"alice", "data2_admin", "domain2", "_", "_"}, {"alice", "data3_admin", "domain3", "_", "_"}, {"alice", "data4_admin", "domain4", "_", "_"}, {"alice", "data5_admin", "domain5", "_", "_"}, }) e.AddNamedDomainLinkConditionFunc("g", "alice", "data2_admin", "domain2", TrueFunc) e.AddNamedDomainLinkConditionFunc("g", "alice", "data3_admin", "domain3", TrueFunc) e.AddNamedDomainLinkConditionFunc("g", "alice", "data4_admin", "domain4", FalseFunc) e.AddNamedDomainLinkConditionFunc("g", "alice", "data5_admin", "domain5", FalseFunc) e.SetNamedDomainLinkConditionFuncParams("g", "alice", "data2_admin", "domain2", "true") e.SetNamedDomainLinkConditionFuncParams("g", "alice", "data3_admin", "domain3", "not true") e.SetNamedDomainLinkConditionFuncParams("g", "alice", "data4_admin", "domain4", "false") e.SetNamedDomainLinkConditionFuncParams("g", "alice", "data5_admin", "domain5", "not false") testDomainEnforce(t, e, "alice", "domain1", "data1", "read", true) testDomainEnforce(t, e, "alice", "domain1", "data1", "write", true) testDomainEnforce(t, e, "alice", "domain2", "data2", "read", true) testDomainEnforce(t, e, "alice", "domain2", "data2", "write", true) testDomainEnforce(t, e, "alice", "domain3", "data3", "read", false) testDomainEnforce(t, e, "alice", "domain3", "data3", "write", false) testDomainEnforce(t, e, "alice", "domain4", "data4", "read", true) testDomainEnforce(t, e, "alice", "domain4", "data4", "write", true) testDomainEnforce(t, e, "alice", "domain5", "data5", "read", false) testDomainEnforce(t, e, "alice", "domain5", "data5", "write", false) } golang-github-casbin-casbin-2.82.0/error_test.go000066400000000000000000000142461456376452400215720ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "testing" fileadapter "github.com/casbin/casbin/v2/persist/file-adapter" ) func TestPathError(t *testing.T) { _, err := NewEnforcer("hope_this_path_wont_exist", "") if err == nil { t.Errorf("Should be error here.") } else { t.Log("Test on error: ") t.Log(err.Error()) } } func TestEnforcerParamError(t *testing.T) { _, err := NewEnforcer(1, 2, 3) if err == nil { t.Errorf("Should not be error here.") } else { t.Log("Test on error: ") t.Log(err.Error()) } _, err2 := NewEnforcer(1, "2") if err2 == nil { t.Errorf("Should not be error here.") } else { t.Log("Test on error: ") t.Log(err2.Error()) } } func TestModelError(t *testing.T) { _, err := NewEnforcer("examples/error/error_model.conf", "examples/error/error_policy.csv") if err == nil { t.Errorf("Should be error here.") } else { t.Log("Test on error: ") t.Log(err.Error()) } } //func TestPolicyError(t *testing.T) { // _, err := NewEnforcer("examples/basic_model.conf", "examples/error/error_policy.csv") // if err == nil { // t.Errorf("Should be error here.") // } else { // t.Log("Test on error: ") // t.Log(err.Error()) // } //} func TestEnforceError(t *testing.T) { e, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") _, err := e.Enforce("wrong", "wrong") if err == nil { t.Errorf("Should be error here.") } else { t.Log("Test on error: ") t.Log(err.Error()) } e, _ = NewEnforcer("examples/abac_rule_model.conf") _, err = e.Enforce("wang", "wang", "wang") if err == nil { t.Errorf("Should be error here.") } else { t.Log("Test on error: ") t.Log(err.Error()) } } func TestNoError(t *testing.T) { e, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") err := e.LoadModel() if err != nil { t.Errorf("Should be no error here.") t.Log("Unexpected error: ") t.Log(err.Error()) } err = e.LoadPolicy() if err != nil { t.Errorf("Should be no error here.") t.Log("Unexpected error: ") t.Log(err.Error()) } err = e.SavePolicy() if err != nil { t.Errorf("Should be no error here.") t.Log("Unexpected error: ") t.Log(err.Error()) } } func TestModelNoError(t *testing.T) { e, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") e.modelPath = "hope_this_path_wont_exist" err := e.LoadModel() if err == nil { t.Errorf("Should be error here.") } else { t.Log("Test on error: ") t.Log(err.Error()) } } func TestMockAdapterErrors(t *testing.T) { adapter := fileadapter.NewAdapterMock("examples/rbac_with_domains_policy.csv") adapter.SetMockErr("mock error") e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", adapter) added, err := e.AddPolicy("admin", "domain3", "data1", "read") if added { t.Errorf("added should be false") } if err == nil { t.Errorf("Should be an error here.") } else { t.Log("Test on error: ") t.Log(err.Error()) } rules := [][]string{ {"admin", "domain4", "data1", "read"}, } added, err = e.AddPolicies(rules) if added { t.Errorf("added should be false") } if err == nil { t.Errorf("Should be an error here.") } else { t.Log("Test on error: ") t.Log(err.Error()) } removed, err2 := e.RemoveFilteredPolicy(1, "domain1", "data1") if removed { t.Errorf("removed should be false") } if err2 == nil { t.Errorf("Should be an error here.") } else { t.Log("Test on error: ") t.Log(err2.Error()) } removed, err3 := e.RemovePolicy("admin", "domain2", "data2", "read") if removed { t.Errorf("removed should be false") } if err3 == nil { t.Errorf("Should be an error here.") } else { t.Log("Test on error: ") t.Log(err3.Error()) } rules = [][]string{ {"admin", "domain1", "data1", "read"}, } removed, err = e.RemovePolicies(rules) if removed { t.Errorf("removed should be false") } if err == nil { t.Errorf("Should be an error here.") } else { t.Log("Test on error: ") t.Log(err.Error()) } added, err4 := e.AddGroupingPolicy("bob", "admin2") if added { t.Errorf("added should be false") } if err4 == nil { t.Errorf("Should be an error here.") } else { t.Log("Test on error: ") t.Log(err4.Error()) } added, err5 := e.AddNamedGroupingPolicy("g", []string{"eve", "admin2", "domain1"}) if added { t.Errorf("added should be false") } if err5 == nil { t.Errorf("Should be an error here.") } else { t.Log("Test on error: ") t.Log(err5.Error()) } added, err6 := e.AddNamedPolicy("p", []string{"admin2", "domain2", "data2", "write"}) if added { t.Errorf("added should be false") } if err6 == nil { t.Errorf("Should be an error here.") } else { t.Log("Test on error: ") t.Log(err6.Error()) } removed, err7 := e.RemoveGroupingPolicy("bob", "admin2") if removed { t.Errorf("removed should be false") } if err7 == nil { t.Errorf("Should be an error here.") } else { t.Log("Test on error: ") t.Log(err7.Error()) } removed, err8 := e.RemoveFilteredGroupingPolicy(0, "bob") if removed { t.Errorf("removed should be false") } if err8 == nil { t.Errorf("Should be an error here.") } else { t.Log("Test on error: ") t.Log(err8.Error()) } removed, err9 := e.RemoveNamedGroupingPolicy("g", []string{"alice", "admin", "domain1"}) if removed { t.Errorf("removed should be false") } if err9 == nil { t.Errorf("Should be an error here.") } else { t.Log("Test on error: ") t.Log(err9.Error()) } removed, err10 := e.RemoveFilteredNamedGroupingPolicy("g", 0, "eve") if removed { t.Errorf("removed should be false") } if err10 == nil { t.Errorf("Should be an error here.") } else { t.Log("Test on error: ") t.Log(err10.Error()) } } golang-github-casbin-casbin-2.82.0/errors/000077500000000000000000000000001456376452400203605ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/errors/rbac_errors.go000066400000000000000000000025111456376452400232110ustar00rootroot00000000000000// Copyright 2018 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package errors import "errors" // Global errors for rbac defined here var ( ErrNameNotFound = errors.New("error: name does not exist") ErrDomainParameter = errors.New("error: domain should be 1 parameter") ErrLinkNotFound = errors.New("error: link between name1 and name2 does not exist") ErrUseDomainParameter = errors.New("error: useDomain should be 1 parameter") ErrInvalidFieldValuesParameter = errors.New("fieldValues requires at least one parameter") // GetAllowedObjectConditions errors ErrObjCondition = errors.New("need to meet the prefix required by the object condition") ErrEmptyCondition = errors.New("GetAllowedObjectConditions have an empty condition") ) golang-github-casbin-casbin-2.82.0/examples/000077500000000000000000000000001456376452400206625ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/examples/abac_model.conf000066400000000000000000000002441456376452400235770ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == r.obj.Ownergolang-github-casbin-casbin-2.82.0/examples/abac_not_using_policy_model.conf000066400000000000000000000003111456376452400272360ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act, eft [policy_effect] e = some(where (p.eft == allow)) && !some(where (p.eft == deny)) [matchers] m = r.sub == r.obj.Ownergolang-github-casbin-casbin-2.82.0/examples/abac_rule_effect_policy.csv000066400000000000000000000001631456376452400262070ustar00rootroot00000000000000p, alice, /data1, read, deny p, alice, /data1, write, allow p, bob, /data2, write, deny p, bob, /data2, read, allowgolang-github-casbin-casbin-2.82.0/examples/abac_rule_model.conf000066400000000000000000000003111456376452400246210ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub_rule, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = eval(p.sub_rule) && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/abac_rule_policy.csv000066400000000000000000000001001456376452400246620ustar00rootroot00000000000000p, r.sub.Age > 18, /data1, read p, r.sub.Age < 60, /data2, writegolang-github-casbin-casbin-2.82.0/examples/basic_inverse_policy.csv000066400000000000000000000000521456376452400255670ustar00rootroot00000000000000p, alice, data1, write p, bob, data2, readgolang-github-casbin-casbin-2.82.0/examples/basic_model.conf000066400000000000000000000003021456376452400237650ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/basic_model_without_spaces.conf000066400000000000000000000002761456376452400271200ustar00rootroot00000000000000[request_definition] r = sub,obj,act [policy_definition] p = sub,obj,act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/basic_policy.csv000066400000000000000000000000521456376452400240340ustar00rootroot00000000000000p, alice, data1, read p, bob, data2, writegolang-github-casbin-casbin-2.82.0/examples/basic_with_root_model.conf000066400000000000000000000003251456376452400260700ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.act || r.sub == "root"golang-github-casbin-casbin-2.82.0/examples/basic_without_resources_model.conf000066400000000000000000000002461456376452400276510ustar00rootroot00000000000000[request_definition] r = sub, act [policy_definition] p = sub, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/basic_without_resources_policy.csv000066400000000000000000000000341456376452400277110ustar00rootroot00000000000000p, alice, read p, bob, writegolang-github-casbin-casbin-2.82.0/examples/basic_without_users_model.conf000066400000000000000000000002461456376452400270000ustar00rootroot00000000000000[request_definition] r = obj, act [policy_definition] p = obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/basic_without_users_policy.csv000066400000000000000000000000361456376452400270420ustar00rootroot00000000000000p, data1, read p, data2, writegolang-github-casbin-casbin-2.82.0/examples/comment_model.conf000066400000000000000000000003761456376452400243610ustar00rootroot00000000000000[request_definition] r = sub, obj, act ; Request definition [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) # This is policy effect. # Matchers [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.act golang-github-casbin-casbin-2.82.0/examples/error/000077500000000000000000000000001456376452400220135ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/examples/error/error_model.conf000066400000000000000000000003011456376452400251650ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/error/error_policy.csv000066400000000000000000000000471456376452400252410ustar00rootroot00000000000000p, alice, data1, read bob, data2, writegolang-github-casbin-casbin-2.82.0/examples/eval_operator_model.conf000066400000000000000000000003431456376452400255530ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub_rule, obj_rule, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = eval(p.sub_rule) && eval(p.obj_rule) && (p.act == '*' || r.act == p.act) golang-github-casbin-casbin-2.82.0/examples/eval_operator_policy.csv000066400000000000000000000000651456376452400256210ustar00rootroot00000000000000p, r.sub == 'admin' || false, r.obj == 'users', writegolang-github-casbin-casbin-2.82.0/examples/glob_model.conf000066400000000000000000000003131456376452400236310ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && globMatch(r.obj, p.obj) && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/glob_policy.csv000066400000000000000000000001131456376452400236740ustar00rootroot00000000000000p, u1, /foo/*, read p, u2, /foo*, read p, u3, /*/foo/*, read p, u4, *, readgolang-github-casbin-casbin-2.82.0/examples/ipmatch_model.conf000066400000000000000000000003111456376452400243310ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = ipMatch(r.sub, p.sub) && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/ipmatch_policy.csv000066400000000000000000000000731456376452400244030ustar00rootroot00000000000000p, 192.168.2.0/24, data1, read p, 10.0.0.0/16, data2, writegolang-github-casbin-casbin-2.82.0/examples/keyget2_model.conf000066400000000000000000000003631456376452400242650ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && keyGet2(r.obj, p.obj, 'resource') in ('age', 'name') && regexMatch(r.act, p.act) golang-github-casbin-casbin-2.82.0/examples/keyget_model.conf000066400000000000000000000003701456376452400242010ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && (r.obj == p.obj || keyGet(r.obj, p.obj) in ('age','name')) && regexMatch(r.act, p.act)golang-github-casbin-casbin-2.82.0/examples/keymatch2_model.conf000066400000000000000000000003251456376452400246000ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)golang-github-casbin-casbin-2.82.0/examples/keymatch2_policy.csv000066400000000000000000000001211456376452400246370ustar00rootroot00000000000000p, alice, /alice_data/:resource, GET p, alice, /alice_data2/:id/using/:resId, GETgolang-github-casbin-casbin-2.82.0/examples/keymatch_custom_model.conf000066400000000000000000000003321456376452400261060ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && keyMatchCustom(r.obj, p.obj) && regexMatch(r.act, p.act)golang-github-casbin-casbin-2.82.0/examples/keymatch_model.conf000066400000000000000000000003241456376452400245150ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)golang-github-casbin-casbin-2.82.0/examples/keymatch_policy.csv000066400000000000000000000002451456376452400245640ustar00rootroot00000000000000p, alice, /alice_data/*, GET p, alice, /alice_data/resource1, POST p, bob, /alice_data/resource2, GET p, bob, /bob_data/*, POST p, cathy, /cathy_data, (GET)|(POST)golang-github-casbin-casbin-2.82.0/examples/keymatch_with_rbac_in_domain.conf000066400000000000000000000004401456376452400273730ustar00rootroot00000000000000[request_definition] r = sub, dom, obj, act [policy_definition] p = sub, dom, obj, act [role_definition] g = _, _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub, r.dom) && keyMatch(r.dom, p.dom) && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act) golang-github-casbin-casbin-2.82.0/examples/keymatch_with_rbac_in_domain.csv000066400000000000000000000002461456376452400272450ustar00rootroot00000000000000g, can_manage, can_use, * p, can_manage, engines/*, *, (pause)|(resume) p, can_use, engines/*, *, (attach)|(detach) g, Username==test2, can_manage, engines/engine1 golang-github-casbin-casbin-2.82.0/examples/multiple_policy_definitions_model.conf000066400000000000000000000005321456376452400305160ustar00rootroot00000000000000[request_definition] r = sub, obj, act r2 = sub, obj, act [policy_definition] p = sub, obj, act p2= sub_rule, obj, act, eft [role_definition] g = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] #RABC m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act #ABAC m2 = eval(p2.sub_rule) && r2.obj == p2.obj && r2.act == p2.act golang-github-casbin-casbin-2.82.0/examples/multiple_policy_definitions_policy.csv000066400000000000000000000002521456376452400305620ustar00rootroot00000000000000p, data2_admin, data2, read p2, r2.sub.Age > 18 && r2.sub.Age < 60, /data1, read, allow p2, r2.sub.Age > 60 && r2.sub.Age < 100, /data1, read, deny g, alice, data2_admingolang-github-casbin-casbin-2.82.0/examples/object_conditions_model.conf000066400000000000000000000003461456376452400264130ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, sub_rule, act [role_definition] g = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && eval(p.sub_rule) && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/object_conditions_policy.csv000066400000000000000000000001711456376452400264540ustar00rootroot00000000000000p, alice, r.obj.price < 25, read p, admin, r.obj.category_id = 2, read p, bob, r.obj.author = bob, write g, alice, admingolang-github-casbin-casbin-2.82.0/examples/performance/000077500000000000000000000000001456376452400231635ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/examples/performance/rbac_with_pattern_large_scale_model.conf000066400000000000000000000003741456376452400332360ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub, r.obj) && keyMatch4(r.obj, p.obj) && regexMatch(r.act, p.act)golang-github-casbin-casbin-2.82.0/examples/performance/rbac_with_pattern_large_scale_policy.csv000066400000000000000000005217151456376452400333120ustar00rootroot00000000000000# 132 policies / 3000 grouping policies / 300 subjuects / 6 roles # Policy - staff001 p, staff001, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1001 p, staff001, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1002 p, staff001, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1003 p, staff001, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1004 p, staff001, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1005 p, staff001, /orgs/{orgID}/sites/{siteID}, App002.*.Action2001 p, staff001, /orgs/{orgID}/sites/{siteID}, App002.*.Action2002 p, staff001, /orgs/{orgID}/sites/{siteID}, App002.*.Action2003 p, staff001, /orgs/{orgID}/sites/{siteID}, App002.*.Action2004 p, staff001, /orgs/{orgID}/sites/{siteID}, App002.*.Action2005 p, staff001, /orgs/{orgID}/sites, App001.Module002.Action1006 p, staff001, /orgs/{orgID}/sites, App001.Module002.Action1007 p, staff001, /orgs/{orgID}/sites, App001.Module002.Action1008 p, staff001, /orgs/{orgID}/sites, App001.Module002.Action1009 p, staff001, /orgs/{orgID}/sites, App001.Module002.Action1010 p, staff001, /orgs/{orgID}/sites, App003.*.Action3001 p, staff001, /orgs/{orgID}/sites, App003.*.Action3002 p, staff001, /orgs/{orgID}/sites, App003.*.Action3003 p, staff001, /orgs/{orgID}/sites, App003.*.Action3004 p, staff001, /orgs/{orgID}/sites, App003.*.Action3005 p, staff001, /orgs/{orgID}, App004.* p, staff001, /orgs, App005.* # Policy - staff002 p, staff002, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1001 p, staff002, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1002 p, staff002, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1003 p, staff002, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1004 p, staff002, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1005 p, staff002, /orgs/{orgID}/sites/{siteID}, App002.*.Action2001 p, staff002, /orgs/{orgID}/sites/{siteID}, App002.*.Action2002 p, staff002, /orgs/{orgID}/sites/{siteID}, App002.*.Action2003 p, staff002, /orgs/{orgID}/sites/{siteID}, App002.*.Action2004 p, staff002, /orgs/{orgID}/sites/{siteID}, App002.*.Action2005 p, staff002, /orgs/{orgID}/sites, App001.Module002.Action1006 p, staff002, /orgs/{orgID}/sites, App001.Module002.Action1007 p, staff002, /orgs/{orgID}/sites, App001.Module002.Action1008 p, staff002, /orgs/{orgID}/sites, App001.Module002.Action1009 p, staff002, /orgs/{orgID}/sites, App001.Module002.Action1010 p, staff002, /orgs/{orgID}/sites, App003.*.Action3001 p, staff002, /orgs/{orgID}/sites, App003.*.Action3002 p, staff002, /orgs/{orgID}/sites, App003.*.Action3003 p, staff002, /orgs/{orgID}/sites, App003.*.Action3004 p, staff002, /orgs/{orgID}/sites, App003.*.Action3005 p, staff002, /orgs/{orgID}, App004.* p, staff002, /orgs, App005.* # Policy - manager001 p, manager001, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1001 p, manager001, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1002 p, manager001, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1003 p, manager001, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1004 p, manager001, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1005 p, manager001, /orgs/{orgID}/sites/{siteID}, App002.*.Action2001 p, manager001, /orgs/{orgID}/sites/{siteID}, App002.*.Action2002 p, manager001, /orgs/{orgID}/sites/{siteID}, App002.*.Action2003 p, manager001, /orgs/{orgID}/sites/{siteID}, App002.*.Action2004 p, manager001, /orgs/{orgID}/sites/{siteID}, App002.*.Action2005 p, manager001, /orgs/{orgID}/sites, App001.Module002.Action1006 p, manager001, /orgs/{orgID}/sites, App001.Module002.Action1007 p, manager001, /orgs/{orgID}/sites, App001.Module002.Action1008 p, manager001, /orgs/{orgID}/sites, App001.Module002.Action1009 p, manager001, /orgs/{orgID}/sites, App001.Module002.Action1010 p, manager001, /orgs/{orgID}/sites, App003.*.Action3001 p, manager001, /orgs/{orgID}/sites, App003.*.Action3002 p, manager001, /orgs/{orgID}/sites, App003.*.Action3003 p, manager001, /orgs/{orgID}/sites, App003.*.Action3004 p, manager001, /orgs/{orgID}/sites, App003.*.Action3005 p, manager001, /orgs/{orgID}, App004.* p, manager001, /orgs, App005.* # Policy - manager002 p, manager002, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1001 p, manager002, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1002 p, manager002, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1003 p, manager002, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1004 p, manager002, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1005 p, manager002, /orgs/{orgID}/sites/{siteID}, App002.*.Action2001 p, manager002, /orgs/{orgID}/sites/{siteID}, App002.*.Action2002 p, manager002, /orgs/{orgID}/sites/{siteID}, App002.*.Action2003 p, manager002, /orgs/{orgID}/sites/{siteID}, App002.*.Action2004 p, manager002, /orgs/{orgID}/sites/{siteID}, App002.*.Action2005 p, manager002, /orgs/{orgID}/sites, App001.Module002.Action1006 p, manager002, /orgs/{orgID}/sites, App001.Module002.Action1007 p, manager002, /orgs/{orgID}/sites, App001.Module002.Action1008 p, manager002, /orgs/{orgID}/sites, App001.Module002.Action1009 p, manager002, /orgs/{orgID}/sites, App001.Module002.Action1010 p, manager002, /orgs/{orgID}/sites, App003.*.Action3001 p, manager002, /orgs/{orgID}/sites, App003.*.Action3002 p, manager002, /orgs/{orgID}/sites, App003.*.Action3003 p, manager002, /orgs/{orgID}/sites, App003.*.Action3004 p, manager002, /orgs/{orgID}/sites, App003.*.Action3005 p, manager002, /orgs/{orgID}, App004.* p, manager002, /orgs, App005.* # Policy - customer001 p, customer001, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1001 p, customer001, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1002 p, customer001, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1003 p, customer001, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1004 p, customer001, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1005 p, customer001, /orgs/{orgID}/sites/{siteID}, App002.*.Action2001 p, customer001, /orgs/{orgID}/sites/{siteID}, App002.*.Action2002 p, customer001, /orgs/{orgID}/sites/{siteID}, App002.*.Action2003 p, customer001, /orgs/{orgID}/sites/{siteID}, App002.*.Action2004 p, customer001, /orgs/{orgID}/sites/{siteID}, App002.*.Action2005 p, customer001, /orgs/{orgID}/sites, App001.Module002.Action1006 p, customer001, /orgs/{orgID}/sites, App001.Module002.Action1007 p, customer001, /orgs/{orgID}/sites, App001.Module002.Action1008 p, customer001, /orgs/{orgID}/sites, App001.Module002.Action1009 p, customer001, /orgs/{orgID}/sites, App001.Module002.Action1010 p, customer001, /orgs/{orgID}/sites, App003.*.Action3001 p, customer001, /orgs/{orgID}/sites, App003.*.Action3002 p, customer001, /orgs/{orgID}/sites, App003.*.Action3003 p, customer001, /orgs/{orgID}/sites, App003.*.Action3004 p, customer001, /orgs/{orgID}/sites, App003.*.Action3005 p, customer001, /orgs/{orgID}, App004.* p, customer001, /orgs, App005.* # Policy - customer002 p, customer002, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1001 p, customer002, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1002 p, customer002, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1003 p, customer002, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1004 p, customer002, /orgs/{orgID}/sites/{siteID}, App001.Module001.Action1005 p, customer002, /orgs/{orgID}/sites/{siteID}, App002.*.Action2001 p, customer002, /orgs/{orgID}/sites/{siteID}, App002.*.Action2002 p, customer002, /orgs/{orgID}/sites/{siteID}, App002.*.Action2003 p, customer002, /orgs/{orgID}/sites/{siteID}, App002.*.Action2004 p, customer002, /orgs/{orgID}/sites/{siteID}, App002.*.Action2005 p, customer002, /orgs/{orgID}/sites, App001.Module002.Action1006 p, customer002, /orgs/{orgID}/sites, App001.Module002.Action1007 p, customer002, /orgs/{orgID}/sites, App001.Module002.Action1008 p, customer002, /orgs/{orgID}/sites, App001.Module002.Action1009 p, customer002, /orgs/{orgID}/sites, App001.Module002.Action1010 p, customer002, /orgs/{orgID}/sites, App003.*.Action3001 p, customer002, /orgs/{orgID}/sites, App003.*.Action3002 p, customer002, /orgs/{orgID}/sites, App003.*.Action3003 p, customer002, /orgs/{orgID}/sites, App003.*.Action3004 p, customer002, /orgs/{orgID}/sites, App003.*.Action3005 p, customer002, /orgs/{orgID}, App004.* p, customer002, /orgs, App005.* # Group - staff001, / org1 g, staffUser1001, staff001, /orgs/1/sites/site001 g, staffUser1001, staff001, /orgs/1/sites/site002 g, staffUser1001, staff001, /orgs/1/sites/site003 g, staffUser1001, staff001, /orgs/1/sites/site004 g, staffUser1001, staff001, /orgs/1/sites/site005 g, staffUser1001, staff001, /orgs/1/sites/site001 g, staffUser1001, staff001, /orgs/1/sites/site002 g, staffUser1001, staff001, /orgs/1/sites/site003 g, staffUser1001, staff001, /orgs/1/sites/site004 g, staffUser1001, staff001, /orgs/1/sites/site005 g, staffUser1003, staff001, /orgs/1/sites/site001 g, staffUser1003, staff001, /orgs/1/sites/site002 g, staffUser1003, staff001, /orgs/1/sites/site003 g, staffUser1003, staff001, /orgs/1/sites/site004 g, staffUser1003, staff001, /orgs/1/sites/site005 g, staffUser1004, staff001, /orgs/1/sites/site001 g, staffUser1004, staff001, /orgs/1/sites/site002 g, staffUser1004, staff001, /orgs/1/sites/site003 g, staffUser1004, staff001, /orgs/1/sites/site004 g, staffUser1004, staff001, /orgs/1/sites/site005 g, staffUser1005, staff001, /orgs/1/sites/site001 g, staffUser1005, staff001, /orgs/1/sites/site002 g, staffUser1005, staff001, /orgs/1/sites/site003 g, staffUser1005, staff001, /orgs/1/sites/site004 g, staffUser1005, staff001, /orgs/1/sites/site005 g, staffUser1006, staff001, /orgs/1/sites/site001 g, staffUser1006, staff001, /orgs/1/sites/site002 g, staffUser1006, staff001, /orgs/1/sites/site003 g, staffUser1006, staff001, /orgs/1/sites/site004 g, staffUser1006, staff001, /orgs/1/sites/site005 g, staffUser1007, staff001, /orgs/1/sites/site001 g, staffUser1007, staff001, /orgs/1/sites/site002 g, staffUser1007, staff001, /orgs/1/sites/site003 g, staffUser1007, staff001, /orgs/1/sites/site004 g, staffUser1007, staff001, /orgs/1/sites/site005 g, staffUser1008, staff001, /orgs/1/sites/site001 g, staffUser1008, staff001, /orgs/1/sites/site002 g, staffUser1008, staff001, /orgs/1/sites/site003 g, staffUser1008, staff001, /orgs/1/sites/site004 g, staffUser1008, staff001, /orgs/1/sites/site005 g, staffUser1009, staff001, /orgs/1/sites/site001 g, staffUser1009, staff001, /orgs/1/sites/site002 g, staffUser1009, staff001, /orgs/1/sites/site003 g, staffUser1009, staff001, /orgs/1/sites/site004 g, staffUser1009, staff001, /orgs/1/sites/site005 g, staffUser1010, staff001, /orgs/1/sites/site001 g, staffUser1010, staff001, /orgs/1/sites/site002 g, staffUser1010, staff001, /orgs/1/sites/site003 g, staffUser1010, staff001, /orgs/1/sites/site004 g, staffUser1010, staff001, /orgs/1/sites/site005 g, staffUser1011, staff001, /orgs/1/sites/site001 g, staffUser1011, staff001, /orgs/1/sites/site002 g, staffUser1011, staff001, /orgs/1/sites/site003 g, staffUser1011, staff001, /orgs/1/sites/site004 g, staffUser1011, staff001, /orgs/1/sites/site005 g, staffUser1012, staff001, /orgs/1/sites/site001 g, staffUser1012, staff001, /orgs/1/sites/site002 g, staffUser1012, staff001, /orgs/1/sites/site003 g, staffUser1012, staff001, /orgs/1/sites/site004 g, staffUser1012, staff001, /orgs/1/sites/site005 g, staffUser1013, staff001, /orgs/1/sites/site001 g, staffUser1013, staff001, /orgs/1/sites/site002 g, staffUser1013, staff001, /orgs/1/sites/site003 g, staffUser1013, staff001, /orgs/1/sites/site004 g, staffUser1013, staff001, /orgs/1/sites/site005 g, staffUser1014, staff001, /orgs/1/sites/site001 g, staffUser1014, staff001, /orgs/1/sites/site002 g, staffUser1014, staff001, /orgs/1/sites/site003 g, staffUser1014, staff001, /orgs/1/sites/site004 g, staffUser1014, staff001, /orgs/1/sites/site005 g, staffUser1015, staff001, /orgs/1/sites/site001 g, staffUser1015, staff001, /orgs/1/sites/site002 g, staffUser1015, staff001, /orgs/1/sites/site003 g, staffUser1015, staff001, /orgs/1/sites/site004 g, staffUser1015, staff001, /orgs/1/sites/site005 g, staffUser1016, staff001, /orgs/1/sites/site001 g, staffUser1016, staff001, /orgs/1/sites/site002 g, staffUser1016, staff001, /orgs/1/sites/site003 g, staffUser1016, staff001, /orgs/1/sites/site004 g, staffUser1016, staff001, /orgs/1/sites/site005 g, staffUser1017, staff001, /orgs/1/sites/site001 g, staffUser1017, staff001, /orgs/1/sites/site002 g, staffUser1017, staff001, /orgs/1/sites/site003 g, staffUser1017, staff001, /orgs/1/sites/site004 g, staffUser1017, staff001, /orgs/1/sites/site005 g, staffUser1018, staff001, /orgs/1/sites/site001 g, staffUser1018, staff001, /orgs/1/sites/site002 g, staffUser1018, staff001, /orgs/1/sites/site003 g, staffUser1018, staff001, /orgs/1/sites/site004 g, staffUser1018, staff001, /orgs/1/sites/site005 g, staffUser1019, staff001, /orgs/1/sites/site001 g, staffUser1019, staff001, /orgs/1/sites/site002 g, staffUser1019, staff001, /orgs/1/sites/site003 g, staffUser1019, staff001, /orgs/1/sites/site004 g, staffUser1019, staff001, /orgs/1/sites/site005 g, staffUser1020, staff001, /orgs/1/sites/site001 g, staffUser1020, staff001, /orgs/1/sites/site002 g, staffUser1020, staff001, /orgs/1/sites/site003 g, staffUser1020, staff001, /orgs/1/sites/site004 g, staffUser1020, staff001, /orgs/1/sites/site005 g, staffUser1021, staff001, /orgs/1/sites/site001 g, staffUser1021, staff001, /orgs/1/sites/site002 g, staffUser1021, staff001, /orgs/1/sites/site003 g, staffUser1021, staff001, /orgs/1/sites/site004 g, staffUser1021, staff001, /orgs/1/sites/site005 g, staffUser1022, staff001, /orgs/1/sites/site001 g, staffUser1022, staff001, /orgs/1/sites/site002 g, staffUser1022, staff001, /orgs/1/sites/site003 g, staffUser1022, staff001, /orgs/1/sites/site004 g, staffUser1022, staff001, /orgs/1/sites/site005 g, staffUser1023, staff001, /orgs/1/sites/site001 g, staffUser1023, staff001, /orgs/1/sites/site002 g, staffUser1023, staff001, /orgs/1/sites/site003 g, staffUser1023, staff001, /orgs/1/sites/site004 g, staffUser1023, staff001, /orgs/1/sites/site005 g, staffUser1024, staff001, /orgs/1/sites/site001 g, staffUser1024, staff001, /orgs/1/sites/site002 g, staffUser1024, staff001, /orgs/1/sites/site003 g, staffUser1024, staff001, /orgs/1/sites/site004 g, staffUser1024, staff001, /orgs/1/sites/site005 g, staffUser1025, staff001, /orgs/1/sites/site001 g, staffUser1025, staff001, /orgs/1/sites/site002 g, staffUser1025, staff001, /orgs/1/sites/site003 g, staffUser1025, staff001, /orgs/1/sites/site004 g, staffUser1025, staff001, /orgs/1/sites/site005 g, staffUser1026, staff001, /orgs/1/sites/site001 g, staffUser1026, staff001, /orgs/1/sites/site002 g, staffUser1026, staff001, /orgs/1/sites/site003 g, staffUser1026, staff001, /orgs/1/sites/site004 g, staffUser1026, staff001, /orgs/1/sites/site005 g, staffUser1027, staff001, /orgs/1/sites/site001 g, staffUser1027, staff001, /orgs/1/sites/site002 g, staffUser1027, staff001, /orgs/1/sites/site003 g, staffUser1027, staff001, /orgs/1/sites/site004 g, staffUser1027, staff001, /orgs/1/sites/site005 g, staffUser1028, staff001, /orgs/1/sites/site001 g, staffUser1028, staff001, /orgs/1/sites/site002 g, staffUser1028, staff001, /orgs/1/sites/site003 g, staffUser1028, staff001, /orgs/1/sites/site004 g, staffUser1028, staff001, /orgs/1/sites/site005 g, staffUser1029, staff001, /orgs/1/sites/site001 g, staffUser1029, staff001, /orgs/1/sites/site002 g, staffUser1029, staff001, /orgs/1/sites/site003 g, staffUser1029, staff001, /orgs/1/sites/site004 g, staffUser1029, staff001, /orgs/1/sites/site005 g, staffUser1030, staff001, /orgs/1/sites/site001 g, staffUser1030, staff001, /orgs/1/sites/site002 g, staffUser1030, staff001, /orgs/1/sites/site003 g, staffUser1030, staff001, /orgs/1/sites/site004 g, staffUser1030, staff001, /orgs/1/sites/site005 g, staffUser1031, staff001, /orgs/1/sites/site001 g, staffUser1031, staff001, /orgs/1/sites/site002 g, staffUser1031, staff001, /orgs/1/sites/site003 g, staffUser1031, staff001, /orgs/1/sites/site004 g, staffUser1031, staff001, /orgs/1/sites/site005 g, staffUser1032, staff001, /orgs/1/sites/site001 g, staffUser1032, staff001, /orgs/1/sites/site002 g, staffUser1032, staff001, /orgs/1/sites/site003 g, staffUser1032, staff001, /orgs/1/sites/site004 g, staffUser1032, staff001, /orgs/1/sites/site005 g, staffUser1033, staff001, /orgs/1/sites/site001 g, staffUser1033, staff001, /orgs/1/sites/site002 g, staffUser1033, staff001, /orgs/1/sites/site003 g, staffUser1033, staff001, /orgs/1/sites/site004 g, staffUser1033, staff001, /orgs/1/sites/site005 g, staffUser1034, staff001, /orgs/1/sites/site001 g, staffUser1034, staff001, /orgs/1/sites/site002 g, staffUser1034, staff001, /orgs/1/sites/site003 g, staffUser1034, staff001, /orgs/1/sites/site004 g, staffUser1034, staff001, /orgs/1/sites/site005 g, staffUser1035, staff001, /orgs/1/sites/site001 g, staffUser1035, staff001, /orgs/1/sites/site002 g, staffUser1035, staff001, /orgs/1/sites/site003 g, staffUser1035, staff001, /orgs/1/sites/site004 g, staffUser1035, staff001, /orgs/1/sites/site005 g, staffUser1036, staff001, /orgs/1/sites/site001 g, staffUser1036, staff001, /orgs/1/sites/site002 g, staffUser1036, staff001, /orgs/1/sites/site003 g, staffUser1036, staff001, /orgs/1/sites/site004 g, staffUser1036, staff001, /orgs/1/sites/site005 g, staffUser1037, staff001, /orgs/1/sites/site001 g, staffUser1037, staff001, /orgs/1/sites/site002 g, staffUser1037, staff001, /orgs/1/sites/site003 g, staffUser1037, staff001, /orgs/1/sites/site004 g, staffUser1037, staff001, /orgs/1/sites/site005 g, staffUser1038, staff001, /orgs/1/sites/site001 g, staffUser1038, staff001, /orgs/1/sites/site002 g, staffUser1038, staff001, /orgs/1/sites/site003 g, staffUser1038, staff001, /orgs/1/sites/site004 g, staffUser1038, staff001, /orgs/1/sites/site005 g, staffUser1039, staff001, /orgs/1/sites/site001 g, staffUser1039, staff001, /orgs/1/sites/site002 g, staffUser1039, staff001, /orgs/1/sites/site003 g, staffUser1039, staff001, /orgs/1/sites/site004 g, staffUser1039, staff001, /orgs/1/sites/site005 g, staffUser1040, staff001, /orgs/1/sites/site001 g, staffUser1040, staff001, /orgs/1/sites/site002 g, staffUser1040, staff001, /orgs/1/sites/site003 g, staffUser1040, staff001, /orgs/1/sites/site004 g, staffUser1040, staff001, /orgs/1/sites/site005 g, staffUser1041, staff001, /orgs/1/sites/site001 g, staffUser1041, staff001, /orgs/1/sites/site002 g, staffUser1041, staff001, /orgs/1/sites/site003 g, staffUser1041, staff001, /orgs/1/sites/site004 g, staffUser1041, staff001, /orgs/1/sites/site005 g, staffUser1042, staff001, /orgs/1/sites/site001 g, staffUser1042, staff001, /orgs/1/sites/site002 g, staffUser1042, staff001, /orgs/1/sites/site003 g, staffUser1042, staff001, /orgs/1/sites/site004 g, staffUser1042, staff001, /orgs/1/sites/site005 g, staffUser1043, staff001, /orgs/1/sites/site001 g, staffUser1043, staff001, /orgs/1/sites/site002 g, staffUser1043, staff001, /orgs/1/sites/site003 g, staffUser1043, staff001, /orgs/1/sites/site004 g, staffUser1043, staff001, /orgs/1/sites/site005 g, staffUser1044, staff001, /orgs/1/sites/site001 g, staffUser1044, staff001, /orgs/1/sites/site002 g, staffUser1044, staff001, /orgs/1/sites/site003 g, staffUser1044, staff001, /orgs/1/sites/site004 g, staffUser1044, staff001, /orgs/1/sites/site005 g, staffUser1045, staff001, /orgs/1/sites/site001 g, staffUser1045, staff001, /orgs/1/sites/site002 g, staffUser1045, staff001, /orgs/1/sites/site003 g, staffUser1045, staff001, /orgs/1/sites/site004 g, staffUser1045, staff001, /orgs/1/sites/site005 g, staffUser1046, staff001, /orgs/1/sites/site001 g, staffUser1046, staff001, /orgs/1/sites/site002 g, staffUser1046, staff001, /orgs/1/sites/site003 g, staffUser1046, staff001, /orgs/1/sites/site004 g, staffUser1046, staff001, /orgs/1/sites/site005 g, staffUser1047, staff001, /orgs/1/sites/site001 g, staffUser1047, staff001, /orgs/1/sites/site002 g, staffUser1047, staff001, /orgs/1/sites/site003 g, staffUser1047, staff001, /orgs/1/sites/site004 g, staffUser1047, staff001, /orgs/1/sites/site005 g, staffUser1048, staff001, /orgs/1/sites/site001 g, staffUser1048, staff001, /orgs/1/sites/site002 g, staffUser1048, staff001, /orgs/1/sites/site003 g, staffUser1048, staff001, /orgs/1/sites/site004 g, staffUser1048, staff001, /orgs/1/sites/site005 g, staffUser1049, staff001, /orgs/1/sites/site001 g, staffUser1049, staff001, /orgs/1/sites/site002 g, staffUser1049, staff001, /orgs/1/sites/site003 g, staffUser1049, staff001, /orgs/1/sites/site004 g, staffUser1049, staff001, /orgs/1/sites/site005 g, staffUser1050, staff001, /orgs/1/sites/site001 g, staffUser1050, staff001, /orgs/1/sites/site002 g, staffUser1050, staff001, /orgs/1/sites/site003 g, staffUser1050, staff001, /orgs/1/sites/site004 g, staffUser1050, staff001, /orgs/1/sites/site005 # Group - staff001, / org1 g, staffUser2001, staff001, /orgs/1/sites/site001 g, staffUser2001, staff001, /orgs/1/sites/site002 g, staffUser2001, staff001, /orgs/1/sites/site003 g, staffUser2001, staff001, /orgs/1/sites/site004 g, staffUser2001, staff001, /orgs/1/sites/site005 g, staffUser2001, staff001, /orgs/1/sites/site001 g, staffUser2001, staff001, /orgs/1/sites/site002 g, staffUser2001, staff001, /orgs/1/sites/site003 g, staffUser2001, staff001, /orgs/1/sites/site004 g, staffUser2001, staff001, /orgs/1/sites/site005 g, staffUser2003, staff001, /orgs/1/sites/site001 g, staffUser2003, staff001, /orgs/1/sites/site002 g, staffUser2003, staff001, /orgs/1/sites/site003 g, staffUser2003, staff001, /orgs/1/sites/site004 g, staffUser2003, staff001, /orgs/1/sites/site005 g, staffUser2004, staff001, /orgs/1/sites/site001 g, staffUser2004, staff001, /orgs/1/sites/site002 g, staffUser2004, staff001, /orgs/1/sites/site003 g, staffUser2004, staff001, /orgs/1/sites/site004 g, staffUser2004, staff001, /orgs/1/sites/site005 g, staffUser2005, staff001, /orgs/1/sites/site001 g, staffUser2005, staff001, /orgs/1/sites/site002 g, staffUser2005, staff001, /orgs/1/sites/site003 g, staffUser2005, staff001, /orgs/1/sites/site004 g, staffUser2005, staff001, /orgs/1/sites/site005 g, staffUser2006, staff001, /orgs/1/sites/site001 g, staffUser2006, staff001, /orgs/1/sites/site002 g, staffUser2006, staff001, /orgs/1/sites/site003 g, staffUser2006, staff001, /orgs/1/sites/site004 g, staffUser2006, staff001, /orgs/1/sites/site005 g, staffUser2007, staff001, /orgs/1/sites/site001 g, staffUser2007, staff001, /orgs/1/sites/site002 g, staffUser2007, staff001, /orgs/1/sites/site003 g, staffUser2007, staff001, /orgs/1/sites/site004 g, staffUser2007, staff001, /orgs/1/sites/site005 g, staffUser2008, staff001, /orgs/1/sites/site001 g, staffUser2008, staff001, /orgs/1/sites/site002 g, staffUser2008, staff001, /orgs/1/sites/site003 g, staffUser2008, staff001, /orgs/1/sites/site004 g, staffUser2008, staff001, /orgs/1/sites/site005 g, staffUser2009, staff001, /orgs/1/sites/site001 g, staffUser2009, staff001, /orgs/1/sites/site002 g, staffUser2009, staff001, /orgs/1/sites/site003 g, staffUser2009, staff001, /orgs/1/sites/site004 g, staffUser2009, staff001, /orgs/1/sites/site005 g, staffUser2010, staff001, /orgs/1/sites/site001 g, staffUser2010, staff001, /orgs/1/sites/site002 g, staffUser2010, staff001, /orgs/1/sites/site003 g, staffUser2010, staff001, /orgs/1/sites/site004 g, staffUser2010, staff001, /orgs/1/sites/site005 g, staffUser2011, staff001, /orgs/1/sites/site001 g, staffUser2011, staff001, /orgs/1/sites/site002 g, staffUser2011, staff001, /orgs/1/sites/site003 g, staffUser2011, staff001, /orgs/1/sites/site004 g, staffUser2011, staff001, /orgs/1/sites/site005 g, staffUser2012, staff001, /orgs/1/sites/site001 g, staffUser2012, staff001, /orgs/1/sites/site002 g, staffUser2012, staff001, /orgs/1/sites/site003 g, staffUser2012, staff001, /orgs/1/sites/site004 g, staffUser2012, staff001, /orgs/1/sites/site005 g, staffUser2013, staff001, /orgs/1/sites/site001 g, staffUser2013, staff001, /orgs/1/sites/site002 g, staffUser2013, staff001, /orgs/1/sites/site003 g, staffUser2013, staff001, /orgs/1/sites/site004 g, staffUser2013, staff001, /orgs/1/sites/site005 g, staffUser2014, staff001, /orgs/1/sites/site001 g, staffUser2014, staff001, /orgs/1/sites/site002 g, staffUser2014, staff001, /orgs/1/sites/site003 g, staffUser2014, staff001, /orgs/1/sites/site004 g, staffUser2014, staff001, /orgs/1/sites/site005 g, staffUser2015, staff001, /orgs/1/sites/site001 g, staffUser2015, staff001, /orgs/1/sites/site002 g, staffUser2015, staff001, /orgs/1/sites/site003 g, staffUser2015, staff001, /orgs/1/sites/site004 g, staffUser2015, staff001, /orgs/1/sites/site005 g, staffUser2016, staff001, /orgs/1/sites/site001 g, staffUser2016, staff001, /orgs/1/sites/site002 g, staffUser2016, staff001, /orgs/1/sites/site003 g, staffUser2016, staff001, /orgs/1/sites/site004 g, staffUser2016, staff001, /orgs/1/sites/site005 g, staffUser2017, staff001, /orgs/1/sites/site001 g, staffUser2017, staff001, /orgs/1/sites/site002 g, staffUser2017, staff001, /orgs/1/sites/site003 g, staffUser2017, staff001, /orgs/1/sites/site004 g, staffUser2017, staff001, /orgs/1/sites/site005 g, staffUser2018, staff001, /orgs/1/sites/site001 g, staffUser2018, staff001, /orgs/1/sites/site002 g, staffUser2018, staff001, /orgs/1/sites/site003 g, staffUser2018, staff001, /orgs/1/sites/site004 g, staffUser2018, staff001, /orgs/1/sites/site005 g, staffUser2019, staff001, /orgs/1/sites/site001 g, staffUser2019, staff001, /orgs/1/sites/site002 g, staffUser2019, staff001, /orgs/1/sites/site003 g, staffUser2019, staff001, /orgs/1/sites/site004 g, staffUser2019, staff001, /orgs/1/sites/site005 g, staffUser2020, staff001, /orgs/1/sites/site001 g, staffUser2020, staff001, /orgs/1/sites/site002 g, staffUser2020, staff001, /orgs/1/sites/site003 g, staffUser2020, staff001, /orgs/1/sites/site004 g, staffUser2020, staff001, /orgs/1/sites/site005 g, staffUser2021, staff001, /orgs/1/sites/site001 g, staffUser2021, staff001, /orgs/1/sites/site002 g, staffUser2021, staff001, /orgs/1/sites/site003 g, staffUser2021, staff001, /orgs/1/sites/site004 g, staffUser2021, staff001, /orgs/1/sites/site005 g, staffUser2022, staff001, /orgs/1/sites/site001 g, staffUser2022, staff001, /orgs/1/sites/site002 g, staffUser2022, staff001, /orgs/1/sites/site003 g, staffUser2022, staff001, /orgs/1/sites/site004 g, staffUser2022, staff001, /orgs/1/sites/site005 g, staffUser2023, staff001, /orgs/1/sites/site001 g, staffUser2023, staff001, /orgs/1/sites/site002 g, staffUser2023, staff001, /orgs/1/sites/site003 g, staffUser2023, staff001, /orgs/1/sites/site004 g, staffUser2023, staff001, /orgs/1/sites/site005 g, staffUser2024, staff001, /orgs/1/sites/site001 g, staffUser2024, staff001, /orgs/1/sites/site002 g, staffUser2024, staff001, /orgs/1/sites/site003 g, staffUser2024, staff001, /orgs/1/sites/site004 g, staffUser2024, staff001, /orgs/1/sites/site005 g, staffUser2025, staff001, /orgs/1/sites/site001 g, staffUser2025, staff001, /orgs/1/sites/site002 g, staffUser2025, staff001, /orgs/1/sites/site003 g, staffUser2025, staff001, /orgs/1/sites/site004 g, staffUser2025, staff001, /orgs/1/sites/site005 g, staffUser2026, staff001, /orgs/1/sites/site001 g, staffUser2026, staff001, /orgs/1/sites/site002 g, staffUser2026, staff001, /orgs/1/sites/site003 g, staffUser2026, staff001, /orgs/1/sites/site004 g, staffUser2026, staff001, /orgs/1/sites/site005 g, staffUser2027, staff001, /orgs/1/sites/site001 g, staffUser2027, staff001, /orgs/1/sites/site002 g, staffUser2027, staff001, /orgs/1/sites/site003 g, staffUser2027, staff001, /orgs/1/sites/site004 g, staffUser2027, staff001, /orgs/1/sites/site005 g, staffUser2028, staff001, /orgs/1/sites/site001 g, staffUser2028, staff001, /orgs/1/sites/site002 g, staffUser2028, staff001, /orgs/1/sites/site003 g, staffUser2028, staff001, /orgs/1/sites/site004 g, staffUser2028, staff001, /orgs/1/sites/site005 g, staffUser2029, staff001, /orgs/1/sites/site001 g, staffUser2029, staff001, /orgs/1/sites/site002 g, staffUser2029, staff001, /orgs/1/sites/site003 g, staffUser2029, staff001, /orgs/1/sites/site004 g, staffUser2029, staff001, /orgs/1/sites/site005 g, staffUser2030, staff001, /orgs/1/sites/site001 g, staffUser2030, staff001, /orgs/1/sites/site002 g, staffUser2030, staff001, /orgs/1/sites/site003 g, staffUser2030, staff001, /orgs/1/sites/site004 g, staffUser2030, staff001, /orgs/1/sites/site005 g, staffUser2031, staff001, /orgs/1/sites/site001 g, staffUser2031, staff001, /orgs/1/sites/site002 g, staffUser2031, staff001, /orgs/1/sites/site003 g, staffUser2031, staff001, /orgs/1/sites/site004 g, staffUser2031, staff001, /orgs/1/sites/site005 g, staffUser2032, staff001, /orgs/1/sites/site001 g, staffUser2032, staff001, /orgs/1/sites/site002 g, staffUser2032, staff001, /orgs/1/sites/site003 g, staffUser2032, staff001, /orgs/1/sites/site004 g, staffUser2032, staff001, /orgs/1/sites/site005 g, staffUser2033, staff001, /orgs/1/sites/site001 g, staffUser2033, staff001, /orgs/1/sites/site002 g, staffUser2033, staff001, /orgs/1/sites/site003 g, staffUser2033, staff001, /orgs/1/sites/site004 g, staffUser2033, staff001, /orgs/1/sites/site005 g, staffUser2034, staff001, /orgs/1/sites/site001 g, staffUser2034, staff001, /orgs/1/sites/site002 g, staffUser2034, staff001, /orgs/1/sites/site003 g, staffUser2034, staff001, /orgs/1/sites/site004 g, staffUser2034, staff001, /orgs/1/sites/site005 g, staffUser2035, staff001, /orgs/1/sites/site001 g, staffUser2035, staff001, /orgs/1/sites/site002 g, staffUser2035, staff001, /orgs/1/sites/site003 g, staffUser2035, staff001, /orgs/1/sites/site004 g, staffUser2035, staff001, /orgs/1/sites/site005 g, staffUser2036, staff001, /orgs/1/sites/site001 g, staffUser2036, staff001, /orgs/1/sites/site002 g, staffUser2036, staff001, /orgs/1/sites/site003 g, staffUser2036, staff001, /orgs/1/sites/site004 g, staffUser2036, staff001, /orgs/1/sites/site005 g, staffUser2037, staff001, /orgs/1/sites/site001 g, staffUser2037, staff001, /orgs/1/sites/site002 g, staffUser2037, staff001, /orgs/1/sites/site003 g, staffUser2037, staff001, /orgs/1/sites/site004 g, staffUser2037, staff001, /orgs/1/sites/site005 g, staffUser2038, staff001, /orgs/1/sites/site001 g, staffUser2038, staff001, /orgs/1/sites/site002 g, staffUser2038, staff001, /orgs/1/sites/site003 g, staffUser2038, staff001, /orgs/1/sites/site004 g, staffUser2038, staff001, /orgs/1/sites/site005 g, staffUser2039, staff001, /orgs/1/sites/site001 g, staffUser2039, staff001, /orgs/1/sites/site002 g, staffUser2039, staff001, /orgs/1/sites/site003 g, staffUser2039, staff001, /orgs/1/sites/site004 g, staffUser2039, staff001, /orgs/1/sites/site005 g, staffUser2040, staff001, /orgs/1/sites/site001 g, staffUser2040, staff001, /orgs/1/sites/site002 g, staffUser2040, staff001, /orgs/1/sites/site003 g, staffUser2040, staff001, /orgs/1/sites/site004 g, staffUser2040, staff001, /orgs/1/sites/site005 g, staffUser2041, staff001, /orgs/1/sites/site001 g, staffUser2041, staff001, /orgs/1/sites/site002 g, staffUser2041, staff001, /orgs/1/sites/site003 g, staffUser2041, staff001, /orgs/1/sites/site004 g, staffUser2041, staff001, /orgs/1/sites/site005 g, staffUser2042, staff001, /orgs/1/sites/site001 g, staffUser2042, staff001, /orgs/1/sites/site002 g, staffUser2042, staff001, /orgs/1/sites/site003 g, staffUser2042, staff001, /orgs/1/sites/site004 g, staffUser2042, staff001, /orgs/1/sites/site005 g, staffUser2043, staff001, /orgs/1/sites/site001 g, staffUser2043, staff001, /orgs/1/sites/site002 g, staffUser2043, staff001, /orgs/1/sites/site003 g, staffUser2043, staff001, /orgs/1/sites/site004 g, staffUser2043, staff001, /orgs/1/sites/site005 g, staffUser2044, staff001, /orgs/1/sites/site001 g, staffUser2044, staff001, /orgs/1/sites/site002 g, staffUser2044, staff001, /orgs/1/sites/site003 g, staffUser2044, staff001, /orgs/1/sites/site004 g, staffUser2044, staff001, /orgs/1/sites/site005 g, staffUser2045, staff001, /orgs/1/sites/site001 g, staffUser2045, staff001, /orgs/1/sites/site002 g, staffUser2045, staff001, /orgs/1/sites/site003 g, staffUser2045, staff001, /orgs/1/sites/site004 g, staffUser2045, staff001, /orgs/1/sites/site005 g, staffUser2046, staff001, /orgs/1/sites/site001 g, staffUser2046, staff001, /orgs/1/sites/site002 g, staffUser2046, staff001, /orgs/1/sites/site003 g, staffUser2046, staff001, /orgs/1/sites/site004 g, staffUser2046, staff001, /orgs/1/sites/site005 g, staffUser2047, staff001, /orgs/1/sites/site001 g, staffUser2047, staff001, /orgs/1/sites/site002 g, staffUser2047, staff001, /orgs/1/sites/site003 g, staffUser2047, staff001, /orgs/1/sites/site004 g, staffUser2047, staff001, /orgs/1/sites/site005 g, staffUser2048, staff001, /orgs/1/sites/site001 g, staffUser2048, staff001, /orgs/1/sites/site002 g, staffUser2048, staff001, /orgs/1/sites/site003 g, staffUser2048, staff001, /orgs/1/sites/site004 g, staffUser2048, staff001, /orgs/1/sites/site005 g, staffUser2049, staff001, /orgs/1/sites/site001 g, staffUser2049, staff001, /orgs/1/sites/site002 g, staffUser2049, staff001, /orgs/1/sites/site003 g, staffUser2049, staff001, /orgs/1/sites/site004 g, staffUser2049, staff001, /orgs/1/sites/site005 g, staffUser2050, staff001, /orgs/1/sites/site001 g, staffUser2050, staff001, /orgs/1/sites/site002 g, staffUser2050, staff001, /orgs/1/sites/site003 g, staffUser2050, staff001, /orgs/1/sites/site004 g, staffUser2050, staff001, /orgs/1/sites/site005 # Group - manager001, / org1 g, managerUser1001, manager001, /orgs/1/sites/site001 g, managerUser1001, manager001, /orgs/1/sites/site002 g, managerUser1001, manager001, /orgs/1/sites/site003 g, managerUser1001, manager001, /orgs/1/sites/site004 g, managerUser1001, manager001, /orgs/1/sites/site005 g, managerUser1001, manager001, /orgs/1/sites/site001 g, managerUser1001, manager001, /orgs/1/sites/site002 g, managerUser1001, manager001, /orgs/1/sites/site003 g, managerUser1001, manager001, /orgs/1/sites/site004 g, managerUser1001, manager001, /orgs/1/sites/site005 g, managerUser1003, manager001, /orgs/1/sites/site001 g, managerUser1003, manager001, /orgs/1/sites/site002 g, managerUser1003, manager001, /orgs/1/sites/site003 g, managerUser1003, manager001, /orgs/1/sites/site004 g, managerUser1003, manager001, /orgs/1/sites/site005 g, managerUser1004, manager001, /orgs/1/sites/site001 g, managerUser1004, manager001, /orgs/1/sites/site002 g, managerUser1004, manager001, /orgs/1/sites/site003 g, managerUser1004, manager001, /orgs/1/sites/site004 g, managerUser1004, manager001, /orgs/1/sites/site005 g, managerUser1005, manager001, /orgs/1/sites/site001 g, managerUser1005, manager001, /orgs/1/sites/site002 g, managerUser1005, manager001, /orgs/1/sites/site003 g, managerUser1005, manager001, /orgs/1/sites/site004 g, managerUser1005, manager001, /orgs/1/sites/site005 g, managerUser1006, manager001, /orgs/1/sites/site001 g, managerUser1006, manager001, /orgs/1/sites/site002 g, managerUser1006, manager001, /orgs/1/sites/site003 g, managerUser1006, manager001, /orgs/1/sites/site004 g, managerUser1006, manager001, /orgs/1/sites/site005 g, managerUser1007, manager001, /orgs/1/sites/site001 g, managerUser1007, manager001, /orgs/1/sites/site002 g, managerUser1007, manager001, /orgs/1/sites/site003 g, managerUser1007, manager001, /orgs/1/sites/site004 g, managerUser1007, manager001, /orgs/1/sites/site005 g, managerUser1008, manager001, /orgs/1/sites/site001 g, managerUser1008, manager001, /orgs/1/sites/site002 g, managerUser1008, manager001, /orgs/1/sites/site003 g, managerUser1008, manager001, /orgs/1/sites/site004 g, managerUser1008, manager001, /orgs/1/sites/site005 g, managerUser1009, manager001, /orgs/1/sites/site001 g, managerUser1009, manager001, /orgs/1/sites/site002 g, managerUser1009, manager001, /orgs/1/sites/site003 g, managerUser1009, manager001, /orgs/1/sites/site004 g, managerUser1009, manager001, /orgs/1/sites/site005 g, managerUser1010, manager001, /orgs/1/sites/site001 g, managerUser1010, manager001, /orgs/1/sites/site002 g, managerUser1010, manager001, /orgs/1/sites/site003 g, managerUser1010, manager001, /orgs/1/sites/site004 g, managerUser1010, manager001, /orgs/1/sites/site005 g, managerUser1011, manager001, /orgs/1/sites/site001 g, managerUser1011, manager001, /orgs/1/sites/site002 g, managerUser1011, manager001, /orgs/1/sites/site003 g, managerUser1011, manager001, /orgs/1/sites/site004 g, managerUser1011, manager001, /orgs/1/sites/site005 g, managerUser1012, manager001, /orgs/1/sites/site001 g, managerUser1012, manager001, /orgs/1/sites/site002 g, managerUser1012, manager001, /orgs/1/sites/site003 g, managerUser1012, manager001, /orgs/1/sites/site004 g, managerUser1012, manager001, /orgs/1/sites/site005 g, managerUser1013, manager001, /orgs/1/sites/site001 g, managerUser1013, manager001, /orgs/1/sites/site002 g, managerUser1013, manager001, /orgs/1/sites/site003 g, managerUser1013, manager001, /orgs/1/sites/site004 g, managerUser1013, manager001, /orgs/1/sites/site005 g, managerUser1014, manager001, /orgs/1/sites/site001 g, managerUser1014, manager001, /orgs/1/sites/site002 g, managerUser1014, manager001, /orgs/1/sites/site003 g, managerUser1014, manager001, /orgs/1/sites/site004 g, managerUser1014, manager001, /orgs/1/sites/site005 g, managerUser1015, manager001, /orgs/1/sites/site001 g, managerUser1015, manager001, /orgs/1/sites/site002 g, managerUser1015, manager001, /orgs/1/sites/site003 g, managerUser1015, manager001, /orgs/1/sites/site004 g, managerUser1015, manager001, /orgs/1/sites/site005 g, managerUser1016, manager001, /orgs/1/sites/site001 g, managerUser1016, manager001, /orgs/1/sites/site002 g, managerUser1016, manager001, /orgs/1/sites/site003 g, managerUser1016, manager001, /orgs/1/sites/site004 g, managerUser1016, manager001, /orgs/1/sites/site005 g, managerUser1017, manager001, /orgs/1/sites/site001 g, managerUser1017, manager001, /orgs/1/sites/site002 g, managerUser1017, manager001, /orgs/1/sites/site003 g, managerUser1017, manager001, /orgs/1/sites/site004 g, managerUser1017, manager001, /orgs/1/sites/site005 g, managerUser1018, manager001, /orgs/1/sites/site001 g, managerUser1018, manager001, /orgs/1/sites/site002 g, managerUser1018, manager001, /orgs/1/sites/site003 g, managerUser1018, manager001, /orgs/1/sites/site004 g, managerUser1018, manager001, /orgs/1/sites/site005 g, managerUser1019, manager001, /orgs/1/sites/site001 g, managerUser1019, manager001, /orgs/1/sites/site002 g, managerUser1019, manager001, /orgs/1/sites/site003 g, managerUser1019, manager001, /orgs/1/sites/site004 g, managerUser1019, manager001, /orgs/1/sites/site005 g, managerUser1020, manager001, /orgs/1/sites/site001 g, managerUser1020, manager001, /orgs/1/sites/site002 g, managerUser1020, manager001, /orgs/1/sites/site003 g, managerUser1020, manager001, /orgs/1/sites/site004 g, managerUser1020, manager001, /orgs/1/sites/site005 g, managerUser1021, manager001, /orgs/1/sites/site001 g, managerUser1021, manager001, /orgs/1/sites/site002 g, managerUser1021, manager001, /orgs/1/sites/site003 g, managerUser1021, manager001, /orgs/1/sites/site004 g, managerUser1021, manager001, /orgs/1/sites/site005 g, managerUser1022, manager001, /orgs/1/sites/site001 g, managerUser1022, manager001, /orgs/1/sites/site002 g, managerUser1022, manager001, /orgs/1/sites/site003 g, managerUser1022, manager001, /orgs/1/sites/site004 g, managerUser1022, manager001, /orgs/1/sites/site005 g, managerUser1023, manager001, /orgs/1/sites/site001 g, managerUser1023, manager001, /orgs/1/sites/site002 g, managerUser1023, manager001, /orgs/1/sites/site003 g, managerUser1023, manager001, /orgs/1/sites/site004 g, managerUser1023, manager001, /orgs/1/sites/site005 g, managerUser1024, manager001, /orgs/1/sites/site001 g, managerUser1024, manager001, /orgs/1/sites/site002 g, managerUser1024, manager001, /orgs/1/sites/site003 g, managerUser1024, manager001, /orgs/1/sites/site004 g, managerUser1024, manager001, /orgs/1/sites/site005 g, managerUser1025, manager001, /orgs/1/sites/site001 g, managerUser1025, manager001, /orgs/1/sites/site002 g, managerUser1025, manager001, /orgs/1/sites/site003 g, managerUser1025, manager001, /orgs/1/sites/site004 g, managerUser1025, manager001, /orgs/1/sites/site005 g, managerUser1026, manager001, /orgs/1/sites/site001 g, managerUser1026, manager001, /orgs/1/sites/site002 g, managerUser1026, manager001, /orgs/1/sites/site003 g, managerUser1026, manager001, /orgs/1/sites/site004 g, managerUser1026, manager001, /orgs/1/sites/site005 g, managerUser1027, manager001, /orgs/1/sites/site001 g, managerUser1027, manager001, /orgs/1/sites/site002 g, managerUser1027, manager001, /orgs/1/sites/site003 g, managerUser1027, manager001, /orgs/1/sites/site004 g, managerUser1027, manager001, /orgs/1/sites/site005 g, managerUser1028, manager001, /orgs/1/sites/site001 g, managerUser1028, manager001, /orgs/1/sites/site002 g, managerUser1028, manager001, /orgs/1/sites/site003 g, managerUser1028, manager001, /orgs/1/sites/site004 g, managerUser1028, manager001, /orgs/1/sites/site005 g, managerUser1029, manager001, /orgs/1/sites/site001 g, managerUser1029, manager001, /orgs/1/sites/site002 g, managerUser1029, manager001, /orgs/1/sites/site003 g, managerUser1029, manager001, /orgs/1/sites/site004 g, managerUser1029, manager001, /orgs/1/sites/site005 g, managerUser1030, manager001, /orgs/1/sites/site001 g, managerUser1030, manager001, /orgs/1/sites/site002 g, managerUser1030, manager001, /orgs/1/sites/site003 g, managerUser1030, manager001, /orgs/1/sites/site004 g, managerUser1030, manager001, /orgs/1/sites/site005 g, managerUser1031, manager001, /orgs/1/sites/site001 g, managerUser1031, manager001, /orgs/1/sites/site002 g, managerUser1031, manager001, /orgs/1/sites/site003 g, managerUser1031, manager001, /orgs/1/sites/site004 g, managerUser1031, manager001, /orgs/1/sites/site005 g, managerUser1032, manager001, /orgs/1/sites/site001 g, managerUser1032, manager001, /orgs/1/sites/site002 g, managerUser1032, manager001, /orgs/1/sites/site003 g, managerUser1032, manager001, /orgs/1/sites/site004 g, managerUser1032, manager001, /orgs/1/sites/site005 g, managerUser1033, manager001, /orgs/1/sites/site001 g, managerUser1033, manager001, /orgs/1/sites/site002 g, managerUser1033, manager001, /orgs/1/sites/site003 g, managerUser1033, manager001, /orgs/1/sites/site004 g, managerUser1033, manager001, /orgs/1/sites/site005 g, managerUser1034, manager001, /orgs/1/sites/site001 g, managerUser1034, manager001, /orgs/1/sites/site002 g, managerUser1034, manager001, /orgs/1/sites/site003 g, managerUser1034, manager001, /orgs/1/sites/site004 g, managerUser1034, manager001, /orgs/1/sites/site005 g, managerUser1035, manager001, /orgs/1/sites/site001 g, managerUser1035, manager001, /orgs/1/sites/site002 g, managerUser1035, manager001, /orgs/1/sites/site003 g, managerUser1035, manager001, /orgs/1/sites/site004 g, managerUser1035, manager001, /orgs/1/sites/site005 g, managerUser1036, manager001, /orgs/1/sites/site001 g, managerUser1036, manager001, /orgs/1/sites/site002 g, managerUser1036, manager001, /orgs/1/sites/site003 g, managerUser1036, manager001, /orgs/1/sites/site004 g, managerUser1036, manager001, /orgs/1/sites/site005 g, managerUser1037, manager001, /orgs/1/sites/site001 g, managerUser1037, manager001, /orgs/1/sites/site002 g, managerUser1037, manager001, /orgs/1/sites/site003 g, managerUser1037, manager001, /orgs/1/sites/site004 g, managerUser1037, manager001, /orgs/1/sites/site005 g, managerUser1038, manager001, /orgs/1/sites/site001 g, managerUser1038, manager001, /orgs/1/sites/site002 g, managerUser1038, manager001, /orgs/1/sites/site003 g, managerUser1038, manager001, /orgs/1/sites/site004 g, managerUser1038, manager001, /orgs/1/sites/site005 g, managerUser1039, manager001, /orgs/1/sites/site001 g, managerUser1039, manager001, /orgs/1/sites/site002 g, managerUser1039, manager001, /orgs/1/sites/site003 g, managerUser1039, manager001, /orgs/1/sites/site004 g, managerUser1039, manager001, /orgs/1/sites/site005 g, managerUser1040, manager001, /orgs/1/sites/site001 g, managerUser1040, manager001, /orgs/1/sites/site002 g, managerUser1040, manager001, /orgs/1/sites/site003 g, managerUser1040, manager001, /orgs/1/sites/site004 g, managerUser1040, manager001, /orgs/1/sites/site005 g, managerUser1041, manager001, /orgs/1/sites/site001 g, managerUser1041, manager001, /orgs/1/sites/site002 g, managerUser1041, manager001, /orgs/1/sites/site003 g, managerUser1041, manager001, /orgs/1/sites/site004 g, managerUser1041, manager001, /orgs/1/sites/site005 g, managerUser1042, manager001, /orgs/1/sites/site001 g, managerUser1042, manager001, /orgs/1/sites/site002 g, managerUser1042, manager001, /orgs/1/sites/site003 g, managerUser1042, manager001, /orgs/1/sites/site004 g, managerUser1042, manager001, /orgs/1/sites/site005 g, managerUser1043, manager001, /orgs/1/sites/site001 g, managerUser1043, manager001, /orgs/1/sites/site002 g, managerUser1043, manager001, /orgs/1/sites/site003 g, managerUser1043, manager001, /orgs/1/sites/site004 g, managerUser1043, manager001, /orgs/1/sites/site005 g, managerUser1044, manager001, /orgs/1/sites/site001 g, managerUser1044, manager001, /orgs/1/sites/site002 g, managerUser1044, manager001, /orgs/1/sites/site003 g, managerUser1044, manager001, /orgs/1/sites/site004 g, managerUser1044, manager001, /orgs/1/sites/site005 g, managerUser1045, manager001, /orgs/1/sites/site001 g, managerUser1045, manager001, /orgs/1/sites/site002 g, managerUser1045, manager001, /orgs/1/sites/site003 g, managerUser1045, manager001, /orgs/1/sites/site004 g, managerUser1045, manager001, /orgs/1/sites/site005 g, managerUser1046, manager001, /orgs/1/sites/site001 g, managerUser1046, manager001, /orgs/1/sites/site002 g, managerUser1046, manager001, /orgs/1/sites/site003 g, managerUser1046, manager001, /orgs/1/sites/site004 g, managerUser1046, manager001, /orgs/1/sites/site005 g, managerUser1047, manager001, /orgs/1/sites/site001 g, managerUser1047, manager001, /orgs/1/sites/site002 g, managerUser1047, manager001, /orgs/1/sites/site003 g, managerUser1047, manager001, /orgs/1/sites/site004 g, managerUser1047, manager001, /orgs/1/sites/site005 g, managerUser1048, manager001, /orgs/1/sites/site001 g, managerUser1048, manager001, /orgs/1/sites/site002 g, managerUser1048, manager001, /orgs/1/sites/site003 g, managerUser1048, manager001, /orgs/1/sites/site004 g, managerUser1048, manager001, /orgs/1/sites/site005 g, managerUser1049, manager001, /orgs/1/sites/site001 g, managerUser1049, manager001, /orgs/1/sites/site002 g, managerUser1049, manager001, /orgs/1/sites/site003 g, managerUser1049, manager001, /orgs/1/sites/site004 g, managerUser1049, manager001, /orgs/1/sites/site005 g, managerUser1050, manager001, /orgs/1/sites/site001 g, managerUser1050, manager001, /orgs/1/sites/site002 g, managerUser1050, manager001, /orgs/1/sites/site003 g, managerUser1050, manager001, /orgs/1/sites/site004 g, managerUser1050, manager001, /orgs/1/sites/site005 # Group - manager001, / org1 g, managerUser2001, manager001, /orgs/1/sites/site001 g, managerUser2001, manager001, /orgs/1/sites/site002 g, managerUser2001, manager001, /orgs/1/sites/site003 g, managerUser2001, manager001, /orgs/1/sites/site004 g, managerUser2001, manager001, /orgs/1/sites/site005 g, managerUser2001, manager001, /orgs/1/sites/site001 g, managerUser2001, manager001, /orgs/1/sites/site002 g, managerUser2001, manager001, /orgs/1/sites/site003 g, managerUser2001, manager001, /orgs/1/sites/site004 g, managerUser2001, manager001, /orgs/1/sites/site005 g, managerUser2003, manager001, /orgs/1/sites/site001 g, managerUser2003, manager001, /orgs/1/sites/site002 g, managerUser2003, manager001, /orgs/1/sites/site003 g, managerUser2003, manager001, /orgs/1/sites/site004 g, managerUser2003, manager001, /orgs/1/sites/site005 g, managerUser2004, manager001, /orgs/1/sites/site001 g, managerUser2004, manager001, /orgs/1/sites/site002 g, managerUser2004, manager001, /orgs/1/sites/site003 g, managerUser2004, manager001, /orgs/1/sites/site004 g, managerUser2004, manager001, /orgs/1/sites/site005 g, managerUser2005, manager001, /orgs/1/sites/site001 g, managerUser2005, manager001, /orgs/1/sites/site002 g, managerUser2005, manager001, /orgs/1/sites/site003 g, managerUser2005, manager001, /orgs/1/sites/site004 g, managerUser2005, manager001, /orgs/1/sites/site005 g, managerUser2006, manager001, /orgs/1/sites/site001 g, managerUser2006, manager001, /orgs/1/sites/site002 g, managerUser2006, manager001, /orgs/1/sites/site003 g, managerUser2006, manager001, /orgs/1/sites/site004 g, managerUser2006, manager001, /orgs/1/sites/site005 g, managerUser2007, manager001, /orgs/1/sites/site001 g, managerUser2007, manager001, /orgs/1/sites/site002 g, managerUser2007, manager001, /orgs/1/sites/site003 g, managerUser2007, manager001, /orgs/1/sites/site004 g, managerUser2007, manager001, /orgs/1/sites/site005 g, managerUser2008, manager001, /orgs/1/sites/site001 g, managerUser2008, manager001, /orgs/1/sites/site002 g, managerUser2008, manager001, /orgs/1/sites/site003 g, managerUser2008, manager001, /orgs/1/sites/site004 g, managerUser2008, manager001, /orgs/1/sites/site005 g, managerUser2009, manager001, /orgs/1/sites/site001 g, managerUser2009, manager001, /orgs/1/sites/site002 g, managerUser2009, manager001, /orgs/1/sites/site003 g, managerUser2009, manager001, /orgs/1/sites/site004 g, managerUser2009, manager001, /orgs/1/sites/site005 g, managerUser2010, manager001, /orgs/1/sites/site001 g, managerUser2010, manager001, /orgs/1/sites/site002 g, managerUser2010, manager001, /orgs/1/sites/site003 g, managerUser2010, manager001, /orgs/1/sites/site004 g, managerUser2010, manager001, /orgs/1/sites/site005 g, managerUser2011, manager001, /orgs/1/sites/site001 g, managerUser2011, manager001, /orgs/1/sites/site002 g, managerUser2011, manager001, /orgs/1/sites/site003 g, managerUser2011, manager001, /orgs/1/sites/site004 g, managerUser2011, manager001, /orgs/1/sites/site005 g, managerUser2012, manager001, /orgs/1/sites/site001 g, managerUser2012, manager001, /orgs/1/sites/site002 g, managerUser2012, manager001, /orgs/1/sites/site003 g, managerUser2012, manager001, /orgs/1/sites/site004 g, managerUser2012, manager001, /orgs/1/sites/site005 g, managerUser2013, manager001, /orgs/1/sites/site001 g, managerUser2013, manager001, /orgs/1/sites/site002 g, managerUser2013, manager001, /orgs/1/sites/site003 g, managerUser2013, manager001, /orgs/1/sites/site004 g, managerUser2013, manager001, /orgs/1/sites/site005 g, managerUser2014, manager001, /orgs/1/sites/site001 g, managerUser2014, manager001, /orgs/1/sites/site002 g, managerUser2014, manager001, /orgs/1/sites/site003 g, managerUser2014, manager001, /orgs/1/sites/site004 g, managerUser2014, manager001, /orgs/1/sites/site005 g, managerUser2015, manager001, /orgs/1/sites/site001 g, managerUser2015, manager001, /orgs/1/sites/site002 g, managerUser2015, manager001, /orgs/1/sites/site003 g, managerUser2015, manager001, /orgs/1/sites/site004 g, managerUser2015, manager001, /orgs/1/sites/site005 g, managerUser2016, manager001, /orgs/1/sites/site001 g, managerUser2016, manager001, /orgs/1/sites/site002 g, managerUser2016, manager001, /orgs/1/sites/site003 g, managerUser2016, manager001, /orgs/1/sites/site004 g, managerUser2016, manager001, /orgs/1/sites/site005 g, managerUser2017, manager001, /orgs/1/sites/site001 g, managerUser2017, manager001, /orgs/1/sites/site002 g, managerUser2017, manager001, /orgs/1/sites/site003 g, managerUser2017, manager001, /orgs/1/sites/site004 g, managerUser2017, manager001, /orgs/1/sites/site005 g, managerUser2018, manager001, /orgs/1/sites/site001 g, managerUser2018, manager001, /orgs/1/sites/site002 g, managerUser2018, manager001, /orgs/1/sites/site003 g, managerUser2018, manager001, /orgs/1/sites/site004 g, managerUser2018, manager001, /orgs/1/sites/site005 g, managerUser2019, manager001, /orgs/1/sites/site001 g, managerUser2019, manager001, /orgs/1/sites/site002 g, managerUser2019, manager001, /orgs/1/sites/site003 g, managerUser2019, manager001, /orgs/1/sites/site004 g, managerUser2019, manager001, /orgs/1/sites/site005 g, managerUser2020, manager001, /orgs/1/sites/site001 g, managerUser2020, manager001, /orgs/1/sites/site002 g, managerUser2020, manager001, /orgs/1/sites/site003 g, managerUser2020, manager001, /orgs/1/sites/site004 g, managerUser2020, manager001, /orgs/1/sites/site005 g, managerUser2021, manager001, /orgs/1/sites/site001 g, managerUser2021, manager001, /orgs/1/sites/site002 g, managerUser2021, manager001, /orgs/1/sites/site003 g, managerUser2021, manager001, /orgs/1/sites/site004 g, managerUser2021, manager001, /orgs/1/sites/site005 g, managerUser2022, manager001, /orgs/1/sites/site001 g, managerUser2022, manager001, /orgs/1/sites/site002 g, managerUser2022, manager001, /orgs/1/sites/site003 g, managerUser2022, manager001, /orgs/1/sites/site004 g, managerUser2022, manager001, /orgs/1/sites/site005 g, managerUser2023, manager001, /orgs/1/sites/site001 g, managerUser2023, manager001, /orgs/1/sites/site002 g, managerUser2023, manager001, /orgs/1/sites/site003 g, managerUser2023, manager001, /orgs/1/sites/site004 g, managerUser2023, manager001, /orgs/1/sites/site005 g, managerUser2024, manager001, /orgs/1/sites/site001 g, managerUser2024, manager001, /orgs/1/sites/site002 g, managerUser2024, manager001, /orgs/1/sites/site003 g, managerUser2024, manager001, /orgs/1/sites/site004 g, managerUser2024, manager001, /orgs/1/sites/site005 g, managerUser2025, manager001, /orgs/1/sites/site001 g, managerUser2025, manager001, /orgs/1/sites/site002 g, managerUser2025, manager001, /orgs/1/sites/site003 g, managerUser2025, manager001, /orgs/1/sites/site004 g, managerUser2025, manager001, /orgs/1/sites/site005 g, managerUser2026, manager001, /orgs/1/sites/site001 g, managerUser2026, manager001, /orgs/1/sites/site002 g, managerUser2026, manager001, /orgs/1/sites/site003 g, managerUser2026, manager001, /orgs/1/sites/site004 g, managerUser2026, manager001, /orgs/1/sites/site005 g, managerUser2027, manager001, /orgs/1/sites/site001 g, managerUser2027, manager001, /orgs/1/sites/site002 g, managerUser2027, manager001, /orgs/1/sites/site003 g, managerUser2027, manager001, /orgs/1/sites/site004 g, managerUser2027, manager001, /orgs/1/sites/site005 g, managerUser2028, manager001, /orgs/1/sites/site001 g, managerUser2028, manager001, /orgs/1/sites/site002 g, managerUser2028, manager001, /orgs/1/sites/site003 g, managerUser2028, manager001, /orgs/1/sites/site004 g, managerUser2028, manager001, /orgs/1/sites/site005 g, managerUser2029, manager001, /orgs/1/sites/site001 g, managerUser2029, manager001, /orgs/1/sites/site002 g, managerUser2029, manager001, /orgs/1/sites/site003 g, managerUser2029, manager001, /orgs/1/sites/site004 g, managerUser2029, manager001, /orgs/1/sites/site005 g, managerUser2030, manager001, /orgs/1/sites/site001 g, managerUser2030, manager001, /orgs/1/sites/site002 g, managerUser2030, manager001, /orgs/1/sites/site003 g, managerUser2030, manager001, /orgs/1/sites/site004 g, managerUser2030, manager001, /orgs/1/sites/site005 g, managerUser2031, manager001, /orgs/1/sites/site001 g, managerUser2031, manager001, /orgs/1/sites/site002 g, managerUser2031, manager001, /orgs/1/sites/site003 g, managerUser2031, manager001, /orgs/1/sites/site004 g, managerUser2031, manager001, /orgs/1/sites/site005 g, managerUser2032, manager001, /orgs/1/sites/site001 g, managerUser2032, manager001, /orgs/1/sites/site002 g, managerUser2032, manager001, /orgs/1/sites/site003 g, managerUser2032, manager001, /orgs/1/sites/site004 g, managerUser2032, manager001, /orgs/1/sites/site005 g, managerUser2033, manager001, /orgs/1/sites/site001 g, managerUser2033, manager001, /orgs/1/sites/site002 g, managerUser2033, manager001, /orgs/1/sites/site003 g, managerUser2033, manager001, /orgs/1/sites/site004 g, managerUser2033, manager001, /orgs/1/sites/site005 g, managerUser2034, manager001, /orgs/1/sites/site001 g, managerUser2034, manager001, /orgs/1/sites/site002 g, managerUser2034, manager001, /orgs/1/sites/site003 g, managerUser2034, manager001, /orgs/1/sites/site004 g, managerUser2034, manager001, /orgs/1/sites/site005 g, managerUser2035, manager001, /orgs/1/sites/site001 g, managerUser2035, manager001, /orgs/1/sites/site002 g, managerUser2035, manager001, /orgs/1/sites/site003 g, managerUser2035, manager001, /orgs/1/sites/site004 g, managerUser2035, manager001, /orgs/1/sites/site005 g, managerUser2036, manager001, /orgs/1/sites/site001 g, managerUser2036, manager001, /orgs/1/sites/site002 g, managerUser2036, manager001, /orgs/1/sites/site003 g, managerUser2036, manager001, /orgs/1/sites/site004 g, managerUser2036, manager001, /orgs/1/sites/site005 g, managerUser2037, manager001, /orgs/1/sites/site001 g, managerUser2037, manager001, /orgs/1/sites/site002 g, managerUser2037, manager001, /orgs/1/sites/site003 g, managerUser2037, manager001, /orgs/1/sites/site004 g, managerUser2037, manager001, /orgs/1/sites/site005 g, managerUser2038, manager001, /orgs/1/sites/site001 g, managerUser2038, manager001, /orgs/1/sites/site002 g, managerUser2038, manager001, /orgs/1/sites/site003 g, managerUser2038, manager001, /orgs/1/sites/site004 g, managerUser2038, manager001, /orgs/1/sites/site005 g, managerUser2039, manager001, /orgs/1/sites/site001 g, managerUser2039, manager001, /orgs/1/sites/site002 g, managerUser2039, manager001, /orgs/1/sites/site003 g, managerUser2039, manager001, /orgs/1/sites/site004 g, managerUser2039, manager001, /orgs/1/sites/site005 g, managerUser2040, manager001, /orgs/1/sites/site001 g, managerUser2040, manager001, /orgs/1/sites/site002 g, managerUser2040, manager001, /orgs/1/sites/site003 g, managerUser2040, manager001, /orgs/1/sites/site004 g, managerUser2040, manager001, /orgs/1/sites/site005 g, managerUser2041, manager001, /orgs/1/sites/site001 g, managerUser2041, manager001, /orgs/1/sites/site002 g, managerUser2041, manager001, /orgs/1/sites/site003 g, managerUser2041, manager001, /orgs/1/sites/site004 g, managerUser2041, manager001, /orgs/1/sites/site005 g, managerUser2042, manager001, /orgs/1/sites/site001 g, managerUser2042, manager001, /orgs/1/sites/site002 g, managerUser2042, manager001, /orgs/1/sites/site003 g, managerUser2042, manager001, /orgs/1/sites/site004 g, managerUser2042, manager001, /orgs/1/sites/site005 g, managerUser2043, manager001, /orgs/1/sites/site001 g, managerUser2043, manager001, /orgs/1/sites/site002 g, managerUser2043, manager001, /orgs/1/sites/site003 g, managerUser2043, manager001, /orgs/1/sites/site004 g, managerUser2043, manager001, /orgs/1/sites/site005 g, managerUser2044, manager001, /orgs/1/sites/site001 g, managerUser2044, manager001, /orgs/1/sites/site002 g, managerUser2044, manager001, /orgs/1/sites/site003 g, managerUser2044, manager001, /orgs/1/sites/site004 g, managerUser2044, manager001, /orgs/1/sites/site005 g, managerUser2045, manager001, /orgs/1/sites/site001 g, managerUser2045, manager001, /orgs/1/sites/site002 g, managerUser2045, manager001, /orgs/1/sites/site003 g, managerUser2045, manager001, /orgs/1/sites/site004 g, managerUser2045, manager001, /orgs/1/sites/site005 g, managerUser2046, manager001, /orgs/1/sites/site001 g, managerUser2046, manager001, /orgs/1/sites/site002 g, managerUser2046, manager001, /orgs/1/sites/site003 g, managerUser2046, manager001, /orgs/1/sites/site004 g, managerUser2046, manager001, /orgs/1/sites/site005 g, managerUser2047, manager001, /orgs/1/sites/site001 g, managerUser2047, manager001, /orgs/1/sites/site002 g, managerUser2047, manager001, /orgs/1/sites/site003 g, managerUser2047, manager001, /orgs/1/sites/site004 g, managerUser2047, manager001, /orgs/1/sites/site005 g, managerUser2048, manager001, /orgs/1/sites/site001 g, managerUser2048, manager001, /orgs/1/sites/site002 g, managerUser2048, manager001, /orgs/1/sites/site003 g, managerUser2048, manager001, /orgs/1/sites/site004 g, managerUser2048, manager001, /orgs/1/sites/site005 g, managerUser2049, manager001, /orgs/1/sites/site001 g, managerUser2049, manager001, /orgs/1/sites/site002 g, managerUser2049, manager001, /orgs/1/sites/site003 g, managerUser2049, manager001, /orgs/1/sites/site004 g, managerUser2049, manager001, /orgs/1/sites/site005 g, managerUser2050, manager001, /orgs/1/sites/site001 g, managerUser2050, manager001, /orgs/1/sites/site002 g, managerUser2050, manager001, /orgs/1/sites/site003 g, managerUser2050, manager001, /orgs/1/sites/site004 g, managerUser2050, manager001, /orgs/1/sites/site005 # Group - customer001, / org1 g, customerUser1001, customer001, /orgs/1/sites/site001 g, customerUser1001, customer001, /orgs/1/sites/site002 g, customerUser1001, customer001, /orgs/1/sites/site003 g, customerUser1001, customer001, /orgs/1/sites/site004 g, customerUser1001, customer001, /orgs/1/sites/site005 g, customerUser1001, customer001, /orgs/1/sites/site001 g, customerUser1001, customer001, /orgs/1/sites/site002 g, customerUser1001, customer001, /orgs/1/sites/site003 g, customerUser1001, customer001, /orgs/1/sites/site004 g, customerUser1001, customer001, /orgs/1/sites/site005 g, customerUser1003, customer001, /orgs/1/sites/site001 g, customerUser1003, customer001, /orgs/1/sites/site002 g, customerUser1003, customer001, /orgs/1/sites/site003 g, customerUser1003, customer001, /orgs/1/sites/site004 g, customerUser1003, customer001, /orgs/1/sites/site005 g, customerUser1004, customer001, /orgs/1/sites/site001 g, customerUser1004, customer001, /orgs/1/sites/site002 g, customerUser1004, customer001, /orgs/1/sites/site003 g, customerUser1004, customer001, /orgs/1/sites/site004 g, customerUser1004, customer001, /orgs/1/sites/site005 g, customerUser1005, customer001, /orgs/1/sites/site001 g, customerUser1005, customer001, /orgs/1/sites/site002 g, customerUser1005, customer001, /orgs/1/sites/site003 g, customerUser1005, customer001, /orgs/1/sites/site004 g, customerUser1005, customer001, /orgs/1/sites/site005 g, customerUser1006, customer001, /orgs/1/sites/site001 g, customerUser1006, customer001, /orgs/1/sites/site002 g, customerUser1006, customer001, /orgs/1/sites/site003 g, customerUser1006, customer001, /orgs/1/sites/site004 g, customerUser1006, customer001, /orgs/1/sites/site005 g, customerUser1007, customer001, /orgs/1/sites/site001 g, customerUser1007, customer001, /orgs/1/sites/site002 g, customerUser1007, customer001, /orgs/1/sites/site003 g, customerUser1007, customer001, /orgs/1/sites/site004 g, customerUser1007, customer001, /orgs/1/sites/site005 g, customerUser1008, customer001, /orgs/1/sites/site001 g, customerUser1008, customer001, /orgs/1/sites/site002 g, customerUser1008, customer001, /orgs/1/sites/site003 g, customerUser1008, customer001, /orgs/1/sites/site004 g, customerUser1008, customer001, /orgs/1/sites/site005 g, customerUser1009, customer001, /orgs/1/sites/site001 g, customerUser1009, customer001, /orgs/1/sites/site002 g, customerUser1009, customer001, /orgs/1/sites/site003 g, customerUser1009, customer001, /orgs/1/sites/site004 g, customerUser1009, customer001, /orgs/1/sites/site005 g, customerUser1010, customer001, /orgs/1/sites/site001 g, customerUser1010, customer001, /orgs/1/sites/site002 g, customerUser1010, customer001, /orgs/1/sites/site003 g, customerUser1010, customer001, /orgs/1/sites/site004 g, customerUser1010, customer001, /orgs/1/sites/site005 g, customerUser1011, customer001, /orgs/1/sites/site001 g, customerUser1011, customer001, /orgs/1/sites/site002 g, customerUser1011, customer001, /orgs/1/sites/site003 g, customerUser1011, customer001, /orgs/1/sites/site004 g, customerUser1011, customer001, /orgs/1/sites/site005 g, customerUser1012, customer001, /orgs/1/sites/site001 g, customerUser1012, customer001, /orgs/1/sites/site002 g, customerUser1012, customer001, /orgs/1/sites/site003 g, customerUser1012, customer001, /orgs/1/sites/site004 g, customerUser1012, customer001, /orgs/1/sites/site005 g, customerUser1013, customer001, /orgs/1/sites/site001 g, customerUser1013, customer001, /orgs/1/sites/site002 g, customerUser1013, customer001, /orgs/1/sites/site003 g, customerUser1013, customer001, /orgs/1/sites/site004 g, customerUser1013, customer001, /orgs/1/sites/site005 g, customerUser1014, customer001, /orgs/1/sites/site001 g, customerUser1014, customer001, /orgs/1/sites/site002 g, customerUser1014, customer001, /orgs/1/sites/site003 g, customerUser1014, customer001, /orgs/1/sites/site004 g, customerUser1014, customer001, /orgs/1/sites/site005 g, customerUser1015, customer001, /orgs/1/sites/site001 g, customerUser1015, customer001, /orgs/1/sites/site002 g, customerUser1015, customer001, /orgs/1/sites/site003 g, customerUser1015, customer001, /orgs/1/sites/site004 g, customerUser1015, customer001, /orgs/1/sites/site005 g, customerUser1016, customer001, /orgs/1/sites/site001 g, customerUser1016, customer001, /orgs/1/sites/site002 g, customerUser1016, customer001, /orgs/1/sites/site003 g, customerUser1016, customer001, /orgs/1/sites/site004 g, customerUser1016, customer001, /orgs/1/sites/site005 g, customerUser1017, customer001, /orgs/1/sites/site001 g, customerUser1017, customer001, /orgs/1/sites/site002 g, customerUser1017, customer001, /orgs/1/sites/site003 g, customerUser1017, customer001, /orgs/1/sites/site004 g, customerUser1017, customer001, /orgs/1/sites/site005 g, customerUser1018, customer001, /orgs/1/sites/site001 g, customerUser1018, customer001, /orgs/1/sites/site002 g, customerUser1018, customer001, /orgs/1/sites/site003 g, customerUser1018, customer001, /orgs/1/sites/site004 g, customerUser1018, customer001, /orgs/1/sites/site005 g, customerUser1019, customer001, /orgs/1/sites/site001 g, customerUser1019, customer001, /orgs/1/sites/site002 g, customerUser1019, customer001, /orgs/1/sites/site003 g, customerUser1019, customer001, /orgs/1/sites/site004 g, customerUser1019, customer001, /orgs/1/sites/site005 g, customerUser1020, customer001, /orgs/1/sites/site001 g, customerUser1020, customer001, /orgs/1/sites/site002 g, customerUser1020, customer001, /orgs/1/sites/site003 g, customerUser1020, customer001, /orgs/1/sites/site004 g, customerUser1020, customer001, /orgs/1/sites/site005 g, customerUser1021, customer001, /orgs/1/sites/site001 g, customerUser1021, customer001, /orgs/1/sites/site002 g, customerUser1021, customer001, /orgs/1/sites/site003 g, customerUser1021, customer001, /orgs/1/sites/site004 g, customerUser1021, customer001, /orgs/1/sites/site005 g, customerUser1022, customer001, /orgs/1/sites/site001 g, customerUser1022, customer001, /orgs/1/sites/site002 g, customerUser1022, customer001, /orgs/1/sites/site003 g, customerUser1022, customer001, /orgs/1/sites/site004 g, customerUser1022, customer001, /orgs/1/sites/site005 g, customerUser1023, customer001, /orgs/1/sites/site001 g, customerUser1023, customer001, /orgs/1/sites/site002 g, customerUser1023, customer001, /orgs/1/sites/site003 g, customerUser1023, customer001, /orgs/1/sites/site004 g, customerUser1023, customer001, /orgs/1/sites/site005 g, customerUser1024, customer001, /orgs/1/sites/site001 g, customerUser1024, customer001, /orgs/1/sites/site002 g, customerUser1024, customer001, /orgs/1/sites/site003 g, customerUser1024, customer001, /orgs/1/sites/site004 g, customerUser1024, customer001, /orgs/1/sites/site005 g, customerUser1025, customer001, /orgs/1/sites/site001 g, customerUser1025, customer001, /orgs/1/sites/site002 g, customerUser1025, customer001, /orgs/1/sites/site003 g, customerUser1025, customer001, /orgs/1/sites/site004 g, customerUser1025, customer001, /orgs/1/sites/site005 g, customerUser1026, customer001, /orgs/1/sites/site001 g, customerUser1026, customer001, /orgs/1/sites/site002 g, customerUser1026, customer001, /orgs/1/sites/site003 g, customerUser1026, customer001, /orgs/1/sites/site004 g, customerUser1026, customer001, /orgs/1/sites/site005 g, customerUser1027, customer001, /orgs/1/sites/site001 g, customerUser1027, customer001, /orgs/1/sites/site002 g, customerUser1027, customer001, /orgs/1/sites/site003 g, customerUser1027, customer001, /orgs/1/sites/site004 g, customerUser1027, customer001, /orgs/1/sites/site005 g, customerUser1028, customer001, /orgs/1/sites/site001 g, customerUser1028, customer001, /orgs/1/sites/site002 g, customerUser1028, customer001, /orgs/1/sites/site003 g, customerUser1028, customer001, /orgs/1/sites/site004 g, customerUser1028, customer001, /orgs/1/sites/site005 g, customerUser1029, customer001, /orgs/1/sites/site001 g, customerUser1029, customer001, /orgs/1/sites/site002 g, customerUser1029, customer001, /orgs/1/sites/site003 g, customerUser1029, customer001, /orgs/1/sites/site004 g, customerUser1029, customer001, /orgs/1/sites/site005 g, customerUser1030, customer001, /orgs/1/sites/site001 g, customerUser1030, customer001, /orgs/1/sites/site002 g, customerUser1030, customer001, /orgs/1/sites/site003 g, customerUser1030, customer001, /orgs/1/sites/site004 g, customerUser1030, customer001, /orgs/1/sites/site005 g, customerUser1031, customer001, /orgs/1/sites/site001 g, customerUser1031, customer001, /orgs/1/sites/site002 g, customerUser1031, customer001, /orgs/1/sites/site003 g, customerUser1031, customer001, /orgs/1/sites/site004 g, customerUser1031, customer001, /orgs/1/sites/site005 g, customerUser1032, customer001, /orgs/1/sites/site001 g, customerUser1032, customer001, /orgs/1/sites/site002 g, customerUser1032, customer001, /orgs/1/sites/site003 g, customerUser1032, customer001, /orgs/1/sites/site004 g, customerUser1032, customer001, /orgs/1/sites/site005 g, customerUser1033, customer001, /orgs/1/sites/site001 g, customerUser1033, customer001, /orgs/1/sites/site002 g, customerUser1033, customer001, /orgs/1/sites/site003 g, customerUser1033, customer001, /orgs/1/sites/site004 g, customerUser1033, customer001, /orgs/1/sites/site005 g, customerUser1034, customer001, /orgs/1/sites/site001 g, customerUser1034, customer001, /orgs/1/sites/site002 g, customerUser1034, customer001, /orgs/1/sites/site003 g, customerUser1034, customer001, /orgs/1/sites/site004 g, customerUser1034, customer001, /orgs/1/sites/site005 g, customerUser1035, customer001, /orgs/1/sites/site001 g, customerUser1035, customer001, /orgs/1/sites/site002 g, customerUser1035, customer001, /orgs/1/sites/site003 g, customerUser1035, customer001, /orgs/1/sites/site004 g, customerUser1035, customer001, /orgs/1/sites/site005 g, customerUser1036, customer001, /orgs/1/sites/site001 g, customerUser1036, customer001, /orgs/1/sites/site002 g, customerUser1036, customer001, /orgs/1/sites/site003 g, customerUser1036, customer001, /orgs/1/sites/site004 g, customerUser1036, customer001, /orgs/1/sites/site005 g, customerUser1037, customer001, /orgs/1/sites/site001 g, customerUser1037, customer001, /orgs/1/sites/site002 g, customerUser1037, customer001, /orgs/1/sites/site003 g, customerUser1037, customer001, /orgs/1/sites/site004 g, customerUser1037, customer001, /orgs/1/sites/site005 g, customerUser1038, customer001, /orgs/1/sites/site001 g, customerUser1038, customer001, /orgs/1/sites/site002 g, customerUser1038, customer001, /orgs/1/sites/site003 g, customerUser1038, customer001, /orgs/1/sites/site004 g, customerUser1038, customer001, /orgs/1/sites/site005 g, customerUser1039, customer001, /orgs/1/sites/site001 g, customerUser1039, customer001, /orgs/1/sites/site002 g, customerUser1039, customer001, /orgs/1/sites/site003 g, customerUser1039, customer001, /orgs/1/sites/site004 g, customerUser1039, customer001, /orgs/1/sites/site005 g, customerUser1040, customer001, /orgs/1/sites/site001 g, customerUser1040, customer001, /orgs/1/sites/site002 g, customerUser1040, customer001, /orgs/1/sites/site003 g, customerUser1040, customer001, /orgs/1/sites/site004 g, customerUser1040, customer001, /orgs/1/sites/site005 g, customerUser1041, customer001, /orgs/1/sites/site001 g, customerUser1041, customer001, /orgs/1/sites/site002 g, customerUser1041, customer001, /orgs/1/sites/site003 g, customerUser1041, customer001, /orgs/1/sites/site004 g, customerUser1041, customer001, /orgs/1/sites/site005 g, customerUser1042, customer001, /orgs/1/sites/site001 g, customerUser1042, customer001, /orgs/1/sites/site002 g, customerUser1042, customer001, /orgs/1/sites/site003 g, customerUser1042, customer001, /orgs/1/sites/site004 g, customerUser1042, customer001, /orgs/1/sites/site005 g, customerUser1043, customer001, /orgs/1/sites/site001 g, customerUser1043, customer001, /orgs/1/sites/site002 g, customerUser1043, customer001, /orgs/1/sites/site003 g, customerUser1043, customer001, /orgs/1/sites/site004 g, customerUser1043, customer001, /orgs/1/sites/site005 g, customerUser1044, customer001, /orgs/1/sites/site001 g, customerUser1044, customer001, /orgs/1/sites/site002 g, customerUser1044, customer001, /orgs/1/sites/site003 g, customerUser1044, customer001, /orgs/1/sites/site004 g, customerUser1044, customer001, /orgs/1/sites/site005 g, customerUser1045, customer001, /orgs/1/sites/site001 g, customerUser1045, customer001, /orgs/1/sites/site002 g, customerUser1045, customer001, /orgs/1/sites/site003 g, customerUser1045, customer001, /orgs/1/sites/site004 g, customerUser1045, customer001, /orgs/1/sites/site005 g, customerUser1046, customer001, /orgs/1/sites/site001 g, customerUser1046, customer001, /orgs/1/sites/site002 g, customerUser1046, customer001, /orgs/1/sites/site003 g, customerUser1046, customer001, /orgs/1/sites/site004 g, customerUser1046, customer001, /orgs/1/sites/site005 g, customerUser1047, customer001, /orgs/1/sites/site001 g, customerUser1047, customer001, /orgs/1/sites/site002 g, customerUser1047, customer001, /orgs/1/sites/site003 g, customerUser1047, customer001, /orgs/1/sites/site004 g, customerUser1047, customer001, /orgs/1/sites/site005 g, customerUser1048, customer001, /orgs/1/sites/site001 g, customerUser1048, customer001, /orgs/1/sites/site002 g, customerUser1048, customer001, /orgs/1/sites/site003 g, customerUser1048, customer001, /orgs/1/sites/site004 g, customerUser1048, customer001, /orgs/1/sites/site005 g, customerUser1049, customer001, /orgs/1/sites/site001 g, customerUser1049, customer001, /orgs/1/sites/site002 g, customerUser1049, customer001, /orgs/1/sites/site003 g, customerUser1049, customer001, /orgs/1/sites/site004 g, customerUser1049, customer001, /orgs/1/sites/site005 g, customerUser1050, customer001, /orgs/1/sites/site001 g, customerUser1050, customer001, /orgs/1/sites/site002 g, customerUser1050, customer001, /orgs/1/sites/site003 g, customerUser1050, customer001, /orgs/1/sites/site004 g, customerUser1050, customer001, /orgs/1/sites/site005 # Group - customer001, / org1 g, customerUser2001, customer001, /orgs/1/sites/site001 g, customerUser2001, customer001, /orgs/1/sites/site002 g, customerUser2001, customer001, /orgs/1/sites/site003 g, customerUser2001, customer001, /orgs/1/sites/site004 g, customerUser2001, customer001, /orgs/1/sites/site005 g, customerUser2001, customer001, /orgs/1/sites/site001 g, customerUser2001, customer001, /orgs/1/sites/site002 g, customerUser2001, customer001, /orgs/1/sites/site003 g, customerUser2001, customer001, /orgs/1/sites/site004 g, customerUser2001, customer001, /orgs/1/sites/site005 g, customerUser2003, customer001, /orgs/1/sites/site001 g, customerUser2003, customer001, /orgs/1/sites/site002 g, customerUser2003, customer001, /orgs/1/sites/site003 g, customerUser2003, customer001, /orgs/1/sites/site004 g, customerUser2003, customer001, /orgs/1/sites/site005 g, customerUser2004, customer001, /orgs/1/sites/site001 g, customerUser2004, customer001, /orgs/1/sites/site002 g, customerUser2004, customer001, /orgs/1/sites/site003 g, customerUser2004, customer001, /orgs/1/sites/site004 g, customerUser2004, customer001, /orgs/1/sites/site005 g, customerUser2005, customer001, /orgs/1/sites/site001 g, customerUser2005, customer001, /orgs/1/sites/site002 g, customerUser2005, customer001, /orgs/1/sites/site003 g, customerUser2005, customer001, /orgs/1/sites/site004 g, customerUser2005, customer001, /orgs/1/sites/site005 g, customerUser2006, customer001, /orgs/1/sites/site001 g, customerUser2006, customer001, /orgs/1/sites/site002 g, customerUser2006, customer001, /orgs/1/sites/site003 g, customerUser2006, customer001, /orgs/1/sites/site004 g, customerUser2006, customer001, /orgs/1/sites/site005 g, customerUser2007, customer001, /orgs/1/sites/site001 g, customerUser2007, customer001, /orgs/1/sites/site002 g, customerUser2007, customer001, /orgs/1/sites/site003 g, customerUser2007, customer001, /orgs/1/sites/site004 g, customerUser2007, customer001, /orgs/1/sites/site005 g, customerUser2008, customer001, /orgs/1/sites/site001 g, customerUser2008, customer001, /orgs/1/sites/site002 g, customerUser2008, customer001, /orgs/1/sites/site003 g, customerUser2008, customer001, /orgs/1/sites/site004 g, customerUser2008, customer001, /orgs/1/sites/site005 g, customerUser2009, customer001, /orgs/1/sites/site001 g, customerUser2009, customer001, /orgs/1/sites/site002 g, customerUser2009, customer001, /orgs/1/sites/site003 g, customerUser2009, customer001, /orgs/1/sites/site004 g, customerUser2009, customer001, /orgs/1/sites/site005 g, customerUser2010, customer001, /orgs/1/sites/site001 g, customerUser2010, customer001, /orgs/1/sites/site002 g, customerUser2010, customer001, /orgs/1/sites/site003 g, customerUser2010, customer001, /orgs/1/sites/site004 g, customerUser2010, customer001, /orgs/1/sites/site005 g, customerUser2011, customer001, /orgs/1/sites/site001 g, customerUser2011, customer001, /orgs/1/sites/site002 g, customerUser2011, customer001, /orgs/1/sites/site003 g, customerUser2011, customer001, /orgs/1/sites/site004 g, customerUser2011, customer001, /orgs/1/sites/site005 g, customerUser2012, customer001, /orgs/1/sites/site001 g, customerUser2012, customer001, /orgs/1/sites/site002 g, customerUser2012, customer001, /orgs/1/sites/site003 g, customerUser2012, customer001, /orgs/1/sites/site004 g, customerUser2012, customer001, /orgs/1/sites/site005 g, customerUser2013, customer001, /orgs/1/sites/site001 g, customerUser2013, customer001, /orgs/1/sites/site002 g, customerUser2013, customer001, /orgs/1/sites/site003 g, customerUser2013, customer001, /orgs/1/sites/site004 g, customerUser2013, customer001, /orgs/1/sites/site005 g, customerUser2014, customer001, /orgs/1/sites/site001 g, customerUser2014, customer001, /orgs/1/sites/site002 g, customerUser2014, customer001, /orgs/1/sites/site003 g, customerUser2014, customer001, /orgs/1/sites/site004 g, customerUser2014, customer001, /orgs/1/sites/site005 g, customerUser2015, customer001, /orgs/1/sites/site001 g, customerUser2015, customer001, /orgs/1/sites/site002 g, customerUser2015, customer001, /orgs/1/sites/site003 g, customerUser2015, customer001, /orgs/1/sites/site004 g, customerUser2015, customer001, /orgs/1/sites/site005 g, customerUser2016, customer001, /orgs/1/sites/site001 g, customerUser2016, customer001, /orgs/1/sites/site002 g, customerUser2016, customer001, /orgs/1/sites/site003 g, customerUser2016, customer001, /orgs/1/sites/site004 g, customerUser2016, customer001, /orgs/1/sites/site005 g, customerUser2017, customer001, /orgs/1/sites/site001 g, customerUser2017, customer001, /orgs/1/sites/site002 g, customerUser2017, customer001, /orgs/1/sites/site003 g, customerUser2017, customer001, /orgs/1/sites/site004 g, customerUser2017, customer001, /orgs/1/sites/site005 g, customerUser2018, customer001, /orgs/1/sites/site001 g, customerUser2018, customer001, /orgs/1/sites/site002 g, customerUser2018, customer001, /orgs/1/sites/site003 g, customerUser2018, customer001, /orgs/1/sites/site004 g, customerUser2018, customer001, /orgs/1/sites/site005 g, customerUser2019, customer001, /orgs/1/sites/site001 g, customerUser2019, customer001, /orgs/1/sites/site002 g, customerUser2019, customer001, /orgs/1/sites/site003 g, customerUser2019, customer001, /orgs/1/sites/site004 g, customerUser2019, customer001, /orgs/1/sites/site005 g, customerUser2020, customer001, /orgs/1/sites/site001 g, customerUser2020, customer001, /orgs/1/sites/site002 g, customerUser2020, customer001, /orgs/1/sites/site003 g, customerUser2020, customer001, /orgs/1/sites/site004 g, customerUser2020, customer001, /orgs/1/sites/site005 g, customerUser2021, customer001, /orgs/1/sites/site001 g, customerUser2021, customer001, /orgs/1/sites/site002 g, customerUser2021, customer001, /orgs/1/sites/site003 g, customerUser2021, customer001, /orgs/1/sites/site004 g, customerUser2021, customer001, /orgs/1/sites/site005 g, customerUser2022, customer001, /orgs/1/sites/site001 g, customerUser2022, customer001, /orgs/1/sites/site002 g, customerUser2022, customer001, /orgs/1/sites/site003 g, customerUser2022, customer001, /orgs/1/sites/site004 g, customerUser2022, customer001, /orgs/1/sites/site005 g, customerUser2023, customer001, /orgs/1/sites/site001 g, customerUser2023, customer001, /orgs/1/sites/site002 g, customerUser2023, customer001, /orgs/1/sites/site003 g, customerUser2023, customer001, /orgs/1/sites/site004 g, customerUser2023, customer001, /orgs/1/sites/site005 g, customerUser2024, customer001, /orgs/1/sites/site001 g, customerUser2024, customer001, /orgs/1/sites/site002 g, customerUser2024, customer001, /orgs/1/sites/site003 g, customerUser2024, customer001, /orgs/1/sites/site004 g, customerUser2024, customer001, /orgs/1/sites/site005 g, customerUser2025, customer001, /orgs/1/sites/site001 g, customerUser2025, customer001, /orgs/1/sites/site002 g, customerUser2025, customer001, /orgs/1/sites/site003 g, customerUser2025, customer001, /orgs/1/sites/site004 g, customerUser2025, customer001, /orgs/1/sites/site005 g, customerUser2026, customer001, /orgs/1/sites/site001 g, customerUser2026, customer001, /orgs/1/sites/site002 g, customerUser2026, customer001, /orgs/1/sites/site003 g, customerUser2026, customer001, /orgs/1/sites/site004 g, customerUser2026, customer001, /orgs/1/sites/site005 g, customerUser2027, customer001, /orgs/1/sites/site001 g, customerUser2027, customer001, /orgs/1/sites/site002 g, customerUser2027, customer001, /orgs/1/sites/site003 g, customerUser2027, customer001, /orgs/1/sites/site004 g, customerUser2027, customer001, /orgs/1/sites/site005 g, customerUser2028, customer001, /orgs/1/sites/site001 g, customerUser2028, customer001, /orgs/1/sites/site002 g, customerUser2028, customer001, /orgs/1/sites/site003 g, customerUser2028, customer001, /orgs/1/sites/site004 g, customerUser2028, customer001, /orgs/1/sites/site005 g, customerUser2029, customer001, /orgs/1/sites/site001 g, customerUser2029, customer001, /orgs/1/sites/site002 g, customerUser2029, customer001, /orgs/1/sites/site003 g, customerUser2029, customer001, /orgs/1/sites/site004 g, customerUser2029, customer001, /orgs/1/sites/site005 g, customerUser2030, customer001, /orgs/1/sites/site001 g, customerUser2030, customer001, /orgs/1/sites/site002 g, customerUser2030, customer001, /orgs/1/sites/site003 g, customerUser2030, customer001, /orgs/1/sites/site004 g, customerUser2030, customer001, /orgs/1/sites/site005 g, customerUser2031, customer001, /orgs/1/sites/site001 g, customerUser2031, customer001, /orgs/1/sites/site002 g, customerUser2031, customer001, /orgs/1/sites/site003 g, customerUser2031, customer001, /orgs/1/sites/site004 g, customerUser2031, customer001, /orgs/1/sites/site005 g, customerUser2032, customer001, /orgs/1/sites/site001 g, customerUser2032, customer001, /orgs/1/sites/site002 g, customerUser2032, customer001, /orgs/1/sites/site003 g, customerUser2032, customer001, /orgs/1/sites/site004 g, customerUser2032, customer001, /orgs/1/sites/site005 g, customerUser2033, customer001, /orgs/1/sites/site001 g, customerUser2033, customer001, /orgs/1/sites/site002 g, customerUser2033, customer001, /orgs/1/sites/site003 g, customerUser2033, customer001, /orgs/1/sites/site004 g, customerUser2033, customer001, /orgs/1/sites/site005 g, customerUser2034, customer001, /orgs/1/sites/site001 g, customerUser2034, customer001, /orgs/1/sites/site002 g, customerUser2034, customer001, /orgs/1/sites/site003 g, customerUser2034, customer001, /orgs/1/sites/site004 g, customerUser2034, customer001, /orgs/1/sites/site005 g, customerUser2035, customer001, /orgs/1/sites/site001 g, customerUser2035, customer001, /orgs/1/sites/site002 g, customerUser2035, customer001, /orgs/1/sites/site003 g, customerUser2035, customer001, /orgs/1/sites/site004 g, customerUser2035, customer001, /orgs/1/sites/site005 g, customerUser2036, customer001, /orgs/1/sites/site001 g, customerUser2036, customer001, /orgs/1/sites/site002 g, customerUser2036, customer001, /orgs/1/sites/site003 g, customerUser2036, customer001, /orgs/1/sites/site004 g, customerUser2036, customer001, /orgs/1/sites/site005 g, customerUser2037, customer001, /orgs/1/sites/site001 g, customerUser2037, customer001, /orgs/1/sites/site002 g, customerUser2037, customer001, /orgs/1/sites/site003 g, customerUser2037, customer001, /orgs/1/sites/site004 g, customerUser2037, customer001, /orgs/1/sites/site005 g, customerUser2038, customer001, /orgs/1/sites/site001 g, customerUser2038, customer001, /orgs/1/sites/site002 g, customerUser2038, customer001, /orgs/1/sites/site003 g, customerUser2038, customer001, /orgs/1/sites/site004 g, customerUser2038, customer001, /orgs/1/sites/site005 g, customerUser2039, customer001, /orgs/1/sites/site001 g, customerUser2039, customer001, /orgs/1/sites/site002 g, customerUser2039, customer001, /orgs/1/sites/site003 g, customerUser2039, customer001, /orgs/1/sites/site004 g, customerUser2039, customer001, /orgs/1/sites/site005 g, customerUser2040, customer001, /orgs/1/sites/site001 g, customerUser2040, customer001, /orgs/1/sites/site002 g, customerUser2040, customer001, /orgs/1/sites/site003 g, customerUser2040, customer001, /orgs/1/sites/site004 g, customerUser2040, customer001, /orgs/1/sites/site005 g, customerUser2041, customer001, /orgs/1/sites/site001 g, customerUser2041, customer001, /orgs/1/sites/site002 g, customerUser2041, customer001, /orgs/1/sites/site003 g, customerUser2041, customer001, /orgs/1/sites/site004 g, customerUser2041, customer001, /orgs/1/sites/site005 g, customerUser2042, customer001, /orgs/1/sites/site001 g, customerUser2042, customer001, /orgs/1/sites/site002 g, customerUser2042, customer001, /orgs/1/sites/site003 g, customerUser2042, customer001, /orgs/1/sites/site004 g, customerUser2042, customer001, /orgs/1/sites/site005 g, customerUser2043, customer001, /orgs/1/sites/site001 g, customerUser2043, customer001, /orgs/1/sites/site002 g, customerUser2043, customer001, /orgs/1/sites/site003 g, customerUser2043, customer001, /orgs/1/sites/site004 g, customerUser2043, customer001, /orgs/1/sites/site005 g, customerUser2044, customer001, /orgs/1/sites/site001 g, customerUser2044, customer001, /orgs/1/sites/site002 g, customerUser2044, customer001, /orgs/1/sites/site003 g, customerUser2044, customer001, /orgs/1/sites/site004 g, customerUser2044, customer001, /orgs/1/sites/site005 g, customerUser2045, customer001, /orgs/1/sites/site001 g, customerUser2045, customer001, /orgs/1/sites/site002 g, customerUser2045, customer001, /orgs/1/sites/site003 g, customerUser2045, customer001, /orgs/1/sites/site004 g, customerUser2045, customer001, /orgs/1/sites/site005 g, customerUser2046, customer001, /orgs/1/sites/site001 g, customerUser2046, customer001, /orgs/1/sites/site002 g, customerUser2046, customer001, /orgs/1/sites/site003 g, customerUser2046, customer001, /orgs/1/sites/site004 g, customerUser2046, customer001, /orgs/1/sites/site005 g, customerUser2047, customer001, /orgs/1/sites/site001 g, customerUser2047, customer001, /orgs/1/sites/site002 g, customerUser2047, customer001, /orgs/1/sites/site003 g, customerUser2047, customer001, /orgs/1/sites/site004 g, customerUser2047, customer001, /orgs/1/sites/site005 g, customerUser2048, customer001, /orgs/1/sites/site001 g, customerUser2048, customer001, /orgs/1/sites/site002 g, customerUser2048, customer001, /orgs/1/sites/site003 g, customerUser2048, customer001, /orgs/1/sites/site004 g, customerUser2048, customer001, /orgs/1/sites/site005 g, customerUser2049, customer001, /orgs/1/sites/site001 g, customerUser2049, customer001, /orgs/1/sites/site002 g, customerUser2049, customer001, /orgs/1/sites/site003 g, customerUser2049, customer001, /orgs/1/sites/site004 g, customerUser2049, customer001, /orgs/1/sites/site005 g, customerUser2050, customer001, /orgs/1/sites/site001 g, customerUser2050, customer001, /orgs/1/sites/site002 g, customerUser2050, customer001, /orgs/1/sites/site003 g, customerUser2050, customer001, /orgs/1/sites/site004 g, customerUser2050, customer001, /orgs/1/sites/site005 # Group - staff001, / org2 g, staffUser1001, staff001, /orgs/2/sites/site001 g, staffUser1001, staff001, /orgs/2/sites/site002 g, staffUser1001, staff001, /orgs/2/sites/site003 g, staffUser1001, staff001, /orgs/2/sites/site004 g, staffUser1001, staff001, /orgs/2/sites/site005 g, staffUser1001, staff001, /orgs/2/sites/site001 g, staffUser1001, staff001, /orgs/2/sites/site002 g, staffUser1001, staff001, /orgs/2/sites/site003 g, staffUser1001, staff001, /orgs/2/sites/site004 g, staffUser1001, staff001, /orgs/2/sites/site005 g, staffUser1003, staff001, /orgs/2/sites/site001 g, staffUser1003, staff001, /orgs/2/sites/site002 g, staffUser1003, staff001, /orgs/2/sites/site003 g, staffUser1003, staff001, /orgs/2/sites/site004 g, staffUser1003, staff001, /orgs/2/sites/site005 g, staffUser1004, staff001, /orgs/2/sites/site001 g, staffUser1004, staff001, /orgs/2/sites/site002 g, staffUser1004, staff001, /orgs/2/sites/site003 g, staffUser1004, staff001, /orgs/2/sites/site004 g, staffUser1004, staff001, /orgs/2/sites/site005 g, staffUser1005, staff001, /orgs/2/sites/site001 g, staffUser1005, staff001, /orgs/2/sites/site002 g, staffUser1005, staff001, /orgs/2/sites/site003 g, staffUser1005, staff001, /orgs/2/sites/site004 g, staffUser1005, staff001, /orgs/2/sites/site005 g, staffUser1006, staff001, /orgs/2/sites/site001 g, staffUser1006, staff001, /orgs/2/sites/site002 g, staffUser1006, staff001, /orgs/2/sites/site003 g, staffUser1006, staff001, /orgs/2/sites/site004 g, staffUser1006, staff001, /orgs/2/sites/site005 g, staffUser1007, staff001, /orgs/2/sites/site001 g, staffUser1007, staff001, /orgs/2/sites/site002 g, staffUser1007, staff001, /orgs/2/sites/site003 g, staffUser1007, staff001, /orgs/2/sites/site004 g, staffUser1007, staff001, /orgs/2/sites/site005 g, staffUser1008, staff001, /orgs/2/sites/site001 g, staffUser1008, staff001, /orgs/2/sites/site002 g, staffUser1008, staff001, /orgs/2/sites/site003 g, staffUser1008, staff001, /orgs/2/sites/site004 g, staffUser1008, staff001, /orgs/2/sites/site005 g, staffUser1009, staff001, /orgs/2/sites/site001 g, staffUser1009, staff001, /orgs/2/sites/site002 g, staffUser1009, staff001, /orgs/2/sites/site003 g, staffUser1009, staff001, /orgs/2/sites/site004 g, staffUser1009, staff001, /orgs/2/sites/site005 g, staffUser1010, staff001, /orgs/2/sites/site001 g, staffUser1010, staff001, /orgs/2/sites/site002 g, staffUser1010, staff001, /orgs/2/sites/site003 g, staffUser1010, staff001, /orgs/2/sites/site004 g, staffUser1010, staff001, /orgs/2/sites/site005 g, staffUser1011, staff001, /orgs/2/sites/site001 g, staffUser1011, staff001, /orgs/2/sites/site002 g, staffUser1011, staff001, /orgs/2/sites/site003 g, staffUser1011, staff001, /orgs/2/sites/site004 g, staffUser1011, staff001, /orgs/2/sites/site005 g, staffUser1012, staff001, /orgs/2/sites/site001 g, staffUser1012, staff001, /orgs/2/sites/site002 g, staffUser1012, staff001, /orgs/2/sites/site003 g, staffUser1012, staff001, /orgs/2/sites/site004 g, staffUser1012, staff001, /orgs/2/sites/site005 g, staffUser1013, staff001, /orgs/2/sites/site001 g, staffUser1013, staff001, /orgs/2/sites/site002 g, staffUser1013, staff001, /orgs/2/sites/site003 g, staffUser1013, staff001, /orgs/2/sites/site004 g, staffUser1013, staff001, /orgs/2/sites/site005 g, staffUser1014, staff001, /orgs/2/sites/site001 g, staffUser1014, staff001, /orgs/2/sites/site002 g, staffUser1014, staff001, /orgs/2/sites/site003 g, staffUser1014, staff001, /orgs/2/sites/site004 g, staffUser1014, staff001, /orgs/2/sites/site005 g, staffUser1015, staff001, /orgs/2/sites/site001 g, staffUser1015, staff001, /orgs/2/sites/site002 g, staffUser1015, staff001, /orgs/2/sites/site003 g, staffUser1015, staff001, /orgs/2/sites/site004 g, staffUser1015, staff001, /orgs/2/sites/site005 g, staffUser1016, staff001, /orgs/2/sites/site001 g, staffUser1016, staff001, /orgs/2/sites/site002 g, staffUser1016, staff001, /orgs/2/sites/site003 g, staffUser1016, staff001, /orgs/2/sites/site004 g, staffUser1016, staff001, /orgs/2/sites/site005 g, staffUser1017, staff001, /orgs/2/sites/site001 g, staffUser1017, staff001, /orgs/2/sites/site002 g, staffUser1017, staff001, /orgs/2/sites/site003 g, staffUser1017, staff001, /orgs/2/sites/site004 g, staffUser1017, staff001, /orgs/2/sites/site005 g, staffUser1018, staff001, /orgs/2/sites/site001 g, staffUser1018, staff001, /orgs/2/sites/site002 g, staffUser1018, staff001, /orgs/2/sites/site003 g, staffUser1018, staff001, /orgs/2/sites/site004 g, staffUser1018, staff001, /orgs/2/sites/site005 g, staffUser1019, staff001, /orgs/2/sites/site001 g, staffUser1019, staff001, /orgs/2/sites/site002 g, staffUser1019, staff001, /orgs/2/sites/site003 g, staffUser1019, staff001, /orgs/2/sites/site004 g, staffUser1019, staff001, /orgs/2/sites/site005 g, staffUser1020, staff001, /orgs/2/sites/site001 g, staffUser1020, staff001, /orgs/2/sites/site002 g, staffUser1020, staff001, /orgs/2/sites/site003 g, staffUser1020, staff001, /orgs/2/sites/site004 g, staffUser1020, staff001, /orgs/2/sites/site005 g, staffUser1021, staff001, /orgs/2/sites/site001 g, staffUser1021, staff001, /orgs/2/sites/site002 g, staffUser1021, staff001, /orgs/2/sites/site003 g, staffUser1021, staff001, /orgs/2/sites/site004 g, staffUser1021, staff001, /orgs/2/sites/site005 g, staffUser1022, staff001, /orgs/2/sites/site001 g, staffUser1022, staff001, /orgs/2/sites/site002 g, staffUser1022, staff001, /orgs/2/sites/site003 g, staffUser1022, staff001, /orgs/2/sites/site004 g, staffUser1022, staff001, /orgs/2/sites/site005 g, staffUser1023, staff001, /orgs/2/sites/site001 g, staffUser1023, staff001, /orgs/2/sites/site002 g, staffUser1023, staff001, /orgs/2/sites/site003 g, staffUser1023, staff001, /orgs/2/sites/site004 g, staffUser1023, staff001, /orgs/2/sites/site005 g, staffUser1024, staff001, /orgs/2/sites/site001 g, staffUser1024, staff001, /orgs/2/sites/site002 g, staffUser1024, staff001, /orgs/2/sites/site003 g, staffUser1024, staff001, /orgs/2/sites/site004 g, staffUser1024, staff001, /orgs/2/sites/site005 g, staffUser1025, staff001, /orgs/2/sites/site001 g, staffUser1025, staff001, /orgs/2/sites/site002 g, staffUser1025, staff001, /orgs/2/sites/site003 g, staffUser1025, staff001, /orgs/2/sites/site004 g, staffUser1025, staff001, /orgs/2/sites/site005 g, staffUser1026, staff001, /orgs/2/sites/site001 g, staffUser1026, staff001, /orgs/2/sites/site002 g, staffUser1026, staff001, /orgs/2/sites/site003 g, staffUser1026, staff001, /orgs/2/sites/site004 g, staffUser1026, staff001, /orgs/2/sites/site005 g, staffUser1027, staff001, /orgs/2/sites/site001 g, staffUser1027, staff001, /orgs/2/sites/site002 g, staffUser1027, staff001, /orgs/2/sites/site003 g, staffUser1027, staff001, /orgs/2/sites/site004 g, staffUser1027, staff001, /orgs/2/sites/site005 g, staffUser1028, staff001, /orgs/2/sites/site001 g, staffUser1028, staff001, /orgs/2/sites/site002 g, staffUser1028, staff001, /orgs/2/sites/site003 g, staffUser1028, staff001, /orgs/2/sites/site004 g, staffUser1028, staff001, /orgs/2/sites/site005 g, staffUser1029, staff001, /orgs/2/sites/site001 g, staffUser1029, staff001, /orgs/2/sites/site002 g, staffUser1029, staff001, /orgs/2/sites/site003 g, staffUser1029, staff001, /orgs/2/sites/site004 g, staffUser1029, staff001, /orgs/2/sites/site005 g, staffUser1030, staff001, /orgs/2/sites/site001 g, staffUser1030, staff001, /orgs/2/sites/site002 g, staffUser1030, staff001, /orgs/2/sites/site003 g, staffUser1030, staff001, /orgs/2/sites/site004 g, staffUser1030, staff001, /orgs/2/sites/site005 g, staffUser1031, staff001, /orgs/2/sites/site001 g, staffUser1031, staff001, /orgs/2/sites/site002 g, staffUser1031, staff001, /orgs/2/sites/site003 g, staffUser1031, staff001, /orgs/2/sites/site004 g, staffUser1031, staff001, /orgs/2/sites/site005 g, staffUser1032, staff001, /orgs/2/sites/site001 g, staffUser1032, staff001, /orgs/2/sites/site002 g, staffUser1032, staff001, /orgs/2/sites/site003 g, staffUser1032, staff001, /orgs/2/sites/site004 g, staffUser1032, staff001, /orgs/2/sites/site005 g, staffUser1033, staff001, /orgs/2/sites/site001 g, staffUser1033, staff001, /orgs/2/sites/site002 g, staffUser1033, staff001, /orgs/2/sites/site003 g, staffUser1033, staff001, /orgs/2/sites/site004 g, staffUser1033, staff001, /orgs/2/sites/site005 g, staffUser1034, staff001, /orgs/2/sites/site001 g, staffUser1034, staff001, /orgs/2/sites/site002 g, staffUser1034, staff001, /orgs/2/sites/site003 g, staffUser1034, staff001, /orgs/2/sites/site004 g, staffUser1034, staff001, /orgs/2/sites/site005 g, staffUser1035, staff001, /orgs/2/sites/site001 g, staffUser1035, staff001, /orgs/2/sites/site002 g, staffUser1035, staff001, /orgs/2/sites/site003 g, staffUser1035, staff001, /orgs/2/sites/site004 g, staffUser1035, staff001, /orgs/2/sites/site005 g, staffUser1036, staff001, /orgs/2/sites/site001 g, staffUser1036, staff001, /orgs/2/sites/site002 g, staffUser1036, staff001, /orgs/2/sites/site003 g, staffUser1036, staff001, /orgs/2/sites/site004 g, staffUser1036, staff001, /orgs/2/sites/site005 g, staffUser1037, staff001, /orgs/2/sites/site001 g, staffUser1037, staff001, /orgs/2/sites/site002 g, staffUser1037, staff001, /orgs/2/sites/site003 g, staffUser1037, staff001, /orgs/2/sites/site004 g, staffUser1037, staff001, /orgs/2/sites/site005 g, staffUser1038, staff001, /orgs/2/sites/site001 g, staffUser1038, staff001, /orgs/2/sites/site002 g, staffUser1038, staff001, /orgs/2/sites/site003 g, staffUser1038, staff001, /orgs/2/sites/site004 g, staffUser1038, staff001, /orgs/2/sites/site005 g, staffUser1039, staff001, /orgs/2/sites/site001 g, staffUser1039, staff001, /orgs/2/sites/site002 g, staffUser1039, staff001, /orgs/2/sites/site003 g, staffUser1039, staff001, /orgs/2/sites/site004 g, staffUser1039, staff001, /orgs/2/sites/site005 g, staffUser1040, staff001, /orgs/2/sites/site001 g, staffUser1040, staff001, /orgs/2/sites/site002 g, staffUser1040, staff001, /orgs/2/sites/site003 g, staffUser1040, staff001, /orgs/2/sites/site004 g, staffUser1040, staff001, /orgs/2/sites/site005 g, staffUser1041, staff001, /orgs/2/sites/site001 g, staffUser1041, staff001, /orgs/2/sites/site002 g, staffUser1041, staff001, /orgs/2/sites/site003 g, staffUser1041, staff001, /orgs/2/sites/site004 g, staffUser1041, staff001, /orgs/2/sites/site005 g, staffUser1042, staff001, /orgs/2/sites/site001 g, staffUser1042, staff001, /orgs/2/sites/site002 g, staffUser1042, staff001, /orgs/2/sites/site003 g, staffUser1042, staff001, /orgs/2/sites/site004 g, staffUser1042, staff001, /orgs/2/sites/site005 g, staffUser1043, staff001, /orgs/2/sites/site001 g, staffUser1043, staff001, /orgs/2/sites/site002 g, staffUser1043, staff001, /orgs/2/sites/site003 g, staffUser1043, staff001, /orgs/2/sites/site004 g, staffUser1043, staff001, /orgs/2/sites/site005 g, staffUser1044, staff001, /orgs/2/sites/site001 g, staffUser1044, staff001, /orgs/2/sites/site002 g, staffUser1044, staff001, /orgs/2/sites/site003 g, staffUser1044, staff001, /orgs/2/sites/site004 g, staffUser1044, staff001, /orgs/2/sites/site005 g, staffUser1045, staff001, /orgs/2/sites/site001 g, staffUser1045, staff001, /orgs/2/sites/site002 g, staffUser1045, staff001, /orgs/2/sites/site003 g, staffUser1045, staff001, /orgs/2/sites/site004 g, staffUser1045, staff001, /orgs/2/sites/site005 g, staffUser1046, staff001, /orgs/2/sites/site001 g, staffUser1046, staff001, /orgs/2/sites/site002 g, staffUser1046, staff001, /orgs/2/sites/site003 g, staffUser1046, staff001, /orgs/2/sites/site004 g, staffUser1046, staff001, /orgs/2/sites/site005 g, staffUser1047, staff001, /orgs/2/sites/site001 g, staffUser1047, staff001, /orgs/2/sites/site002 g, staffUser1047, staff001, /orgs/2/sites/site003 g, staffUser1047, staff001, /orgs/2/sites/site004 g, staffUser1047, staff001, /orgs/2/sites/site005 g, staffUser1048, staff001, /orgs/2/sites/site001 g, staffUser1048, staff001, /orgs/2/sites/site002 g, staffUser1048, staff001, /orgs/2/sites/site003 g, staffUser1048, staff001, /orgs/2/sites/site004 g, staffUser1048, staff001, /orgs/2/sites/site005 g, staffUser1049, staff001, /orgs/2/sites/site001 g, staffUser1049, staff001, /orgs/2/sites/site002 g, staffUser1049, staff001, /orgs/2/sites/site003 g, staffUser1049, staff001, /orgs/2/sites/site004 g, staffUser1049, staff001, /orgs/2/sites/site005 g, staffUser1050, staff001, /orgs/2/sites/site001 g, staffUser1050, staff001, /orgs/2/sites/site002 g, staffUser1050, staff001, /orgs/2/sites/site003 g, staffUser1050, staff001, /orgs/2/sites/site004 g, staffUser1050, staff001, /orgs/2/sites/site005 # Group - staff001, / org2 g, staffUser2001, staff001, /orgs/2/sites/site001 g, staffUser2001, staff001, /orgs/2/sites/site002 g, staffUser2001, staff001, /orgs/2/sites/site003 g, staffUser2001, staff001, /orgs/2/sites/site004 g, staffUser2001, staff001, /orgs/2/sites/site005 g, staffUser2001, staff001, /orgs/2/sites/site001 g, staffUser2001, staff001, /orgs/2/sites/site002 g, staffUser2001, staff001, /orgs/2/sites/site003 g, staffUser2001, staff001, /orgs/2/sites/site004 g, staffUser2001, staff001, /orgs/2/sites/site005 g, staffUser2003, staff001, /orgs/2/sites/site001 g, staffUser2003, staff001, /orgs/2/sites/site002 g, staffUser2003, staff001, /orgs/2/sites/site003 g, staffUser2003, staff001, /orgs/2/sites/site004 g, staffUser2003, staff001, /orgs/2/sites/site005 g, staffUser2004, staff001, /orgs/2/sites/site001 g, staffUser2004, staff001, /orgs/2/sites/site002 g, staffUser2004, staff001, /orgs/2/sites/site003 g, staffUser2004, staff001, /orgs/2/sites/site004 g, staffUser2004, staff001, /orgs/2/sites/site005 g, staffUser2005, staff001, /orgs/2/sites/site001 g, staffUser2005, staff001, /orgs/2/sites/site002 g, staffUser2005, staff001, /orgs/2/sites/site003 g, staffUser2005, staff001, /orgs/2/sites/site004 g, staffUser2005, staff001, /orgs/2/sites/site005 g, staffUser2006, staff001, /orgs/2/sites/site001 g, staffUser2006, staff001, /orgs/2/sites/site002 g, staffUser2006, staff001, /orgs/2/sites/site003 g, staffUser2006, staff001, /orgs/2/sites/site004 g, staffUser2006, staff001, /orgs/2/sites/site005 g, staffUser2007, staff001, /orgs/2/sites/site001 g, staffUser2007, staff001, /orgs/2/sites/site002 g, staffUser2007, staff001, /orgs/2/sites/site003 g, staffUser2007, staff001, /orgs/2/sites/site004 g, staffUser2007, staff001, /orgs/2/sites/site005 g, staffUser2008, staff001, /orgs/2/sites/site001 g, staffUser2008, staff001, /orgs/2/sites/site002 g, staffUser2008, staff001, /orgs/2/sites/site003 g, staffUser2008, staff001, /orgs/2/sites/site004 g, staffUser2008, staff001, /orgs/2/sites/site005 g, staffUser2009, staff001, /orgs/2/sites/site001 g, staffUser2009, staff001, /orgs/2/sites/site002 g, staffUser2009, staff001, /orgs/2/sites/site003 g, staffUser2009, staff001, /orgs/2/sites/site004 g, staffUser2009, staff001, /orgs/2/sites/site005 g, staffUser2010, staff001, /orgs/2/sites/site001 g, staffUser2010, staff001, /orgs/2/sites/site002 g, staffUser2010, staff001, /orgs/2/sites/site003 g, staffUser2010, staff001, /orgs/2/sites/site004 g, staffUser2010, staff001, /orgs/2/sites/site005 g, staffUser2011, staff001, /orgs/2/sites/site001 g, staffUser2011, staff001, /orgs/2/sites/site002 g, staffUser2011, staff001, /orgs/2/sites/site003 g, staffUser2011, staff001, /orgs/2/sites/site004 g, staffUser2011, staff001, /orgs/2/sites/site005 g, staffUser2012, staff001, /orgs/2/sites/site001 g, staffUser2012, staff001, /orgs/2/sites/site002 g, staffUser2012, staff001, /orgs/2/sites/site003 g, staffUser2012, staff001, /orgs/2/sites/site004 g, staffUser2012, staff001, /orgs/2/sites/site005 g, staffUser2013, staff001, /orgs/2/sites/site001 g, staffUser2013, staff001, /orgs/2/sites/site002 g, staffUser2013, staff001, /orgs/2/sites/site003 g, staffUser2013, staff001, /orgs/2/sites/site004 g, staffUser2013, staff001, /orgs/2/sites/site005 g, staffUser2014, staff001, /orgs/2/sites/site001 g, staffUser2014, staff001, /orgs/2/sites/site002 g, staffUser2014, staff001, /orgs/2/sites/site003 g, staffUser2014, staff001, /orgs/2/sites/site004 g, staffUser2014, staff001, /orgs/2/sites/site005 g, staffUser2015, staff001, /orgs/2/sites/site001 g, staffUser2015, staff001, /orgs/2/sites/site002 g, staffUser2015, staff001, /orgs/2/sites/site003 g, staffUser2015, staff001, /orgs/2/sites/site004 g, staffUser2015, staff001, /orgs/2/sites/site005 g, staffUser2016, staff001, /orgs/2/sites/site001 g, staffUser2016, staff001, /orgs/2/sites/site002 g, staffUser2016, staff001, /orgs/2/sites/site003 g, staffUser2016, staff001, /orgs/2/sites/site004 g, staffUser2016, staff001, /orgs/2/sites/site005 g, staffUser2017, staff001, /orgs/2/sites/site001 g, staffUser2017, staff001, /orgs/2/sites/site002 g, staffUser2017, staff001, /orgs/2/sites/site003 g, staffUser2017, staff001, /orgs/2/sites/site004 g, staffUser2017, staff001, /orgs/2/sites/site005 g, staffUser2018, staff001, /orgs/2/sites/site001 g, staffUser2018, staff001, /orgs/2/sites/site002 g, staffUser2018, staff001, /orgs/2/sites/site003 g, staffUser2018, staff001, /orgs/2/sites/site004 g, staffUser2018, staff001, /orgs/2/sites/site005 g, staffUser2019, staff001, /orgs/2/sites/site001 g, staffUser2019, staff001, /orgs/2/sites/site002 g, staffUser2019, staff001, /orgs/2/sites/site003 g, staffUser2019, staff001, /orgs/2/sites/site004 g, staffUser2019, staff001, /orgs/2/sites/site005 g, staffUser2020, staff001, /orgs/2/sites/site001 g, staffUser2020, staff001, /orgs/2/sites/site002 g, staffUser2020, staff001, /orgs/2/sites/site003 g, staffUser2020, staff001, /orgs/2/sites/site004 g, staffUser2020, staff001, /orgs/2/sites/site005 g, staffUser2021, staff001, /orgs/2/sites/site001 g, staffUser2021, staff001, /orgs/2/sites/site002 g, staffUser2021, staff001, /orgs/2/sites/site003 g, staffUser2021, staff001, /orgs/2/sites/site004 g, staffUser2021, staff001, /orgs/2/sites/site005 g, staffUser2022, staff001, /orgs/2/sites/site001 g, staffUser2022, staff001, /orgs/2/sites/site002 g, staffUser2022, staff001, /orgs/2/sites/site003 g, staffUser2022, staff001, /orgs/2/sites/site004 g, staffUser2022, staff001, /orgs/2/sites/site005 g, staffUser2023, staff001, /orgs/2/sites/site001 g, staffUser2023, staff001, /orgs/2/sites/site002 g, staffUser2023, staff001, /orgs/2/sites/site003 g, staffUser2023, staff001, /orgs/2/sites/site004 g, staffUser2023, staff001, /orgs/2/sites/site005 g, staffUser2024, staff001, /orgs/2/sites/site001 g, staffUser2024, staff001, /orgs/2/sites/site002 g, staffUser2024, staff001, /orgs/2/sites/site003 g, staffUser2024, staff001, /orgs/2/sites/site004 g, staffUser2024, staff001, /orgs/2/sites/site005 g, staffUser2025, staff001, /orgs/2/sites/site001 g, staffUser2025, staff001, /orgs/2/sites/site002 g, staffUser2025, staff001, /orgs/2/sites/site003 g, staffUser2025, staff001, /orgs/2/sites/site004 g, staffUser2025, staff001, /orgs/2/sites/site005 g, staffUser2026, staff001, /orgs/2/sites/site001 g, staffUser2026, staff001, /orgs/2/sites/site002 g, staffUser2026, staff001, /orgs/2/sites/site003 g, staffUser2026, staff001, /orgs/2/sites/site004 g, staffUser2026, staff001, /orgs/2/sites/site005 g, staffUser2027, staff001, /orgs/2/sites/site001 g, staffUser2027, staff001, /orgs/2/sites/site002 g, staffUser2027, staff001, /orgs/2/sites/site003 g, staffUser2027, staff001, /orgs/2/sites/site004 g, staffUser2027, staff001, /orgs/2/sites/site005 g, staffUser2028, staff001, /orgs/2/sites/site001 g, staffUser2028, staff001, /orgs/2/sites/site002 g, staffUser2028, staff001, /orgs/2/sites/site003 g, staffUser2028, staff001, /orgs/2/sites/site004 g, staffUser2028, staff001, /orgs/2/sites/site005 g, staffUser2029, staff001, /orgs/2/sites/site001 g, staffUser2029, staff001, /orgs/2/sites/site002 g, staffUser2029, staff001, /orgs/2/sites/site003 g, staffUser2029, staff001, /orgs/2/sites/site004 g, staffUser2029, staff001, /orgs/2/sites/site005 g, staffUser2030, staff001, /orgs/2/sites/site001 g, staffUser2030, staff001, /orgs/2/sites/site002 g, staffUser2030, staff001, /orgs/2/sites/site003 g, staffUser2030, staff001, /orgs/2/sites/site004 g, staffUser2030, staff001, /orgs/2/sites/site005 g, staffUser2031, staff001, /orgs/2/sites/site001 g, staffUser2031, staff001, /orgs/2/sites/site002 g, staffUser2031, staff001, /orgs/2/sites/site003 g, staffUser2031, staff001, /orgs/2/sites/site004 g, staffUser2031, staff001, /orgs/2/sites/site005 g, staffUser2032, staff001, /orgs/2/sites/site001 g, staffUser2032, staff001, /orgs/2/sites/site002 g, staffUser2032, staff001, /orgs/2/sites/site003 g, staffUser2032, staff001, /orgs/2/sites/site004 g, staffUser2032, staff001, /orgs/2/sites/site005 g, staffUser2033, staff001, /orgs/2/sites/site001 g, staffUser2033, staff001, /orgs/2/sites/site002 g, staffUser2033, staff001, /orgs/2/sites/site003 g, staffUser2033, staff001, /orgs/2/sites/site004 g, staffUser2033, staff001, /orgs/2/sites/site005 g, staffUser2034, staff001, /orgs/2/sites/site001 g, staffUser2034, staff001, /orgs/2/sites/site002 g, staffUser2034, staff001, /orgs/2/sites/site003 g, staffUser2034, staff001, /orgs/2/sites/site004 g, staffUser2034, staff001, /orgs/2/sites/site005 g, staffUser2035, staff001, /orgs/2/sites/site001 g, staffUser2035, staff001, /orgs/2/sites/site002 g, staffUser2035, staff001, /orgs/2/sites/site003 g, staffUser2035, staff001, /orgs/2/sites/site004 g, staffUser2035, staff001, /orgs/2/sites/site005 g, staffUser2036, staff001, /orgs/2/sites/site001 g, staffUser2036, staff001, /orgs/2/sites/site002 g, staffUser2036, staff001, /orgs/2/sites/site003 g, staffUser2036, staff001, /orgs/2/sites/site004 g, staffUser2036, staff001, /orgs/2/sites/site005 g, staffUser2037, staff001, /orgs/2/sites/site001 g, staffUser2037, staff001, /orgs/2/sites/site002 g, staffUser2037, staff001, /orgs/2/sites/site003 g, staffUser2037, staff001, /orgs/2/sites/site004 g, staffUser2037, staff001, /orgs/2/sites/site005 g, staffUser2038, staff001, /orgs/2/sites/site001 g, staffUser2038, staff001, /orgs/2/sites/site002 g, staffUser2038, staff001, /orgs/2/sites/site003 g, staffUser2038, staff001, /orgs/2/sites/site004 g, staffUser2038, staff001, /orgs/2/sites/site005 g, staffUser2039, staff001, /orgs/2/sites/site001 g, staffUser2039, staff001, /orgs/2/sites/site002 g, staffUser2039, staff001, /orgs/2/sites/site003 g, staffUser2039, staff001, /orgs/2/sites/site004 g, staffUser2039, staff001, /orgs/2/sites/site005 g, staffUser2040, staff001, /orgs/2/sites/site001 g, staffUser2040, staff001, /orgs/2/sites/site002 g, staffUser2040, staff001, /orgs/2/sites/site003 g, staffUser2040, staff001, /orgs/2/sites/site004 g, staffUser2040, staff001, /orgs/2/sites/site005 g, staffUser2041, staff001, /orgs/2/sites/site001 g, staffUser2041, staff001, /orgs/2/sites/site002 g, staffUser2041, staff001, /orgs/2/sites/site003 g, staffUser2041, staff001, /orgs/2/sites/site004 g, staffUser2041, staff001, /orgs/2/sites/site005 g, staffUser2042, staff001, /orgs/2/sites/site001 g, staffUser2042, staff001, /orgs/2/sites/site002 g, staffUser2042, staff001, /orgs/2/sites/site003 g, staffUser2042, staff001, /orgs/2/sites/site004 g, staffUser2042, staff001, /orgs/2/sites/site005 g, staffUser2043, staff001, /orgs/2/sites/site001 g, staffUser2043, staff001, /orgs/2/sites/site002 g, staffUser2043, staff001, /orgs/2/sites/site003 g, staffUser2043, staff001, /orgs/2/sites/site004 g, staffUser2043, staff001, /orgs/2/sites/site005 g, staffUser2044, staff001, /orgs/2/sites/site001 g, staffUser2044, staff001, /orgs/2/sites/site002 g, staffUser2044, staff001, /orgs/2/sites/site003 g, staffUser2044, staff001, /orgs/2/sites/site004 g, staffUser2044, staff001, /orgs/2/sites/site005 g, staffUser2045, staff001, /orgs/2/sites/site001 g, staffUser2045, staff001, /orgs/2/sites/site002 g, staffUser2045, staff001, /orgs/2/sites/site003 g, staffUser2045, staff001, /orgs/2/sites/site004 g, staffUser2045, staff001, /orgs/2/sites/site005 g, staffUser2046, staff001, /orgs/2/sites/site001 g, staffUser2046, staff001, /orgs/2/sites/site002 g, staffUser2046, staff001, /orgs/2/sites/site003 g, staffUser2046, staff001, /orgs/2/sites/site004 g, staffUser2046, staff001, /orgs/2/sites/site005 g, staffUser2047, staff001, /orgs/2/sites/site001 g, staffUser2047, staff001, /orgs/2/sites/site002 g, staffUser2047, staff001, /orgs/2/sites/site003 g, staffUser2047, staff001, /orgs/2/sites/site004 g, staffUser2047, staff001, /orgs/2/sites/site005 g, staffUser2048, staff001, /orgs/2/sites/site001 g, staffUser2048, staff001, /orgs/2/sites/site002 g, staffUser2048, staff001, /orgs/2/sites/site003 g, staffUser2048, staff001, /orgs/2/sites/site004 g, staffUser2048, staff001, /orgs/2/sites/site005 g, staffUser2049, staff001, /orgs/2/sites/site001 g, staffUser2049, staff001, /orgs/2/sites/site002 g, staffUser2049, staff001, /orgs/2/sites/site003 g, staffUser2049, staff001, /orgs/2/sites/site004 g, staffUser2049, staff001, /orgs/2/sites/site005 g, staffUser2050, staff001, /orgs/2/sites/site001 g, staffUser2050, staff001, /orgs/2/sites/site002 g, staffUser2050, staff001, /orgs/2/sites/site003 g, staffUser2050, staff001, /orgs/2/sites/site004 g, staffUser2050, staff001, /orgs/2/sites/site005 # Group - manager001, / org2 g, managerUser1001, manager001, /orgs/2/sites/site001 g, managerUser1001, manager001, /orgs/2/sites/site002 g, managerUser1001, manager001, /orgs/2/sites/site003 g, managerUser1001, manager001, /orgs/2/sites/site004 g, managerUser1001, manager001, /orgs/2/sites/site005 g, managerUser1001, manager001, /orgs/2/sites/site001 g, managerUser1001, manager001, /orgs/2/sites/site002 g, managerUser1001, manager001, /orgs/2/sites/site003 g, managerUser1001, manager001, /orgs/2/sites/site004 g, managerUser1001, manager001, /orgs/2/sites/site005 g, managerUser1003, manager001, /orgs/2/sites/site001 g, managerUser1003, manager001, /orgs/2/sites/site002 g, managerUser1003, manager001, /orgs/2/sites/site003 g, managerUser1003, manager001, /orgs/2/sites/site004 g, managerUser1003, manager001, /orgs/2/sites/site005 g, managerUser1004, manager001, /orgs/2/sites/site001 g, managerUser1004, manager001, /orgs/2/sites/site002 g, managerUser1004, manager001, /orgs/2/sites/site003 g, managerUser1004, manager001, /orgs/2/sites/site004 g, managerUser1004, manager001, /orgs/2/sites/site005 g, managerUser1005, manager001, /orgs/2/sites/site001 g, managerUser1005, manager001, /orgs/2/sites/site002 g, managerUser1005, manager001, /orgs/2/sites/site003 g, managerUser1005, manager001, /orgs/2/sites/site004 g, managerUser1005, manager001, /orgs/2/sites/site005 g, managerUser1006, manager001, /orgs/2/sites/site001 g, managerUser1006, manager001, /orgs/2/sites/site002 g, managerUser1006, manager001, /orgs/2/sites/site003 g, managerUser1006, manager001, /orgs/2/sites/site004 g, managerUser1006, manager001, /orgs/2/sites/site005 g, managerUser1007, manager001, /orgs/2/sites/site001 g, managerUser1007, manager001, /orgs/2/sites/site002 g, managerUser1007, manager001, /orgs/2/sites/site003 g, managerUser1007, manager001, /orgs/2/sites/site004 g, managerUser1007, manager001, /orgs/2/sites/site005 g, managerUser1008, manager001, /orgs/2/sites/site001 g, managerUser1008, manager001, /orgs/2/sites/site002 g, managerUser1008, manager001, /orgs/2/sites/site003 g, managerUser1008, manager001, /orgs/2/sites/site004 g, managerUser1008, manager001, /orgs/2/sites/site005 g, managerUser1009, manager001, /orgs/2/sites/site001 g, managerUser1009, manager001, /orgs/2/sites/site002 g, managerUser1009, manager001, /orgs/2/sites/site003 g, managerUser1009, manager001, /orgs/2/sites/site004 g, managerUser1009, manager001, /orgs/2/sites/site005 g, managerUser1010, manager001, /orgs/2/sites/site001 g, managerUser1010, manager001, /orgs/2/sites/site002 g, managerUser1010, manager001, /orgs/2/sites/site003 g, managerUser1010, manager001, /orgs/2/sites/site004 g, managerUser1010, manager001, /orgs/2/sites/site005 g, managerUser1011, manager001, /orgs/2/sites/site001 g, managerUser1011, manager001, /orgs/2/sites/site002 g, managerUser1011, manager001, /orgs/2/sites/site003 g, managerUser1011, manager001, /orgs/2/sites/site004 g, managerUser1011, manager001, /orgs/2/sites/site005 g, managerUser1012, manager001, /orgs/2/sites/site001 g, managerUser1012, manager001, /orgs/2/sites/site002 g, managerUser1012, manager001, /orgs/2/sites/site003 g, managerUser1012, manager001, /orgs/2/sites/site004 g, managerUser1012, manager001, /orgs/2/sites/site005 g, managerUser1013, manager001, /orgs/2/sites/site001 g, managerUser1013, manager001, /orgs/2/sites/site002 g, managerUser1013, manager001, /orgs/2/sites/site003 g, managerUser1013, manager001, /orgs/2/sites/site004 g, managerUser1013, manager001, /orgs/2/sites/site005 g, managerUser1014, manager001, /orgs/2/sites/site001 g, managerUser1014, manager001, /orgs/2/sites/site002 g, managerUser1014, manager001, /orgs/2/sites/site003 g, managerUser1014, manager001, /orgs/2/sites/site004 g, managerUser1014, manager001, /orgs/2/sites/site005 g, managerUser1015, manager001, /orgs/2/sites/site001 g, managerUser1015, manager001, /orgs/2/sites/site002 g, managerUser1015, manager001, /orgs/2/sites/site003 g, managerUser1015, manager001, /orgs/2/sites/site004 g, managerUser1015, manager001, /orgs/2/sites/site005 g, managerUser1016, manager001, /orgs/2/sites/site001 g, managerUser1016, manager001, /orgs/2/sites/site002 g, managerUser1016, manager001, /orgs/2/sites/site003 g, managerUser1016, manager001, /orgs/2/sites/site004 g, managerUser1016, manager001, /orgs/2/sites/site005 g, managerUser1017, manager001, /orgs/2/sites/site001 g, managerUser1017, manager001, /orgs/2/sites/site002 g, managerUser1017, manager001, /orgs/2/sites/site003 g, managerUser1017, manager001, /orgs/2/sites/site004 g, managerUser1017, manager001, /orgs/2/sites/site005 g, managerUser1018, manager001, /orgs/2/sites/site001 g, managerUser1018, manager001, /orgs/2/sites/site002 g, managerUser1018, manager001, /orgs/2/sites/site003 g, managerUser1018, manager001, /orgs/2/sites/site004 g, managerUser1018, manager001, /orgs/2/sites/site005 g, managerUser1019, manager001, /orgs/2/sites/site001 g, managerUser1019, manager001, /orgs/2/sites/site002 g, managerUser1019, manager001, /orgs/2/sites/site003 g, managerUser1019, manager001, /orgs/2/sites/site004 g, managerUser1019, manager001, /orgs/2/sites/site005 g, managerUser1020, manager001, /orgs/2/sites/site001 g, managerUser1020, manager001, /orgs/2/sites/site002 g, managerUser1020, manager001, /orgs/2/sites/site003 g, managerUser1020, manager001, /orgs/2/sites/site004 g, managerUser1020, manager001, /orgs/2/sites/site005 g, managerUser1021, manager001, /orgs/2/sites/site001 g, managerUser1021, manager001, /orgs/2/sites/site002 g, managerUser1021, manager001, /orgs/2/sites/site003 g, managerUser1021, manager001, /orgs/2/sites/site004 g, managerUser1021, manager001, /orgs/2/sites/site005 g, managerUser1022, manager001, /orgs/2/sites/site001 g, managerUser1022, manager001, /orgs/2/sites/site002 g, managerUser1022, manager001, /orgs/2/sites/site003 g, managerUser1022, manager001, /orgs/2/sites/site004 g, managerUser1022, manager001, /orgs/2/sites/site005 g, managerUser1023, manager001, /orgs/2/sites/site001 g, managerUser1023, manager001, /orgs/2/sites/site002 g, managerUser1023, manager001, /orgs/2/sites/site003 g, managerUser1023, manager001, /orgs/2/sites/site004 g, managerUser1023, manager001, /orgs/2/sites/site005 g, managerUser1024, manager001, /orgs/2/sites/site001 g, managerUser1024, manager001, /orgs/2/sites/site002 g, managerUser1024, manager001, /orgs/2/sites/site003 g, managerUser1024, manager001, /orgs/2/sites/site004 g, managerUser1024, manager001, /orgs/2/sites/site005 g, managerUser1025, manager001, /orgs/2/sites/site001 g, managerUser1025, manager001, /orgs/2/sites/site002 g, managerUser1025, manager001, /orgs/2/sites/site003 g, managerUser1025, manager001, /orgs/2/sites/site004 g, managerUser1025, manager001, /orgs/2/sites/site005 g, managerUser1026, manager001, /orgs/2/sites/site001 g, managerUser1026, manager001, /orgs/2/sites/site002 g, managerUser1026, manager001, /orgs/2/sites/site003 g, managerUser1026, manager001, /orgs/2/sites/site004 g, managerUser1026, manager001, /orgs/2/sites/site005 g, managerUser1027, manager001, /orgs/2/sites/site001 g, managerUser1027, manager001, /orgs/2/sites/site002 g, managerUser1027, manager001, /orgs/2/sites/site003 g, managerUser1027, manager001, /orgs/2/sites/site004 g, managerUser1027, manager001, /orgs/2/sites/site005 g, managerUser1028, manager001, /orgs/2/sites/site001 g, managerUser1028, manager001, /orgs/2/sites/site002 g, managerUser1028, manager001, /orgs/2/sites/site003 g, managerUser1028, manager001, /orgs/2/sites/site004 g, managerUser1028, manager001, /orgs/2/sites/site005 g, managerUser1029, manager001, /orgs/2/sites/site001 g, managerUser1029, manager001, /orgs/2/sites/site002 g, managerUser1029, manager001, /orgs/2/sites/site003 g, managerUser1029, manager001, /orgs/2/sites/site004 g, managerUser1029, manager001, /orgs/2/sites/site005 g, managerUser1030, manager001, /orgs/2/sites/site001 g, managerUser1030, manager001, /orgs/2/sites/site002 g, managerUser1030, manager001, /orgs/2/sites/site003 g, managerUser1030, manager001, /orgs/2/sites/site004 g, managerUser1030, manager001, /orgs/2/sites/site005 g, managerUser1031, manager001, /orgs/2/sites/site001 g, managerUser1031, manager001, /orgs/2/sites/site002 g, managerUser1031, manager001, /orgs/2/sites/site003 g, managerUser1031, manager001, /orgs/2/sites/site004 g, managerUser1031, manager001, /orgs/2/sites/site005 g, managerUser1032, manager001, /orgs/2/sites/site001 g, managerUser1032, manager001, /orgs/2/sites/site002 g, managerUser1032, manager001, /orgs/2/sites/site003 g, managerUser1032, manager001, /orgs/2/sites/site004 g, managerUser1032, manager001, /orgs/2/sites/site005 g, managerUser1033, manager001, /orgs/2/sites/site001 g, managerUser1033, manager001, /orgs/2/sites/site002 g, managerUser1033, manager001, /orgs/2/sites/site003 g, managerUser1033, manager001, /orgs/2/sites/site004 g, managerUser1033, manager001, /orgs/2/sites/site005 g, managerUser1034, manager001, /orgs/2/sites/site001 g, managerUser1034, manager001, /orgs/2/sites/site002 g, managerUser1034, manager001, /orgs/2/sites/site003 g, managerUser1034, manager001, /orgs/2/sites/site004 g, managerUser1034, manager001, /orgs/2/sites/site005 g, managerUser1035, manager001, /orgs/2/sites/site001 g, managerUser1035, manager001, /orgs/2/sites/site002 g, managerUser1035, manager001, /orgs/2/sites/site003 g, managerUser1035, manager001, /orgs/2/sites/site004 g, managerUser1035, manager001, /orgs/2/sites/site005 g, managerUser1036, manager001, /orgs/2/sites/site001 g, managerUser1036, manager001, /orgs/2/sites/site002 g, managerUser1036, manager001, /orgs/2/sites/site003 g, managerUser1036, manager001, /orgs/2/sites/site004 g, managerUser1036, manager001, /orgs/2/sites/site005 g, managerUser1037, manager001, /orgs/2/sites/site001 g, managerUser1037, manager001, /orgs/2/sites/site002 g, managerUser1037, manager001, /orgs/2/sites/site003 g, managerUser1037, manager001, /orgs/2/sites/site004 g, managerUser1037, manager001, /orgs/2/sites/site005 g, managerUser1038, manager001, /orgs/2/sites/site001 g, managerUser1038, manager001, /orgs/2/sites/site002 g, managerUser1038, manager001, /orgs/2/sites/site003 g, managerUser1038, manager001, /orgs/2/sites/site004 g, managerUser1038, manager001, /orgs/2/sites/site005 g, managerUser1039, manager001, /orgs/2/sites/site001 g, managerUser1039, manager001, /orgs/2/sites/site002 g, managerUser1039, manager001, /orgs/2/sites/site003 g, managerUser1039, manager001, /orgs/2/sites/site004 g, managerUser1039, manager001, /orgs/2/sites/site005 g, managerUser1040, manager001, /orgs/2/sites/site001 g, managerUser1040, manager001, /orgs/2/sites/site002 g, managerUser1040, manager001, /orgs/2/sites/site003 g, managerUser1040, manager001, /orgs/2/sites/site004 g, managerUser1040, manager001, /orgs/2/sites/site005 g, managerUser1041, manager001, /orgs/2/sites/site001 g, managerUser1041, manager001, /orgs/2/sites/site002 g, managerUser1041, manager001, /orgs/2/sites/site003 g, managerUser1041, manager001, /orgs/2/sites/site004 g, managerUser1041, manager001, /orgs/2/sites/site005 g, managerUser1042, manager001, /orgs/2/sites/site001 g, managerUser1042, manager001, /orgs/2/sites/site002 g, managerUser1042, manager001, /orgs/2/sites/site003 g, managerUser1042, manager001, /orgs/2/sites/site004 g, managerUser1042, manager001, /orgs/2/sites/site005 g, managerUser1043, manager001, /orgs/2/sites/site001 g, managerUser1043, manager001, /orgs/2/sites/site002 g, managerUser1043, manager001, /orgs/2/sites/site003 g, managerUser1043, manager001, /orgs/2/sites/site004 g, managerUser1043, manager001, /orgs/2/sites/site005 g, managerUser1044, manager001, /orgs/2/sites/site001 g, managerUser1044, manager001, /orgs/2/sites/site002 g, managerUser1044, manager001, /orgs/2/sites/site003 g, managerUser1044, manager001, /orgs/2/sites/site004 g, managerUser1044, manager001, /orgs/2/sites/site005 g, managerUser1045, manager001, /orgs/2/sites/site001 g, managerUser1045, manager001, /orgs/2/sites/site002 g, managerUser1045, manager001, /orgs/2/sites/site003 g, managerUser1045, manager001, /orgs/2/sites/site004 g, managerUser1045, manager001, /orgs/2/sites/site005 g, managerUser1046, manager001, /orgs/2/sites/site001 g, managerUser1046, manager001, /orgs/2/sites/site002 g, managerUser1046, manager001, /orgs/2/sites/site003 g, managerUser1046, manager001, /orgs/2/sites/site004 g, managerUser1046, manager001, /orgs/2/sites/site005 g, managerUser1047, manager001, /orgs/2/sites/site001 g, managerUser1047, manager001, /orgs/2/sites/site002 g, managerUser1047, manager001, /orgs/2/sites/site003 g, managerUser1047, manager001, /orgs/2/sites/site004 g, managerUser1047, manager001, /orgs/2/sites/site005 g, managerUser1048, manager001, /orgs/2/sites/site001 g, managerUser1048, manager001, /orgs/2/sites/site002 g, managerUser1048, manager001, /orgs/2/sites/site003 g, managerUser1048, manager001, /orgs/2/sites/site004 g, managerUser1048, manager001, /orgs/2/sites/site005 g, managerUser1049, manager001, /orgs/2/sites/site001 g, managerUser1049, manager001, /orgs/2/sites/site002 g, managerUser1049, manager001, /orgs/2/sites/site003 g, managerUser1049, manager001, /orgs/2/sites/site004 g, managerUser1049, manager001, /orgs/2/sites/site005 g, managerUser1050, manager001, /orgs/2/sites/site001 g, managerUser1050, manager001, /orgs/2/sites/site002 g, managerUser1050, manager001, /orgs/2/sites/site003 g, managerUser1050, manager001, /orgs/2/sites/site004 g, managerUser1050, manager001, /orgs/2/sites/site005 # Group - manager001, / org2 g, managerUser2001, manager001, /orgs/2/sites/site001 g, managerUser2001, manager001, /orgs/2/sites/site002 g, managerUser2001, manager001, /orgs/2/sites/site003 g, managerUser2001, manager001, /orgs/2/sites/site004 g, managerUser2001, manager001, /orgs/2/sites/site005 g, managerUser2001, manager001, /orgs/2/sites/site001 g, managerUser2001, manager001, /orgs/2/sites/site002 g, managerUser2001, manager001, /orgs/2/sites/site003 g, managerUser2001, manager001, /orgs/2/sites/site004 g, managerUser2001, manager001, /orgs/2/sites/site005 g, managerUser2003, manager001, /orgs/2/sites/site001 g, managerUser2003, manager001, /orgs/2/sites/site002 g, managerUser2003, manager001, /orgs/2/sites/site003 g, managerUser2003, manager001, /orgs/2/sites/site004 g, managerUser2003, manager001, /orgs/2/sites/site005 g, managerUser2004, manager001, /orgs/2/sites/site001 g, managerUser2004, manager001, /orgs/2/sites/site002 g, managerUser2004, manager001, /orgs/2/sites/site003 g, managerUser2004, manager001, /orgs/2/sites/site004 g, managerUser2004, manager001, /orgs/2/sites/site005 g, managerUser2005, manager001, /orgs/2/sites/site001 g, managerUser2005, manager001, /orgs/2/sites/site002 g, managerUser2005, manager001, /orgs/2/sites/site003 g, managerUser2005, manager001, /orgs/2/sites/site004 g, managerUser2005, manager001, /orgs/2/sites/site005 g, managerUser2006, manager001, /orgs/2/sites/site001 g, managerUser2006, manager001, /orgs/2/sites/site002 g, managerUser2006, manager001, /orgs/2/sites/site003 g, managerUser2006, manager001, /orgs/2/sites/site004 g, managerUser2006, manager001, /orgs/2/sites/site005 g, managerUser2007, manager001, /orgs/2/sites/site001 g, managerUser2007, manager001, /orgs/2/sites/site002 g, managerUser2007, manager001, /orgs/2/sites/site003 g, managerUser2007, manager001, /orgs/2/sites/site004 g, managerUser2007, manager001, /orgs/2/sites/site005 g, managerUser2008, manager001, /orgs/2/sites/site001 g, managerUser2008, manager001, /orgs/2/sites/site002 g, managerUser2008, manager001, /orgs/2/sites/site003 g, managerUser2008, manager001, /orgs/2/sites/site004 g, managerUser2008, manager001, /orgs/2/sites/site005 g, managerUser2009, manager001, /orgs/2/sites/site001 g, managerUser2009, manager001, /orgs/2/sites/site002 g, managerUser2009, manager001, /orgs/2/sites/site003 g, managerUser2009, manager001, /orgs/2/sites/site004 g, managerUser2009, manager001, /orgs/2/sites/site005 g, managerUser2010, manager001, /orgs/2/sites/site001 g, managerUser2010, manager001, /orgs/2/sites/site002 g, managerUser2010, manager001, /orgs/2/sites/site003 g, managerUser2010, manager001, /orgs/2/sites/site004 g, managerUser2010, manager001, /orgs/2/sites/site005 g, managerUser2011, manager001, /orgs/2/sites/site001 g, managerUser2011, manager001, /orgs/2/sites/site002 g, managerUser2011, manager001, /orgs/2/sites/site003 g, managerUser2011, manager001, /orgs/2/sites/site004 g, managerUser2011, manager001, /orgs/2/sites/site005 g, managerUser2012, manager001, /orgs/2/sites/site001 g, managerUser2012, manager001, /orgs/2/sites/site002 g, managerUser2012, manager001, /orgs/2/sites/site003 g, managerUser2012, manager001, /orgs/2/sites/site004 g, managerUser2012, manager001, /orgs/2/sites/site005 g, managerUser2013, manager001, /orgs/2/sites/site001 g, managerUser2013, manager001, /orgs/2/sites/site002 g, managerUser2013, manager001, /orgs/2/sites/site003 g, managerUser2013, manager001, /orgs/2/sites/site004 g, managerUser2013, manager001, /orgs/2/sites/site005 g, managerUser2014, manager001, /orgs/2/sites/site001 g, managerUser2014, manager001, /orgs/2/sites/site002 g, managerUser2014, manager001, /orgs/2/sites/site003 g, managerUser2014, manager001, /orgs/2/sites/site004 g, managerUser2014, manager001, /orgs/2/sites/site005 g, managerUser2015, manager001, /orgs/2/sites/site001 g, managerUser2015, manager001, /orgs/2/sites/site002 g, managerUser2015, manager001, /orgs/2/sites/site003 g, managerUser2015, manager001, /orgs/2/sites/site004 g, managerUser2015, manager001, /orgs/2/sites/site005 g, managerUser2016, manager001, /orgs/2/sites/site001 g, managerUser2016, manager001, /orgs/2/sites/site002 g, managerUser2016, manager001, /orgs/2/sites/site003 g, managerUser2016, manager001, /orgs/2/sites/site004 g, managerUser2016, manager001, /orgs/2/sites/site005 g, managerUser2017, manager001, /orgs/2/sites/site001 g, managerUser2017, manager001, /orgs/2/sites/site002 g, managerUser2017, manager001, /orgs/2/sites/site003 g, managerUser2017, manager001, /orgs/2/sites/site004 g, managerUser2017, manager001, /orgs/2/sites/site005 g, managerUser2018, manager001, /orgs/2/sites/site001 g, managerUser2018, manager001, /orgs/2/sites/site002 g, managerUser2018, manager001, /orgs/2/sites/site003 g, managerUser2018, manager001, /orgs/2/sites/site004 g, managerUser2018, manager001, /orgs/2/sites/site005 g, managerUser2019, manager001, /orgs/2/sites/site001 g, managerUser2019, manager001, /orgs/2/sites/site002 g, managerUser2019, manager001, /orgs/2/sites/site003 g, managerUser2019, manager001, /orgs/2/sites/site004 g, managerUser2019, manager001, /orgs/2/sites/site005 g, managerUser2020, manager001, /orgs/2/sites/site001 g, managerUser2020, manager001, /orgs/2/sites/site002 g, managerUser2020, manager001, /orgs/2/sites/site003 g, managerUser2020, manager001, /orgs/2/sites/site004 g, managerUser2020, manager001, /orgs/2/sites/site005 g, managerUser2021, manager001, /orgs/2/sites/site001 g, managerUser2021, manager001, /orgs/2/sites/site002 g, managerUser2021, manager001, /orgs/2/sites/site003 g, managerUser2021, manager001, /orgs/2/sites/site004 g, managerUser2021, manager001, /orgs/2/sites/site005 g, managerUser2022, manager001, /orgs/2/sites/site001 g, managerUser2022, manager001, /orgs/2/sites/site002 g, managerUser2022, manager001, /orgs/2/sites/site003 g, managerUser2022, manager001, /orgs/2/sites/site004 g, managerUser2022, manager001, /orgs/2/sites/site005 g, managerUser2023, manager001, /orgs/2/sites/site001 g, managerUser2023, manager001, /orgs/2/sites/site002 g, managerUser2023, manager001, /orgs/2/sites/site003 g, managerUser2023, manager001, /orgs/2/sites/site004 g, managerUser2023, manager001, /orgs/2/sites/site005 g, managerUser2024, manager001, /orgs/2/sites/site001 g, managerUser2024, manager001, /orgs/2/sites/site002 g, managerUser2024, manager001, /orgs/2/sites/site003 g, managerUser2024, manager001, /orgs/2/sites/site004 g, managerUser2024, manager001, /orgs/2/sites/site005 g, managerUser2025, manager001, /orgs/2/sites/site001 g, managerUser2025, manager001, /orgs/2/sites/site002 g, managerUser2025, manager001, /orgs/2/sites/site003 g, managerUser2025, manager001, /orgs/2/sites/site004 g, managerUser2025, manager001, /orgs/2/sites/site005 g, managerUser2026, manager001, /orgs/2/sites/site001 g, managerUser2026, manager001, /orgs/2/sites/site002 g, managerUser2026, manager001, /orgs/2/sites/site003 g, managerUser2026, manager001, /orgs/2/sites/site004 g, managerUser2026, manager001, /orgs/2/sites/site005 g, managerUser2027, manager001, /orgs/2/sites/site001 g, managerUser2027, manager001, /orgs/2/sites/site002 g, managerUser2027, manager001, /orgs/2/sites/site003 g, managerUser2027, manager001, /orgs/2/sites/site004 g, managerUser2027, manager001, /orgs/2/sites/site005 g, managerUser2028, manager001, /orgs/2/sites/site001 g, managerUser2028, manager001, /orgs/2/sites/site002 g, managerUser2028, manager001, /orgs/2/sites/site003 g, managerUser2028, manager001, /orgs/2/sites/site004 g, managerUser2028, manager001, /orgs/2/sites/site005 g, managerUser2029, manager001, /orgs/2/sites/site001 g, managerUser2029, manager001, /orgs/2/sites/site002 g, managerUser2029, manager001, /orgs/2/sites/site003 g, managerUser2029, manager001, /orgs/2/sites/site004 g, managerUser2029, manager001, /orgs/2/sites/site005 g, managerUser2030, manager001, /orgs/2/sites/site001 g, managerUser2030, manager001, /orgs/2/sites/site002 g, managerUser2030, manager001, /orgs/2/sites/site003 g, managerUser2030, manager001, /orgs/2/sites/site004 g, managerUser2030, manager001, /orgs/2/sites/site005 g, managerUser2031, manager001, /orgs/2/sites/site001 g, managerUser2031, manager001, /orgs/2/sites/site002 g, managerUser2031, manager001, /orgs/2/sites/site003 g, managerUser2031, manager001, /orgs/2/sites/site004 g, managerUser2031, manager001, /orgs/2/sites/site005 g, managerUser2032, manager001, /orgs/2/sites/site001 g, managerUser2032, manager001, /orgs/2/sites/site002 g, managerUser2032, manager001, /orgs/2/sites/site003 g, managerUser2032, manager001, /orgs/2/sites/site004 g, managerUser2032, manager001, /orgs/2/sites/site005 g, managerUser2033, manager001, /orgs/2/sites/site001 g, managerUser2033, manager001, /orgs/2/sites/site002 g, managerUser2033, manager001, /orgs/2/sites/site003 g, managerUser2033, manager001, /orgs/2/sites/site004 g, managerUser2033, manager001, /orgs/2/sites/site005 g, managerUser2034, manager001, /orgs/2/sites/site001 g, managerUser2034, manager001, /orgs/2/sites/site002 g, managerUser2034, manager001, /orgs/2/sites/site003 g, managerUser2034, manager001, /orgs/2/sites/site004 g, managerUser2034, manager001, /orgs/2/sites/site005 g, managerUser2035, manager001, /orgs/2/sites/site001 g, managerUser2035, manager001, /orgs/2/sites/site002 g, managerUser2035, manager001, /orgs/2/sites/site003 g, managerUser2035, manager001, /orgs/2/sites/site004 g, managerUser2035, manager001, /orgs/2/sites/site005 g, managerUser2036, manager001, /orgs/2/sites/site001 g, managerUser2036, manager001, /orgs/2/sites/site002 g, managerUser2036, manager001, /orgs/2/sites/site003 g, managerUser2036, manager001, /orgs/2/sites/site004 g, managerUser2036, manager001, /orgs/2/sites/site005 g, managerUser2037, manager001, /orgs/2/sites/site001 g, managerUser2037, manager001, /orgs/2/sites/site002 g, managerUser2037, manager001, /orgs/2/sites/site003 g, managerUser2037, manager001, /orgs/2/sites/site004 g, managerUser2037, manager001, /orgs/2/sites/site005 g, managerUser2038, manager001, /orgs/2/sites/site001 g, managerUser2038, manager001, /orgs/2/sites/site002 g, managerUser2038, manager001, /orgs/2/sites/site003 g, managerUser2038, manager001, /orgs/2/sites/site004 g, managerUser2038, manager001, /orgs/2/sites/site005 g, managerUser2039, manager001, /orgs/2/sites/site001 g, managerUser2039, manager001, /orgs/2/sites/site002 g, managerUser2039, manager001, /orgs/2/sites/site003 g, managerUser2039, manager001, /orgs/2/sites/site004 g, managerUser2039, manager001, /orgs/2/sites/site005 g, managerUser2040, manager001, /orgs/2/sites/site001 g, managerUser2040, manager001, /orgs/2/sites/site002 g, managerUser2040, manager001, /orgs/2/sites/site003 g, managerUser2040, manager001, /orgs/2/sites/site004 g, managerUser2040, manager001, /orgs/2/sites/site005 g, managerUser2041, manager001, /orgs/2/sites/site001 g, managerUser2041, manager001, /orgs/2/sites/site002 g, managerUser2041, manager001, /orgs/2/sites/site003 g, managerUser2041, manager001, /orgs/2/sites/site004 g, managerUser2041, manager001, /orgs/2/sites/site005 g, managerUser2042, manager001, /orgs/2/sites/site001 g, managerUser2042, manager001, /orgs/2/sites/site002 g, managerUser2042, manager001, /orgs/2/sites/site003 g, managerUser2042, manager001, /orgs/2/sites/site004 g, managerUser2042, manager001, /orgs/2/sites/site005 g, managerUser2043, manager001, /orgs/2/sites/site001 g, managerUser2043, manager001, /orgs/2/sites/site002 g, managerUser2043, manager001, /orgs/2/sites/site003 g, managerUser2043, manager001, /orgs/2/sites/site004 g, managerUser2043, manager001, /orgs/2/sites/site005 g, managerUser2044, manager001, /orgs/2/sites/site001 g, managerUser2044, manager001, /orgs/2/sites/site002 g, managerUser2044, manager001, /orgs/2/sites/site003 g, managerUser2044, manager001, /orgs/2/sites/site004 g, managerUser2044, manager001, /orgs/2/sites/site005 g, managerUser2045, manager001, /orgs/2/sites/site001 g, managerUser2045, manager001, /orgs/2/sites/site002 g, managerUser2045, manager001, /orgs/2/sites/site003 g, managerUser2045, manager001, /orgs/2/sites/site004 g, managerUser2045, manager001, /orgs/2/sites/site005 g, managerUser2046, manager001, /orgs/2/sites/site001 g, managerUser2046, manager001, /orgs/2/sites/site002 g, managerUser2046, manager001, /orgs/2/sites/site003 g, managerUser2046, manager001, /orgs/2/sites/site004 g, managerUser2046, manager001, /orgs/2/sites/site005 g, managerUser2047, manager001, /orgs/2/sites/site001 g, managerUser2047, manager001, /orgs/2/sites/site002 g, managerUser2047, manager001, /orgs/2/sites/site003 g, managerUser2047, manager001, /orgs/2/sites/site004 g, managerUser2047, manager001, /orgs/2/sites/site005 g, managerUser2048, manager001, /orgs/2/sites/site001 g, managerUser2048, manager001, /orgs/2/sites/site002 g, managerUser2048, manager001, /orgs/2/sites/site003 g, managerUser2048, manager001, /orgs/2/sites/site004 g, managerUser2048, manager001, /orgs/2/sites/site005 g, managerUser2049, manager001, /orgs/2/sites/site001 g, managerUser2049, manager001, /orgs/2/sites/site002 g, managerUser2049, manager001, /orgs/2/sites/site003 g, managerUser2049, manager001, /orgs/2/sites/site004 g, managerUser2049, manager001, /orgs/2/sites/site005 g, managerUser2050, manager001, /orgs/2/sites/site001 g, managerUser2050, manager001, /orgs/2/sites/site002 g, managerUser2050, manager001, /orgs/2/sites/site003 g, managerUser2050, manager001, /orgs/2/sites/site004 g, managerUser2050, manager001, /orgs/2/sites/site005 # Group - customer001, / org2 g, customerUser1001, customer001, /orgs/2/sites/site001 g, customerUser1001, customer001, /orgs/2/sites/site002 g, customerUser1001, customer001, /orgs/2/sites/site003 g, customerUser1001, customer001, /orgs/2/sites/site004 g, customerUser1001, customer001, /orgs/2/sites/site005 g, customerUser1001, customer001, /orgs/2/sites/site001 g, customerUser1001, customer001, /orgs/2/sites/site002 g, customerUser1001, customer001, /orgs/2/sites/site003 g, customerUser1001, customer001, /orgs/2/sites/site004 g, customerUser1001, customer001, /orgs/2/sites/site005 g, customerUser1003, customer001, /orgs/2/sites/site001 g, customerUser1003, customer001, /orgs/2/sites/site002 g, customerUser1003, customer001, /orgs/2/sites/site003 g, customerUser1003, customer001, /orgs/2/sites/site004 g, customerUser1003, customer001, /orgs/2/sites/site005 g, customerUser1004, customer001, /orgs/2/sites/site001 g, customerUser1004, customer001, /orgs/2/sites/site002 g, customerUser1004, customer001, /orgs/2/sites/site003 g, customerUser1004, customer001, /orgs/2/sites/site004 g, customerUser1004, customer001, /orgs/2/sites/site005 g, customerUser1005, customer001, /orgs/2/sites/site001 g, customerUser1005, customer001, /orgs/2/sites/site002 g, customerUser1005, customer001, /orgs/2/sites/site003 g, customerUser1005, customer001, /orgs/2/sites/site004 g, customerUser1005, customer001, /orgs/2/sites/site005 g, customerUser1006, customer001, /orgs/2/sites/site001 g, customerUser1006, customer001, /orgs/2/sites/site002 g, customerUser1006, customer001, /orgs/2/sites/site003 g, customerUser1006, customer001, /orgs/2/sites/site004 g, customerUser1006, customer001, /orgs/2/sites/site005 g, customerUser1007, customer001, /orgs/2/sites/site001 g, customerUser1007, customer001, /orgs/2/sites/site002 g, customerUser1007, customer001, /orgs/2/sites/site003 g, customerUser1007, customer001, /orgs/2/sites/site004 g, customerUser1007, customer001, /orgs/2/sites/site005 g, customerUser1008, customer001, /orgs/2/sites/site001 g, customerUser1008, customer001, /orgs/2/sites/site002 g, customerUser1008, customer001, /orgs/2/sites/site003 g, customerUser1008, customer001, /orgs/2/sites/site004 g, customerUser1008, customer001, /orgs/2/sites/site005 g, customerUser1009, customer001, /orgs/2/sites/site001 g, customerUser1009, customer001, /orgs/2/sites/site002 g, customerUser1009, customer001, /orgs/2/sites/site003 g, customerUser1009, customer001, /orgs/2/sites/site004 g, customerUser1009, customer001, /orgs/2/sites/site005 g, customerUser1010, customer001, /orgs/2/sites/site001 g, customerUser1010, customer001, /orgs/2/sites/site002 g, customerUser1010, customer001, /orgs/2/sites/site003 g, customerUser1010, customer001, /orgs/2/sites/site004 g, customerUser1010, customer001, /orgs/2/sites/site005 g, customerUser1011, customer001, /orgs/2/sites/site001 g, customerUser1011, customer001, /orgs/2/sites/site002 g, customerUser1011, customer001, /orgs/2/sites/site003 g, customerUser1011, customer001, /orgs/2/sites/site004 g, customerUser1011, customer001, /orgs/2/sites/site005 g, customerUser1012, customer001, /orgs/2/sites/site001 g, customerUser1012, customer001, /orgs/2/sites/site002 g, customerUser1012, customer001, /orgs/2/sites/site003 g, customerUser1012, customer001, /orgs/2/sites/site004 g, customerUser1012, customer001, /orgs/2/sites/site005 g, customerUser1013, customer001, /orgs/2/sites/site001 g, customerUser1013, customer001, /orgs/2/sites/site002 g, customerUser1013, customer001, /orgs/2/sites/site003 g, customerUser1013, customer001, /orgs/2/sites/site004 g, customerUser1013, customer001, /orgs/2/sites/site005 g, customerUser1014, customer001, /orgs/2/sites/site001 g, customerUser1014, customer001, /orgs/2/sites/site002 g, customerUser1014, customer001, /orgs/2/sites/site003 g, customerUser1014, customer001, /orgs/2/sites/site004 g, customerUser1014, customer001, /orgs/2/sites/site005 g, customerUser1015, customer001, /orgs/2/sites/site001 g, customerUser1015, customer001, /orgs/2/sites/site002 g, customerUser1015, customer001, /orgs/2/sites/site003 g, customerUser1015, customer001, /orgs/2/sites/site004 g, customerUser1015, customer001, /orgs/2/sites/site005 g, customerUser1016, customer001, /orgs/2/sites/site001 g, customerUser1016, customer001, /orgs/2/sites/site002 g, customerUser1016, customer001, /orgs/2/sites/site003 g, customerUser1016, customer001, /orgs/2/sites/site004 g, customerUser1016, customer001, /orgs/2/sites/site005 g, customerUser1017, customer001, /orgs/2/sites/site001 g, customerUser1017, customer001, /orgs/2/sites/site002 g, customerUser1017, customer001, /orgs/2/sites/site003 g, customerUser1017, customer001, /orgs/2/sites/site004 g, customerUser1017, customer001, /orgs/2/sites/site005 g, customerUser1018, customer001, /orgs/2/sites/site001 g, customerUser1018, customer001, /orgs/2/sites/site002 g, customerUser1018, customer001, /orgs/2/sites/site003 g, customerUser1018, customer001, /orgs/2/sites/site004 g, customerUser1018, customer001, /orgs/2/sites/site005 g, customerUser1019, customer001, /orgs/2/sites/site001 g, customerUser1019, customer001, /orgs/2/sites/site002 g, customerUser1019, customer001, /orgs/2/sites/site003 g, customerUser1019, customer001, /orgs/2/sites/site004 g, customerUser1019, customer001, /orgs/2/sites/site005 g, customerUser1020, customer001, /orgs/2/sites/site001 g, customerUser1020, customer001, /orgs/2/sites/site002 g, customerUser1020, customer001, /orgs/2/sites/site003 g, customerUser1020, customer001, /orgs/2/sites/site004 g, customerUser1020, customer001, /orgs/2/sites/site005 g, customerUser1021, customer001, /orgs/2/sites/site001 g, customerUser1021, customer001, /orgs/2/sites/site002 g, customerUser1021, customer001, /orgs/2/sites/site003 g, customerUser1021, customer001, /orgs/2/sites/site004 g, customerUser1021, customer001, /orgs/2/sites/site005 g, customerUser1022, customer001, /orgs/2/sites/site001 g, customerUser1022, customer001, /orgs/2/sites/site002 g, customerUser1022, customer001, /orgs/2/sites/site003 g, customerUser1022, customer001, /orgs/2/sites/site004 g, customerUser1022, customer001, /orgs/2/sites/site005 g, customerUser1023, customer001, /orgs/2/sites/site001 g, customerUser1023, customer001, /orgs/2/sites/site002 g, customerUser1023, customer001, /orgs/2/sites/site003 g, customerUser1023, customer001, /orgs/2/sites/site004 g, customerUser1023, customer001, /orgs/2/sites/site005 g, customerUser1024, customer001, /orgs/2/sites/site001 g, customerUser1024, customer001, /orgs/2/sites/site002 g, customerUser1024, customer001, /orgs/2/sites/site003 g, customerUser1024, customer001, /orgs/2/sites/site004 g, customerUser1024, customer001, /orgs/2/sites/site005 g, customerUser1025, customer001, /orgs/2/sites/site001 g, customerUser1025, customer001, /orgs/2/sites/site002 g, customerUser1025, customer001, /orgs/2/sites/site003 g, customerUser1025, customer001, /orgs/2/sites/site004 g, customerUser1025, customer001, /orgs/2/sites/site005 g, customerUser1026, customer001, /orgs/2/sites/site001 g, customerUser1026, customer001, /orgs/2/sites/site002 g, customerUser1026, customer001, /orgs/2/sites/site003 g, customerUser1026, customer001, /orgs/2/sites/site004 g, customerUser1026, customer001, /orgs/2/sites/site005 g, customerUser1027, customer001, /orgs/2/sites/site001 g, customerUser1027, customer001, /orgs/2/sites/site002 g, customerUser1027, customer001, /orgs/2/sites/site003 g, customerUser1027, customer001, /orgs/2/sites/site004 g, customerUser1027, customer001, /orgs/2/sites/site005 g, customerUser1028, customer001, /orgs/2/sites/site001 g, customerUser1028, customer001, /orgs/2/sites/site002 g, customerUser1028, customer001, /orgs/2/sites/site003 g, customerUser1028, customer001, /orgs/2/sites/site004 g, customerUser1028, customer001, /orgs/2/sites/site005 g, customerUser1029, customer001, /orgs/2/sites/site001 g, customerUser1029, customer001, /orgs/2/sites/site002 g, customerUser1029, customer001, /orgs/2/sites/site003 g, customerUser1029, customer001, /orgs/2/sites/site004 g, customerUser1029, customer001, /orgs/2/sites/site005 g, customerUser1030, customer001, /orgs/2/sites/site001 g, customerUser1030, customer001, /orgs/2/sites/site002 g, customerUser1030, customer001, /orgs/2/sites/site003 g, customerUser1030, customer001, /orgs/2/sites/site004 g, customerUser1030, customer001, /orgs/2/sites/site005 g, customerUser1031, customer001, /orgs/2/sites/site001 g, customerUser1031, customer001, /orgs/2/sites/site002 g, customerUser1031, customer001, /orgs/2/sites/site003 g, customerUser1031, customer001, /orgs/2/sites/site004 g, customerUser1031, customer001, /orgs/2/sites/site005 g, customerUser1032, customer001, /orgs/2/sites/site001 g, customerUser1032, customer001, /orgs/2/sites/site002 g, customerUser1032, customer001, /orgs/2/sites/site003 g, customerUser1032, customer001, /orgs/2/sites/site004 g, customerUser1032, customer001, /orgs/2/sites/site005 g, customerUser1033, customer001, /orgs/2/sites/site001 g, customerUser1033, customer001, /orgs/2/sites/site002 g, customerUser1033, customer001, /orgs/2/sites/site003 g, customerUser1033, customer001, /orgs/2/sites/site004 g, customerUser1033, customer001, /orgs/2/sites/site005 g, customerUser1034, customer001, /orgs/2/sites/site001 g, customerUser1034, customer001, /orgs/2/sites/site002 g, customerUser1034, customer001, /orgs/2/sites/site003 g, customerUser1034, customer001, /orgs/2/sites/site004 g, customerUser1034, customer001, /orgs/2/sites/site005 g, customerUser1035, customer001, /orgs/2/sites/site001 g, customerUser1035, customer001, /orgs/2/sites/site002 g, customerUser1035, customer001, /orgs/2/sites/site003 g, customerUser1035, customer001, /orgs/2/sites/site004 g, customerUser1035, customer001, /orgs/2/sites/site005 g, customerUser1036, customer001, /orgs/2/sites/site001 g, customerUser1036, customer001, /orgs/2/sites/site002 g, customerUser1036, customer001, /orgs/2/sites/site003 g, customerUser1036, customer001, /orgs/2/sites/site004 g, customerUser1036, customer001, /orgs/2/sites/site005 g, customerUser1037, customer001, /orgs/2/sites/site001 g, customerUser1037, customer001, /orgs/2/sites/site002 g, customerUser1037, customer001, /orgs/2/sites/site003 g, customerUser1037, customer001, /orgs/2/sites/site004 g, customerUser1037, customer001, /orgs/2/sites/site005 g, customerUser1038, customer001, /orgs/2/sites/site001 g, customerUser1038, customer001, /orgs/2/sites/site002 g, customerUser1038, customer001, /orgs/2/sites/site003 g, customerUser1038, customer001, /orgs/2/sites/site004 g, customerUser1038, customer001, /orgs/2/sites/site005 g, customerUser1039, customer001, /orgs/2/sites/site001 g, customerUser1039, customer001, /orgs/2/sites/site002 g, customerUser1039, customer001, /orgs/2/sites/site003 g, customerUser1039, customer001, /orgs/2/sites/site004 g, customerUser1039, customer001, /orgs/2/sites/site005 g, customerUser1040, customer001, /orgs/2/sites/site001 g, customerUser1040, customer001, /orgs/2/sites/site002 g, customerUser1040, customer001, /orgs/2/sites/site003 g, customerUser1040, customer001, /orgs/2/sites/site004 g, customerUser1040, customer001, /orgs/2/sites/site005 g, customerUser1041, customer001, /orgs/2/sites/site001 g, customerUser1041, customer001, /orgs/2/sites/site002 g, customerUser1041, customer001, /orgs/2/sites/site003 g, customerUser1041, customer001, /orgs/2/sites/site004 g, customerUser1041, customer001, /orgs/2/sites/site005 g, customerUser1042, customer001, /orgs/2/sites/site001 g, customerUser1042, customer001, /orgs/2/sites/site002 g, customerUser1042, customer001, /orgs/2/sites/site003 g, customerUser1042, customer001, /orgs/2/sites/site004 g, customerUser1042, customer001, /orgs/2/sites/site005 g, customerUser1043, customer001, /orgs/2/sites/site001 g, customerUser1043, customer001, /orgs/2/sites/site002 g, customerUser1043, customer001, /orgs/2/sites/site003 g, customerUser1043, customer001, /orgs/2/sites/site004 g, customerUser1043, customer001, /orgs/2/sites/site005 g, customerUser1044, customer001, /orgs/2/sites/site001 g, customerUser1044, customer001, /orgs/2/sites/site002 g, customerUser1044, customer001, /orgs/2/sites/site003 g, customerUser1044, customer001, /orgs/2/sites/site004 g, customerUser1044, customer001, /orgs/2/sites/site005 g, customerUser1045, customer001, /orgs/2/sites/site001 g, customerUser1045, customer001, /orgs/2/sites/site002 g, customerUser1045, customer001, /orgs/2/sites/site003 g, customerUser1045, customer001, /orgs/2/sites/site004 g, customerUser1045, customer001, /orgs/2/sites/site005 g, customerUser1046, customer001, /orgs/2/sites/site001 g, customerUser1046, customer001, /orgs/2/sites/site002 g, customerUser1046, customer001, /orgs/2/sites/site003 g, customerUser1046, customer001, /orgs/2/sites/site004 g, customerUser1046, customer001, /orgs/2/sites/site005 g, customerUser1047, customer001, /orgs/2/sites/site001 g, customerUser1047, customer001, /orgs/2/sites/site002 g, customerUser1047, customer001, /orgs/2/sites/site003 g, customerUser1047, customer001, /orgs/2/sites/site004 g, customerUser1047, customer001, /orgs/2/sites/site005 g, customerUser1048, customer001, /orgs/2/sites/site001 g, customerUser1048, customer001, /orgs/2/sites/site002 g, customerUser1048, customer001, /orgs/2/sites/site003 g, customerUser1048, customer001, /orgs/2/sites/site004 g, customerUser1048, customer001, /orgs/2/sites/site005 g, customerUser1049, customer001, /orgs/2/sites/site001 g, customerUser1049, customer001, /orgs/2/sites/site002 g, customerUser1049, customer001, /orgs/2/sites/site003 g, customerUser1049, customer001, /orgs/2/sites/site004 g, customerUser1049, customer001, /orgs/2/sites/site005 g, customerUser1050, customer001, /orgs/2/sites/site001 g, customerUser1050, customer001, /orgs/2/sites/site002 g, customerUser1050, customer001, /orgs/2/sites/site003 g, customerUser1050, customer001, /orgs/2/sites/site004 g, customerUser1050, customer001, /orgs/2/sites/site005 # Group - customer001, / org2 g, customerUser2001, customer001, /orgs/2/sites/site001 g, customerUser2001, customer001, /orgs/2/sites/site002 g, customerUser2001, customer001, /orgs/2/sites/site003 g, customerUser2001, customer001, /orgs/2/sites/site004 g, customerUser2001, customer001, /orgs/2/sites/site005 g, customerUser2001, customer001, /orgs/2/sites/site001 g, customerUser2001, customer001, /orgs/2/sites/site002 g, customerUser2001, customer001, /orgs/2/sites/site003 g, customerUser2001, customer001, /orgs/2/sites/site004 g, customerUser2001, customer001, /orgs/2/sites/site005 g, customerUser2003, customer001, /orgs/2/sites/site001 g, customerUser2003, customer001, /orgs/2/sites/site002 g, customerUser2003, customer001, /orgs/2/sites/site003 g, customerUser2003, customer001, /orgs/2/sites/site004 g, customerUser2003, customer001, /orgs/2/sites/site005 g, customerUser2004, customer001, /orgs/2/sites/site001 g, customerUser2004, customer001, /orgs/2/sites/site002 g, customerUser2004, customer001, /orgs/2/sites/site003 g, customerUser2004, customer001, /orgs/2/sites/site004 g, customerUser2004, customer001, /orgs/2/sites/site005 g, customerUser2005, customer001, /orgs/2/sites/site001 g, customerUser2005, customer001, /orgs/2/sites/site002 g, customerUser2005, customer001, /orgs/2/sites/site003 g, customerUser2005, customer001, /orgs/2/sites/site004 g, customerUser2005, customer001, /orgs/2/sites/site005 g, customerUser2006, customer001, /orgs/2/sites/site001 g, customerUser2006, customer001, /orgs/2/sites/site002 g, customerUser2006, customer001, /orgs/2/sites/site003 g, customerUser2006, customer001, /orgs/2/sites/site004 g, customerUser2006, customer001, /orgs/2/sites/site005 g, customerUser2007, customer001, /orgs/2/sites/site001 g, customerUser2007, customer001, /orgs/2/sites/site002 g, customerUser2007, customer001, /orgs/2/sites/site003 g, customerUser2007, customer001, /orgs/2/sites/site004 g, customerUser2007, customer001, /orgs/2/sites/site005 g, customerUser2008, customer001, /orgs/2/sites/site001 g, customerUser2008, customer001, /orgs/2/sites/site002 g, customerUser2008, customer001, /orgs/2/sites/site003 g, customerUser2008, customer001, /orgs/2/sites/site004 g, customerUser2008, customer001, /orgs/2/sites/site005 g, customerUser2009, customer001, /orgs/2/sites/site001 g, customerUser2009, customer001, /orgs/2/sites/site002 g, customerUser2009, customer001, /orgs/2/sites/site003 g, customerUser2009, customer001, /orgs/2/sites/site004 g, customerUser2009, customer001, /orgs/2/sites/site005 g, customerUser2010, customer001, /orgs/2/sites/site001 g, customerUser2010, customer001, /orgs/2/sites/site002 g, customerUser2010, customer001, /orgs/2/sites/site003 g, customerUser2010, customer001, /orgs/2/sites/site004 g, customerUser2010, customer001, /orgs/2/sites/site005 g, customerUser2011, customer001, /orgs/2/sites/site001 g, customerUser2011, customer001, /orgs/2/sites/site002 g, customerUser2011, customer001, /orgs/2/sites/site003 g, customerUser2011, customer001, /orgs/2/sites/site004 g, customerUser2011, customer001, /orgs/2/sites/site005 g, customerUser2012, customer001, /orgs/2/sites/site001 g, customerUser2012, customer001, /orgs/2/sites/site002 g, customerUser2012, customer001, /orgs/2/sites/site003 g, customerUser2012, customer001, /orgs/2/sites/site004 g, customerUser2012, customer001, /orgs/2/sites/site005 g, customerUser2013, customer001, /orgs/2/sites/site001 g, customerUser2013, customer001, /orgs/2/sites/site002 g, customerUser2013, customer001, /orgs/2/sites/site003 g, customerUser2013, customer001, /orgs/2/sites/site004 g, customerUser2013, customer001, /orgs/2/sites/site005 g, customerUser2014, customer001, /orgs/2/sites/site001 g, customerUser2014, customer001, /orgs/2/sites/site002 g, customerUser2014, customer001, /orgs/2/sites/site003 g, customerUser2014, customer001, /orgs/2/sites/site004 g, customerUser2014, customer001, /orgs/2/sites/site005 g, customerUser2015, customer001, /orgs/2/sites/site001 g, customerUser2015, customer001, /orgs/2/sites/site002 g, customerUser2015, customer001, /orgs/2/sites/site003 g, customerUser2015, customer001, /orgs/2/sites/site004 g, customerUser2015, customer001, /orgs/2/sites/site005 g, customerUser2016, customer001, /orgs/2/sites/site001 g, customerUser2016, customer001, /orgs/2/sites/site002 g, customerUser2016, customer001, /orgs/2/sites/site003 g, customerUser2016, customer001, /orgs/2/sites/site004 g, customerUser2016, customer001, /orgs/2/sites/site005 g, customerUser2017, customer001, /orgs/2/sites/site001 g, customerUser2017, customer001, /orgs/2/sites/site002 g, customerUser2017, customer001, /orgs/2/sites/site003 g, customerUser2017, customer001, /orgs/2/sites/site004 g, customerUser2017, customer001, /orgs/2/sites/site005 g, customerUser2018, customer001, /orgs/2/sites/site001 g, customerUser2018, customer001, /orgs/2/sites/site002 g, customerUser2018, customer001, /orgs/2/sites/site003 g, customerUser2018, customer001, /orgs/2/sites/site004 g, customerUser2018, customer001, /orgs/2/sites/site005 g, customerUser2019, customer001, /orgs/2/sites/site001 g, customerUser2019, customer001, /orgs/2/sites/site002 g, customerUser2019, customer001, /orgs/2/sites/site003 g, customerUser2019, customer001, /orgs/2/sites/site004 g, customerUser2019, customer001, /orgs/2/sites/site005 g, customerUser2020, customer001, /orgs/2/sites/site001 g, customerUser2020, customer001, /orgs/2/sites/site002 g, customerUser2020, customer001, /orgs/2/sites/site003 g, customerUser2020, customer001, /orgs/2/sites/site004 g, customerUser2020, customer001, /orgs/2/sites/site005 g, customerUser2021, customer001, /orgs/2/sites/site001 g, customerUser2021, customer001, /orgs/2/sites/site002 g, customerUser2021, customer001, /orgs/2/sites/site003 g, customerUser2021, customer001, /orgs/2/sites/site004 g, customerUser2021, customer001, /orgs/2/sites/site005 g, customerUser2022, customer001, /orgs/2/sites/site001 g, customerUser2022, customer001, /orgs/2/sites/site002 g, customerUser2022, customer001, /orgs/2/sites/site003 g, customerUser2022, customer001, /orgs/2/sites/site004 g, customerUser2022, customer001, /orgs/2/sites/site005 g, customerUser2023, customer001, /orgs/2/sites/site001 g, customerUser2023, customer001, /orgs/2/sites/site002 g, customerUser2023, customer001, /orgs/2/sites/site003 g, customerUser2023, customer001, /orgs/2/sites/site004 g, customerUser2023, customer001, /orgs/2/sites/site005 g, customerUser2024, customer001, /orgs/2/sites/site001 g, customerUser2024, customer001, /orgs/2/sites/site002 g, customerUser2024, customer001, /orgs/2/sites/site003 g, customerUser2024, customer001, /orgs/2/sites/site004 g, customerUser2024, customer001, /orgs/2/sites/site005 g, customerUser2025, customer001, /orgs/2/sites/site001 g, customerUser2025, customer001, /orgs/2/sites/site002 g, customerUser2025, customer001, /orgs/2/sites/site003 g, customerUser2025, customer001, /orgs/2/sites/site004 g, customerUser2025, customer001, /orgs/2/sites/site005 g, customerUser2026, customer001, /orgs/2/sites/site001 g, customerUser2026, customer001, /orgs/2/sites/site002 g, customerUser2026, customer001, /orgs/2/sites/site003 g, customerUser2026, customer001, /orgs/2/sites/site004 g, customerUser2026, customer001, /orgs/2/sites/site005 g, customerUser2027, customer001, /orgs/2/sites/site001 g, customerUser2027, customer001, /orgs/2/sites/site002 g, customerUser2027, customer001, /orgs/2/sites/site003 g, customerUser2027, customer001, /orgs/2/sites/site004 g, customerUser2027, customer001, /orgs/2/sites/site005 g, customerUser2028, customer001, /orgs/2/sites/site001 g, customerUser2028, customer001, /orgs/2/sites/site002 g, customerUser2028, customer001, /orgs/2/sites/site003 g, customerUser2028, customer001, /orgs/2/sites/site004 g, customerUser2028, customer001, /orgs/2/sites/site005 g, customerUser2029, customer001, /orgs/2/sites/site001 g, customerUser2029, customer001, /orgs/2/sites/site002 g, customerUser2029, customer001, /orgs/2/sites/site003 g, customerUser2029, customer001, /orgs/2/sites/site004 g, customerUser2029, customer001, /orgs/2/sites/site005 g, customerUser2030, customer001, /orgs/2/sites/site001 g, customerUser2030, customer001, /orgs/2/sites/site002 g, customerUser2030, customer001, /orgs/2/sites/site003 g, customerUser2030, customer001, /orgs/2/sites/site004 g, customerUser2030, customer001, /orgs/2/sites/site005 g, customerUser2031, customer001, /orgs/2/sites/site001 g, customerUser2031, customer001, /orgs/2/sites/site002 g, customerUser2031, customer001, /orgs/2/sites/site003 g, customerUser2031, customer001, /orgs/2/sites/site004 g, customerUser2031, customer001, /orgs/2/sites/site005 g, customerUser2032, customer001, /orgs/2/sites/site001 g, customerUser2032, customer001, /orgs/2/sites/site002 g, customerUser2032, customer001, /orgs/2/sites/site003 g, customerUser2032, customer001, /orgs/2/sites/site004 g, customerUser2032, customer001, /orgs/2/sites/site005 g, customerUser2033, customer001, /orgs/2/sites/site001 g, customerUser2033, customer001, /orgs/2/sites/site002 g, customerUser2033, customer001, /orgs/2/sites/site003 g, customerUser2033, customer001, /orgs/2/sites/site004 g, customerUser2033, customer001, /orgs/2/sites/site005 g, customerUser2034, customer001, /orgs/2/sites/site001 g, customerUser2034, customer001, /orgs/2/sites/site002 g, customerUser2034, customer001, /orgs/2/sites/site003 g, customerUser2034, customer001, /orgs/2/sites/site004 g, customerUser2034, customer001, /orgs/2/sites/site005 g, customerUser2035, customer001, /orgs/2/sites/site001 g, customerUser2035, customer001, /orgs/2/sites/site002 g, customerUser2035, customer001, /orgs/2/sites/site003 g, customerUser2035, customer001, /orgs/2/sites/site004 g, customerUser2035, customer001, /orgs/2/sites/site005 g, customerUser2036, customer001, /orgs/2/sites/site001 g, customerUser2036, customer001, /orgs/2/sites/site002 g, customerUser2036, customer001, /orgs/2/sites/site003 g, customerUser2036, customer001, /orgs/2/sites/site004 g, customerUser2036, customer001, /orgs/2/sites/site005 g, customerUser2037, customer001, /orgs/2/sites/site001 g, customerUser2037, customer001, /orgs/2/sites/site002 g, customerUser2037, customer001, /orgs/2/sites/site003 g, customerUser2037, customer001, /orgs/2/sites/site004 g, customerUser2037, customer001, /orgs/2/sites/site005 g, customerUser2038, customer001, /orgs/2/sites/site001 g, customerUser2038, customer001, /orgs/2/sites/site002 g, customerUser2038, customer001, /orgs/2/sites/site003 g, customerUser2038, customer001, /orgs/2/sites/site004 g, customerUser2038, customer001, /orgs/2/sites/site005 g, customerUser2039, customer001, /orgs/2/sites/site001 g, customerUser2039, customer001, /orgs/2/sites/site002 g, customerUser2039, customer001, /orgs/2/sites/site003 g, customerUser2039, customer001, /orgs/2/sites/site004 g, customerUser2039, customer001, /orgs/2/sites/site005 g, customerUser2040, customer001, /orgs/2/sites/site001 g, customerUser2040, customer001, /orgs/2/sites/site002 g, customerUser2040, customer001, /orgs/2/sites/site003 g, customerUser2040, customer001, /orgs/2/sites/site004 g, customerUser2040, customer001, /orgs/2/sites/site005 g, customerUser2041, customer001, /orgs/2/sites/site001 g, customerUser2041, customer001, /orgs/2/sites/site002 g, customerUser2041, customer001, /orgs/2/sites/site003 g, customerUser2041, customer001, /orgs/2/sites/site004 g, customerUser2041, customer001, /orgs/2/sites/site005 g, customerUser2042, customer001, /orgs/2/sites/site001 g, customerUser2042, customer001, /orgs/2/sites/site002 g, customerUser2042, customer001, /orgs/2/sites/site003 g, customerUser2042, customer001, /orgs/2/sites/site004 g, customerUser2042, customer001, /orgs/2/sites/site005 g, customerUser2043, customer001, /orgs/2/sites/site001 g, customerUser2043, customer001, /orgs/2/sites/site002 g, customerUser2043, customer001, /orgs/2/sites/site003 g, customerUser2043, customer001, /orgs/2/sites/site004 g, customerUser2043, customer001, /orgs/2/sites/site005 g, customerUser2044, customer001, /orgs/2/sites/site001 g, customerUser2044, customer001, /orgs/2/sites/site002 g, customerUser2044, customer001, /orgs/2/sites/site003 g, customerUser2044, customer001, /orgs/2/sites/site004 g, customerUser2044, customer001, /orgs/2/sites/site005 g, customerUser2045, customer001, /orgs/2/sites/site001 g, customerUser2045, customer001, /orgs/2/sites/site002 g, customerUser2045, customer001, /orgs/2/sites/site003 g, customerUser2045, customer001, /orgs/2/sites/site004 g, customerUser2045, customer001, /orgs/2/sites/site005 g, customerUser2046, customer001, /orgs/2/sites/site001 g, customerUser2046, customer001, /orgs/2/sites/site002 g, customerUser2046, customer001, /orgs/2/sites/site003 g, customerUser2046, customer001, /orgs/2/sites/site004 g, customerUser2046, customer001, /orgs/2/sites/site005 g, customerUser2047, customer001, /orgs/2/sites/site001 g, customerUser2047, customer001, /orgs/2/sites/site002 g, customerUser2047, customer001, /orgs/2/sites/site003 g, customerUser2047, customer001, /orgs/2/sites/site004 g, customerUser2047, customer001, /orgs/2/sites/site005 g, customerUser2048, customer001, /orgs/2/sites/site001 g, customerUser2048, customer001, /orgs/2/sites/site002 g, customerUser2048, customer001, /orgs/2/sites/site003 g, customerUser2048, customer001, /orgs/2/sites/site004 g, customerUser2048, customer001, /orgs/2/sites/site005 g, customerUser2049, customer001, /orgs/2/sites/site001 g, customerUser2049, customer001, /orgs/2/sites/site002 g, customerUser2049, customer001, /orgs/2/sites/site003 g, customerUser2049, customer001, /orgs/2/sites/site004 g, customerUser2049, customer001, /orgs/2/sites/site005 g, customerUser2050, customer001, /orgs/2/sites/site001 g, customerUser2050, customer001, /orgs/2/sites/site002 g, customerUser2050, customer001, /orgs/2/sites/site003 g, customerUser2050, customer001, /orgs/2/sites/site004 g, customerUser2050, customer001, /orgs/2/sites/site005golang-github-casbin-casbin-2.82.0/examples/priority_indeterminate_policy.csv000066400000000000000000000000441456376452400275450ustar00rootroot00000000000000p, alice, data1, read, indeterminategolang-github-casbin-casbin-2.82.0/examples/priority_model.conf000066400000000000000000000003371456376452400245750ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act, eft [role_definition] g = _, _ [policy_effect] e = priority(p.eft) || deny [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/priority_model_enforce_context.conf000066400000000000000000000004041456376452400300350ustar00rootroot00000000000000[request_definition] r = sub, obj, act r2 = sub, obj [policy_definition] p = sub, obj, act, eft [role_definition] g = _, _ [policy_effect] e = priority(p.eft) || deny [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act m2 = g(r2.sub, p.sub) golang-github-casbin-casbin-2.82.0/examples/priority_model_explicit.conf000066400000000000000000000003511456376452400264720ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = priority, sub, obj, act, eft [role_definition] g = _, _ [policy_effect] e = priority(p.eft) || deny [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/priority_model_explicit_customized.conf000066400000000000000000000004041456376452400307370ustar00rootroot00000000000000[request_definition] r = subject, obj, act [policy_definition] p = customized_priority, obj, act, eft, subject [role_definition] g = _, _ [policy_effect] e = priority(p.eft) || deny [matchers] m = g(r.subject, p.subject) && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/priority_policy.csv000066400000000000000000000004371456376452400246430ustar00rootroot00000000000000p, alice, data1, read, allow p, data1_deny_group, data1, read, deny p, data1_deny_group, data1, write, deny p, alice, data1, write, allow g, alice, data1_deny_group p, data2_allow_group, data2, read, allow p, bob, data2, read, deny p, bob, data2, write, deny g, bob, data2_allow_groupgolang-github-casbin-casbin-2.82.0/examples/priority_policy_enforce_context.csv000066400000000000000000000004401456376452400301020ustar00rootroot00000000000000p, alice, data1, read, allow p, data1_deny_group, data1, read, deny p, data1_deny_group, data1, write, deny p, alice, data1, write, allow g, alice, data1_deny_group p, data2_allow_group, data2, read, allow p, bob, data2, read, deny p, bob, data2, write, deny g, bob, data2_allow_group golang-github-casbin-casbin-2.82.0/examples/priority_policy_explicit.csv000066400000000000000000000005101456376452400265340ustar00rootroot00000000000000p, 10, data1_deny_group, data1, read, deny p, 10, data1_deny_group, data1, write, deny p, 10, data2_allow_group, data2, read, allow p, 10, data2_allow_group, data2, write, allow p, 1, alice, data1, write, allow p, 1, alice, data1, read, allow p, 1, bob, data2, read, deny g, bob, data2_allow_group g, alice, data1_deny_group golang-github-casbin-casbin-2.82.0/examples/priority_policy_explicit_customized.csv000066400000000000000000000005101456376452400310020ustar00rootroot00000000000000p, 10, data1, read, deny, data1_deny_group p, 10, data1, write, deny, data1_deny_group p, 10, data2, read, allow, data2_allow_group p, 10, data2, write, allow, data2_allow_group p, 1, data1, write, allow, alice p, 1, data1, read, allow, alice p, 1, data2, read, deny, bob g, bob, data2_allow_group g, alice, data1_deny_group golang-github-casbin-casbin-2.82.0/examples/rbac_model.conf000066400000000000000000000003371456376452400236230ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/rbac_model_in_multi_line.conf000066400000000000000000000003421456376452400265260ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && r.obj == p.obj \ && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/rbac_model_matcher_using_in_op.conf000066400000000000000000000003761456376452400277220ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act || r.obj in ('data2', 'data3')golang-github-casbin-casbin-2.82.0/examples/rbac_model_matcher_using_in_op_bracket.conf000066400000000000000000000003761456376452400314150ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act || r.obj in ['data2', 'data3']golang-github-casbin-casbin-2.82.0/examples/rbac_policy.csv000066400000000000000000000001711456376452400236640ustar00rootroot00000000000000p, alice, data1, read p, bob, data2, write p, data2_admin, data2, read p, data2_admin, data2, write g, alice, data2_admingolang-github-casbin-casbin-2.82.0/examples/rbac_with_all_pattern_model.conf000066400000000000000000000004061456376452400272400ustar00rootroot00000000000000[request_definition] r = sub, dom, obj, act [policy_definition] p = sub, dom, obj, act [role_definition] g = _, _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && g(r.obj, p.obj, r.dom) && r.dom == p.dom && r.act == p.act golang-github-casbin-casbin-2.82.0/examples/rbac_with_all_pattern_policy.csv000066400000000000000000000001451456376452400273050ustar00rootroot00000000000000p, alice, domain1, book_group, read p, alice, domain2, book_group, write g, /book/:id, book_group, *golang-github-casbin-casbin-2.82.0/examples/rbac_with_deny_model.conf000066400000000000000000000004041456376452400256700ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act, eft [role_definition] g = _, _ [policy_effect] e = some(where (p.eft == allow)) && !some(where (p.eft == deny)) [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/rbac_with_deny_policy.csv000066400000000000000000000002631456376452400257400ustar00rootroot00000000000000p, alice, data1, read, allow p, bob, data2, write, allow p, data2_admin, data2, read, allow p, data2_admin, data2, write, allow p, alice, data2, write, deny g, alice, data2_admingolang-github-casbin-casbin-2.82.0/examples/rbac_with_different_types_of_roles_model.conf000066400000000000000000000004151456376452400320150ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, dom, obj, act [role_definition] g = _, _, _, (_, _) g2 = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub, p.dom) && g2(r.obj, p.dom) && regexMatch(r.act, p.act) golang-github-casbin-casbin-2.82.0/examples/rbac_with_different_types_of_roles_policy.csv000066400000000000000000000005231456376452400320620ustar00rootroot00000000000000p, role:owner, domain1, _, (read|write) p, role:developer, domain1, _, read p, role:owner, domain2, _, (read|write) p, role:developer, domain2, _, read g, alice, role:owner, domain1, _, _ g, bob, role:developer, domain2, _, 9999-12-30 00:00:00 g, carol, role:owner, domain2, _, 0000-01-02 00:00:00 g2, data1, domain1 g2, data2, domain2 golang-github-casbin-casbin-2.82.0/examples/rbac_with_domain_pattern_model.conf000066400000000000000000000004061456376452400277370ustar00rootroot00000000000000[request_definition] r = sub, dom, obj, act [policy_definition] p = sub, dom, obj, act [role_definition] g = _, _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act golang-github-casbin-casbin-2.82.0/examples/rbac_with_domain_pattern_policy.csv000066400000000000000000000003011456376452400277760ustar00rootroot00000000000000p, admin, domain1, data1, read p, admin, domain1, data1, write p, admin, domain2, data2, read p, admin, domain2, data2, write p, admin, *, data3, read g, alice, admin, * g, bob, admin, domain2golang-github-casbin-casbin-2.82.0/examples/rbac_with_domain_temporal_roles_model.conf000066400000000000000000000004151456376452400313110ustar00rootroot00000000000000[request_definition] r = sub, dom, obj, act [policy_definition] p = sub, dom, obj, act [role_definition] g = _, _, _, (_, _) [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/rbac_with_domain_temporal_roles_policy.csv000066400000000000000000000017371456376452400313660ustar00rootroot00000000000000p, alice, domain1, data1, read p, alice, domain1, data1, write p, data2_admin, domain2, data2, read p, data2_admin, domain2, data2, write p, data3_admin, domain3, data3, read p, data3_admin, domain3, data3, write p, data4_admin, domain4, data4, read p, data4_admin, domain4, data4, write p, data5_admin, domain5, data5, read p, data5_admin, domain5, data5, write p, data6_admin, domain6, data6, read p, data6_admin, domain6, data6, write p, data7_admin, domain7, data7, read p, data7_admin, domain7, data7, write p, data8_admin, domain8, data8, read p, data8_admin, domain8, data8, write g, alice, data2_admin, domain2, 0000-01-01 00:00:00, 0000-01-02 00:00:00 g, alice, data3_admin, domain3, 0000-01-01 00:00:00, 9999-12-30 00:00:00 g, alice, data4_admin, domain4, _, _ g, alice, data5_admin, domain5, _, 9999-12-30 00:00:00 g, alice, data6_admin, domain6, _, 0000-01-02 00:00:00 g, alice, data7_admin, domain7, 0000-01-01 00:00:00, _ g, alice, data8_admin, domain8, 9999-12-30 00:00:00, _golang-github-casbin-casbin-2.82.0/examples/rbac_with_domains_model.conf000066400000000000000000000004051456376452400263640ustar00rootroot00000000000000[request_definition] r = sub, dom, obj, act [policy_definition] p = sub, dom, obj, act [role_definition] g = _, _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/rbac_with_domains_policy.csv000066400000000000000000000002551456376452400264340ustar00rootroot00000000000000p, admin, domain1, data1, read p, admin, domain1, data1, write p, admin, domain2, data2, read p, admin, domain2, data2, write g, alice, admin, domain1 g, bob, admin, domain2golang-github-casbin-casbin-2.82.0/examples/rbac_with_domains_policy2.csv000066400000000000000000000003731456376452400265170ustar00rootroot00000000000000p, admin, domain1, data1, read p, admin, domain1, data1, write p, admin, domain2, data2, read p, admin, domain2, data2, write p, user, domain3, data2, read g, alice, admin, domain1 g, alice, admin, domain2 g, bob, admin, domain2 g, bob, user, domain3 golang-github-casbin-casbin-2.82.0/examples/rbac_with_hierarchy_policy.csv000066400000000000000000000003311456376452400267530ustar00rootroot00000000000000p, alice, data1, read p, bob, data2, write p, data1_admin, data1, read p, data1_admin, data1, write p, data2_admin, data2, read p, data2_admin, data2, write g, alice, admin g, admin, data1_admin g, admin, data2_admingolang-github-casbin-casbin-2.82.0/examples/rbac_with_hierarchy_with_domains_policy.csv000066400000000000000000000004101456376452400315160ustar00rootroot00000000000000p, role:reader, domain1, data1, read p, role:writer, domain1, data1, write p, alice, domain1, data2, read p, alice, domain2, data2, read g, role:global_admin, role:reader, domain1 g, role:global_admin, role:writer, domain1 g, alice, role:global_admin, domain1 golang-github-casbin-casbin-2.82.0/examples/rbac_with_multiple_policy_model.conf000066400000000000000000000004701456376452400301460ustar00rootroot00000000000000[request_definition] r = user, thing, action [policy_definition] p = role, thing, action p2 = role, action [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.user, p.role) && r.thing == p.thing && r.action == p.action m2 = g(r.user, p2.role) && r.action == p.action [role_definition] g = _,_golang-github-casbin-casbin-2.82.0/examples/rbac_with_multiple_policy_policy.csv000066400000000000000000000001531456376452400302110ustar00rootroot00000000000000p, user, /data, GET p, admin, /data, POST p2, user, view p2, admin, create g, admin, user g, alice, admingolang-github-casbin-casbin-2.82.0/examples/rbac_with_not_deny_model.conf000066400000000000000000000003611456376452400265520ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act, eft [role_definition] g = _, _ [policy_effect] e = !some(where (p.eft == deny)) [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/rbac_with_pattern_model.conf000066400000000000000000000003651456376452400264140ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _ g2 = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && g2(r.obj, p.obj) && regexMatch(r.act, p.act)golang-github-casbin-casbin-2.82.0/examples/rbac_with_pattern_policy.csv000066400000000000000000000010321456376452400264510ustar00rootroot00000000000000p, alice, /pen/1, GET p, alice, /pen2/1, GET p, book_admin, book_group, GET p, pen_admin, pen_group, GET p, *, pen3_group, GET p, /book/admin/:id, pen4_group, GET g, /book/user/:id, /book/admin/1 p, /book/leader/2, pen4_group, POST g, /book/user/:id, /book/leader/2 g, alice, book_admin g, bob, pen_admin g, cathy, /book/1/2/3/4/5 g, cathy, pen_admin g2, /book/*, book_group g2, /book/:id, book_group g2, /pen/:id, pen_group g2, /book2/{id}, book_group g2, /pen2/{id}, pen_group g2, /pen3/:id, pen3_group g2, /pen4/:id, pen4_groupgolang-github-casbin-casbin-2.82.0/examples/rbac_with_resource_roles_model.conf000066400000000000000000000003531456376452400277670ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _ g2 = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/rbac_with_resource_roles_policy.csv000066400000000000000000000002311456376452400300270ustar00rootroot00000000000000p, alice, data1, read p, bob, data2, write p, data_group_admin, data_group, write g, alice, data_group_admin g2, data1, data_group g2, data2, data_groupgolang-github-casbin-casbin-2.82.0/examples/rbac_with_temporal_roles_model.conf000066400000000000000000000003471456376452400277660ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _, (_, _) [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/rbac_with_temporal_roles_policy.csv000066400000000000000000000014201456376452400300240ustar00rootroot00000000000000p, alice, data1, read p, alice, data1, write p, data2_admin, data2, read p, data2_admin, data2, write p, data3_admin, data3, read p, data3_admin, data3, write p, data4_admin, data4, read p, data4_admin, data4, write p, data5_admin, data5, read p, data5_admin, data5, write p, data6_admin, data6, read p, data6_admin, data6, write p, data7_admin, data7, read p, data7_admin, data7, write p, data8_admin, data8, read p, data8_admin, data8, write g, alice, data2_admin, 0000-01-01 00:00:00, 0000-01-02 00:00:00 g, alice, data3_admin, 0000-01-01 00:00:00, 9999-12-30 00:00:00 g, alice, data4_admin, _, _ g, alice, data5_admin, _, 9999-12-30 00:00:00 g, alice, data6_admin, _, 0000-01-02 00:00:00 g, alice, data7_admin, 0000-01-01 00:00:00, _ g, alice, data8_admin, 9999-12-30 00:00:00, _golang-github-casbin-casbin-2.82.0/examples/subject_priority_model.conf000066400000000000000000000003461456376452400263140ustar00rootroot00000000000000[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act, eft [role_definition] g = _, _ [policy_effect] e = subjectPriority(p.eft) || deny [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/subject_priority_model_with_domain.conf000066400000000000000000000004711456376452400306750ustar00rootroot00000000000000[request_definition] r = sub, obj, dom, act [policy_definition] p = sub, obj, dom, act, eft #sub can't change position,must be first [role_definition] g = _, _, _ [policy_effect] e = subjectPriority(p.eft) || deny [matchers] m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.actgolang-github-casbin-casbin-2.82.0/examples/subject_priority_policy.csv000066400000000000000000000004141456376452400263550ustar00rootroot00000000000000p, root, data1, read, deny p, admin, data1, read, deny p, editor, data1, read, deny p, subscriber, data1, read, deny p, jane, data1, read, allow p, alice, data1, read, allow g, admin, root g, editor, admin g, subscriber, admin g, jane, editor g, alice, subscribergolang-github-casbin-casbin-2.82.0/examples/subject_priority_policy_with_domain.csv000066400000000000000000000003101456376452400307320ustar00rootroot00000000000000p, admin, data1, domain1, write, deny p, alice, data1, domain1, write, allow p, admin, data2, domain2, write, deny p, bob, data2, domain2, write, allow g, alice, admin, domain1 g, bob, admin, domain2golang-github-casbin-casbin-2.82.0/filter_test.go000066400000000000000000000161331456376452400217230ustar00rootroot00000000000000// Copyright 2018 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "testing" fileadapter "github.com/casbin/casbin/v2/persist/file-adapter" "github.com/casbin/casbin/v2/util" ) func TestInitFilteredAdapter(t *testing.T) { e, _ := NewEnforcer() adapter := fileadapter.NewFilteredAdapter("examples/rbac_with_domains_policy.csv") _ = e.InitWithAdapter("examples/rbac_with_domains_model.conf", adapter) // policy should not be loaded yet testHasPolicy(t, e, []string{"admin", "domain1", "data1", "read"}, false) } func TestLoadFilteredPolicy(t *testing.T) { e, _ := NewEnforcer() adapter := fileadapter.NewFilteredAdapter("examples/rbac_with_domains_policy.csv") _ = e.InitWithAdapter("examples/rbac_with_domains_model.conf", adapter) if err := e.LoadPolicy(); err != nil { t.Errorf("unexpected error in LoadPolicy: %v", err) } // validate initial conditions testHasPolicy(t, e, []string{"admin", "domain1", "data1", "read"}, true) testHasPolicy(t, e, []string{"admin", "domain2", "data2", "read"}, true) if err := e.LoadFilteredPolicy(&fileadapter.Filter{ P: []string{"", "domain1"}, G: []string{"", "", "domain1"}, }); err != nil { t.Errorf("unexpected error in LoadFilteredPolicy: %v", err) } if !e.IsFiltered() { t.Errorf("adapter did not set the filtered flag correctly") } // only policies for domain1 should be loaded testHasPolicy(t, e, []string{"admin", "domain1", "data1", "read"}, true) testHasPolicy(t, e, []string{"admin", "domain2", "data2", "read"}, false) if err := e.SavePolicy(); err == nil { t.Errorf("enforcer did not prevent saving filtered policy") } if err := e.GetAdapter().SavePolicy(e.GetModel()); err == nil { t.Errorf("adapter did not prevent saving filtered policy") } } func TestLoadMoreTypeFilteredPolicy(t *testing.T) { e, _ := NewEnforcer() adapter := fileadapter.NewFilteredAdapter("examples/rbac_with_pattern_policy.csv") _ = e.InitWithAdapter("examples/rbac_with_pattern_model.conf", adapter) if err := e.LoadPolicy(); err != nil { t.Errorf("unexpected error in LoadPolicy: %v", err) } e.AddNamedMatchingFunc("g2", "matching func", util.KeyMatch2) _ = e.BuildRoleLinks() testEnforce(t, e, "alice", "/book/1", "GET", true) // validate initial conditions testHasPolicy(t, e, []string{"book_admin", "book_group", "GET"}, true) testHasPolicy(t, e, []string{"pen_admin", "pen_group", "GET"}, true) if err := e.LoadFilteredPolicy(&fileadapter.Filter{ P: []string{"book_admin"}, G: []string{"alice"}, G2: []string{"", "book_group"}, }); err != nil { t.Errorf("unexpected error in LoadFilteredPolicy: %v", err) } if !e.IsFiltered() { t.Errorf("adapter did not set the filtered flag correctly") } testHasPolicy(t, e, []string{"alice", "/pen/1", "GET"}, false) testHasPolicy(t, e, []string{"alice", "/pen2/1", "GET"}, false) testHasPolicy(t, e, []string{"pen_admin", "pen_group", "GET"}, false) testHasGroupingPolicy(t, e, []string{"alice", "book_admin"}, true) testHasGroupingPolicy(t, e, []string{"bob", "pen_admin"}, false) testHasGroupingPolicy(t, e, []string{"cathy", "pen_admin"}, false) testHasGroupingPolicy(t, e, []string{"cathy", "/book/1/2/3/4/5"}, false) testEnforce(t, e, "alice", "/book/1", "GET", true) testEnforce(t, e, "alice", "/pen/1", "GET", false) } func TestAppendFilteredPolicy(t *testing.T) { e, _ := NewEnforcer() adapter := fileadapter.NewFilteredAdapter("examples/rbac_with_domains_policy.csv") _ = e.InitWithAdapter("examples/rbac_with_domains_model.conf", adapter) if err := e.LoadPolicy(); err != nil { t.Errorf("unexpected error in LoadPolicy: %v", err) } // validate initial conditions testHasPolicy(t, e, []string{"admin", "domain1", "data1", "read"}, true) testHasPolicy(t, e, []string{"admin", "domain2", "data2", "read"}, true) if err := e.LoadFilteredPolicy(&fileadapter.Filter{ P: []string{"", "domain1"}, G: []string{"", "", "domain1"}, }); err != nil { t.Errorf("unexpected error in LoadFilteredPolicy: %v", err) } if !e.IsFiltered() { t.Errorf("adapter did not set the filtered flag correctly") } // only policies for domain1 should be loaded testHasPolicy(t, e, []string{"admin", "domain1", "data1", "read"}, true) testHasPolicy(t, e, []string{"admin", "domain2", "data2", "read"}, false) //disable clear policy and load second domain if err := e.LoadIncrementalFilteredPolicy(&fileadapter.Filter{ P: []string{"", "domain2"}, G: []string{"", "", "domain2"}, }); err != nil { t.Errorf("unexpected error in LoadFilteredPolicy: %v", err) } //both domain policies should be loaded testHasPolicy(t, e, []string{"admin", "domain1", "data1", "read"}, true) testHasPolicy(t, e, []string{"admin", "domain2", "data2", "read"}, true) } func TestFilteredPolicyInvalidFilter(t *testing.T) { e, _ := NewEnforcer() adapter := fileadapter.NewFilteredAdapter("examples/rbac_with_domains_policy.csv") _ = e.InitWithAdapter("examples/rbac_with_domains_model.conf", adapter) if err := e.LoadFilteredPolicy([]string{"", "domain1"}); err == nil { t.Errorf("expected error in LoadFilteredPolicy, but got nil") } } func TestFilteredPolicyEmptyFilter(t *testing.T) { e, _ := NewEnforcer() adapter := fileadapter.NewFilteredAdapter("examples/rbac_with_domains_policy.csv") _ = e.InitWithAdapter("examples/rbac_with_domains_model.conf", adapter) if err := e.LoadFilteredPolicy(nil); err != nil { t.Errorf("unexpected error in LoadFilteredPolicy: %v", err) } if e.IsFiltered() { t.Errorf("adapter did not reset the filtered flag correctly") } if err := e.SavePolicy(); err != nil { t.Errorf("unexpected error in SavePolicy: %v", err) } } func TestUnsupportedFilteredPolicy(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv") err := e.LoadFilteredPolicy(&fileadapter.Filter{ P: []string{"", "domain1"}, G: []string{"", "", "domain1"}, }) if err == nil { t.Errorf("encorcer should have reported incompatibility error") } } func TestFilteredAdapterEmptyFilepath(t *testing.T) { e, _ := NewEnforcer() adapter := fileadapter.NewFilteredAdapter("") _ = e.InitWithAdapter("examples/rbac_with_domains_model.conf", adapter) if err := e.LoadFilteredPolicy(nil); err != nil { t.Errorf("unexpected error in LoadFilteredPolicy: %v", err) } } func TestFilteredAdapterInvalidFilepath(t *testing.T) { e, _ := NewEnforcer() adapter := fileadapter.NewFilteredAdapter("examples/does_not_exist_policy.csv") _ = e.InitWithAdapter("examples/rbac_with_domains_model.conf", adapter) if err := e.LoadFilteredPolicy(nil); err == nil { t.Errorf("expected error in LoadFilteredPolicy, but got nil") } } golang-github-casbin-casbin-2.82.0/frontend.go000066400000000000000000000026321456376452400212150ustar00rootroot00000000000000// Copyright 2020 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "bytes" "encoding/json" ) func CasbinJsGetPermissionForUser(e IEnforcer, user string) (string, error) { model := e.GetModel() m := map[string]interface{}{} m["m"] = model.ToText() pRules := [][]string{} for ptype := range model["p"] { policies := model.GetPolicy("p", ptype) for _, rules := range policies { pRules = append(pRules, append([]string{ptype}, rules...)) } } m["p"] = pRules gRules := [][]string{} for ptype := range model["g"] { policies := model.GetPolicy("g", ptype) for _, rules := range policies { gRules = append(gRules, append([]string{ptype}, rules...)) } } m["g"] = gRules result := bytes.NewBuffer([]byte{}) encoder := json.NewEncoder(result) encoder.SetEscapeHTML(false) err := encoder.Encode(m) return result.String(), err } golang-github-casbin-casbin-2.82.0/frontend_old.go000066400000000000000000000020171456376452400220500ustar00rootroot00000000000000// Copyright 2021 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import "encoding/json" func CasbinJsGetPermissionForUserOld(e IEnforcer, user string) ([]byte, error) { policy, err := e.GetImplicitPermissionsForUser(user) if err != nil { return nil, err } permission := make(map[string][]string) for i := 0; i < len(policy); i++ { permission[policy[i][2]] = append(permission[policy[i][2]], policy[i][1]) } b, _ := json.Marshal(permission) return b, nil } golang-github-casbin-casbin-2.82.0/frontend_old_test.go000066400000000000000000000050701456376452400231110ustar00rootroot00000000000000// Copyright 2021 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "encoding/json" "testing" ) func contains(arr []string, target string) bool { for _, item := range arr { if item == target { return true } } return false } func TestCasbinJsGetPermissionForUserOld(t *testing.T) { e, err := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") if err != nil { panic(err) } target_str, _ := CasbinJsGetPermissionForUserOld(e, "alice") t.Log("GetPermissionForUser Alice", string(target_str)) alice_target := make(map[string][]string) err = json.Unmarshal(target_str, &alice_target) if err != nil { t.Errorf("Test error: %s", err) } perm, ok := alice_target["read"] if !ok { t.Errorf("Test error: Alice doesn't have read permission") } if !contains(perm, "data1") { t.Errorf("Test error: Alice cannot read data1") } if !contains(perm, "data2") { t.Errorf("Test error: Alice cannot read data2") } perm, ok = alice_target["write"] if !ok { t.Errorf("Test error: Alice doesn't have write permission") } if contains(perm, "data1") { t.Errorf("Test error: Alice can write data1") } if !contains(perm, "data2") { t.Errorf("Test error: Alice cannot write data2") } target_str, _ = CasbinJsGetPermissionForUserOld(e, "bob") t.Log("GetPermissionForUser Bob", string(target_str)) bob_target := make(map[string][]string) err = json.Unmarshal(target_str, &bob_target) if err != nil { t.Errorf("Test error: %s", err) } _, ok = bob_target["read"] if ok { t.Errorf("Test error: Bob has read permission") } perm, ok = bob_target["write"] if !ok { t.Errorf("Test error: Bob doesn't have permission") } if !contains(perm, "data2") { t.Errorf("Test error: Bob cannot write data2") } if contains(perm, "data1") { t.Errorf("Test error: Bob can write data1") } if contains(perm, "data_not_exist") { t.Errorf("Test error: Bob can access a non-existing data") } _, ok = bob_target["rm_rf"] if ok { t.Errorf("Someone can have a non-existing action (rm -rf)") } } golang-github-casbin-casbin-2.82.0/frontend_test.go000066400000000000000000000041741456376452400222570ustar00rootroot00000000000000// Copyright 2020 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "encoding/json" "io/ioutil" "regexp" "strings" "testing" ) func TestCasbinJsGetPermissionForUser(t *testing.T) { e, err := NewSyncedEnforcer("examples/rbac_model.conf", "examples/rbac_with_hierarchy_policy.csv") if err != nil { panic(err) } receivedString, err := CasbinJsGetPermissionForUser(e, "alice") // make sure CasbinJsGetPermissionForUser can be used with a SyncedEnforcer. if err != nil { t.Errorf("Test error: %s", err) } received := map[string]interface{}{} err = json.Unmarshal([]byte(receivedString), &received) if err != nil { t.Errorf("Test error: %s", err) } expectedModel, err := ioutil.ReadFile("examples/rbac_model.conf") if err != nil { t.Errorf("Test error: %s", err) } expectedModelStr := regexp.MustCompile("\n+").ReplaceAllString(string(expectedModel), "\n") if strings.TrimSpace(received["m"].(string)) != expectedModelStr { t.Errorf("%s supposed to be %s", strings.TrimSpace(received["m"].(string)), expectedModelStr) } expectedPolicies, err := ioutil.ReadFile("examples/rbac_with_hierarchy_policy.csv") if err != nil { t.Errorf("Test error: %s", err) } expectedPoliciesItem := regexp.MustCompile(",|\n").Split(string(expectedPolicies), -1) i := 0 for _, sArr := range received["p"].([]interface{}) { for _, s := range sArr.([]interface{}) { if strings.TrimSpace(s.(string)) != strings.TrimSpace(expectedPoliciesItem[i]) { t.Errorf("%s supposed to be %s", strings.TrimSpace(s.(string)), strings.TrimSpace(expectedPoliciesItem[i])) } i++ } } } golang-github-casbin-casbin-2.82.0/go.mod000066400000000000000000000001741456376452400201540ustar00rootroot00000000000000module github.com/casbin/casbin/v2 require ( github.com/casbin/govaluate v1.1.0 github.com/golang/mock v1.4.4 ) go 1.13 golang-github-casbin-casbin-2.82.0/go.sum000066400000000000000000000026661456376452400202110ustar00rootroot00000000000000github.com/casbin/govaluate v1.1.0 h1:6xdCWIpE9CwHdZhlVQW+froUrCsjb6/ZYNcXODfLT+E= github.com/casbin/govaluate v1.1.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang-github-casbin-casbin-2.82.0/internal_api.go000066400000000000000000000317261456376452400220510ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "fmt" Err "github.com/casbin/casbin/v2/errors" "github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/persist" ) const ( notImplemented = "not implemented" ) func (e *Enforcer) shouldPersist() bool { return e.adapter != nil && e.autoSave } func (e *Enforcer) shouldNotify() bool { return e.watcher != nil && e.autoNotifyWatcher } // addPolicy adds a rule to the current policy. func (e *Enforcer) addPolicyWithoutNotify(sec string, ptype string, rule []string) (bool, error) { if e.dispatcher != nil && e.autoNotifyDispatcher { return true, e.dispatcher.AddPolicies(sec, ptype, [][]string{rule}) } if e.model.HasPolicy(sec, ptype, rule) { return false, nil } if e.shouldPersist() { if err := e.adapter.AddPolicy(sec, ptype, rule); err != nil { if err.Error() != notImplemented { return false, err } } } e.model.AddPolicy(sec, ptype, rule) if sec == "g" { err := e.BuildIncrementalRoleLinks(model.PolicyAdd, ptype, [][]string{rule}) if err != nil { return true, err } } return true, nil } // addPoliciesWithoutNotify adds rules to the current policy without notify // If autoRemoveRepeat == true, existing rules are automatically filtered // Otherwise, false is returned directly func (e *Enforcer) addPoliciesWithoutNotify(sec string, ptype string, rules [][]string, autoRemoveRepeat bool) (bool, error) { if e.dispatcher != nil && e.autoNotifyDispatcher { return true, e.dispatcher.AddPolicies(sec, ptype, rules) } if !autoRemoveRepeat && e.model.HasPolicies(sec, ptype, rules) { return false, nil } if e.shouldPersist() { if err := e.adapter.(persist.BatchAdapter).AddPolicies(sec, ptype, rules); err != nil { if err.Error() != notImplemented { return false, err } } } e.model.AddPolicies(sec, ptype, rules) if sec == "g" { err := e.BuildIncrementalRoleLinks(model.PolicyAdd, ptype, rules) if err != nil { return true, err } err = e.BuildIncrementalConditionalRoleLinks(model.PolicyAdd, ptype, rules) if err != nil { return true, err } } return true, nil } // removePolicy removes a rule from the current policy. func (e *Enforcer) removePolicyWithoutNotify(sec string, ptype string, rule []string) (bool, error) { if e.dispatcher != nil && e.autoNotifyDispatcher { return true, e.dispatcher.RemovePolicies(sec, ptype, [][]string{rule}) } if e.shouldPersist() { if err := e.adapter.RemovePolicy(sec, ptype, rule); err != nil { if err.Error() != notImplemented { return false, err } } } ruleRemoved := e.model.RemovePolicy(sec, ptype, rule) if !ruleRemoved { return ruleRemoved, nil } if sec == "g" { err := e.BuildIncrementalRoleLinks(model.PolicyRemove, ptype, [][]string{rule}) if err != nil { return ruleRemoved, err } } return ruleRemoved, nil } func (e *Enforcer) updatePolicyWithoutNotify(sec string, ptype string, oldRule []string, newRule []string) (bool, error) { if e.dispatcher != nil && e.autoNotifyDispatcher { return true, e.dispatcher.UpdatePolicy(sec, ptype, oldRule, newRule) } if e.shouldPersist() { if err := e.adapter.(persist.UpdatableAdapter).UpdatePolicy(sec, ptype, oldRule, newRule); err != nil { if err.Error() != notImplemented { return false, err } } } ruleUpdated := e.model.UpdatePolicy(sec, ptype, oldRule, newRule) if !ruleUpdated { return ruleUpdated, nil } if sec == "g" { err := e.BuildIncrementalRoleLinks(model.PolicyRemove, ptype, [][]string{oldRule}) // remove the old rule if err != nil { return ruleUpdated, err } err = e.BuildIncrementalRoleLinks(model.PolicyAdd, ptype, [][]string{newRule}) // add the new rule if err != nil { return ruleUpdated, err } } return ruleUpdated, nil } func (e *Enforcer) updatePoliciesWithoutNotify(sec string, ptype string, oldRules [][]string, newRules [][]string) (bool, error) { if len(newRules) != len(oldRules) { return false, fmt.Errorf("the length of oldRules should be equal to the length of newRules, but got the length of oldRules is %d, the length of newRules is %d", len(oldRules), len(newRules)) } if e.dispatcher != nil && e.autoNotifyDispatcher { return true, e.dispatcher.UpdatePolicies(sec, ptype, oldRules, newRules) } if e.shouldPersist() { if err := e.adapter.(persist.UpdatableAdapter).UpdatePolicies(sec, ptype, oldRules, newRules); err != nil { if err.Error() != notImplemented { return false, err } } } ruleUpdated := e.model.UpdatePolicies(sec, ptype, oldRules, newRules) if !ruleUpdated { return ruleUpdated, nil } if sec == "g" { err := e.BuildIncrementalRoleLinks(model.PolicyRemove, ptype, oldRules) // remove the old rules if err != nil { return ruleUpdated, err } err = e.BuildIncrementalRoleLinks(model.PolicyAdd, ptype, newRules) // add the new rules if err != nil { return ruleUpdated, err } } return ruleUpdated, nil } // removePolicies removes rules from the current policy. func (e *Enforcer) removePoliciesWithoutNotify(sec string, ptype string, rules [][]string) (bool, error) { if !e.model.HasPolicies(sec, ptype, rules) { return false, nil } if e.dispatcher != nil && e.autoNotifyDispatcher { return true, e.dispatcher.RemovePolicies(sec, ptype, rules) } if e.shouldPersist() { if err := e.adapter.(persist.BatchAdapter).RemovePolicies(sec, ptype, rules); err != nil { if err.Error() != notImplemented { return false, err } } } rulesRemoved := e.model.RemovePolicies(sec, ptype, rules) if !rulesRemoved { return rulesRemoved, nil } if sec == "g" { err := e.BuildIncrementalRoleLinks(model.PolicyRemove, ptype, rules) if err != nil { return rulesRemoved, err } } return rulesRemoved, nil } // removeFilteredPolicy removes rules based on field filters from the current policy. func (e *Enforcer) removeFilteredPolicyWithoutNotify(sec string, ptype string, fieldIndex int, fieldValues []string) (bool, error) { if len(fieldValues) == 0 { return false, Err.ErrInvalidFieldValuesParameter } if e.dispatcher != nil && e.autoNotifyDispatcher { return true, e.dispatcher.RemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues...) } if e.shouldPersist() { if err := e.adapter.RemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues...); err != nil { if err.Error() != notImplemented { return false, err } } } ruleRemoved, effects := e.model.RemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues...) if !ruleRemoved { return ruleRemoved, nil } if sec == "g" { err := e.BuildIncrementalRoleLinks(model.PolicyRemove, ptype, effects) if err != nil { return ruleRemoved, err } } return ruleRemoved, nil } func (e *Enforcer) updateFilteredPoliciesWithoutNotify(sec string, ptype string, newRules [][]string, fieldIndex int, fieldValues ...string) ([][]string, error) { var ( oldRules [][]string err error ) if e.shouldPersist() { if oldRules, err = e.adapter.(persist.UpdatableAdapter).UpdateFilteredPolicies(sec, ptype, newRules, fieldIndex, fieldValues...); err != nil { if err.Error() != notImplemented { return nil, err } } // For compatibility, because some adapters return oldRules containing ptype, see https://github.com/casbin/xorm-adapter/issues/49 for i, oldRule := range oldRules { if len(oldRules[i]) == len(e.model[sec][ptype].Tokens)+1 { oldRules[i] = oldRule[1:] } } } if e.dispatcher != nil && e.autoNotifyDispatcher { return oldRules, e.dispatcher.UpdateFilteredPolicies(sec, ptype, oldRules, newRules) } ruleChanged := e.model.RemovePolicies(sec, ptype, oldRules) e.model.AddPolicies(sec, ptype, newRules) ruleChanged = ruleChanged && len(newRules) != 0 if !ruleChanged { return make([][]string, 0), nil } if sec == "g" { err := e.BuildIncrementalRoleLinks(model.PolicyRemove, ptype, oldRules) // remove the old rules if err != nil { return oldRules, err } err = e.BuildIncrementalRoleLinks(model.PolicyAdd, ptype, newRules) // add the new rules if err != nil { return oldRules, err } } return oldRules, nil } // addPolicy adds a rule to the current policy. func (e *Enforcer) addPolicy(sec string, ptype string, rule []string) (bool, error) { ok, err := e.addPolicyWithoutNotify(sec, ptype, rule) if !ok || err != nil { return ok, err } if e.shouldNotify() { var err error if watcher, ok := e.watcher.(persist.WatcherEx); ok { err = watcher.UpdateForAddPolicy(sec, ptype, rule...) } else { err = e.watcher.Update() } return true, err } return true, nil } // addPolicies adds rules to the current policy. // If autoRemoveRepeat == true, existing rules are automatically filtered // Otherwise, false is returned directly func (e *Enforcer) addPolicies(sec string, ptype string, rules [][]string, autoRemoveRepeat bool) (bool, error) { ok, err := e.addPoliciesWithoutNotify(sec, ptype, rules, autoRemoveRepeat) if !ok || err != nil { return ok, err } if e.shouldNotify() { var err error if watcher, ok := e.watcher.(persist.WatcherEx); ok { err = watcher.UpdateForAddPolicies(sec, ptype, rules...) } else { err = e.watcher.Update() } return true, err } return true, nil } // removePolicy removes a rule from the current policy. func (e *Enforcer) removePolicy(sec string, ptype string, rule []string) (bool, error) { ok, err := e.removePolicyWithoutNotify(sec, ptype, rule) if !ok || err != nil { return ok, err } if e.shouldNotify() { var err error if watcher, ok := e.watcher.(persist.WatcherEx); ok { err = watcher.UpdateForRemovePolicy(sec, ptype, rule...) } else { err = e.watcher.Update() } return true, err } return true, nil } func (e *Enforcer) updatePolicy(sec string, ptype string, oldRule []string, newRule []string) (bool, error) { ok, err := e.updatePolicyWithoutNotify(sec, ptype, oldRule, newRule) if !ok || err != nil { return ok, err } if e.shouldNotify() { var err error if watcher, ok := e.watcher.(persist.UpdatableWatcher); ok { err = watcher.UpdateForUpdatePolicy(sec, ptype, oldRule, newRule) } else { err = e.watcher.Update() } return true, err } return true, nil } func (e *Enforcer) updatePolicies(sec string, ptype string, oldRules [][]string, newRules [][]string) (bool, error) { ok, err := e.updatePoliciesWithoutNotify(sec, ptype, oldRules, newRules) if !ok || err != nil { return ok, err } if e.shouldNotify() { var err error if watcher, ok := e.watcher.(persist.UpdatableWatcher); ok { err = watcher.UpdateForUpdatePolicies(sec, ptype, oldRules, newRules) } else { err = e.watcher.Update() } return true, err } return true, nil } // removePolicies removes rules from the current policy. func (e *Enforcer) removePolicies(sec string, ptype string, rules [][]string) (bool, error) { ok, err := e.removePoliciesWithoutNotify(sec, ptype, rules) if !ok || err != nil { return ok, err } if e.shouldNotify() { var err error if watcher, ok := e.watcher.(persist.WatcherEx); ok { err = watcher.UpdateForRemovePolicies(sec, ptype, rules...) } else { err = e.watcher.Update() } return true, err } return true, nil } // removeFilteredPolicy removes rules based on field filters from the current policy. func (e *Enforcer) removeFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues []string) (bool, error) { ok, err := e.removeFilteredPolicyWithoutNotify(sec, ptype, fieldIndex, fieldValues) if !ok || err != nil { return ok, err } if e.shouldNotify() { var err error if watcher, ok := e.watcher.(persist.WatcherEx); ok { err = watcher.UpdateForRemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues...) } else { err = e.watcher.Update() } return true, err } return true, nil } func (e *Enforcer) updateFilteredPolicies(sec string, ptype string, newRules [][]string, fieldIndex int, fieldValues ...string) (bool, error) { oldRules, err := e.updateFilteredPoliciesWithoutNotify(sec, ptype, newRules, fieldIndex, fieldValues...) ok := len(oldRules) != 0 if !ok || err != nil { return ok, err } if e.shouldNotify() { var err error if watcher, ok := e.watcher.(persist.UpdatableWatcher); ok { err = watcher.UpdateForUpdatePolicies(sec, ptype, oldRules, newRules) } else { err = e.watcher.Update() } return true, err } return true, nil } func (e *Enforcer) GetFieldIndex(ptype string, field string) (int, error) { return e.model.GetFieldIndex(ptype, field) } func (e *Enforcer) SetFieldIndex(ptype string, field string, index int) { assertion := e.model["p"][ptype] assertion.FieldIndexMap[field] = index } golang-github-casbin-casbin-2.82.0/log/000077500000000000000000000000001456376452400176255ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/log/default_logger.go000066400000000000000000000044531456376452400231450ustar00rootroot00000000000000// Copyright 2018 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package log import ( "fmt" "log" "strings" ) // DefaultLogger is the implementation for a Logger using golang log. type DefaultLogger struct { enabled bool } func (l *DefaultLogger) EnableLog(enable bool) { l.enabled = enable } func (l *DefaultLogger) IsEnabled() bool { return l.enabled } func (l *DefaultLogger) LogModel(model [][]string) { if !l.enabled { return } var str strings.Builder str.WriteString("Model: ") for _, v := range model { str.WriteString(fmt.Sprintf("%v\n", v)) } log.Println(str.String()) } func (l *DefaultLogger) LogEnforce(matcher string, request []interface{}, result bool, explains [][]string) { if !l.enabled { return } var reqStr strings.Builder reqStr.WriteString("Request: ") for i, rval := range request { if i != len(request)-1 { reqStr.WriteString(fmt.Sprintf("%v, ", rval)) } else { reqStr.WriteString(fmt.Sprintf("%v", rval)) } } reqStr.WriteString(fmt.Sprintf(" ---> %t\n", result)) reqStr.WriteString("Hit Policy: ") for i, pval := range explains { if i != len(explains)-1 { reqStr.WriteString(fmt.Sprintf("%v, ", pval)) } else { reqStr.WriteString(fmt.Sprintf("%v \n", pval)) } } log.Println(reqStr.String()) } func (l *DefaultLogger) LogPolicy(policy map[string][][]string) { if !l.enabled { return } var str strings.Builder str.WriteString("Policy: ") for k, v := range policy { str.WriteString(fmt.Sprintf("%s : %v\n", k, v)) } log.Println(str.String()) } func (l *DefaultLogger) LogRole(roles []string) { if !l.enabled { return } log.Println("Roles: ", strings.Join(roles, "\n")) } func (l *DefaultLogger) LogError(err error, msg ...string) { if !l.enabled { return } log.Println(msg, err) } golang-github-casbin-casbin-2.82.0/log/log_util.go000066400000000000000000000026571456376452400220040ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package log var logger Logger = &DefaultLogger{} // SetLogger sets the current logger. func SetLogger(l Logger) { logger = l } // GetLogger returns the current logger. func GetLogger() Logger { return logger } // LogModel logs the model information. func LogModel(model [][]string) { logger.LogModel(model) } // LogEnforce logs the enforcer information. func LogEnforce(matcher string, request []interface{}, result bool, explains [][]string) { logger.LogEnforce(matcher, request, result, explains) } // LogRole log info related to role. func LogRole(roles []string) { logger.LogRole(roles) } // LogPolicy logs the policy information. func LogPolicy(policy map[string][][]string) { logger.LogPolicy(policy) } // LogError logs the error information func LogError(err error, msg ...string) { logger.LogError(err, msg...) } golang-github-casbin-casbin-2.82.0/log/log_util_test.go000066400000000000000000000025511456376452400230340ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package log import ( "testing" "github.com/casbin/casbin/v2/log/mocks" "github.com/golang/mock/gomock" ) func TestLog(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() m := mocks.NewMockLogger(ctrl) SetLogger(m) m.EXPECT().EnableLog(true) m.EXPECT().IsEnabled() logger.EnableLog(true) logger.IsEnabled() policy := map[string][][]string{} m.EXPECT().LogPolicy(policy) LogPolicy(policy) var model [][]string m.EXPECT().LogModel(model) LogModel(model) matcher := "my_matcher" request := []interface{}{"bob"} result := true var explains [][]string m.EXPECT().LogEnforce(matcher, request, result, explains) LogEnforce(matcher, request, result, explains) var roles []string m.EXPECT().LogRole(roles) LogRole(roles) } golang-github-casbin-casbin-2.82.0/log/logger.go000066400000000000000000000025361456376452400214410ustar00rootroot00000000000000// Copyright 2018 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package log //go:generate mockgen -destination=./mocks/mock_logger.go -package=mocks github.com/casbin/casbin/v2/log Logger // Logger is the logging interface implementation. type Logger interface { // EnableLog controls whether print the message. EnableLog(bool) // IsEnabled returns if logger is enabled. IsEnabled() bool // LogModel log info related to model. LogModel(model [][]string) // LogEnforce log info related to enforce. LogEnforce(matcher string, request []interface{}, result bool, explains [][]string) // LogRole log info related to role. LogRole(roles []string) // LogPolicy log info related to policy. LogPolicy(policy map[string][][]string) // LogError log info relate to error LogError(err error, msg ...string) } golang-github-casbin-casbin-2.82.0/log/mocks/000077500000000000000000000000001456376452400207415ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/log/mocks/mock_logger.go000066400000000000000000000077221456376452400235700ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/casbin/casbin/v2/log (interfaces: Logger) // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" gomock "github.com/golang/mock/gomock" ) // MockLogger is a mock of Logger interface. type MockLogger struct { ctrl *gomock.Controller recorder *MockLoggerMockRecorder } // MockLoggerMockRecorder is the mock recorder for MockLogger. type MockLoggerMockRecorder struct { mock *MockLogger } // NewMockLogger creates a new mock instance. func NewMockLogger(ctrl *gomock.Controller) *MockLogger { mock := &MockLogger{ctrl: ctrl} mock.recorder = &MockLoggerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockLogger) EXPECT() *MockLoggerMockRecorder { return m.recorder } // EnableLog mocks base method. func (m *MockLogger) EnableLog(arg0 bool) { m.ctrl.T.Helper() m.ctrl.Call(m, "EnableLog", arg0) } // EnableLog indicates an expected call of EnableLog. func (mr *MockLoggerMockRecorder) EnableLog(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableLog", reflect.TypeOf((*MockLogger)(nil).EnableLog), arg0) } // IsEnabled mocks base method. func (m *MockLogger) IsEnabled() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsEnabled") ret0, _ := ret[0].(bool) return ret0 } // IsEnabled indicates an expected call of IsEnabled. func (mr *MockLoggerMockRecorder) IsEnabled() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsEnabled", reflect.TypeOf((*MockLogger)(nil).IsEnabled)) } // LogEnforce mocks base method. func (m *MockLogger) LogEnforce(arg0 string, arg1 []interface{}, arg2 bool, arg3 [][]string) { m.ctrl.T.Helper() m.ctrl.Call(m, "LogEnforce", arg0, arg1, arg2, arg3) } // LogEnforce indicates an expected call of LogEnforce. func (mr *MockLoggerMockRecorder) LogEnforce(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LogEnforce", reflect.TypeOf((*MockLogger)(nil).LogEnforce), arg0, arg1, arg2, arg3) } // LogError mocks base method. func (m *MockLogger) LogError(arg0 error, arg1 ...string) { m.ctrl.T.Helper() varargs := []interface{}{arg0} for _, a := range arg1 { varargs = append(varargs, a) } m.ctrl.Call(m, "LogError", varargs...) } // LogError indicates an expected call of LogError. func (mr *MockLoggerMockRecorder) LogError(arg0 interface{}, arg1 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{arg0}, arg1...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LogError", reflect.TypeOf((*MockLogger)(nil).LogError), varargs...) } // LogModel mocks base method. func (m *MockLogger) LogModel(arg0 [][]string) { m.ctrl.T.Helper() m.ctrl.Call(m, "LogModel", arg0) } // LogModel indicates an expected call of LogModel. func (mr *MockLoggerMockRecorder) LogModel(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LogModel", reflect.TypeOf((*MockLogger)(nil).LogModel), arg0) } // LogPolicy mocks base method. func (m *MockLogger) LogPolicy(arg0 map[string][][]string) { m.ctrl.T.Helper() m.ctrl.Call(m, "LogPolicy", arg0) } // LogPolicy indicates an expected call of LogPolicy. func (mr *MockLoggerMockRecorder) LogPolicy(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LogPolicy", reflect.TypeOf((*MockLogger)(nil).LogPolicy), arg0) } // LogRole mocks base method. func (m *MockLogger) LogRole(arg0 []string) { m.ctrl.T.Helper() m.ctrl.Call(m, "LogRole", arg0) } // LogRole indicates an expected call of LogRole. func (mr *MockLoggerMockRecorder) LogRole(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LogRole", reflect.TypeOf((*MockLogger)(nil).LogRole), arg0) } golang-github-casbin-casbin-2.82.0/management_api.go000066400000000000000000000452761456376452400223560ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "errors" "fmt" "strings" "github.com/casbin/casbin/v2/util" "github.com/casbin/govaluate" ) // GetAllSubjects gets the list of subjects that show up in the current policy. func (e *Enforcer) GetAllSubjects() []string { return e.model.GetValuesForFieldInPolicyAllTypes("p", 0) } // GetAllNamedSubjects gets the list of subjects that show up in the current named policy. func (e *Enforcer) GetAllNamedSubjects(ptype string) []string { return e.model.GetValuesForFieldInPolicy("p", ptype, 0) } // GetAllObjects gets the list of objects that show up in the current policy. func (e *Enforcer) GetAllObjects() []string { return e.model.GetValuesForFieldInPolicyAllTypes("p", 1) } // GetAllNamedObjects gets the list of objects that show up in the current named policy. func (e *Enforcer) GetAllNamedObjects(ptype string) []string { return e.model.GetValuesForFieldInPolicy("p", ptype, 1) } // GetAllActions gets the list of actions that show up in the current policy. func (e *Enforcer) GetAllActions() []string { return e.model.GetValuesForFieldInPolicyAllTypes("p", 2) } // GetAllNamedActions gets the list of actions that show up in the current named policy. func (e *Enforcer) GetAllNamedActions(ptype string) []string { return e.model.GetValuesForFieldInPolicy("p", ptype, 2) } // GetAllRoles gets the list of roles that show up in the current policy. func (e *Enforcer) GetAllRoles() []string { return e.model.GetValuesForFieldInPolicyAllTypes("g", 1) } // GetAllNamedRoles gets the list of roles that show up in the current named policy. func (e *Enforcer) GetAllNamedRoles(ptype string) []string { return e.model.GetValuesForFieldInPolicy("g", ptype, 1) } // GetPolicy gets all the authorization rules in the policy. func (e *Enforcer) GetPolicy() [][]string { return e.GetNamedPolicy("p") } // GetFilteredPolicy gets all the authorization rules in the policy, field filters can be specified. func (e *Enforcer) GetFilteredPolicy(fieldIndex int, fieldValues ...string) [][]string { return e.GetFilteredNamedPolicy("p", fieldIndex, fieldValues...) } // GetNamedPolicy gets all the authorization rules in the named policy. func (e *Enforcer) GetNamedPolicy(ptype string) [][]string { return e.model.GetPolicy("p", ptype) } // GetFilteredNamedPolicy gets all the authorization rules in the named policy, field filters can be specified. func (e *Enforcer) GetFilteredNamedPolicy(ptype string, fieldIndex int, fieldValues ...string) [][]string { return e.model.GetFilteredPolicy("p", ptype, fieldIndex, fieldValues...) } // GetGroupingPolicy gets all the role inheritance rules in the policy. func (e *Enforcer) GetGroupingPolicy() [][]string { return e.GetNamedGroupingPolicy("g") } // GetFilteredGroupingPolicy gets all the role inheritance rules in the policy, field filters can be specified. func (e *Enforcer) GetFilteredGroupingPolicy(fieldIndex int, fieldValues ...string) [][]string { return e.GetFilteredNamedGroupingPolicy("g", fieldIndex, fieldValues...) } // GetNamedGroupingPolicy gets all the role inheritance rules in the policy. func (e *Enforcer) GetNamedGroupingPolicy(ptype string) [][]string { return e.model.GetPolicy("g", ptype) } // GetFilteredNamedGroupingPolicy gets all the role inheritance rules in the policy, field filters can be specified. func (e *Enforcer) GetFilteredNamedGroupingPolicy(ptype string, fieldIndex int, fieldValues ...string) [][]string { return e.model.GetFilteredPolicy("g", ptype, fieldIndex, fieldValues...) } // GetFilteredNamedPolicyWithMatcher gets rules based on matcher from the policy. func (e *Enforcer) GetFilteredNamedPolicyWithMatcher(ptype string, matcher string) ([][]string, error) { var res [][]string var err error functions := e.fm.GetFunctions() if _, ok := e.model["g"]; ok { for key, ast := range e.model["g"] { rm := ast.RM functions[key] = util.GenerateGFunction(rm) } } var expString string if matcher == "" { return res, fmt.Errorf("matcher is empty") } else { expString = util.RemoveComments(util.EscapeAssertion(matcher)) } var expression *govaluate.EvaluableExpression expression, err = govaluate.NewEvaluableExpressionWithFunctions(expString, functions) if err != nil { return res, err } pTokens := make(map[string]int, len(e.model["p"][ptype].Tokens)) for i, token := range e.model["p"][ptype].Tokens { pTokens[token] = i } parameters := enforceParameters{ pTokens: pTokens, } if policyLen := len(e.model["p"][ptype].Policy); policyLen != 0 && strings.Contains(expString, ptype+"_") { for _, pvals := range e.model["p"][ptype].Policy { if len(e.model["p"][ptype].Tokens) != len(pvals) { return res, fmt.Errorf( "invalid policy size: expected %d, got %d, pvals: %v", len(e.model["p"][ptype].Tokens), len(pvals), pvals) } parameters.pVals = pvals result, err := expression.Eval(parameters) if err != nil { return res, err } switch result := result.(type) { case bool: if result { res = append(res, pvals) } case float64: if result != 0 { res = append(res, pvals) } default: return res, errors.New("matcher result should be bool, int or float") } } } return res, nil } // HasPolicy determines whether an authorization rule exists. func (e *Enforcer) HasPolicy(params ...interface{}) bool { return e.HasNamedPolicy("p", params...) } // HasNamedPolicy determines whether a named authorization rule exists. func (e *Enforcer) HasNamedPolicy(ptype string, params ...interface{}) bool { if strSlice, ok := params[0].([]string); len(params) == 1 && ok { return e.model.HasPolicy("p", ptype, strSlice) } policy := make([]string, 0) for _, param := range params { policy = append(policy, param.(string)) } return e.model.HasPolicy("p", ptype, policy) } // AddPolicy adds an authorization rule to the current policy. // If the rule already exists, the function returns false and the rule will not be added. // Otherwise the function returns true by adding the new rule. func (e *Enforcer) AddPolicy(params ...interface{}) (bool, error) { return e.AddNamedPolicy("p", params...) } // AddPolicies adds authorization rules to the current policy. // If the rule already exists, the function returns false for the corresponding rule and the rule will not be added. // Otherwise the function returns true for the corresponding rule by adding the new rule. func (e *Enforcer) AddPolicies(rules [][]string) (bool, error) { return e.AddNamedPolicies("p", rules) } // AddPoliciesEx adds authorization rules to the current policy. // If the rule already exists, the rule will not be added. // But unlike AddPolicies, other non-existent rules are added instead of returning false directly func (e *Enforcer) AddPoliciesEx(rules [][]string) (bool, error) { return e.AddNamedPoliciesEx("p", rules) } // AddNamedPolicy adds an authorization rule to the current named policy. // If the rule already exists, the function returns false and the rule will not be added. // Otherwise the function returns true by adding the new rule. func (e *Enforcer) AddNamedPolicy(ptype string, params ...interface{}) (bool, error) { if strSlice, ok := params[0].([]string); len(params) == 1 && ok { strSlice = append(make([]string, 0, len(strSlice)), strSlice...) return e.addPolicy("p", ptype, strSlice) } policy := make([]string, 0) for _, param := range params { policy = append(policy, param.(string)) } return e.addPolicy("p", ptype, policy) } // AddNamedPolicies adds authorization rules to the current named policy. // If the rule already exists, the function returns false for the corresponding rule and the rule will not be added. // Otherwise the function returns true for the corresponding by adding the new rule. func (e *Enforcer) AddNamedPolicies(ptype string, rules [][]string) (bool, error) { return e.addPolicies("p", ptype, rules, false) } // AddNamedPoliciesEx adds authorization rules to the current named policy. // If the rule already exists, the rule will not be added. // But unlike AddNamedPolicies, other non-existent rules are added instead of returning false directly func (e *Enforcer) AddNamedPoliciesEx(ptype string, rules [][]string) (bool, error) { return e.addPolicies("p", ptype, rules, true) } // RemovePolicy removes an authorization rule from the current policy. func (e *Enforcer) RemovePolicy(params ...interface{}) (bool, error) { return e.RemoveNamedPolicy("p", params...) } // UpdatePolicy updates an authorization rule from the current policy. func (e *Enforcer) UpdatePolicy(oldPolicy []string, newPolicy []string) (bool, error) { return e.UpdateNamedPolicy("p", oldPolicy, newPolicy) } func (e *Enforcer) UpdateNamedPolicy(ptype string, p1 []string, p2 []string) (bool, error) { return e.updatePolicy("p", ptype, p1, p2) } // UpdatePolicies updates authorization rules from the current policies. func (e *Enforcer) UpdatePolicies(oldPolices [][]string, newPolicies [][]string) (bool, error) { return e.UpdateNamedPolicies("p", oldPolices, newPolicies) } func (e *Enforcer) UpdateNamedPolicies(ptype string, p1 [][]string, p2 [][]string) (bool, error) { return e.updatePolicies("p", ptype, p1, p2) } func (e *Enforcer) UpdateFilteredPolicies(newPolicies [][]string, fieldIndex int, fieldValues ...string) (bool, error) { return e.UpdateFilteredNamedPolicies("p", newPolicies, fieldIndex, fieldValues...) } func (e *Enforcer) UpdateFilteredNamedPolicies(ptype string, newPolicies [][]string, fieldIndex int, fieldValues ...string) (bool, error) { return e.updateFilteredPolicies("p", ptype, newPolicies, fieldIndex, fieldValues...) } // RemovePolicies removes authorization rules from the current policy. func (e *Enforcer) RemovePolicies(rules [][]string) (bool, error) { return e.RemoveNamedPolicies("p", rules) } // RemoveFilteredPolicy removes an authorization rule from the current policy, field filters can be specified. func (e *Enforcer) RemoveFilteredPolicy(fieldIndex int, fieldValues ...string) (bool, error) { return e.RemoveFilteredNamedPolicy("p", fieldIndex, fieldValues...) } // RemoveNamedPolicy removes an authorization rule from the current named policy. func (e *Enforcer) RemoveNamedPolicy(ptype string, params ...interface{}) (bool, error) { if strSlice, ok := params[0].([]string); len(params) == 1 && ok { return e.removePolicy("p", ptype, strSlice) } policy := make([]string, 0) for _, param := range params { policy = append(policy, param.(string)) } return e.removePolicy("p", ptype, policy) } // RemoveNamedPolicies removes authorization rules from the current named policy. func (e *Enforcer) RemoveNamedPolicies(ptype string, rules [][]string) (bool, error) { return e.removePolicies("p", ptype, rules) } // RemoveFilteredNamedPolicy removes an authorization rule from the current named policy, field filters can be specified. func (e *Enforcer) RemoveFilteredNamedPolicy(ptype string, fieldIndex int, fieldValues ...string) (bool, error) { return e.removeFilteredPolicy("p", ptype, fieldIndex, fieldValues) } // HasGroupingPolicy determines whether a role inheritance rule exists. func (e *Enforcer) HasGroupingPolicy(params ...interface{}) bool { return e.HasNamedGroupingPolicy("g", params...) } // HasNamedGroupingPolicy determines whether a named role inheritance rule exists. func (e *Enforcer) HasNamedGroupingPolicy(ptype string, params ...interface{}) bool { if strSlice, ok := params[0].([]string); len(params) == 1 && ok { return e.model.HasPolicy("g", ptype, strSlice) } policy := make([]string, 0) for _, param := range params { policy = append(policy, param.(string)) } return e.model.HasPolicy("g", ptype, policy) } // AddGroupingPolicy adds a role inheritance rule to the current policy. // If the rule already exists, the function returns false and the rule will not be added. // Otherwise the function returns true by adding the new rule. func (e *Enforcer) AddGroupingPolicy(params ...interface{}) (bool, error) { return e.AddNamedGroupingPolicy("g", params...) } // AddGroupingPolicies adds role inheritance rules to the current policy. // If the rule already exists, the function returns false for the corresponding policy rule and the rule will not be added. // Otherwise the function returns true for the corresponding policy rule by adding the new rule. func (e *Enforcer) AddGroupingPolicies(rules [][]string) (bool, error) { return e.AddNamedGroupingPolicies("g", rules) } // AddGroupingPoliciesEx adds role inheritance rules to the current policy. // If the rule already exists, the rule will not be added. // But unlike AddGroupingPolicies, other non-existent rules are added instead of returning false directly func (e *Enforcer) AddGroupingPoliciesEx(rules [][]string) (bool, error) { return e.AddNamedGroupingPoliciesEx("g", rules) } // AddNamedGroupingPolicy adds a named role inheritance rule to the current policy. // If the rule already exists, the function returns false and the rule will not be added. // Otherwise the function returns true by adding the new rule. func (e *Enforcer) AddNamedGroupingPolicy(ptype string, params ...interface{}) (bool, error) { var ruleAdded bool var err error if strSlice, ok := params[0].([]string); len(params) == 1 && ok { ruleAdded, err = e.addPolicy("g", ptype, strSlice) } else { policy := make([]string, 0) for _, param := range params { policy = append(policy, param.(string)) } ruleAdded, err = e.addPolicy("g", ptype, policy) } return ruleAdded, err } // AddNamedGroupingPolicies adds named role inheritance rules to the current policy. // If the rule already exists, the function returns false for the corresponding policy rule and the rule will not be added. // Otherwise the function returns true for the corresponding policy rule by adding the new rule. func (e *Enforcer) AddNamedGroupingPolicies(ptype string, rules [][]string) (bool, error) { return e.addPolicies("g", ptype, rules, false) } // AddNamedGroupingPoliciesEx adds named role inheritance rules to the current policy. // If the rule already exists, the rule will not be added. // But unlike AddNamedGroupingPolicies, other non-existent rules are added instead of returning false directly func (e *Enforcer) AddNamedGroupingPoliciesEx(ptype string, rules [][]string) (bool, error) { return e.addPolicies("g", ptype, rules, true) } // RemoveGroupingPolicy removes a role inheritance rule from the current policy. func (e *Enforcer) RemoveGroupingPolicy(params ...interface{}) (bool, error) { return e.RemoveNamedGroupingPolicy("g", params...) } // RemoveGroupingPolicies removes role inheritance rules from the current policy. func (e *Enforcer) RemoveGroupingPolicies(rules [][]string) (bool, error) { return e.RemoveNamedGroupingPolicies("g", rules) } // RemoveFilteredGroupingPolicy removes a role inheritance rule from the current policy, field filters can be specified. func (e *Enforcer) RemoveFilteredGroupingPolicy(fieldIndex int, fieldValues ...string) (bool, error) { return e.RemoveFilteredNamedGroupingPolicy("g", fieldIndex, fieldValues...) } // RemoveNamedGroupingPolicy removes a role inheritance rule from the current named policy. func (e *Enforcer) RemoveNamedGroupingPolicy(ptype string, params ...interface{}) (bool, error) { var ruleRemoved bool var err error if strSlice, ok := params[0].([]string); len(params) == 1 && ok { ruleRemoved, err = e.removePolicy("g", ptype, strSlice) } else { policy := make([]string, 0) for _, param := range params { policy = append(policy, param.(string)) } ruleRemoved, err = e.removePolicy("g", ptype, policy) } return ruleRemoved, err } // RemoveNamedGroupingPolicies removes role inheritance rules from the current named policy. func (e *Enforcer) RemoveNamedGroupingPolicies(ptype string, rules [][]string) (bool, error) { return e.removePolicies("g", ptype, rules) } func (e *Enforcer) UpdateGroupingPolicy(oldRule []string, newRule []string) (bool, error) { return e.UpdateNamedGroupingPolicy("g", oldRule, newRule) } // UpdateGroupingPolicies updates authorization rules from the current policies. func (e *Enforcer) UpdateGroupingPolicies(oldRules [][]string, newRules [][]string) (bool, error) { return e.UpdateNamedGroupingPolicies("g", oldRules, newRules) } func (e *Enforcer) UpdateNamedGroupingPolicy(ptype string, oldRule []string, newRule []string) (bool, error) { return e.updatePolicy("g", ptype, oldRule, newRule) } func (e *Enforcer) UpdateNamedGroupingPolicies(ptype string, oldRules [][]string, newRules [][]string) (bool, error) { return e.updatePolicies("g", ptype, oldRules, newRules) } // RemoveFilteredNamedGroupingPolicy removes a role inheritance rule from the current named policy, field filters can be specified. func (e *Enforcer) RemoveFilteredNamedGroupingPolicy(ptype string, fieldIndex int, fieldValues ...string) (bool, error) { return e.removeFilteredPolicy("g", ptype, fieldIndex, fieldValues) } // AddFunction adds a customized function. func (e *Enforcer) AddFunction(name string, function govaluate.ExpressionFunction) { e.fm.AddFunction(name, function) } func (e *Enforcer) SelfAddPolicy(sec string, ptype string, rule []string) (bool, error) { return e.addPolicyWithoutNotify(sec, ptype, rule) } func (e *Enforcer) SelfAddPolicies(sec string, ptype string, rules [][]string) (bool, error) { return e.addPoliciesWithoutNotify(sec, ptype, rules, false) } func (e *Enforcer) SelfAddPoliciesEx(sec string, ptype string, rules [][]string) (bool, error) { return e.addPoliciesWithoutNotify(sec, ptype, rules, true) } func (e *Enforcer) SelfRemovePolicy(sec string, ptype string, rule []string) (bool, error) { return e.removePolicyWithoutNotify(sec, ptype, rule) } func (e *Enforcer) SelfRemovePolicies(sec string, ptype string, rules [][]string) (bool, error) { return e.removePoliciesWithoutNotify(sec, ptype, rules) } func (e *Enforcer) SelfRemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) (bool, error) { return e.removeFilteredPolicyWithoutNotify(sec, ptype, fieldIndex, fieldValues) } func (e *Enforcer) SelfUpdatePolicy(sec string, ptype string, oldRule, newRule []string) (bool, error) { return e.updatePolicyWithoutNotify(sec, ptype, oldRule, newRule) } func (e *Enforcer) SelfUpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) (bool, error) { return e.updatePoliciesWithoutNotify(sec, ptype, oldRules, newRules) } golang-github-casbin-casbin-2.82.0/management_api_test.go000066400000000000000000000322441456376452400234040ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "testing" "github.com/casbin/casbin/v2/util" ) func testStringList(t *testing.T, title string, f func() []string, res []string) { t.Helper() myRes := f() t.Log(title+": ", myRes) if !util.ArrayEquals(res, myRes) { t.Error(title+": ", myRes, ", supposed to be ", res) } } func TestGetList(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") testStringList(t, "Subjects", e.GetAllSubjects, []string{"alice", "bob", "data2_admin"}) testStringList(t, "Objects", e.GetAllObjects, []string{"data1", "data2"}) testStringList(t, "Actions", e.GetAllActions, []string{"read", "write"}) testStringList(t, "Roles", e.GetAllRoles, []string{"data2_admin"}) } func testGetPolicy(t *testing.T, e *Enforcer, res [][]string) { t.Helper() myRes := e.GetPolicy() t.Log("Policy: ", myRes) if !util.Array2DEquals(res, myRes) { t.Error("Policy: ", myRes, ", supposed to be ", res) } } func testGetFilteredPolicy(t *testing.T, e *Enforcer, fieldIndex int, res [][]string, fieldValues ...string) { t.Helper() myRes := e.GetFilteredPolicy(fieldIndex, fieldValues...) t.Log("Policy for ", util.ParamsToString(fieldValues...), ": ", myRes) if !util.Array2DEquals(res, myRes) { t.Error("Policy for ", util.ParamsToString(fieldValues...), ": ", myRes, ", supposed to be ", res) } } func testGetFilteredNamedPolicyWithMatcher(t *testing.T, e *Enforcer, ptype string, matcher string, res [][]string) { t.Helper() myRes, err := e.GetFilteredNamedPolicyWithMatcher(ptype, matcher) t.Log("Policy for", matcher, ": ", myRes) if err != nil { t.Error(err) } if !util.Array2DEquals(res, myRes) { t.Error("Policy for ", matcher, ": ", myRes, ", supposed to be ", res) } } func testGetGroupingPolicy(t *testing.T, e *Enforcer, res [][]string) { t.Helper() myRes := e.GetGroupingPolicy() t.Log("Grouping policy: ", myRes) if !util.Array2DEquals(res, myRes) { t.Error("Grouping policy: ", myRes, ", supposed to be ", res) } } func testGetFilteredGroupingPolicy(t *testing.T, e *Enforcer, fieldIndex int, res [][]string, fieldValues ...string) { t.Helper() myRes := e.GetFilteredGroupingPolicy(fieldIndex, fieldValues...) t.Log("Grouping policy for ", util.ParamsToString(fieldValues...), ": ", myRes) if !util.Array2DEquals(res, myRes) { t.Error("Grouping policy for ", util.ParamsToString(fieldValues...), ": ", myRes, ", supposed to be ", res) } } func testHasPolicy(t *testing.T, e *Enforcer, policy []string, res bool) { t.Helper() myRes := e.HasPolicy(policy) t.Log("Has policy ", util.ArrayToString(policy), ": ", myRes) if res != myRes { t.Error("Has policy ", util.ArrayToString(policy), ": ", myRes, ", supposed to be ", res) } } func testHasGroupingPolicy(t *testing.T, e *Enforcer, policy []string, res bool) { t.Helper() myRes := e.HasGroupingPolicy(policy) t.Log("Has grouping policy ", util.ArrayToString(policy), ": ", myRes) if res != myRes { t.Error("Has grouping policy ", util.ArrayToString(policy), ": ", myRes, ", supposed to be ", res) } } func TestGetPolicyAPI(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") testGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}) testGetFilteredPolicy(t, e, 0, [][]string{{"alice", "data1", "read"}}, "alice") testGetFilteredPolicy(t, e, 0, [][]string{{"bob", "data2", "write"}}, "bob") testGetFilteredPolicy(t, e, 0, [][]string{{"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}, "data2_admin") testGetFilteredPolicy(t, e, 1, [][]string{{"alice", "data1", "read"}}, "data1") testGetFilteredPolicy(t, e, 1, [][]string{{"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}, "data2") testGetFilteredPolicy(t, e, 2, [][]string{{"alice", "data1", "read"}, {"data2_admin", "data2", "read"}}, "read") testGetFilteredPolicy(t, e, 2, [][]string{{"bob", "data2", "write"}, {"data2_admin", "data2", "write"}}, "write") testGetFilteredNamedPolicyWithMatcher(t, e, "p", "'alice' == p.sub", [][]string{{"alice", "data1", "read"}}) testGetFilteredNamedPolicyWithMatcher(t, e, "p", "keyMatch2(p.sub, '*')", [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}) testGetFilteredPolicy(t, e, 0, [][]string{{"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}, "data2_admin", "data2") // Note: "" (empty string) in fieldValues means matching all values. testGetFilteredPolicy(t, e, 0, [][]string{{"data2_admin", "data2", "read"}}, "data2_admin", "", "read") testGetFilteredPolicy(t, e, 1, [][]string{{"bob", "data2", "write"}, {"data2_admin", "data2", "write"}}, "data2", "write") testHasPolicy(t, e, []string{"alice", "data1", "read"}, true) testHasPolicy(t, e, []string{"bob", "data2", "write"}, true) testHasPolicy(t, e, []string{"alice", "data2", "read"}, false) testHasPolicy(t, e, []string{"bob", "data3", "write"}, false) testGetGroupingPolicy(t, e, [][]string{{"alice", "data2_admin"}}) testGetFilteredGroupingPolicy(t, e, 0, [][]string{{"alice", "data2_admin"}}, "alice") testGetFilteredGroupingPolicy(t, e, 0, [][]string{}, "bob") testGetFilteredGroupingPolicy(t, e, 1, [][]string{}, "data1_admin") testGetFilteredGroupingPolicy(t, e, 1, [][]string{{"alice", "data2_admin"}}, "data2_admin") // Note: "" (empty string) in fieldValues means matching all values. testGetFilteredGroupingPolicy(t, e, 0, [][]string{{"alice", "data2_admin"}}, "", "data2_admin") testHasGroupingPolicy(t, e, []string{"alice", "data2_admin"}, true) testHasGroupingPolicy(t, e, []string{"bob", "data2_admin"}, false) } func TestModifyPolicyAPI(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") testGetPolicy(t, e, [][]string{ {"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}) _, _ = e.RemovePolicy("alice", "data1", "read") _, _ = e.RemovePolicy("bob", "data2", "write") _, _ = e.RemovePolicy("alice", "data1", "read") _, _ = e.AddPolicy("eve", "data3", "read") _, _ = e.AddPolicy("eve", "data3", "read") rules := [][]string{ {"jack", "data4", "read"}, {"jack", "data4", "read"}, {"jack", "data4", "read"}, {"katy", "data4", "write"}, {"leyo", "data4", "read"}, {"katy", "data4", "write"}, {"katy", "data4", "write"}, {"ham", "data4", "write"}, } _, _ = e.AddPolicies(rules) _, _ = e.AddPolicies(rules) testGetPolicy(t, e, [][]string{ {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}, {"eve", "data3", "read"}, {"jack", "data4", "read"}, {"katy", "data4", "write"}, {"leyo", "data4", "read"}, {"ham", "data4", "write"}}) _, _ = e.RemovePolicies(rules) _, _ = e.RemovePolicies(rules) namedPolicy := []string{"eve", "data3", "read"} _, _ = e.RemoveNamedPolicy("p", namedPolicy) _, _ = e.AddNamedPolicy("p", namedPolicy) testGetPolicy(t, e, [][]string{ {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}, {"eve", "data3", "read"}}) _, _ = e.RemoveFilteredPolicy(1, "data2") testGetPolicy(t, e, [][]string{{"eve", "data3", "read"}}) _, _ = e.UpdatePolicy([]string{"eve", "data3", "read"}, []string{"eve", "data3", "write"}) testGetPolicy(t, e, [][]string{{"eve", "data3", "write"}}) // This test shows a rollback effect. // _, _ = e.UpdatePolicies([][]string{{"eve", "data3", "write"}, {"jack", "data4", "read"}}, [][]string{{"eve", "data3", "read"}, {"jack", "data4", "write"}}) // testGetPolicy(t, e, [][]string{{"eve", "data3", "read"}, {"jack", "data4", "write"}}) _, _ = e.AddPolicies(rules) _, _ = e.UpdatePolicies([][]string{{"eve", "data3", "write"}, {"leyo", "data4", "read"}, {"katy", "data4", "write"}}, [][]string{{"eve", "data3", "read"}, {"leyo", "data4", "write"}, {"katy", "data1", "write"}}) testGetPolicy(t, e, [][]string{{"eve", "data3", "read"}, {"jack", "data4", "read"}, {"katy", "data1", "write"}, {"leyo", "data4", "write"}, {"ham", "data4", "write"}}) e.ClearPolicy() _, _ = e.AddPoliciesEx([][]string{{"user1", "data1", "read"}, {"user1", "data1", "read"}}) testGetPolicy(t, e, [][]string{{"user1", "data1", "read"}}) // {"user1", "data1", "read"} repeated _, _ = e.AddPoliciesEx([][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}}) testGetPolicy(t, e, [][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}}) // {"user1", "data1", "read"}, {"user2", "data2", "read"} repeated _, _ = e.AddNamedPoliciesEx("p", [][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}, {"user3", "data3", "read"}}) testGetPolicy(t, e, [][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}, {"user3", "data3", "read"}}) // {"user1", "data1", "read"}, {"user2", "data2", "read"}, , {"user3", "data3", "read"} repeated _, _ = e.SelfAddPoliciesEx("p", "p", [][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}, {"user3", "data3", "read"}, {"user4", "data4", "read"}}) testGetPolicy(t, e, [][]string{{"user1", "data1", "read"}, {"user2", "data2", "read"}, {"user3", "data3", "read"}, {"user4", "data4", "read"}}) } func TestModifyGroupingPolicyAPI(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") testGetRoles(t, e, []string{"data2_admin"}, "alice") testGetRoles(t, e, []string{}, "bob") testGetRoles(t, e, []string{}, "eve") testGetRoles(t, e, []string{}, "non_exist") _, _ = e.RemoveGroupingPolicy("alice", "data2_admin") _, _ = e.AddGroupingPolicy("bob", "data1_admin") _, _ = e.AddGroupingPolicy("eve", "data3_admin") groupingRules := [][]string{ {"ham", "data4_admin"}, {"jack", "data5_admin"}, } _, _ = e.AddGroupingPolicies(groupingRules) testGetRoles(t, e, []string{"data4_admin"}, "ham") testGetRoles(t, e, []string{"data5_admin"}, "jack") _, _ = e.RemoveGroupingPolicies(groupingRules) testGetRoles(t, e, []string{}, "alice") namedGroupingPolicy := []string{"alice", "data2_admin"} testGetRoles(t, e, []string{}, "alice") _, _ = e.AddNamedGroupingPolicy("g", namedGroupingPolicy) testGetRoles(t, e, []string{"data2_admin"}, "alice") _, _ = e.RemoveNamedGroupingPolicy("g", namedGroupingPolicy) _, _ = e.AddNamedGroupingPolicies("g", groupingRules) _, _ = e.AddNamedGroupingPolicies("g", groupingRules) testGetRoles(t, e, []string{"data4_admin"}, "ham") testGetRoles(t, e, []string{"data5_admin"}, "jack") _, _ = e.RemoveNamedGroupingPolicies("g", groupingRules) _, _ = e.RemoveNamedGroupingPolicies("g", groupingRules) testGetRoles(t, e, []string{}, "alice") testGetRoles(t, e, []string{"data1_admin"}, "bob") testGetRoles(t, e, []string{"data3_admin"}, "eve") testGetRoles(t, e, []string{}, "non_exist") testGetUsers(t, e, []string{"bob"}, "data1_admin") testGetUsers(t, e, []string{}, "data2_admin") testGetUsers(t, e, []string{"eve"}, "data3_admin") _, _ = e.RemoveFilteredGroupingPolicy(0, "bob") testGetRoles(t, e, []string{}, "alice") testGetRoles(t, e, []string{}, "bob") testGetRoles(t, e, []string{"data3_admin"}, "eve") testGetRoles(t, e, []string{}, "non_exist") testGetUsers(t, e, []string{}, "data1_admin") testGetUsers(t, e, []string{}, "data2_admin") testGetUsers(t, e, []string{"eve"}, "data3_admin") _, _ = e.AddGroupingPolicy("data3_admin", "data4_admin") _, _ = e.UpdateGroupingPolicy([]string{"eve", "data3_admin"}, []string{"eve", "admin"}) _, _ = e.UpdateGroupingPolicy([]string{"data3_admin", "data4_admin"}, []string{"admin", "data4_admin"}) testGetUsers(t, e, []string{"admin"}, "data4_admin") testGetUsers(t, e, []string{"eve"}, "admin") testGetRoles(t, e, []string{"admin"}, "eve") testGetRoles(t, e, []string{"data4_admin"}, "admin") _, _ = e.UpdateGroupingPolicies([][]string{{"eve", "admin"}}, [][]string{{"eve", "admin_groups"}}) _, _ = e.UpdateGroupingPolicies([][]string{{"admin", "data4_admin"}}, [][]string{{"admin", "data5_admin"}}) testGetUsers(t, e, []string{"admin"}, "data5_admin") testGetUsers(t, e, []string{"eve"}, "admin_groups") testGetRoles(t, e, []string{"data5_admin"}, "admin") testGetRoles(t, e, []string{"admin_groups"}, "eve") e.ClearPolicy() _, _ = e.AddGroupingPoliciesEx([][]string{{"user1", "member"}}) testGetUsers(t, e, []string{"user1"}, "member") // {"user1", "member"} repeated _, _ = e.AddGroupingPoliciesEx([][]string{{"user1", "member"}, {"user2", "member"}}) testGetUsers(t, e, []string{"user1", "user2"}, "member") // {"user1", "member"}, {"user2", "member"} repeated _, _ = e.AddNamedGroupingPoliciesEx("g", [][]string{{"user1", "member"}, {"user2", "member"}, {"user3", "member"}}) testGetUsers(t, e, []string{"user1", "user2", "user3"}, "member") } golang-github-casbin-casbin-2.82.0/managerment_api_b_test.go000066400000000000000000000120721456376452400240640ustar00rootroot00000000000000// Copyright 2020 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "fmt" "math/rand" "testing" ) func BenchmarkHasPolicySmall(b *testing.B) { e, _ := NewEnforcer("examples/basic_model.conf", false) // 100 roles, 10 resources. for i := 0; i < 100; i++ { _, _ = e.AddPolicy(fmt.Sprintf("user%d", i), fmt.Sprintf("data%d", i/10), "read") } b.ResetTimer() for i := 0; i < b.N; i++ { e.HasPolicy(fmt.Sprintf("user%d", rand.Intn(100)), fmt.Sprintf("data%d", rand.Intn(100)/10), "read") } } func BenchmarkHasPolicyMedium(b *testing.B) { e, _ := NewEnforcer("examples/basic_model.conf", false) // 1000 roles, 100 resources. pPolicies := make([][]string, 0) for i := 0; i < 1000; i++ { pPolicies = append(pPolicies, []string{fmt.Sprintf("user%d", i), fmt.Sprintf("data%d", i/10), "read"}) } _, err := e.AddPolicies(pPolicies) if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { e.HasPolicy(fmt.Sprintf("user%d", rand.Intn(1000)), fmt.Sprintf("data%d", rand.Intn(1000)/10), "read") } } func BenchmarkHasPolicyLarge(b *testing.B) { e, _ := NewEnforcer("examples/basic_model.conf", false) // 10000 roles, 1000 resources. pPolicies := make([][]string, 0) for i := 0; i < 10000; i++ { pPolicies = append(pPolicies, []string{fmt.Sprintf("user%d", i), fmt.Sprintf("data%d", i/10), "read"}) } _, err := e.AddPolicies(pPolicies) if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { e.HasPolicy(fmt.Sprintf("user%d", rand.Intn(10000)), fmt.Sprintf("data%d", rand.Intn(10000)/10), "read") } } func BenchmarkAddPolicySmall(b *testing.B) { e, _ := NewEnforcer("examples/basic_model.conf", false) // 100 roles, 10 resources. for i := 0; i < 100; i++ { _, _ = e.AddPolicy(fmt.Sprintf("user%d", i), fmt.Sprintf("data%d", i/10), "read") } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.AddPolicy(fmt.Sprintf("user%d", rand.Intn(100)+100), fmt.Sprintf("data%d", (rand.Intn(100)+100)/10), "read") } } func BenchmarkAddPolicyMedium(b *testing.B) { e, _ := NewEnforcer("examples/basic_model.conf", false) // 1000 roles, 100 resources. pPolicies := make([][]string, 0) for i := 0; i < 1000; i++ { pPolicies = append(pPolicies, []string{fmt.Sprintf("user%d", i), fmt.Sprintf("data%d", i/10), "read"}) } _, err := e.AddPolicies(pPolicies) if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.AddPolicy(fmt.Sprintf("user%d", rand.Intn(1000)+1000), fmt.Sprintf("data%d", (rand.Intn(1000)+1000)/10), "read") } } func BenchmarkAddPolicyLarge(b *testing.B) { e, _ := NewEnforcer("examples/basic_model.conf", false) // 10000 roles, 1000 resources. pPolicies := make([][]string, 0) for i := 0; i < 10000; i++ { pPolicies = append(pPolicies, []string{fmt.Sprintf("user%d", i), fmt.Sprintf("data%d", i/10), "read"}) } _, err := e.AddPolicies(pPolicies) if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.AddPolicy(fmt.Sprintf("user%d", rand.Intn(10000)+10000), fmt.Sprintf("data%d", (rand.Intn(10000)+10000)/10), "read") } } func BenchmarkRemovePolicySmall(b *testing.B) { e, _ := NewEnforcer("examples/basic_model.conf", false) // 100 roles, 10 resources. for i := 0; i < 100; i++ { _, _ = e.AddPolicy(fmt.Sprintf("user%d", i), fmt.Sprintf("data%d", i/10), "read") } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.RemovePolicy(fmt.Sprintf("user%d", rand.Intn(100)), fmt.Sprintf("data%d", rand.Intn(100)/10), "read") } } func BenchmarkRemovePolicyMedium(b *testing.B) { e, _ := NewEnforcer("examples/basic_model.conf", false) // 1000 roles, 100 resources. pPolicies := make([][]string, 0) for i := 0; i < 1000; i++ { pPolicies = append(pPolicies, []string{fmt.Sprintf("user%d", i), fmt.Sprintf("data%d", i/10), "read"}) } _, err := e.AddPolicies(pPolicies) if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.RemovePolicy(fmt.Sprintf("user%d", rand.Intn(1000)), fmt.Sprintf("data%d", rand.Intn(1000)/10), "read") } } func BenchmarkRemovePolicyLarge(b *testing.B) { e, _ := NewEnforcer("examples/basic_model.conf", false) // 10000 roles, 1000 resources. pPolicies := make([][]string, 0) for i := 0; i < 10000; i++ { pPolicies = append(pPolicies, []string{fmt.Sprintf("user%d", i), fmt.Sprintf("data%d", i/10), "read"}) } _, err := e.AddPolicies(pPolicies) if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.RemovePolicy(fmt.Sprintf("user%d", rand.Intn(10000)), fmt.Sprintf("data%d", rand.Intn(10000)/10), "read") } } golang-github-casbin-casbin-2.82.0/model/000077500000000000000000000000001456376452400201445ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/model/assertion.go000066400000000000000000000115431456376452400225060ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "errors" "strings" "github.com/casbin/casbin/v2/log" "github.com/casbin/casbin/v2/rbac" ) // Assertion represents an expression in a section of the model. // For example: r = sub, obj, act type Assertion struct { Key string Value string Tokens []string ParamsTokens []string Policy [][]string PolicyMap map[string]int RM rbac.RoleManager CondRM rbac.ConditionalRoleManager FieldIndexMap map[string]int logger log.Logger } func (ast *Assertion) buildIncrementalRoleLinks(rm rbac.RoleManager, op PolicyOp, rules [][]string) error { ast.RM = rm count := strings.Count(ast.Value, "_") if count < 2 { return errors.New("the number of \"_\" in role definition should be at least 2") } for _, rule := range rules { if len(rule) < count { return errors.New("grouping policy elements do not meet role definition") } if len(rule) > count { rule = rule[:count] } switch op { case PolicyAdd: err := rm.AddLink(rule[0], rule[1], rule[2:]...) if err != nil { return err } case PolicyRemove: err := rm.DeleteLink(rule[0], rule[1], rule[2:]...) if err != nil { return err } } } return nil } func (ast *Assertion) buildRoleLinks(rm rbac.RoleManager) error { ast.RM = rm count := strings.Count(ast.Value, "_") if count < 2 { return errors.New("the number of \"_\" in role definition should be at least 2") } for _, rule := range ast.Policy { if len(rule) < count { return errors.New("grouping policy elements do not meet role definition") } if len(rule) > count { rule = rule[:count] } err := ast.RM.AddLink(rule[0], rule[1], rule[2:]...) if err != nil { return err } } return nil } func (ast *Assertion) buildIncrementalConditionalRoleLinks(condRM rbac.ConditionalRoleManager, op PolicyOp, rules [][]string) error { ast.CondRM = condRM count := strings.Count(ast.Value, "_") if count < 2 { return errors.New("the number of \"_\" in role definition should be at least 2") } for _, rule := range rules { if len(rule) < count { return errors.New("grouping policy elements do not meet role definition") } if len(rule) > count { rule = rule[:count] } var err error domainRule := rule[2:len(ast.Tokens)] switch op { case PolicyAdd: err = ast.addConditionalRoleLink(rule, domainRule) case PolicyRemove: err = ast.CondRM.DeleteLink(rule[0], rule[1], rule[2:]...) } if err != nil { return err } } return nil } func (ast *Assertion) buildConditionalRoleLinks(condRM rbac.ConditionalRoleManager) error { ast.CondRM = condRM count := strings.Count(ast.Value, "_") if count < 2 { return errors.New("the number of \"_\" in role definition should be at least 2") } for _, rule := range ast.Policy { if len(rule) < count { return errors.New("grouping policy elements do not meet role definition") } if len(rule) > count { rule = rule[:count] } domainRule := rule[2:len(ast.Tokens)] err := ast.addConditionalRoleLink(rule, domainRule) if err != nil { return err } } return nil } // addConditionalRoleLinks adds Link to rbac.ConditionalRoleManager and sets the parameters for LinkConditionFunc func (ast *Assertion) addConditionalRoleLink(rule []string, domainRule []string) error { var err error if len(domainRule) == 0 { err = ast.CondRM.AddLink(rule[0], rule[1]) if err == nil { ast.CondRM.SetLinkConditionFuncParams(rule[0], rule[1], rule[len(ast.Tokens):]...) } } else { domain := domainRule[0] err = ast.CondRM.AddLink(rule[0], rule[1], domain) if err == nil { ast.CondRM.SetDomainLinkConditionFuncParams(rule[0], rule[1], domain, rule[len(ast.Tokens):]...) } } return err } func (ast *Assertion) setLogger(logger log.Logger) { ast.logger = logger } func (ast *Assertion) copy() *Assertion { tokens := append([]string(nil), ast.Tokens...) policy := make([][]string, len(ast.Policy)) for i, p := range ast.Policy { policy[i] = append(policy[i], p...) } policyMap := make(map[string]int) for k, v := range ast.PolicyMap { policyMap[k] = v } newAst := &Assertion{ Key: ast.Key, Value: ast.Value, PolicyMap: policyMap, Tokens: tokens, Policy: policy, FieldIndexMap: ast.FieldIndexMap, } return newAst } golang-github-casbin-casbin-2.82.0/model/function.go000066400000000000000000000037571456376452400223340ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "sync" "github.com/casbin/casbin/v2/util" "github.com/casbin/govaluate" ) // FunctionMap represents the collection of Function. type FunctionMap struct { fns *sync.Map } // [string]govaluate.ExpressionFunction // AddFunction adds an expression function. func (fm *FunctionMap) AddFunction(name string, function govaluate.ExpressionFunction) { fm.fns.LoadOrStore(name, function) } // LoadFunctionMap loads an initial function map. func LoadFunctionMap() FunctionMap { fm := &FunctionMap{} fm.fns = &sync.Map{} fm.AddFunction("keyMatch", util.KeyMatchFunc) fm.AddFunction("keyGet", util.KeyGetFunc) fm.AddFunction("keyMatch2", util.KeyMatch2Func) fm.AddFunction("keyGet2", util.KeyGet2Func) fm.AddFunction("keyMatch3", util.KeyMatch3Func) fm.AddFunction("keyGet3", util.KeyGet3Func) fm.AddFunction("keyMatch4", util.KeyMatch4Func) fm.AddFunction("keyMatch5", util.KeyMatch5Func) fm.AddFunction("regexMatch", util.RegexMatchFunc) fm.AddFunction("ipMatch", util.IPMatchFunc) fm.AddFunction("globMatch", util.GlobMatchFunc) return *fm } // GetFunctions return a map with all the functions func (fm *FunctionMap) GetFunctions() map[string]govaluate.ExpressionFunction { ret := make(map[string]govaluate.ExpressionFunction) fm.fns.Range(func(k interface{}, v interface{}) bool { ret[k.(string)] = v.(govaluate.ExpressionFunction) return true }) return ret } golang-github-casbin-casbin-2.82.0/model/model.go000066400000000000000000000244221456376452400215770ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "container/list" "errors" "fmt" "regexp" "sort" "strconv" "strings" "github.com/casbin/casbin/v2/config" "github.com/casbin/casbin/v2/constant" "github.com/casbin/casbin/v2/log" "github.com/casbin/casbin/v2/util" ) // Model represents the whole access control model. type Model map[string]AssertionMap // AssertionMap is the collection of assertions, can be "r", "p", "g", "e", "m". type AssertionMap map[string]*Assertion const defaultDomain string = "" const defaultSeparator = "::" var sectionNameMap = map[string]string{ "r": "request_definition", "p": "policy_definition", "g": "role_definition", "e": "policy_effect", "m": "matchers", } // Minimal required sections for a model to be valid var requiredSections = []string{"r", "p", "e", "m"} func loadAssertion(model Model, cfg config.ConfigInterface, sec string, key string) bool { value := cfg.String(sectionNameMap[sec] + "::" + key) return model.AddDef(sec, key, value) } var paramsRegex = regexp.MustCompile(`\((.*?)\)`) // getParamsToken Get ParamsToken from Assertion.Value func getParamsToken(value string) []string { paramsString := paramsRegex.FindString(value) if paramsString == "" { return nil } paramsString = strings.TrimSuffix(strings.TrimPrefix(paramsString, "("), ")") return strings.Split(paramsString, ",") } // AddDef adds an assertion to the model. func (model Model) AddDef(sec string, key string, value string) bool { if value == "" { return false } ast := Assertion{} ast.Key = key ast.Value = value ast.PolicyMap = make(map[string]int) ast.FieldIndexMap = make(map[string]int) ast.setLogger(model.GetLogger()) if sec == "r" || sec == "p" { ast.Tokens = strings.Split(ast.Value, ",") for i := range ast.Tokens { ast.Tokens[i] = key + "_" + strings.TrimSpace(ast.Tokens[i]) } } else if sec == "g" { ast.ParamsTokens = getParamsToken(ast.Value) ast.Tokens = strings.Split(ast.Value, ",") ast.Tokens = ast.Tokens[:len(ast.Tokens)-len(ast.ParamsTokens)] } else { ast.Value = util.RemoveComments(util.EscapeAssertion(ast.Value)) } if sec == "m" && strings.Contains(ast.Value, "in") { ast.Value = strings.Replace(strings.Replace(ast.Value, "[", "(", -1), "]", ")", -1) } _, ok := model[sec] if !ok { model[sec] = make(AssertionMap) } model[sec][key] = &ast return true } func getKeySuffix(i int) string { if i == 1 { return "" } return strconv.Itoa(i) } func loadSection(model Model, cfg config.ConfigInterface, sec string) { i := 1 for { if !loadAssertion(model, cfg, sec, sec+getKeySuffix(i)) { break } else { i++ } } } // SetLogger sets the model's logger. func (model Model) SetLogger(logger log.Logger) { for _, astMap := range model { for _, ast := range astMap { ast.logger = logger } } model["logger"] = AssertionMap{"logger": &Assertion{logger: logger}} } // GetLogger returns the model's logger. func (model Model) GetLogger() log.Logger { return model["logger"]["logger"].logger } // NewModel creates an empty model. func NewModel() Model { m := make(Model) m.SetLogger(&log.DefaultLogger{}) return m } // NewModelFromFile creates a model from a .CONF file. func NewModelFromFile(path string) (Model, error) { m := NewModel() err := m.LoadModel(path) if err != nil { return nil, err } return m, nil } // NewModelFromString creates a model from a string which contains model text. func NewModelFromString(text string) (Model, error) { m := NewModel() err := m.LoadModelFromText(text) if err != nil { return nil, err } return m, nil } // LoadModel loads the model from model CONF file. func (model Model) LoadModel(path string) error { cfg, err := config.NewConfig(path) if err != nil { return err } return model.loadModelFromConfig(cfg) } // LoadModelFromText loads the model from the text. func (model Model) LoadModelFromText(text string) error { cfg, err := config.NewConfigFromText(text) if err != nil { return err } return model.loadModelFromConfig(cfg) } func (model Model) loadModelFromConfig(cfg config.ConfigInterface) error { for s := range sectionNameMap { loadSection(model, cfg, s) } ms := make([]string, 0) for _, rs := range requiredSections { if !model.hasSection(rs) { ms = append(ms, sectionNameMap[rs]) } } if len(ms) > 0 { return fmt.Errorf("missing required sections: %s", strings.Join(ms, ",")) } return nil } func (model Model) hasSection(sec string) bool { section := model[sec] return section != nil } // PrintModel prints the model to the log. func (model Model) PrintModel() { if !model.GetLogger().IsEnabled() { return } var modelInfo [][]string for k, v := range model { if k == "logger" { continue } for i, j := range v { modelInfo = append(modelInfo, []string{k, i, j.Value}) } } model.GetLogger().LogModel(modelInfo) } func (model Model) SortPoliciesBySubjectHierarchy() error { if model["e"]["e"].Value != constant.SubjectPriorityEffect { return nil } subIndex := 0 for ptype, assertion := range model["p"] { domainIndex, err := model.GetFieldIndex(ptype, constant.DomainIndex) if err != nil { domainIndex = -1 } policies := assertion.Policy subjectHierarchyMap, err := getSubjectHierarchyMap(model["g"]["g"].Policy) if err != nil { return err } sort.SliceStable(policies, func(i, j int) bool { domain1, domain2 := defaultDomain, defaultDomain if domainIndex != -1 { domain1 = policies[i][domainIndex] domain2 = policies[j][domainIndex] } name1, name2 := getNameWithDomain(domain1, policies[i][subIndex]), getNameWithDomain(domain2, policies[j][subIndex]) p1 := subjectHierarchyMap[name1] p2 := subjectHierarchyMap[name2] return p1 > p2 }) for i, policy := range assertion.Policy { assertion.PolicyMap[strings.Join(policy, ",")] = i } } return nil } func getSubjectHierarchyMap(policies [][]string) (map[string]int, error) { subjectHierarchyMap := make(map[string]int) // Tree structure of role policyMap := make(map[string][]string) for _, policy := range policies { if len(policy) < 2 { return nil, errors.New("policy g expect 2 more params") } domain := defaultDomain if len(policy) != 2 { domain = policy[2] } child := getNameWithDomain(domain, policy[0]) parent := getNameWithDomain(domain, policy[1]) policyMap[parent] = append(policyMap[parent], child) if _, ok := subjectHierarchyMap[child]; !ok { subjectHierarchyMap[child] = 0 } if _, ok := subjectHierarchyMap[parent]; !ok { subjectHierarchyMap[parent] = 0 } subjectHierarchyMap[child] = 1 } // Use queues for levelOrder queue := list.New() for k, v := range subjectHierarchyMap { root := k if v != 0 { continue } lv := 0 queue.PushBack(root) for queue.Len() != 0 { sz := queue.Len() for i := 0; i < sz; i++ { node := queue.Front() queue.Remove(node) nodeValue := node.Value.(string) subjectHierarchyMap[nodeValue] = lv if _, ok := policyMap[nodeValue]; ok { for _, child := range policyMap[nodeValue] { queue.PushBack(child) } } } lv++ } } return subjectHierarchyMap, nil } func getNameWithDomain(domain string, name string) string { return domain + defaultSeparator + name } func (model Model) SortPoliciesByPriority() error { for ptype, assertion := range model["p"] { priorityIndex, err := model.GetFieldIndex(ptype, constant.PriorityIndex) if err != nil { continue } policies := assertion.Policy sort.SliceStable(policies, func(i, j int) bool { p1, err := strconv.Atoi(policies[i][priorityIndex]) if err != nil { return true } p2, err := strconv.Atoi(policies[j][priorityIndex]) if err != nil { return true } return p1 < p2 }) for i, policy := range assertion.Policy { assertion.PolicyMap[strings.Join(policy, ",")] = i } } return nil } func (model Model) ToText() string { tokenPatterns := make(map[string]string) pPattern, rPattern := regexp.MustCompile("^p_"), regexp.MustCompile("^r_") for _, ptype := range []string{"r", "p"} { for _, token := range model[ptype][ptype].Tokens { tokenPatterns[token] = rPattern.ReplaceAllString(pPattern.ReplaceAllString(token, "p."), "r.") } } if strings.Contains(model["e"]["e"].Value, "p_eft") { tokenPatterns["p_eft"] = "p.eft" } s := strings.Builder{} writeString := func(sec string) { for ptype := range model[sec] { value := model[sec][ptype].Value for tokenPattern, newToken := range tokenPatterns { value = strings.Replace(value, tokenPattern, newToken, -1) } s.WriteString(fmt.Sprintf("%s = %s\n", sec, value)) } } s.WriteString("[request_definition]\n") writeString("r") s.WriteString("[policy_definition]\n") writeString("p") if _, ok := model["g"]; ok { s.WriteString("[role_definition]\n") for ptype := range model["g"] { s.WriteString(fmt.Sprintf("%s = %s\n", ptype, model["g"][ptype].Value)) } } s.WriteString("[policy_effect]\n") writeString("e") s.WriteString("[matchers]\n") writeString("m") return s.String() } func (model Model) Copy() Model { newModel := NewModel() for sec, m := range model { newAstMap := make(AssertionMap) for ptype, ast := range m { newAstMap[ptype] = ast.copy() } newModel[sec] = newAstMap } newModel.SetLogger(model.GetLogger()) return newModel } func (model Model) GetFieldIndex(ptype string, field string) (int, error) { assertion := model["p"][ptype] if index, ok := assertion.FieldIndexMap[field]; ok { return index, nil } pattern := fmt.Sprintf("%s_"+field, ptype) index := -1 for i, token := range assertion.Tokens { if token == pattern { index = i break } } if index == -1 { return index, fmt.Errorf(field + " index is not set, please use enforcer.SetFieldIndex() to set index") } assertion.FieldIndexMap[field] = index return index, nil } golang-github-casbin-casbin-2.82.0/model/model_test.go000066400000000000000000000104031456376452400226300ustar00rootroot00000000000000// Copyright 2019 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "io/ioutil" "path/filepath" "strings" "testing" "github.com/casbin/casbin/v2/config" "github.com/casbin/casbin/v2/constant" ) var ( basicExample = filepath.Join("..", "examples", "basic_model.conf") basicConfig = &MockConfig{ data: map[string]string{ "request_definition::r": "sub, obj, act", "policy_definition::p": "sub, obj, act", "policy_effect::e": "some(where (p.eft == allow))", "matchers::m": "r.sub == p.sub && r.obj == p.obj && r.act == p.act", }, } ) type MockConfig struct { data map[string]string config.ConfigInterface } func (mc *MockConfig) String(key string) string { return mc.data[key] } func TestNewModel(t *testing.T) { m := NewModel() if m == nil { t.Error("new model should not be nil") } } func TestNewModelFromFile(t *testing.T) { m, err := NewModelFromFile(basicExample) if err != nil { t.Errorf("model failed to load from file: %s", err) } if m == nil { t.Error("model should not be nil") } } func TestNewModelFromString(t *testing.T) { modelBytes, _ := ioutil.ReadFile(basicExample) modelString := string(modelBytes) m, err := NewModelFromString(modelString) if err != nil { t.Errorf("model faild to load from string: %s", err) } if m == nil { t.Error("model should not be nil") } } func TestLoadModelFromConfig(t *testing.T) { m := NewModel() err := m.loadModelFromConfig(basicConfig) if err != nil { t.Error("basic config should not return an error") } m = NewModel() err = m.loadModelFromConfig(&MockConfig{}) if err == nil { t.Error("empty config should return error") } else { // check for missing sections in message for _, rs := range requiredSections { if !strings.Contains(err.Error(), sectionNameMap[rs]) { t.Errorf("section name: %s should be in message", sectionNameMap[rs]) } } } } func TestHasSection(t *testing.T) { m := NewModel() _ = m.loadModelFromConfig(basicConfig) for _, sec := range requiredSections { if !m.hasSection(sec) { t.Errorf("%s section was expected in model", sec) } } m = NewModel() _ = m.loadModelFromConfig(&MockConfig{}) for _, sec := range requiredSections { if m.hasSection(sec) { t.Errorf("%s section was not expected in model", sec) } } } func TestModel_AddDef(t *testing.T) { m := NewModel() s := "r" v := "sub, obj, act" ok := m.AddDef(s, s, v) if !ok { t.Errorf("non empty assertion should be added") } ok = m.AddDef(s, s, "") if ok { t.Errorf("empty assertion value should not be added") } } func TestModelToTest(t *testing.T) { testModelToText(t, "r.sub == p.sub && r.obj == p.obj && r_func(r.act, p.act) && testr_func(r.act, p.act)", "r_sub == p_sub && r_obj == p_obj && r_func(r_act, p_act) && testr_func(r_act, p_act)") testModelToText(t, "r.sub == p.sub && r.obj == p.obj && p_func(r.act, p.act) && testp_func(r.act, p.act)", "r_sub == p_sub && r_obj == p_obj && p_func(r_act, p_act) && testp_func(r_act, p_act)") } func testModelToText(t *testing.T, mData, mExpected string) { m := NewModel() data := map[string]string{ "r": "sub, obj, act", "p": "sub, obj, act", "e": "some(where (p.eft == allow))", "m": mData, } expected := map[string]string{ "r": "sub, obj, act", "p": "sub, obj, act", "e": constant.AllowOverrideEffect, "m": mExpected, } addData := func(ptype string) { m.AddDef(ptype, ptype, data[ptype]) } for ptype := range data { addData(ptype) } newM := NewModel() print(m.ToText()) _ = newM.LoadModelFromText(m.ToText()) for ptype := range data { if newM[ptype][ptype].Value != expected[ptype] { t.Errorf("\"%s\" assertion value changed, current value: %s, it should be: %s", ptype, newM[ptype][ptype].Value, expected[ptype]) } } } golang-github-casbin-casbin-2.82.0/model/policy.go000066400000000000000000000265611456376452400220040ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "fmt" "strconv" "strings" "github.com/casbin/casbin/v2/constant" "github.com/casbin/casbin/v2/rbac" "github.com/casbin/casbin/v2/util" ) type ( PolicyOp int ) const ( PolicyAdd PolicyOp = iota PolicyRemove ) const DefaultSep = "," // BuildIncrementalRoleLinks provides incremental build the role inheritance relations. func (model Model) BuildIncrementalRoleLinks(rmMap map[string]rbac.RoleManager, op PolicyOp, sec string, ptype string, rules [][]string) error { if sec == "g" && rmMap[ptype] != nil { return model[sec][ptype].buildIncrementalRoleLinks(rmMap[ptype], op, rules) } return nil } // BuildRoleLinks initializes the roles in RBAC. func (model Model) BuildRoleLinks(rmMap map[string]rbac.RoleManager) error { model.PrintPolicy() for ptype, ast := range model["g"] { if rm := rmMap[ptype]; rm != nil { err := ast.buildRoleLinks(rm) if err != nil { return err } } } return nil } // BuildIncrementalConditionalRoleLinks provides incremental build the role inheritance relations. func (model Model) BuildIncrementalConditionalRoleLinks(condRmMap map[string]rbac.ConditionalRoleManager, op PolicyOp, sec string, ptype string, rules [][]string) error { if sec == "g" && condRmMap[ptype] != nil { return model[sec][ptype].buildIncrementalConditionalRoleLinks(condRmMap[ptype], op, rules) } return nil } // BuildConditionalRoleLinks initializes the roles in RBAC. func (model Model) BuildConditionalRoleLinks(condRmMap map[string]rbac.ConditionalRoleManager) error { model.PrintPolicy() for ptype, ast := range model["g"] { if condRm := condRmMap[ptype]; condRm != nil { err := ast.buildConditionalRoleLinks(condRm) if err != nil { return err } } } return nil } // PrintPolicy prints the policy to log. func (model Model) PrintPolicy() { if !model.GetLogger().IsEnabled() { return } policy := make(map[string][][]string) for key, ast := range model["p"] { value, found := policy[key] if found { value = append(value, ast.Policy...) policy[key] = value } else { policy[key] = ast.Policy } } for key, ast := range model["g"] { value, found := policy[key] if found { value = append(value, ast.Policy...) policy[key] = value } else { policy[key] = ast.Policy } } model.GetLogger().LogPolicy(policy) } // ClearPolicy clears all current policy. func (model Model) ClearPolicy() { for _, ast := range model["p"] { ast.Policy = nil ast.PolicyMap = map[string]int{} } for _, ast := range model["g"] { ast.Policy = nil ast.PolicyMap = map[string]int{} } } // GetPolicy gets all rules in a policy. func (model Model) GetPolicy(sec string, ptype string) [][]string { return model[sec][ptype].Policy } // GetFilteredPolicy gets rules based on field filters from a policy. func (model Model) GetFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) [][]string { res := [][]string{} for _, rule := range model[sec][ptype].Policy { matched := true for i, fieldValue := range fieldValues { if fieldValue != "" && rule[fieldIndex+i] != fieldValue { matched = false break } } if matched { res = append(res, rule) } } return res } // HasPolicyEx determines whether a model has the specified policy rule with error. func (model Model) HasPolicyEx(sec string, ptype string, rule []string) (bool, error) { assertion := model[sec][ptype] switch sec { case "p": if len(rule) != len(assertion.Tokens) { return false, fmt.Errorf( "invalid policy rule size: expected %d, got %d, rule: %v", len(model["p"][ptype].Tokens), len(rule), rule) } case "g": if len(rule) < len(assertion.Tokens) { return false, fmt.Errorf( "invalid policy rule size: expected %d, got %d, rule: %v", len(model["g"][ptype].Tokens), len(rule), rule) } } return model.HasPolicy(sec, ptype, rule), nil } // HasPolicy determines whether a model has the specified policy rule. func (model Model) HasPolicy(sec string, ptype string, rule []string) bool { _, ok := model[sec][ptype].PolicyMap[strings.Join(rule, DefaultSep)] return ok } // HasPolicies determines whether a model has any of the specified policies. If one is found we return true. func (model Model) HasPolicies(sec string, ptype string, rules [][]string) bool { for i := 0; i < len(rules); i++ { if model.HasPolicy(sec, ptype, rules[i]) { return true } } return false } // AddPolicy adds a policy rule to the model. func (model Model) AddPolicy(sec string, ptype string, rule []string) { assertion := model[sec][ptype] assertion.Policy = append(assertion.Policy, rule) assertion.PolicyMap[strings.Join(rule, DefaultSep)] = len(model[sec][ptype].Policy) - 1 hasPriority := false if _, ok := assertion.FieldIndexMap[constant.PriorityIndex]; ok { hasPriority = true } if sec == "p" && hasPriority { if idxInsert, err := strconv.Atoi(rule[assertion.FieldIndexMap[constant.PriorityIndex]]); err == nil { i := len(assertion.Policy) - 1 for ; i > 0; i-- { idx, err := strconv.Atoi(assertion.Policy[i-1][assertion.FieldIndexMap[constant.PriorityIndex]]) if err != nil { break } if idx > idxInsert { assertion.Policy[i] = assertion.Policy[i-1] assertion.PolicyMap[strings.Join(assertion.Policy[i-1], DefaultSep)]++ } else { break } } assertion.Policy[i] = rule assertion.PolicyMap[strings.Join(rule, DefaultSep)] = i } } } // AddPolicies adds policy rules to the model. func (model Model) AddPolicies(sec string, ptype string, rules [][]string) { _ = model.AddPoliciesWithAffected(sec, ptype, rules) } // AddPoliciesWithAffected adds policy rules to the model, and returns affected rules. func (model Model) AddPoliciesWithAffected(sec string, ptype string, rules [][]string) [][]string { var affected [][]string for _, rule := range rules { hashKey := strings.Join(rule, DefaultSep) _, ok := model[sec][ptype].PolicyMap[hashKey] if ok { continue } affected = append(affected, rule) model.AddPolicy(sec, ptype, rule) } return affected } // RemovePolicy removes a policy rule from the model. // Deprecated: Using AddPoliciesWithAffected instead. func (model Model) RemovePolicy(sec string, ptype string, rule []string) bool { index, ok := model[sec][ptype].PolicyMap[strings.Join(rule, DefaultSep)] if !ok { return false } model[sec][ptype].Policy = append(model[sec][ptype].Policy[:index], model[sec][ptype].Policy[index+1:]...) delete(model[sec][ptype].PolicyMap, strings.Join(rule, DefaultSep)) for i := index; i < len(model[sec][ptype].Policy); i++ { model[sec][ptype].PolicyMap[strings.Join(model[sec][ptype].Policy[i], DefaultSep)] = i } return true } // UpdatePolicy updates a policy rule from the model. func (model Model) UpdatePolicy(sec string, ptype string, oldRule []string, newRule []string) bool { oldPolicy := strings.Join(oldRule, DefaultSep) index, ok := model[sec][ptype].PolicyMap[oldPolicy] if !ok { return false } model[sec][ptype].Policy[index] = newRule delete(model[sec][ptype].PolicyMap, oldPolicy) model[sec][ptype].PolicyMap[strings.Join(newRule, DefaultSep)] = index return true } // UpdatePolicies updates a policy rule from the model. func (model Model) UpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) bool { rollbackFlag := false // index -> []{oldIndex, newIndex} modifiedRuleIndex := make(map[int][]int) // rollback defer func() { if rollbackFlag { for index, oldNewIndex := range modifiedRuleIndex { model[sec][ptype].Policy[index] = oldRules[oldNewIndex[0]] oldPolicy := strings.Join(oldRules[oldNewIndex[0]], DefaultSep) newPolicy := strings.Join(newRules[oldNewIndex[1]], DefaultSep) delete(model[sec][ptype].PolicyMap, newPolicy) model[sec][ptype].PolicyMap[oldPolicy] = index } } }() newIndex := 0 for oldIndex, oldRule := range oldRules { oldPolicy := strings.Join(oldRule, DefaultSep) index, ok := model[sec][ptype].PolicyMap[oldPolicy] if !ok { rollbackFlag = true return false } model[sec][ptype].Policy[index] = newRules[newIndex] delete(model[sec][ptype].PolicyMap, oldPolicy) model[sec][ptype].PolicyMap[strings.Join(newRules[newIndex], DefaultSep)] = index modifiedRuleIndex[index] = []int{oldIndex, newIndex} newIndex++ } return true } // RemovePolicies removes policy rules from the model. func (model Model) RemovePolicies(sec string, ptype string, rules [][]string) bool { affected := model.RemovePoliciesWithAffected(sec, ptype, rules) return len(affected) != 0 } // RemovePoliciesWithAffected removes policy rules from the model, and returns affected rules. func (model Model) RemovePoliciesWithAffected(sec string, ptype string, rules [][]string) [][]string { var affected [][]string for _, rule := range rules { index, ok := model[sec][ptype].PolicyMap[strings.Join(rule, DefaultSep)] if !ok { continue } affected = append(affected, rule) model[sec][ptype].Policy = append(model[sec][ptype].Policy[:index], model[sec][ptype].Policy[index+1:]...) delete(model[sec][ptype].PolicyMap, strings.Join(rule, DefaultSep)) for i := index; i < len(model[sec][ptype].Policy); i++ { model[sec][ptype].PolicyMap[strings.Join(model[sec][ptype].Policy[i], DefaultSep)] = i } } return affected } // RemoveFilteredPolicy removes policy rules based on field filters from the model. func (model Model) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) (bool, [][]string) { var tmp [][]string var effects [][]string res := false model[sec][ptype].PolicyMap = map[string]int{} for _, rule := range model[sec][ptype].Policy { matched := true for i, fieldValue := range fieldValues { if fieldValue != "" && rule[fieldIndex+i] != fieldValue { matched = false break } } if matched { effects = append(effects, rule) } else { tmp = append(tmp, rule) model[sec][ptype].PolicyMap[strings.Join(rule, DefaultSep)] = len(tmp) - 1 } } if len(tmp) != len(model[sec][ptype].Policy) { model[sec][ptype].Policy = tmp res = true } return res, effects } // GetValuesForFieldInPolicy gets all values for a field for all rules in a policy, duplicated values are removed. func (model Model) GetValuesForFieldInPolicy(sec string, ptype string, fieldIndex int) []string { values := []string{} for _, rule := range model[sec][ptype].Policy { values = append(values, rule[fieldIndex]) } util.ArrayRemoveDuplicates(&values) return values } // GetValuesForFieldInPolicyAllTypes gets all values for a field for all rules in a policy of all ptypes, duplicated values are removed. func (model Model) GetValuesForFieldInPolicyAllTypes(sec string, fieldIndex int) []string { values := []string{} for ptype := range model[sec] { values = append(values, model.GetValuesForFieldInPolicy(sec, ptype, fieldIndex)...) } util.ArrayRemoveDuplicates(&values) return values } golang-github-casbin-casbin-2.82.0/model_b_test.go000066400000000000000000000170461456376452400220430ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "fmt" "testing" "github.com/casbin/casbin/v2/util" ) func rawEnforce(sub string, obj string, act string) bool { policy := [2][3]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}} for _, rule := range policy { if sub == rule[0] && obj == rule[1] && act == rule[2] { return true } } return false } func BenchmarkRaw(b *testing.B) { for i := 0; i < b.N; i++ { rawEnforce("alice", "data1", "read") } } func BenchmarkBasicModel(b *testing.B) { e, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv", false) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", "data1", "read") } } func BenchmarkRBACModel(b *testing.B) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv", false) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", "data2", "read") } } func BenchmarkRBACModelSizes(b *testing.B) { cases := []struct { name string roles int resources int users int }{ {name: "small", roles: 100, resources: 10, users: 1000}, {name: "medium", roles: 1000, resources: 100, users: 10000}, {name: "large", roles: 10000, resources: 1000, users: 100000}, } for _, c := range cases { c := c e, err := NewEnforcer("examples/rbac_model.conf", false) if err != nil { b.Fatal(err) } pPolicies := make([][]string, c.roles) for i := range pPolicies { pPolicies[i] = []string{ fmt.Sprintf("group-has-a-very-long-name-%d", i), fmt.Sprintf("data-has-a-very-long-name-%d", i%c.resources), "read", } } if _, err := e.AddPolicies(pPolicies); err != nil { b.Fatal(err) } gPolicies := make([][]string, c.users) for i := range gPolicies { gPolicies[i] = []string{ fmt.Sprintf("user-has-a-very-long-name-%d", i), fmt.Sprintf("group-has-a-very-long-name-%d", i%c.roles), } } if _, err := e.AddGroupingPolicies(gPolicies); err != nil { b.Fatal(err) } // Set up enforcements, alternating between things a user can access // and things they cannot. Use 17 tests so that we get a variety of users // and roles rather than always landing on a multiple of 2/10/whatever. enforcements := make([][]interface{}, 17) for i := range enforcements { userNum := (c.users / len(enforcements)) * i roleNum := userNum % c.roles resourceNum := roleNum % c.resources if i%2 == 0 { resourceNum += 1 resourceNum %= c.resources } enforcements[i] = []interface{}{ fmt.Sprintf("user-has-a-very-long-name-%d", userNum), fmt.Sprintf("data-has-a-very-long-name-%d", resourceNum), "read", } } b.Run(c.name, func(b *testing.B) { for i := 0; i < b.N; i++ { _, _ = e.Enforce(enforcements[i%len(enforcements)]...) } }) } } func BenchmarkRBACModelSmall(b *testing.B) { e, _ := NewEnforcer("examples/rbac_model.conf", false) // 100 roles, 10 resources. for i := 0; i < 100; i++ { _, err := e.AddPolicy(fmt.Sprintf("group%d", i), fmt.Sprintf("data%d", i/10), "read") if err != nil { b.Fatal(err) } } // 1000 users. for i := 0; i < 1000; i++ { _, err := e.AddGroupingPolicy(fmt.Sprintf("user%d", i), fmt.Sprintf("group%d", i/10)) if err != nil { b.Fatal(err) } } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("user501", "data9", "read") } } func BenchmarkRBACModelMedium(b *testing.B) { e, _ := NewEnforcer("examples/rbac_model.conf", false) // 1000 roles, 100 resources. pPolicies := make([][]string, 0) for i := 0; i < 1000; i++ { pPolicies = append(pPolicies, []string{fmt.Sprintf("group%d", i), fmt.Sprintf("data%d", i/10), "read"}) } _, err := e.AddPolicies(pPolicies) if err != nil { b.Fatal(err) } // 10000 users. gPolicies := make([][]string, 0) for i := 0; i < 10000; i++ { gPolicies = append(gPolicies, []string{fmt.Sprintf("user%d", i), fmt.Sprintf("group%d", i/10)}) } _, err = e.AddGroupingPolicies(gPolicies) if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("user5001", "data99", "read") } } func BenchmarkRBACModelLarge(b *testing.B) { e, _ := NewEnforcer("examples/rbac_model.conf", false) // 10000 roles, 1000 resources. pPolicies := make([][]string, 0) for i := 0; i < 10000; i++ { pPolicies = append(pPolicies, []string{fmt.Sprintf("group%d", i), fmt.Sprintf("data%d", i/10), "read"}) } _, err := e.AddPolicies(pPolicies) if err != nil { b.Fatal(err) } // 100000 users. gPolicies := make([][]string, 0) for i := 0; i < 100000; i++ { gPolicies = append(gPolicies, []string{fmt.Sprintf("user%d", i), fmt.Sprintf("group%d", i/10)}) } _, err = e.AddGroupingPolicies(gPolicies) if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("user50001", "data999", "read") } } func BenchmarkRBACModelWithResourceRoles(b *testing.B) { e, _ := NewEnforcer("examples/rbac_with_resource_roles_model.conf", "examples/rbac_with_resource_roles_policy.csv", false) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", "data1", "read") } } func BenchmarkRBACModelWithDomains(b *testing.B) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv", false) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", "domain1", "data1", "read") } } func BenchmarkABACModel(b *testing.B) { e, _ := NewEnforcer("examples/abac_model.conf", false) data1 := newTestResource("data1", "alice") b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", data1, "read") } } func BenchmarkABACRuleModel(b *testing.B) { e, _ := NewEnforcer("examples/abac_rule_model.conf", false) sub := newTestSubject("alice", 18) for i := 0; i < 1000; i++ { _, _ = e.AddPolicy("r.sub.Age > 20", fmt.Sprintf("data%d", i), "read") } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce(sub, "data100", "read") } } func BenchmarkKeyMatchModel(b *testing.B) { e, _ := NewEnforcer("examples/keymatch_model.conf", "examples/keymatch_policy.csv", false) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", "/alice_data/resource1", "GET") } } func BenchmarkRBACModelWithDeny(b *testing.B) { e, _ := NewEnforcer("examples/rbac_with_deny_model.conf", "examples/rbac_with_deny_policy.csv") b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", "data1", "read") } } func BenchmarkPriorityModel(b *testing.B) { e, _ := NewEnforcer("examples/priority_model.conf", "examples/priority_policy.csv") b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("alice", "data1", "read") } } func BenchmarkRBACModelWithDomainPatternLarge(b *testing.B) { e, _ := NewEnforcer("examples/performance/rbac_with_pattern_large_scale_model.conf", "examples/performance/rbac_with_pattern_large_scale_policy.csv") e.AddNamedDomainMatchingFunc("g", "", util.KeyMatch4) _ = e.BuildRoleLinks() b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = e.Enforce("staffUser1001", "/orgs/1/sites/site001", "App001.Module001.Action1001") } } golang-github-casbin-casbin-2.82.0/model_test.go000066400000000000000000001077351456376452400215470ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "encoding/json" "fmt" "testing" "github.com/casbin/casbin/v2/log" fileadapter "github.com/casbin/casbin/v2/persist/file-adapter" "github.com/casbin/casbin/v2/rbac" "github.com/casbin/casbin/v2/util" ) func testEnforce(t *testing.T, e *Enforcer, sub interface{}, obj interface{}, act string, res bool) { t.Helper() if myRes, err := e.Enforce(sub, obj, act); err != nil { t.Errorf("Enforce Error: %s", err) } else if myRes != res { t.Errorf("%s, %v, %s: %t, supposed to be %t", sub, obj, act, myRes, res) } } func testEnforceWithoutUsers(t *testing.T, e *Enforcer, obj string, act string, res bool) { t.Helper() if myRes, _ := e.Enforce(obj, act); myRes != res { t.Errorf("%s, %s: %t, supposed to be %t", obj, act, myRes, res) } } func testDomainEnforce(t *testing.T, e *Enforcer, sub string, dom string, obj string, act string, res bool) { t.Helper() if myRes, err := e.Enforce(sub, dom, obj, act); err != nil { t.Errorf("Enforce Error: %s", err) } else if myRes != res { t.Errorf("%s, %s, %s, %s: %t, supposed to be %t", sub, dom, obj, act, myRes, res) } } func TestBasicModel(t *testing.T) { e, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } func TestBasicModelWithoutSpaces(t *testing.T) { e, _ := NewEnforcer("examples/basic_model_without_spaces.conf", "examples/basic_policy.csv") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } func TestBasicModelNoPolicy(t *testing.T) { e, _ := NewEnforcer("examples/basic_model.conf") testEnforce(t, e, "alice", "data1", "read", false) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", false) } func TestBasicModelWithRoot(t *testing.T) { e, _ := NewEnforcer("examples/basic_with_root_model.conf", "examples/basic_policy.csv") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) testEnforce(t, e, "root", "data1", "read", true) testEnforce(t, e, "root", "data1", "write", true) testEnforce(t, e, "root", "data2", "read", true) testEnforce(t, e, "root", "data2", "write", true) } func TestBasicModelWithRootNoPolicy(t *testing.T) { e, _ := NewEnforcer("examples/basic_with_root_model.conf") testEnforce(t, e, "alice", "data1", "read", false) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", false) testEnforce(t, e, "root", "data1", "read", true) testEnforce(t, e, "root", "data1", "write", true) testEnforce(t, e, "root", "data2", "read", true) testEnforce(t, e, "root", "data2", "write", true) } func TestBasicModelWithoutUsers(t *testing.T) { e, _ := NewEnforcer("examples/basic_without_users_model.conf", "examples/basic_without_users_policy.csv") testEnforceWithoutUsers(t, e, "data1", "read", true) testEnforceWithoutUsers(t, e, "data1", "write", false) testEnforceWithoutUsers(t, e, "data2", "read", false) testEnforceWithoutUsers(t, e, "data2", "write", true) } func TestBasicModelWithoutResources(t *testing.T) { e, _ := NewEnforcer("examples/basic_without_resources_model.conf", "examples/basic_without_resources_policy.csv") testEnforceWithoutUsers(t, e, "alice", "read", true) testEnforceWithoutUsers(t, e, "alice", "write", false) testEnforceWithoutUsers(t, e, "bob", "read", false) testEnforceWithoutUsers(t, e, "bob", "write", true) } func TestRBACModel(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", true) testEnforce(t, e, "alice", "data2", "write", true) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } func TestRBACModelWithResourceRoles(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_resource_roles_model.conf", "examples/rbac_with_resource_roles_policy.csv") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", true) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", true) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } func TestRBACModelWithDomains(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv") testDomainEnforce(t, e, "alice", "domain1", "data1", "read", true) testDomainEnforce(t, e, "alice", "domain1", "data1", "write", true) testDomainEnforce(t, e, "alice", "domain1", "data2", "read", false) testDomainEnforce(t, e, "alice", "domain1", "data2", "write", false) testDomainEnforce(t, e, "bob", "domain2", "data1", "read", false) testDomainEnforce(t, e, "bob", "domain2", "data1", "write", false) testDomainEnforce(t, e, "bob", "domain2", "data2", "read", true) testDomainEnforce(t, e, "bob", "domain2", "data2", "write", true) } func TestRBACModelWithDomainsAtRuntime(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf") _, _ = e.AddPolicy("admin", "domain1", "data1", "read") _, _ = e.AddPolicy("admin", "domain1", "data1", "write") _, _ = e.AddPolicy("admin", "domain2", "data2", "read") _, _ = e.AddPolicy("admin", "domain2", "data2", "write") _, _ = e.AddGroupingPolicy("alice", "admin", "domain1") _, _ = e.AddGroupingPolicy("bob", "admin", "domain2") testDomainEnforce(t, e, "alice", "domain1", "data1", "read", true) testDomainEnforce(t, e, "alice", "domain1", "data1", "write", true) testDomainEnforce(t, e, "alice", "domain1", "data2", "read", false) testDomainEnforce(t, e, "alice", "domain1", "data2", "write", false) testDomainEnforce(t, e, "bob", "domain2", "data1", "read", false) testDomainEnforce(t, e, "bob", "domain2", "data1", "write", false) testDomainEnforce(t, e, "bob", "domain2", "data2", "read", true) testDomainEnforce(t, e, "bob", "domain2", "data2", "write", true) // Remove all policy rules related to domain1 and data1. _, _ = e.RemoveFilteredPolicy(1, "domain1", "data1") testDomainEnforce(t, e, "alice", "domain1", "data1", "read", false) testDomainEnforce(t, e, "alice", "domain1", "data1", "write", false) testDomainEnforce(t, e, "alice", "domain1", "data2", "read", false) testDomainEnforce(t, e, "alice", "domain1", "data2", "write", false) testDomainEnforce(t, e, "bob", "domain2", "data1", "read", false) testDomainEnforce(t, e, "bob", "domain2", "data1", "write", false) testDomainEnforce(t, e, "bob", "domain2", "data2", "read", true) testDomainEnforce(t, e, "bob", "domain2", "data2", "write", true) // Remove the specified policy rule. _, _ = e.RemovePolicy("admin", "domain2", "data2", "read") testDomainEnforce(t, e, "alice", "domain1", "data1", "read", false) testDomainEnforce(t, e, "alice", "domain1", "data1", "write", false) testDomainEnforce(t, e, "alice", "domain1", "data2", "read", false) testDomainEnforce(t, e, "alice", "domain1", "data2", "write", false) testDomainEnforce(t, e, "bob", "domain2", "data1", "read", false) testDomainEnforce(t, e, "bob", "domain2", "data1", "write", false) testDomainEnforce(t, e, "bob", "domain2", "data2", "read", false) testDomainEnforce(t, e, "bob", "domain2", "data2", "write", true) } func TestRBACModelWithDomainsAtRuntimeMockAdapter(t *testing.T) { adapter := fileadapter.NewAdapterMock("examples/rbac_with_domains_policy.csv") e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", adapter) _, _ = e.AddPolicy("admin", "domain3", "data1", "read") _, _ = e.AddGroupingPolicy("alice", "admin", "domain3") testDomainEnforce(t, e, "alice", "domain3", "data1", "read", true) testDomainEnforce(t, e, "alice", "domain1", "data1", "read", true) _, _ = e.RemoveFilteredPolicy(1, "domain1", "data1") testDomainEnforce(t, e, "alice", "domain1", "data1", "read", false) testDomainEnforce(t, e, "bob", "domain2", "data2", "read", true) _, _ = e.RemovePolicy("admin", "domain2", "data2", "read") testDomainEnforce(t, e, "bob", "domain2", "data2", "read", false) } func TestRBACModelWithDeny(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_deny_model.conf", "examples/rbac_with_deny_policy.csv") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", true) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } func TestRBACModelWithOnlyDeny(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_not_deny_model.conf", "examples/rbac_with_deny_policy.csv") testEnforce(t, e, "alice", "data2", "write", false) } func TestRBACModelWithCustomData(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") // You can add custom data to a grouping policy, Casbin will ignore it. It is only meaningful to the caller. // This feature can be used to store information like whether "bob" is an end user (so no subject will inherit "bob") // For Casbin, it is equivalent to: e.AddGroupingPolicy("bob", "data2_admin") _, _ = e.AddGroupingPolicy("bob", "data2_admin", "custom_data") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", true) testEnforce(t, e, "alice", "data2", "write", true) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", true) testEnforce(t, e, "bob", "data2", "write", true) // You should also take the custom data as a parameter when deleting a grouping policy. // e.RemoveGroupingPolicy("bob", "data2_admin") won't work. // Or you can remove it by using RemoveFilteredGroupingPolicy(). _, _ = e.RemoveGroupingPolicy("bob", "data2_admin", "custom_data") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", true) testEnforce(t, e, "alice", "data2", "write", true) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } func TestRBACModelWithPattern(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_pattern_model.conf", "examples/rbac_with_pattern_policy.csv") // Here's a little confusing: the matching function here is not the custom function used in matcher. // It is the matching function used by "g" (and "g2", "g3" if any..) // You can see in policy that: "g2, /book/:id, book_group", so in "g2()" function in the matcher, instead // of checking whether "/book/:id" equals the obj: "/book/1", it checks whether the pattern matches. // You can see it as normal RBAC: "/book/:id" == "/book/1" becomes KeyMatch2("/book/:id", "/book/1") e.AddNamedMatchingFunc("g2", "KeyMatch2", util.KeyMatch2) e.AddNamedMatchingFunc("g", "KeyMatch2", util.KeyMatch2) testEnforce(t, e, "any_user", "/pen3/1", "GET", true) testEnforce(t, e, "/book/user/1", "/pen4/1", "GET", true) testEnforce(t, e, "/book/user/1", "/pen4/1", "POST", true) testEnforce(t, e, "alice", "/book/1", "GET", true) testEnforce(t, e, "alice", "/book/2", "GET", true) testEnforce(t, e, "alice", "/pen/1", "GET", true) testEnforce(t, e, "alice", "/pen/2", "GET", false) testEnforce(t, e, "bob", "/book/1", "GET", false) testEnforce(t, e, "bob", "/book/2", "GET", false) testEnforce(t, e, "bob", "/pen/1", "GET", true) testEnforce(t, e, "bob", "/pen/2", "GET", true) // AddMatchingFunc() is actually setting a function because only one function is allowed, // so when we set "KeyMatch3", we are actually replacing "KeyMatch2" with "KeyMatch3". e.AddNamedMatchingFunc("g2", "KeyMatch2", util.KeyMatch3) testEnforce(t, e, "alice", "/book2/1", "GET", true) testEnforce(t, e, "alice", "/book2/2", "GET", true) testEnforce(t, e, "alice", "/pen2/1", "GET", true) testEnforce(t, e, "alice", "/pen2/2", "GET", false) testEnforce(t, e, "bob", "/book2/1", "GET", false) testEnforce(t, e, "bob", "/book2/2", "GET", false) testEnforce(t, e, "bob", "/pen2/1", "GET", true) testEnforce(t, e, "bob", "/pen2/2", "GET", true) } func TestRBACModelWithDifferentTypesOfRoles(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_different_types_of_roles_model.conf", "examples/rbac_with_different_types_of_roles_policy.csv") g := e.GetNamedGroupingPolicy("g") for _, gp := range g { if len(gp) != 5 { t.Error("g parameters' num isn't 5") return } e.AddNamedDomainLinkConditionFunc("g", gp[0], gp[1], gp[2], util.TimeMatchFunc) } testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", true) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", true) testEnforce(t, e, "bob", "data2", "write", false) testEnforce(t, e, "carol", "data1", "read", false) testEnforce(t, e, "carol", "data1", "write", false) testEnforce(t, e, "carol", "data2", "read", false) testEnforce(t, e, "carol", "data2", "write", false) } type testCustomRoleManager struct{} func NewRoleManager() rbac.RoleManager { return &testCustomRoleManager{} } func (rm *testCustomRoleManager) Clear() error { return nil } func (rm *testCustomRoleManager) AddLink(name1 string, name2 string, domain ...string) error { return nil } func (rm *testCustomRoleManager) BuildRelationship(name1 string, name2 string, domain ...string) error { return nil } func (rm *testCustomRoleManager) DeleteLink(name1 string, name2 string, domain ...string) error { return nil } func (rm *testCustomRoleManager) HasLink(name1 string, name2 string, domain ...string) (bool, error) { if name1 == "alice" && name2 == "alice" { return true, nil } else if name1 == "alice" && name2 == "data2_admin" { return true, nil } else if name1 == "bob" && name2 == "bob" { return true, nil } return false, nil } func (rm *testCustomRoleManager) GetRoles(name string, domain ...string) ([]string, error) { return []string{}, nil } func (rm *testCustomRoleManager) GetUsers(name string, domain ...string) ([]string, error) { return []string{}, nil } func (rm *testCustomRoleManager) GetDomains(name string) ([]string, error) { return []string{}, nil } func (rm *testCustomRoleManager) GetAllDomains() ([]string, error) { return []string{}, nil } func (rm *testCustomRoleManager) PrintRoles() error { return nil } func (rm *testCustomRoleManager) SetLogger(logger log.Logger) {} func (rm *testCustomRoleManager) Match(str string, pattern string) bool { return true } func (rm *testCustomRoleManager) AddMatchingFunc(name string, fn rbac.MatchingFunc) {} func (rm *testCustomRoleManager) AddDomainMatchingFunc(name string, fn rbac.MatchingFunc) {} func (rm *testCustomRoleManager) AddLinkConditionFunc(userName, roleName string, fn rbac.LinkConditionFunc) { } func (rm *testCustomRoleManager) SetLinkConditionFuncParams(userName, roleName string, params ...string) { } func (rm *testCustomRoleManager) AddDomainLinkConditionFunc(user string, role string, domain string, fn rbac.LinkConditionFunc) { } func (rm *testCustomRoleManager) SetDomainLinkConditionFuncParams(user string, role string, domain string, params ...string) { } func TestRBACModelWithCustomRoleManager(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") e.SetRoleManager(NewRoleManager()) _ = e.LoadModel() _ = e.LoadPolicy() testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", true) testEnforce(t, e, "alice", "data2", "write", true) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } type testResource struct { Name string Owner string } func newTestResource(name string, owner string) testResource { r := testResource{} r.Name = name r.Owner = owner return r } func TestABACModel(t *testing.T) { e, _ := NewEnforcer("examples/abac_model.conf") data1 := newTestResource("data1", "alice") data2 := newTestResource("data2", "bob") testEnforce(t, e, "alice", data1, "read", true) testEnforce(t, e, "alice", data1, "write", true) testEnforce(t, e, "alice", data2, "read", false) testEnforce(t, e, "alice", data2, "write", false) testEnforce(t, e, "bob", data1, "read", false) testEnforce(t, e, "bob", data1, "write", false) testEnforce(t, e, "bob", data2, "read", true) testEnforce(t, e, "bob", data2, "write", true) } func TestABACMapRequest(t *testing.T) { e, _ := NewEnforcer("examples/abac_model.conf") data1 := map[string]interface{}{ "Name": "data1", "Owner": "alice", } data2 := map[string]interface{}{ "Name": "data2", "Owner": "bob", } testEnforce(t, e, "alice", data1, "read", true) testEnforce(t, e, "alice", data1, "write", true) testEnforce(t, e, "alice", data2, "read", false) testEnforce(t, e, "alice", data2, "write", false) testEnforce(t, e, "bob", data1, "read", false) testEnforce(t, e, "bob", data1, "write", false) testEnforce(t, e, "bob", data2, "read", true) testEnforce(t, e, "bob", data2, "write", true) } func TestABACTypes(t *testing.T) { e, _ := NewEnforcer("examples/abac_model.conf") matcher := `"moderator" IN r.sub.Roles && r.sub.Enabled == true && r.sub.Age >= 21 && r.sub.Name != "foo"` e.GetModel()["m"]["m"].Value = util.RemoveComments(util.EscapeAssertion(matcher)) structRequest := struct { Roles []interface{} Enabled bool Age int Name string }{ Roles: []interface{}{"user", "moderator"}, Enabled: true, Age: 30, Name: "alice", } testEnforce(t, e, structRequest, "", "", true) mapRequest := map[string]interface{}{ "Roles": []interface{}{"user", "moderator"}, "Enabled": true, "Age": 30, "Name": "alice", } testEnforce(t, e, mapRequest, nil, "", true) e.EnableAcceptJsonRequest(true) jsonRequest, _ := json.Marshal(mapRequest) testEnforce(t, e, string(jsonRequest), "", "", true) } func TestABACJsonRequest(t *testing.T) { e, _ := NewEnforcer("examples/abac_model.conf") e.EnableAcceptJsonRequest(true) data1Json := `{ "Name": "data1", "Owner": "alice"}` data2Json := `{ "Name": "data2", "Owner": "bob"}` testEnforce(t, e, "alice", data1Json, "read", true) testEnforce(t, e, "alice", data1Json, "write", true) testEnforce(t, e, "alice", data2Json, "read", false) testEnforce(t, e, "alice", data2Json, "write", false) testEnforce(t, e, "bob", data1Json, "read", false) testEnforce(t, e, "bob", data1Json, "write", false) testEnforce(t, e, "bob", data2Json, "read", true) testEnforce(t, e, "bob", data2Json, "write", true) e, _ = NewEnforcer("examples/abac_not_using_policy_model.conf", "examples/abac_rule_effect_policy.csv") e.EnableAcceptJsonRequest(true) testEnforce(t, e, "alice", data1Json, "read", true) testEnforce(t, e, "alice", data1Json, "write", true) testEnforce(t, e, "alice", data2Json, "read", false) testEnforce(t, e, "alice", data2Json, "write", false) e, _ = NewEnforcer("examples/abac_rule_model.conf", "examples/abac_rule_policy.csv") e.EnableAcceptJsonRequest(true) sub1Json := `{"Name": "alice", "Age": 16}` sub2Json := `{"Name": "alice", "Age": 20}` sub3Json := `{"Name": "alice", "Age": 65}` testEnforce(t, e, sub1Json, "/data1", "read", false) testEnforce(t, e, sub1Json, "/data2", "read", false) testEnforce(t, e, sub1Json, "/data1", "write", false) testEnforce(t, e, sub1Json, "/data2", "write", true) testEnforce(t, e, sub2Json, "/data1", "read", true) testEnforce(t, e, sub2Json, "/data2", "read", false) testEnforce(t, e, sub2Json, "/data1", "write", false) testEnforce(t, e, sub2Json, "/data2", "write", true) testEnforce(t, e, sub3Json, "/data1", "read", true) testEnforce(t, e, sub3Json, "/data2", "read", false) testEnforce(t, e, sub3Json, "/data1", "write", false) testEnforce(t, e, sub3Json, "/data2", "write", false) } func TestKeyMatchModel(t *testing.T) { e, _ := NewEnforcer("examples/keymatch_model.conf", "examples/keymatch_policy.csv") testEnforce(t, e, "alice", "/alice_data/resource1", "GET", true) testEnforce(t, e, "alice", "/alice_data/resource1", "POST", true) testEnforce(t, e, "alice", "/alice_data/resource2", "GET", true) testEnforce(t, e, "alice", "/alice_data/resource2", "POST", false) testEnforce(t, e, "alice", "/bob_data/resource1", "GET", false) testEnforce(t, e, "alice", "/bob_data/resource1", "POST", false) testEnforce(t, e, "alice", "/bob_data/resource2", "GET", false) testEnforce(t, e, "alice", "/bob_data/resource2", "POST", false) testEnforce(t, e, "bob", "/alice_data/resource1", "GET", false) testEnforce(t, e, "bob", "/alice_data/resource1", "POST", false) testEnforce(t, e, "bob", "/alice_data/resource2", "GET", true) testEnforce(t, e, "bob", "/alice_data/resource2", "POST", false) testEnforce(t, e, "bob", "/bob_data/resource1", "GET", false) testEnforce(t, e, "bob", "/bob_data/resource1", "POST", true) testEnforce(t, e, "bob", "/bob_data/resource2", "GET", false) testEnforce(t, e, "bob", "/bob_data/resource2", "POST", true) testEnforce(t, e, "cathy", "/cathy_data", "GET", true) testEnforce(t, e, "cathy", "/cathy_data", "POST", true) testEnforce(t, e, "cathy", "/cathy_data", "DELETE", false) } func TestKeyMatch2Model(t *testing.T) { e, _ := NewEnforcer("examples/keymatch2_model.conf", "examples/keymatch2_policy.csv") testEnforce(t, e, "alice", "/alice_data", "GET", false) testEnforce(t, e, "alice", "/alice_data/resource1", "GET", true) testEnforce(t, e, "alice", "/alice_data2/myid", "GET", false) testEnforce(t, e, "alice", "/alice_data2/myid/using/res_id", "GET", true) } func CustomFunction(key1 string, key2 string) bool { if key1 == "/alice_data2/myid/using/res_id" && key2 == "/alice_data/:resource" { return true } else if key1 == "/alice_data2/myid/using/res_id" && key2 == "/alice_data2/:id/using/:resId" { return true } else { return false } } func CustomFunctionWrapper(args ...interface{}) (interface{}, error) { key1 := args[0].(string) key2 := args[1].(string) return bool(CustomFunction(key1, key2)), nil } func TestKeyMatchCustomModel(t *testing.T) { e, _ := NewEnforcer("examples/keymatch_custom_model.conf", "examples/keymatch2_policy.csv") e.AddFunction("keyMatchCustom", CustomFunctionWrapper) testEnforce(t, e, "alice", "/alice_data2/myid", "GET", false) testEnforce(t, e, "alice", "/alice_data2/myid/using/res_id", "GET", true) } func TestIPMatchModel(t *testing.T) { e, _ := NewEnforcer("examples/ipmatch_model.conf", "examples/ipmatch_policy.csv") testEnforce(t, e, "192.168.2.123", "data1", "read", true) testEnforce(t, e, "192.168.2.123", "data1", "write", false) testEnforce(t, e, "192.168.2.123", "data2", "read", false) testEnforce(t, e, "192.168.2.123", "data2", "write", false) testEnforce(t, e, "192.168.0.123", "data1", "read", false) testEnforce(t, e, "192.168.0.123", "data1", "write", false) testEnforce(t, e, "192.168.0.123", "data2", "read", false) testEnforce(t, e, "192.168.0.123", "data2", "write", false) testEnforce(t, e, "10.0.0.5", "data1", "read", false) testEnforce(t, e, "10.0.0.5", "data1", "write", false) testEnforce(t, e, "10.0.0.5", "data2", "read", false) testEnforce(t, e, "10.0.0.5", "data2", "write", true) testEnforce(t, e, "192.168.0.1", "data1", "read", false) testEnforce(t, e, "192.168.0.1", "data1", "write", false) testEnforce(t, e, "192.168.0.1", "data2", "read", false) testEnforce(t, e, "192.168.0.1", "data2", "write", false) } func TestGlobMatchModel(t *testing.T) { e, _ := NewEnforcer("examples/glob_model.conf", "examples/glob_policy.csv") testEnforce(t, e, "u1", "/foo/", "read", true) testEnforce(t, e, "u1", "/foo", "read", false) testEnforce(t, e, "u1", "/foo/subprefix", "read", true) testEnforce(t, e, "u1", "foo", "read", false) testEnforce(t, e, "u2", "/foosubprefix", "read", true) testEnforce(t, e, "u2", "/foo/subprefix", "read", false) testEnforce(t, e, "u2", "foo", "read", false) testEnforce(t, e, "u3", "/prefix/foo/subprefix", "read", true) testEnforce(t, e, "u3", "/prefix/foo/", "read", true) testEnforce(t, e, "u3", "/prefix/foo", "read", false) testEnforce(t, e, "u4", "/foo", "read", false) testEnforce(t, e, "u4", "foo", "read", true) } func TestPriorityModel(t *testing.T) { e, _ := NewEnforcer("examples/priority_model.conf", "examples/priority_policy.csv") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", true) testEnforce(t, e, "bob", "data2", "write", false) } func TestPriorityModelIndeterminate(t *testing.T) { e, _ := NewEnforcer("examples/priority_model.conf", "examples/priority_indeterminate_policy.csv") testEnforce(t, e, "alice", "data1", "read", false) } func TestRBACModelInMultiLines(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model_in_multi_line.conf", "examples/rbac_policy.csv") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", true) testEnforce(t, e, "alice", "data2", "write", true) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } type testSub struct { Name string Age int } func newTestSubject(name string, age int) testSub { s := testSub{} s.Name = name s.Age = age return s } func TestABACNotUsingPolicy(t *testing.T) { e, _ := NewEnforcer("examples/abac_not_using_policy_model.conf", "examples/abac_rule_effect_policy.csv") data1 := newTestResource("data1", "alice") data2 := newTestResource("data2", "bob") testEnforce(t, e, "alice", data1, "read", true) testEnforce(t, e, "alice", data1, "write", true) testEnforce(t, e, "alice", data2, "read", false) testEnforce(t, e, "alice", data2, "write", false) } func TestABACPolicy(t *testing.T) { e, _ := NewEnforcer("examples/abac_rule_model.conf", "examples/abac_rule_policy.csv") m := e.GetModel() for sec, ast := range m { fmt.Println(sec) for ptype, p := range ast { fmt.Println(ptype, p) } } sub1 := newTestSubject("alice", 16) sub2 := newTestSubject("alice", 20) sub3 := newTestSubject("alice", 65) testEnforce(t, e, sub1, "/data1", "read", false) testEnforce(t, e, sub1, "/data2", "read", false) testEnforce(t, e, sub1, "/data1", "write", false) testEnforce(t, e, sub1, "/data2", "write", true) testEnforce(t, e, sub2, "/data1", "read", true) testEnforce(t, e, sub2, "/data2", "read", false) testEnforce(t, e, sub2, "/data1", "write", false) testEnforce(t, e, sub2, "/data2", "write", true) testEnforce(t, e, sub3, "/data1", "read", true) testEnforce(t, e, sub3, "/data2", "read", false) testEnforce(t, e, sub3, "/data1", "write", false) testEnforce(t, e, sub3, "/data2", "write", false) } func TestCommentModel(t *testing.T) { e, _ := NewEnforcer("examples/comment_model.conf", "examples/basic_policy.csv") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } func TestDomainMatchModel(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domain_pattern_model.conf", "examples/rbac_with_domain_pattern_policy.csv") e.AddNamedDomainMatchingFunc("g", "keyMatch2", util.KeyMatch2) testDomainEnforce(t, e, "alice", "domain1", "data1", "read", true) testDomainEnforce(t, e, "alice", "domain1", "data1", "write", true) testDomainEnforce(t, e, "alice", "domain1", "data2", "read", false) testDomainEnforce(t, e, "alice", "domain1", "data2", "write", false) testDomainEnforce(t, e, "alice", "domain2", "data2", "read", true) testDomainEnforce(t, e, "alice", "domain2", "data2", "write", true) testDomainEnforce(t, e, "bob", "domain2", "data1", "read", false) testDomainEnforce(t, e, "bob", "domain2", "data1", "write", false) testDomainEnforce(t, e, "bob", "domain2", "data2", "read", true) testDomainEnforce(t, e, "bob", "domain2", "data2", "write", true) } func TestAllMatchModel(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_all_pattern_model.conf", "examples/rbac_with_all_pattern_policy.csv") e.AddNamedMatchingFunc("g", "keyMatch2", util.KeyMatch2) e.AddNamedDomainMatchingFunc("g", "keyMatch2", util.KeyMatch2) testDomainEnforce(t, e, "alice", "domain1", "/book/1", "read", true) testDomainEnforce(t, e, "alice", "domain1", "/book/1", "write", false) testDomainEnforce(t, e, "alice", "domain2", "/book/1", "read", false) testDomainEnforce(t, e, "alice", "domain2", "/book/1", "write", true) } func TestTemporalRolesModel(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_temporal_roles_model.conf", "examples/rbac_with_temporal_roles_policy.csv") e.AddNamedLinkConditionFunc("g", "alice", "data2_admin", util.TimeMatchFunc) e.AddNamedLinkConditionFunc("g", "alice", "data3_admin", util.TimeMatchFunc) e.AddNamedLinkConditionFunc("g", "alice", "data4_admin", util.TimeMatchFunc) e.AddNamedLinkConditionFunc("g", "alice", "data5_admin", util.TimeMatchFunc) e.AddNamedLinkConditionFunc("g", "alice", "data6_admin", util.TimeMatchFunc) e.AddNamedLinkConditionFunc("g", "alice", "data7_admin", util.TimeMatchFunc) e.AddNamedLinkConditionFunc("g", "alice", "data8_admin", util.TimeMatchFunc) testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data1", "write", true) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "alice", "data3", "read", true) testEnforce(t, e, "alice", "data3", "write", true) testEnforce(t, e, "alice", "data4", "read", true) testEnforce(t, e, "alice", "data4", "write", true) testEnforce(t, e, "alice", "data5", "read", true) testEnforce(t, e, "alice", "data5", "write", true) testEnforce(t, e, "alice", "data6", "read", false) testEnforce(t, e, "alice", "data6", "write", false) testEnforce(t, e, "alice", "data7", "read", true) testEnforce(t, e, "alice", "data7", "write", true) testEnforce(t, e, "alice", "data8", "read", false) testEnforce(t, e, "alice", "data8", "write", false) } func TestTemporalRolesModelWithDomain(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domain_temporal_roles_model.conf", "examples/rbac_with_domain_temporal_roles_policy.csv") e.AddNamedDomainLinkConditionFunc("g", "alice", "data2_admin", "domain2", util.TimeMatchFunc) e.AddNamedDomainLinkConditionFunc("g", "alice", "data3_admin", "domain3", util.TimeMatchFunc) e.AddNamedDomainLinkConditionFunc("g", "alice", "data4_admin", "domain4", util.TimeMatchFunc) e.AddNamedDomainLinkConditionFunc("g", "alice", "data5_admin", "domain5", util.TimeMatchFunc) e.AddNamedDomainLinkConditionFunc("g", "alice", "data6_admin", "domain6", util.TimeMatchFunc) e.AddNamedDomainLinkConditionFunc("g", "alice", "data7_admin", "domain7", util.TimeMatchFunc) e.AddNamedDomainLinkConditionFunc("g", "alice", "data8_admin", "domain8", util.TimeMatchFunc) testDomainEnforce(t, e, "alice", "domain1", "data1", "read", true) testDomainEnforce(t, e, "alice", "domain1", "data1", "write", true) testDomainEnforce(t, e, "alice", "domain2", "data2", "read", false) testDomainEnforce(t, e, "alice", "domain2", "data2", "write", false) testDomainEnforce(t, e, "alice", "domain3", "data3", "read", true) testDomainEnforce(t, e, "alice", "domain3", "data3", "write", true) testDomainEnforce(t, e, "alice", "domain4", "data4", "read", true) testDomainEnforce(t, e, "alice", "domain4", "data4", "write", true) testDomainEnforce(t, e, "alice", "domain5", "data5", "read", true) testDomainEnforce(t, e, "alice", "domain5", "data5", "write", true) testDomainEnforce(t, e, "alice", "domain6", "data6", "read", false) testDomainEnforce(t, e, "alice", "domain6", "data6", "write", false) testDomainEnforce(t, e, "alice", "domain7", "data7", "read", true) testDomainEnforce(t, e, "alice", "domain7", "data7", "write", true) testDomainEnforce(t, e, "alice", "domain8", "data8", "read", false) testDomainEnforce(t, e, "alice", "domain8", "data8", "write", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data1", "read", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data1", "write", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data2", "read", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data2", "write", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data3", "read", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data3", "write", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data4", "read", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data4", "write", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data5", "read", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data5", "write", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data6", "read", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data6", "write", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data7", "read", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data7", "write", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data8", "read", false) testDomainEnforce(t, e, "alice", "domain_not_exist", "data8", "write", false) } golang-github-casbin-casbin-2.82.0/persist/000077500000000000000000000000001456376452400205355ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/persist/adapter.go000066400000000000000000000042441456376452400225100ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package persist import ( "encoding/csv" "strings" "github.com/casbin/casbin/v2/model" ) // LoadPolicyLine loads a text line as a policy rule to model. func LoadPolicyLine(line string, m model.Model) error { if line == "" || strings.HasPrefix(line, "#") { return nil } r := csv.NewReader(strings.NewReader(line)) r.Comma = ',' r.Comment = '#' r.TrimLeadingSpace = true tokens, err := r.Read() if err != nil { return err } return LoadPolicyArray(tokens, m) } // LoadPolicyArray loads a policy rule to model. func LoadPolicyArray(rule []string, m model.Model) error { key := rule[0] sec := key[:1] ok, err := m.HasPolicyEx(sec, key, rule[1:]) if err != nil { return err } if ok { return nil // skip duplicated policy } m.AddPolicy(sec, key, rule[1:]) return nil } // Adapter is the interface for Casbin adapters. type Adapter interface { // LoadPolicy loads all policy rules from the storage. LoadPolicy(model model.Model) error // SavePolicy saves all policy rules to the storage. SavePolicy(model model.Model) error // AddPolicy adds a policy rule to the storage. // This is part of the Auto-Save feature. AddPolicy(sec string, ptype string, rule []string) error // RemovePolicy removes a policy rule from the storage. // This is part of the Auto-Save feature. RemovePolicy(sec string, ptype string, rule []string) error // RemoveFilteredPolicy removes policy rules that match the filter from the storage. // This is part of the Auto-Save feature. RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error } golang-github-casbin-casbin-2.82.0/persist/adapter_context.go000066400000000000000000000033051456376452400242510ustar00rootroot00000000000000// Copyright 2023 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package persist import ( "context" "github.com/casbin/casbin/v2/model" ) // ContextAdapter provides a context-aware interface for Casbin adapters. type ContextAdapter interface { Adapter // LoadPolicyCtx loads all policy rules from the storage with context. LoadPolicyCtx(ctx context.Context, model model.Model) error // SavePolicyCtx saves all policy rules to the storage with context. SavePolicyCtx(ctx context.Context, model model.Model) error // AddPolicyCtx adds a policy rule to the storage with context. // This is part of the Auto-Save feature. AddPolicyCtx(ctx context.Context, sec string, ptype string, rule []string) error // RemovePolicyCtx removes a policy rule from the storage with context. // This is part of the Auto-Save feature. RemovePolicyCtx(ctx context.Context, sec string, ptype string, rule []string) error // RemoveFilteredPolicyCtx removes policy rules that match the filter from the storage with context. // This is part of the Auto-Save feature. RemoveFilteredPolicyCtx(ctx context.Context, sec string, ptype string, fieldIndex int, fieldValues ...string) error } golang-github-casbin-casbin-2.82.0/persist/adapter_filtered.go000066400000000000000000000020161456376452400243610ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package persist import ( "github.com/casbin/casbin/v2/model" ) // FilteredAdapter is the interface for Casbin adapters supporting filtered policies. type FilteredAdapter interface { Adapter // LoadFilteredPolicy loads only policy rules that match the filter. LoadFilteredPolicy(model model.Model, filter interface{}) error // IsFiltered returns true if the loaded policy has been filtered. IsFiltered() bool } golang-github-casbin-casbin-2.82.0/persist/batch_adapter.go000066400000000000000000000021141456376452400236430ustar00rootroot00000000000000// Copyright 2020 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package persist // BatchAdapter is the interface for Casbin adapters with multiple add and remove policy functions. type BatchAdapter interface { Adapter // AddPolicies adds policy rules to the storage. // This is part of the Auto-Save feature. AddPolicies(sec string, ptype string, rules [][]string) error // RemovePolicies removes policy rules from the storage. // This is part of the Auto-Save feature. RemovePolicies(sec string, ptype string, rules [][]string) error } golang-github-casbin-casbin-2.82.0/persist/cache/000077500000000000000000000000001456376452400216005ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/persist/cache/cache.go000066400000000000000000000025361456376452400232000ustar00rootroot00000000000000// Copyright 2021 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cache import "errors" var ErrNoSuchKey = errors.New("there's no such key existing in cache") type Cache interface { // Set puts key and value into cache. // First parameter for extra should be time.Time object denoting expected survival time. // If survival time equals 0 or less, the key will always be survival. Set(key string, value bool, extra ...interface{}) error // Get returns result for key, // If there's no such key existing in cache, // ErrNoSuchKey will be returned. Get(key string) (bool, error) // Delete will remove the specific key in cache. // If there's no such key existing in cache, // ErrNoSuchKey will be returned. Delete(key string) error // Clear deletes all the items stored in cache. Clear() error } golang-github-casbin-casbin-2.82.0/persist/cache/cache_sync.go000066400000000000000000000033731456376452400242340ustar00rootroot00000000000000// Copyright 2021 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cache import ( "sync" "time" ) type SyncCache struct { cache DefaultCache sync.RWMutex } func (c *SyncCache) Set(key string, value bool, extra ...interface{}) error { ttl := time.Duration(-1) if len(extra) > 0 { ttl = extra[0].(time.Duration) } c.Lock() defer c.Unlock() c.cache[key] = cacheItem{ value: value, expiresAt: time.Now().Add(ttl), ttl: ttl, } return nil } func (c *SyncCache) Get(key string) (bool, error) { c.RLock() res, ok := c.cache[key] c.RUnlock() if !ok { return false, ErrNoSuchKey } else { if res.ttl > 0 && time.Now().After(res.expiresAt) { c.Lock() defer c.Unlock() delete(c.cache, key) return false, ErrNoSuchKey } return res.value, nil } } func (c *SyncCache) Delete(key string) error { c.RLock() _, ok := c.cache[key] c.RUnlock() if !ok { return ErrNoSuchKey } else { c.Lock() defer c.Unlock() delete(c.cache, key) return nil } } func (c *SyncCache) Clear() error { c.Lock() c.cache = make(DefaultCache) c.Unlock() return nil } func NewSyncCache() (Cache, error) { cache := SyncCache{ make(DefaultCache), sync.RWMutex{}, } return &cache, nil } golang-github-casbin-casbin-2.82.0/persist/cache/default-cache.go000066400000000000000000000031461456376452400246200ustar00rootroot00000000000000// Copyright 2021 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cache import "time" type cacheItem struct { value bool expiresAt time.Time ttl time.Duration } type DefaultCache map[string]cacheItem func (c *DefaultCache) Set(key string, value bool, extra ...interface{}) error { ttl := time.Duration(-1) if len(extra) > 0 { ttl = extra[0].(time.Duration) } (*c)[key] = cacheItem{ value: value, expiresAt: time.Now().Add(ttl), ttl: ttl, } return nil } func (c *DefaultCache) Get(key string) (bool, error) { if res, ok := (*c)[key]; !ok { return false, ErrNoSuchKey } else { if res.ttl > 0 && time.Now().After(res.expiresAt) { delete(*c, key) return false, ErrNoSuchKey } return res.value, nil } } func (c *DefaultCache) Delete(key string) error { if _, ok := (*c)[key]; !ok { return ErrNoSuchKey } else { delete(*c, key) return nil } } func (c *DefaultCache) Clear() error { *c = make(DefaultCache) return nil } func NewDefaultCache() (Cache, error) { cache := make(DefaultCache) return &cache, nil } golang-github-casbin-casbin-2.82.0/persist/dispatcher.go000066400000000000000000000031661456376452400232200ustar00rootroot00000000000000// Copyright 2020 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package persist // Dispatcher is the interface for Casbin dispatcher type Dispatcher interface { // AddPolicies adds policies rule to all instance. AddPolicies(sec string, ptype string, rules [][]string) error // RemovePolicies removes policies rule from all instance. RemovePolicies(sec string, ptype string, rules [][]string) error // RemoveFilteredPolicy removes policy rules that match the filter from all instance. RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error // ClearPolicy clears all current policy in all instances ClearPolicy() error // UpdatePolicy updates policy rule from all instance. UpdatePolicy(sec string, ptype string, oldRule, newRule []string) error // UpdatePolicies updates some policy rules from all instance UpdatePolicies(sec string, ptype string, oldrules, newRules [][]string) error // UpdateFilteredPolicies deletes old rules and adds new rules. UpdateFilteredPolicies(sec string, ptype string, oldRules [][]string, newRules [][]string) error } golang-github-casbin-casbin-2.82.0/persist/file-adapter/000077500000000000000000000000001456376452400230725ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/persist/file-adapter/adapter.go000066400000000000000000000077441456376452400250550ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package fileadapter import ( "bufio" "bytes" "errors" "os" "strings" "github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/persist" "github.com/casbin/casbin/v2/util" ) // Adapter is the file adapter for Casbin. // It can load policy from file or save policy to file. type Adapter struct { filePath string } func (a *Adapter) UpdatePolicy(sec string, ptype string, oldRule, newRule []string) error { return errors.New("not implemented") } func (a *Adapter) UpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) error { return errors.New("not implemented") } func (a *Adapter) UpdateFilteredPolicies(sec string, ptype string, newRules [][]string, fieldIndex int, fieldValues ...string) ([][]string, error) { return nil, errors.New("not implemented") } // NewAdapter is the constructor for Adapter. func NewAdapter(filePath string) *Adapter { return &Adapter{filePath: filePath} } // LoadPolicy loads all policy rules from the storage. func (a *Adapter) LoadPolicy(model model.Model) error { if a.filePath == "" { return errors.New("invalid file path, file path cannot be empty") } return a.loadPolicyFile(model, persist.LoadPolicyLine) } // SavePolicy saves all policy rules to the storage. func (a *Adapter) SavePolicy(model model.Model) error { if a.filePath == "" { return errors.New("invalid file path, file path cannot be empty") } var tmp bytes.Buffer for ptype, ast := range model["p"] { for _, rule := range ast.Policy { tmp.WriteString(ptype + ", ") tmp.WriteString(util.ArrayToString(rule)) tmp.WriteString("\n") } } for ptype, ast := range model["g"] { for _, rule := range ast.Policy { tmp.WriteString(ptype + ", ") tmp.WriteString(util.ArrayToString(rule)) tmp.WriteString("\n") } } return a.savePolicyFile(strings.TrimRight(tmp.String(), "\n")) } func (a *Adapter) loadPolicyFile(model model.Model, handler func(string, model.Model) error) error { f, err := os.Open(a.filePath) if err != nil { return err } defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) err = handler(line, model) if err != nil { return err } } return scanner.Err() } func (a *Adapter) savePolicyFile(text string) error { f, err := os.Create(a.filePath) if err != nil { return err } w := bufio.NewWriter(f) _, err = w.WriteString(text) if err != nil { return err } err = w.Flush() if err != nil { return err } return f.Close() } // AddPolicy adds a policy rule to the storage. func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error { return errors.New("not implemented") } // AddPolicies adds policy rules to the storage. func (a *Adapter) AddPolicies(sec string, ptype string, rules [][]string) error { return errors.New("not implemented") } // RemovePolicy removes a policy rule from the storage. func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error { return errors.New("not implemented") } // RemovePolicies removes policy rules from the storage. func (a *Adapter) RemovePolicies(sec string, ptype string, rules [][]string) error { return errors.New("not implemented") } // RemoveFilteredPolicy removes policy rules that match the filter from the storage. func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error { return errors.New("not implemented") } golang-github-casbin-casbin-2.82.0/persist/file-adapter/adapter_filtered.go000066400000000000000000000073011456376452400267200ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package fileadapter import ( "bufio" "errors" "os" "strings" "github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/persist" ) // FilteredAdapter is the filtered file adapter for Casbin. It can load policy // from file or save policy to file and supports loading of filtered policies. type FilteredAdapter struct { *Adapter filtered bool } // Filter defines the filtering rules for a FilteredAdapter's policy. Empty values // are ignored, but all others must match the filter. type Filter struct { P []string G []string G1 []string G2 []string G3 []string G4 []string G5 []string } // NewFilteredAdapter is the constructor for FilteredAdapter. func NewFilteredAdapter(filePath string) *FilteredAdapter { a := FilteredAdapter{} a.filtered = true a.Adapter = NewAdapter(filePath) return &a } // LoadPolicy loads all policy rules from the storage. func (a *FilteredAdapter) LoadPolicy(model model.Model) error { a.filtered = false return a.Adapter.LoadPolicy(model) } // LoadFilteredPolicy loads only policy rules that match the filter. func (a *FilteredAdapter) LoadFilteredPolicy(model model.Model, filter interface{}) error { if filter == nil { return a.LoadPolicy(model) } if a.filePath == "" { return errors.New("invalid file path, file path cannot be empty") } filterValue, ok := filter.(*Filter) if !ok { return errors.New("invalid filter type") } err := a.loadFilteredPolicyFile(model, filterValue, persist.LoadPolicyLine) if err == nil { a.filtered = true } return err } func (a *FilteredAdapter) loadFilteredPolicyFile(model model.Model, filter *Filter, handler func(string, model.Model) error) error { f, err := os.Open(a.filePath) if err != nil { return err } defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if filterLine(line, filter) { continue } err = handler(line, model) if err != nil { return err } } return scanner.Err() } // IsFiltered returns true if the loaded policy has been filtered. func (a *FilteredAdapter) IsFiltered() bool { return a.filtered } // SavePolicy saves all policy rules to the storage. func (a *FilteredAdapter) SavePolicy(model model.Model) error { if a.filtered { return errors.New("cannot save a filtered policy") } return a.Adapter.SavePolicy(model) } func filterLine(line string, filter *Filter) bool { if filter == nil { return false } p := strings.Split(line, ",") if len(p) == 0 { return true } var filterSlice []string switch strings.TrimSpace(p[0]) { case "p": filterSlice = filter.P case "g": filterSlice = filter.G case "g1": filterSlice = filter.G1 case "g2": filterSlice = filter.G2 case "g3": filterSlice = filter.G3 case "g4": filterSlice = filter.G4 case "g5": filterSlice = filter.G5 } return filterWords(p, filterSlice) } func filterWords(line []string, filter []string) bool { if len(line) < len(filter)+1 { return true } var skipLine bool for i, v := range filter { if len(v) > 0 && strings.TrimSpace(v) != strings.TrimSpace(line[i+1]) { skipLine = true break } } return skipLine } golang-github-casbin-casbin-2.82.0/persist/file-adapter/adapter_mock.go000066400000000000000000000065201456376452400260550ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package fileadapter import ( "bufio" "errors" "io" "os" "strings" "github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/persist" ) // AdapterMock is the file adapter for Casbin. // It can load policy from file or save policy to file. type AdapterMock struct { filePath string errorValue string } // NewAdapterMock is the constructor for AdapterMock. func NewAdapterMock(filePath string) *AdapterMock { a := AdapterMock{} a.filePath = filePath return &a } // LoadPolicy loads all policy rules from the storage. func (a *AdapterMock) LoadPolicy(model model.Model) error { err := a.loadPolicyFile(model, persist.LoadPolicyLine) return err } // SavePolicy saves all policy rules to the storage. func (a *AdapterMock) SavePolicy(model model.Model) error { return nil } func (a *AdapterMock) loadPolicyFile(model model.Model, handler func(string, model.Model) error) error { f, err := os.Open(a.filePath) if err != nil { return err } defer f.Close() buf := bufio.NewReader(f) for { line, err := buf.ReadString('\n') line = strings.TrimSpace(line) if err2 := handler(line, model); err2 != nil { return err2 } if err != nil { if err == io.EOF { return nil } return err } } } // SetMockErr sets string to be returned by of the mock during testing func (a *AdapterMock) SetMockErr(errorToSet string) { a.errorValue = errorToSet } // GetMockErr returns a mock error or nil func (a *AdapterMock) GetMockErr() error { var returnError error if a.errorValue != "" { returnError = errors.New(a.errorValue) } return returnError } // AddPolicy adds a policy rule to the storage. func (a *AdapterMock) AddPolicy(sec string, ptype string, rule []string) error { return a.GetMockErr() } // AddPolicies removes policy rules from the storage. func (a *AdapterMock) AddPolicies(sec string, ptype string, rules [][]string) error { return a.GetMockErr() } // RemovePolicy removes a policy rule from the storage. func (a *AdapterMock) RemovePolicy(sec string, ptype string, rule []string) error { return a.GetMockErr() } // RemovePolicies removes policy rules from the storage. func (a *AdapterMock) RemovePolicies(sec string, ptype string, rules [][]string) error { return a.GetMockErr() } // UpdatePolicy removes a policy rule from the storage. func (a *AdapterMock) UpdatePolicy(sec string, ptype string, oldRule, newPolicy []string) error { return a.GetMockErr() } func (a *AdapterMock) UpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) error { return a.GetMockErr() } // RemoveFilteredPolicy removes policy rules that match the filter from the storage. func (a *AdapterMock) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error { return a.GetMockErr() } golang-github-casbin-casbin-2.82.0/persist/persist_test.go000066400000000000000000000031741456376452400236210ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package persist_test import ( "testing" "github.com/casbin/casbin/v2" "github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/persist" ) func TestPersist(t *testing.T) { //No tests yet } func testRuleCount(t *testing.T, model model.Model, expected int, sec string, ptype string, tag string) { t.Helper() ruleCount := len(model[sec][ptype].Policy) if ruleCount != expected { t.Errorf("[%s] rule count: %d, expected %d", tag, ruleCount, expected) } } func TestDuplicateRuleInAdapter(t *testing.T) { e, _ := casbin.NewEnforcer("../examples/basic_model.conf") _, _ = e.AddPolicy("alice", "data1", "read") _, _ = e.AddPolicy("alice", "data1", "read") testRuleCount(t, e.GetModel(), 1, "p", "p", "AddPolicy") e.ClearPolicy() //simulate adapter.LoadPolicy with duplicate rules _ = persist.LoadPolicyArray([]string{"p", "alice", "data1", "read"}, e.GetModel()) _ = persist.LoadPolicyArray([]string{"p", "alice", "data1", "read"}, e.GetModel()) testRuleCount(t, e.GetModel(), 1, "p", "p", "LoadPolicyArray") } golang-github-casbin-casbin-2.82.0/persist/string-adapter/000077500000000000000000000000001456376452400234615ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/persist/string-adapter/adapter.go000066400000000000000000000047571456376452400254450ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stringadapter import ( "bytes" "errors" "strings" "github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/persist" "github.com/casbin/casbin/v2/util" ) // Adapter is the string adapter for Casbin. // It can load policy from string or save policy to string. type Adapter struct { Line string } // NewAdapter is the constructor for Adapter. func NewAdapter(line string) *Adapter { return &Adapter{ Line: line, } } // LoadPolicy loads all policy rules from the storage. func (a *Adapter) LoadPolicy(model model.Model) error { if a.Line == "" { return errors.New("invalid line, line cannot be empty") } strs := strings.Split(a.Line, "\n") for _, str := range strs { if str == "" { continue } _ = persist.LoadPolicyLine(str, model) } return nil } // SavePolicy saves all policy rules to the storage. func (a *Adapter) SavePolicy(model model.Model) error { var tmp bytes.Buffer for ptype, ast := range model["p"] { for _, rule := range ast.Policy { tmp.WriteString(ptype + ", ") tmp.WriteString(util.ArrayToString(rule)) tmp.WriteString("\n") } } for ptype, ast := range model["g"] { for _, rule := range ast.Policy { tmp.WriteString(ptype + ", ") tmp.WriteString(util.ArrayToString(rule)) tmp.WriteString("\n") } } a.Line = strings.TrimRight(tmp.String(), "\n") return nil } // AddPolicy adds a policy rule to the storage. func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error { return errors.New("not implemented") } // RemovePolicy removes a policy rule from the storage. func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error { a.Line = "" return nil } // RemoveFilteredPolicy removes policy rules that match the filter from the storage. func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error { return errors.New("not implemented") } golang-github-casbin-casbin-2.82.0/persist/string-adapter/adapter_test.go000066400000000000000000000046431456376452400264760ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stringadapter import ( "testing" "github.com/casbin/casbin/v2" "github.com/casbin/casbin/v2/model" ) func Test_KeyMatchRbac(t *testing.T) { conf := ` [request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _ , _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act) ` line := ` p, alice, /alice_data/*, (GET)|(POST) p, alice, /alice_data/resource1, POST p, data_group_admin, /admin/*, POST p, data_group_admin, /bob_data/*, POST g, alice, data_group_admin ` a := NewAdapter(line) m := model.NewModel() err := m.LoadModelFromText(conf) if err != nil { t.Errorf("load model from text failed: %v", err.Error()) return } e, _ := casbin.NewEnforcer(m, a) sub := "alice" obj := "/alice_data/login" act := "POST" if res, _ := e.Enforce(sub, obj, act); !res { t.Error("unexpected enforce result") } } func Test_StringRbac(t *testing.T) { conf := ` [request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _ , _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act ` line := ` p, alice, data1, read p, data_group_admin, data3, read p, data_group_admin, data3, write g, alice, data_group_admin ` a := NewAdapter(line) m := model.NewModel() err := m.LoadModelFromText(conf) if err != nil { t.Errorf("load model from text failed: %v", err.Error()) return } e, _ := casbin.NewEnforcer(m, a) sub := "alice" // the user that wants to access a resource. obj := "data1" // the resource that is going to be accessed. act := "read" // the operation that the user performs on the resource. if res, _ := e.Enforce(sub, obj, act); !res { t.Error("unexpected enforce result") } } golang-github-casbin-casbin-2.82.0/persist/update_adapter.go000066400000000000000000000024071456376452400240510ustar00rootroot00000000000000// Copyright 2020 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package persist // UpdatableAdapter is the interface for Casbin adapters with add update policy function. type UpdatableAdapter interface { Adapter // UpdatePolicy updates a policy rule from storage. // This is part of the Auto-Save feature. UpdatePolicy(sec string, ptype string, oldRule, newRule []string) error // UpdatePolicies updates some policy rules to storage, like db, redis. UpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) error // UpdateFilteredPolicies deletes old rules and adds new rules. UpdateFilteredPolicies(sec string, ptype string, newRules [][]string, fieldIndex int, fieldValues ...string) ([][]string, error) } golang-github-casbin-casbin-2.82.0/persist/watcher.go000066400000000000000000000024041456376452400225210ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package persist // Watcher is the interface for Casbin watchers. type Watcher interface { // SetUpdateCallback sets the callback function that the watcher will call // when the policy in DB has been changed by other instances. // A classic callback is Enforcer.LoadPolicy(). SetUpdateCallback(func(string)) error // Update calls the update callback of other instances to synchronize their policy. // It is usually called after changing the policy in DB, like Enforcer.SavePolicy(), // Enforcer.AddPolicy(), Enforcer.RemovePolicy(), etc. Update() error // Close stops and releases the watcher, the callback function will not be called any more. Close() } golang-github-casbin-casbin-2.82.0/persist/watcher_ex.go000066400000000000000000000041011456376452400232110ustar00rootroot00000000000000// Copyright 2020 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package persist import "github.com/casbin/casbin/v2/model" // WatcherEx is the strengthened Casbin watchers. type WatcherEx interface { Watcher // UpdateForAddPolicy calls the update callback of other instances to synchronize their policy. // It is called after Enforcer.AddPolicy() UpdateForAddPolicy(sec, ptype string, params ...string) error // UpdateForRemovePolicy calls the update callback of other instances to synchronize their policy. // It is called after Enforcer.RemovePolicy() UpdateForRemovePolicy(sec, ptype string, params ...string) error // UpdateForRemoveFilteredPolicy calls the update callback of other instances to synchronize their policy. // It is called after Enforcer.RemoveFilteredNamedGroupingPolicy() UpdateForRemoveFilteredPolicy(sec, ptype string, fieldIndex int, fieldValues ...string) error // UpdateForSavePolicy calls the update callback of other instances to synchronize their policy. // It is called after Enforcer.RemoveFilteredNamedGroupingPolicy() UpdateForSavePolicy(model model.Model) error // UpdateForAddPolicies calls the update callback of other instances to synchronize their policy. // It is called after Enforcer.AddPolicies() UpdateForAddPolicies(sec string, ptype string, rules ...[]string) error // UpdateForRemovePolicies calls the update callback of other instances to synchronize their policy. // It is called after Enforcer.RemovePolicies() UpdateForRemovePolicies(sec string, ptype string, rules ...[]string) error } golang-github-casbin-casbin-2.82.0/persist/watcher_update.go000066400000000000000000000022661456376452400240710ustar00rootroot00000000000000// Copyright 2020 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package persist // UpdatableWatcher is strengthened for Casbin watchers. type UpdatableWatcher interface { Watcher // UpdateForUpdatePolicy calls the update callback of other instances to synchronize their policy. // It is called after Enforcer.UpdatePolicy() UpdateForUpdatePolicy(sec string, ptype string, oldRule, newRule []string) error // UpdateForUpdatePolicies calls the update callback of other instances to synchronize their policy. // It is called after Enforcer.UpdatePolicies() UpdateForUpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) error } golang-github-casbin-casbin-2.82.0/rbac/000077500000000000000000000000001456376452400177535ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/rbac/context_role_manager.go000066400000000000000000000047611456376452400245110ustar00rootroot00000000000000// Copyright 2023 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rbac import "context" // ContextRoleManager provides a context-aware interface to define the operations for managing roles. // Prefer this over RoleManager interface for context propagation, which is useful for things like handling // request timeouts. type ContextRoleManager interface { RoleManager // ClearCtx clears all stored data and resets the role manager to the initial state with context. ClearCtx(ctx context.Context) error // AddLinkCtx adds the inheritance link between two roles. role: name1 and role: name2 with context. // domain is a prefix to the roles (can be used for other purposes). AddLinkCtx(ctx context.Context, name1 string, name2 string, domain ...string) error // DeleteLinkCtx deletes the inheritance link between two roles. role: name1 and role: name2 with context. // domain is a prefix to the roles (can be used for other purposes). DeleteLinkCtx(ctx context.Context, name1 string, name2 string, domain ...string) error // HasLinkCtx determines whether a link exists between two roles. role: name1 inherits role: name2 with context. // domain is a prefix to the roles (can be used for other purposes). HasLinkCtx(ctx context.Context, name1 string, name2 string, domain ...string) (bool, error) // GetRolesCtx gets the roles that a user inherits with context. // domain is a prefix to the roles (can be used for other purposes). GetRolesCtx(ctx context.Context, name string, domain ...string) ([]string, error) // GetUsersCtx gets the users that inherits a role with context. // domain is a prefix to the users (can be used for other purposes). GetUsersCtx(ctx context.Context, name string, domain ...string) ([]string, error) // GetDomainsCtx gets domains that a user has with context. GetDomainsCtx(ctx context.Context, name string) ([]string, error) // GetAllDomainsCtx gets all domains with context. GetAllDomainsCtx(ctx context.Context) ([]string, error) } golang-github-casbin-casbin-2.82.0/rbac/default-role-manager/000077500000000000000000000000001456376452400237465ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/rbac/default-role-manager/role_manager.go000066400000000000000000000712551456376452400267420ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package defaultrolemanager import ( "fmt" "strings" "sync" "github.com/casbin/casbin/v2/log" "github.com/casbin/casbin/v2/rbac" "github.com/casbin/casbin/v2/util" ) const defaultDomain string = "" // Role represents the data structure for a role in RBAC. type Role struct { name string roles *sync.Map users *sync.Map matched *sync.Map matchedBy *sync.Map linkConditionFuncMap *sync.Map linkConditionFuncParamsMap *sync.Map } func newRole(name string) *Role { r := Role{} r.name = name r.roles = &sync.Map{} r.users = &sync.Map{} r.matched = &sync.Map{} r.matchedBy = &sync.Map{} r.linkConditionFuncMap = &sync.Map{} r.linkConditionFuncParamsMap = &sync.Map{} return &r } func (r *Role) addRole(role *Role) { r.roles.Store(role.name, role) role.addUser(r) } func (r *Role) removeRole(role *Role) { r.roles.Delete(role.name) role.removeUser(r) } // should only be called inside addRole func (r *Role) addUser(user *Role) { r.users.Store(user.name, user) } // should only be called inside removeRole func (r *Role) removeUser(user *Role) { r.users.Delete(user.name) } func (r *Role) addMatch(role *Role) { r.matched.Store(role.name, role) role.matchedBy.Store(r.name, r) } func (r *Role) removeMatch(role *Role) { r.matched.Delete(role.name) role.matchedBy.Delete(r.name) } func (r *Role) removeMatches() { r.matched.Range(func(key, value interface{}) bool { r.removeMatch(value.(*Role)) return true }) r.matchedBy.Range(func(key, value interface{}) bool { value.(*Role).removeMatch(r) return true }) } func (r *Role) rangeRoles(fn func(key, value interface{}) bool) { r.roles.Range(fn) r.roles.Range(func(key, value interface{}) bool { role := value.(*Role) role.matched.Range(fn) return true }) r.matchedBy.Range(func(key, value interface{}) bool { role := value.(*Role) role.roles.Range(fn) return true }) } func (r *Role) rangeUsers(fn func(key, value interface{}) bool) { r.users.Range(fn) r.users.Range(func(key, value interface{}) bool { role := value.(*Role) role.matched.Range(fn) return true }) r.matchedBy.Range(func(key, value interface{}) bool { role := value.(*Role) role.users.Range(fn) return true }) } func (r *Role) toString() string { roles := r.getRoles() if len(roles) == 0 { return "" } var sb strings.Builder sb.WriteString(r.name) sb.WriteString(" < ") if len(roles) != 1 { sb.WriteString("(") } for i, role := range roles { if i == 0 { sb.WriteString(role) } else { sb.WriteString(", ") sb.WriteString(role) } } if len(roles) != 1 { sb.WriteString(")") } return sb.String() } func (r *Role) getRoles() []string { var names []string r.rangeRoles(func(key, value interface{}) bool { names = append(names, key.(string)) return true }) return util.RemoveDuplicateElement(names) } func (r *Role) getUsers() []string { var names []string r.rangeUsers(func(key, value interface{}) bool { names = append(names, key.(string)) return true }) return names } type linkConditionFuncKey struct { roleName string domainName string } func (r *Role) addLinkConditionFunc(role *Role, domain string, fn rbac.LinkConditionFunc) { r.linkConditionFuncMap.Store(linkConditionFuncKey{role.name, domain}, fn) } func (r *Role) getLinkConditionFunc(role *Role, domain string) (rbac.LinkConditionFunc, bool) { fn, ok := r.linkConditionFuncMap.Load(linkConditionFuncKey{role.name, domain}) if fn == nil { return nil, ok } return fn.(rbac.LinkConditionFunc), ok } func (r *Role) setLinkConditionFuncParams(role *Role, domain string, params ...string) { r.linkConditionFuncParamsMap.Store(linkConditionFuncKey{role.name, domain}, params) } func (r *Role) getLinkConditionFuncParams(role *Role, domain string) ([]string, bool) { params, ok := r.linkConditionFuncParamsMap.Load(linkConditionFuncKey{role.name, domain}) if params == nil { return nil, ok } return params.([]string), ok } // RoleManagerImpl provides a default implementation for the RoleManager interface type RoleManagerImpl struct { allRoles *sync.Map maxHierarchyLevel int matchingFunc rbac.MatchingFunc domainMatchingFunc rbac.MatchingFunc logger log.Logger matchingFuncCache *util.SyncLRUCache } // NewRoleManagerImpl is the constructor for creating an instance of the // default RoleManager implementation. func NewRoleManagerImpl(maxHierarchyLevel int) *RoleManagerImpl { rm := RoleManagerImpl{} _ = rm.Clear() //init allRoles and matchingFuncCache rm.maxHierarchyLevel = maxHierarchyLevel rm.SetLogger(&log.DefaultLogger{}) return &rm } // use this constructor to avoid rebuild of AddMatchingFunc func newRoleManagerWithMatchingFunc(maxHierarchyLevel int, fn rbac.MatchingFunc) *RoleManagerImpl { rm := NewRoleManagerImpl(maxHierarchyLevel) rm.matchingFunc = fn return rm } // rebuilds role cache func (rm *RoleManagerImpl) rebuild() { roles := rm.allRoles _ = rm.Clear() rangeLinks(roles, func(name1, name2 string, domain ...string) bool { _ = rm.AddLink(name1, name2, domain...) return true }) } func (rm *RoleManagerImpl) Match(str string, pattern string) bool { if str == pattern { return true } if rm.matchingFunc != nil { return rm.matchingFunc(str, pattern) } else { return false } } func (rm *RoleManagerImpl) rangeMatchingRoles(name string, isPattern bool, fn func(role *Role) bool) { rm.allRoles.Range(func(key, value interface{}) bool { name2 := key.(string) if isPattern && name != name2 && rm.Match(name2, name) { fn(value.(*Role)) } else if !isPattern && name != name2 && rm.Match(name, name2) { fn(value.(*Role)) } return true }) } func (rm *RoleManagerImpl) load(name interface{}) (value *Role, ok bool) { if r, ok := rm.allRoles.Load(name); ok { return r.(*Role), true } return nil, false } // loads or creates a role func (rm *RoleManagerImpl) getRole(name string) (r *Role, created bool) { var role *Role var ok bool if role, ok = rm.load(name); !ok { role = newRole(name) rm.allRoles.Store(name, role) if rm.matchingFunc != nil { rm.rangeMatchingRoles(name, false, func(r *Role) bool { r.addMatch(role) return true }) rm.rangeMatchingRoles(name, true, func(r *Role) bool { role.addMatch(r) return true }) } } return role, !ok } func loadAndDelete(m *sync.Map, name string) (value interface{}, loaded bool) { value, loaded = m.Load(name) if loaded { m.Delete(name) } return value, loaded } func (rm *RoleManagerImpl) removeRole(name string) { if role, ok := loadAndDelete(rm.allRoles, name); ok { role.(*Role).removeMatches() } } // AddMatchingFunc support use pattern in g func (rm *RoleManagerImpl) AddMatchingFunc(name string, fn rbac.MatchingFunc) { rm.matchingFunc = fn rm.rebuild() } // AddDomainMatchingFunc support use domain pattern in g func (rm *RoleManagerImpl) AddDomainMatchingFunc(name string, fn rbac.MatchingFunc) { rm.domainMatchingFunc = fn } // SetLogger sets role manager's logger. func (rm *RoleManagerImpl) SetLogger(logger log.Logger) { rm.logger = logger } // Clear clears all stored data and resets the role manager to the initial state. func (rm *RoleManagerImpl) Clear() error { rm.matchingFuncCache = util.NewSyncLRUCache(100) rm.allRoles = &sync.Map{} return nil } // AddLink adds the inheritance link between role: name1 and role: name2. // aka role: name1 inherits role: name2. func (rm *RoleManagerImpl) AddLink(name1 string, name2 string, domains ...string) error { user, _ := rm.getRole(name1) role, _ := rm.getRole(name2) user.addRole(role) return nil } // DeleteLink deletes the inheritance link between role: name1 and role: name2. // aka role: name1 does not inherit role: name2 any more. func (rm *RoleManagerImpl) DeleteLink(name1 string, name2 string, domains ...string) error { user, _ := rm.getRole(name1) role, _ := rm.getRole(name2) user.removeRole(role) return nil } // HasLink determines whether role: name1 inherits role: name2. func (rm *RoleManagerImpl) HasLink(name1 string, name2 string, domains ...string) (bool, error) { if name1 == name2 || (rm.matchingFunc != nil && rm.Match(name1, name2)) { return true, nil } user, userCreated := rm.getRole(name1) role, roleCreated := rm.getRole(name2) if userCreated { defer rm.removeRole(user.name) } if roleCreated { defer rm.removeRole(role.name) } return rm.hasLinkHelper(role.name, map[string]*Role{user.name: user}, rm.maxHierarchyLevel), nil } func (rm *RoleManagerImpl) hasLinkHelper(targetName string, roles map[string]*Role, level int) bool { if level < 0 || len(roles) == 0 { return false } nextRoles := map[string]*Role{} for _, role := range roles { if targetName == role.name || (rm.matchingFunc != nil && rm.Match(role.name, targetName)) { return true } role.rangeRoles(func(key, value interface{}) bool { nextRoles[key.(string)] = value.(*Role) return true }) } return rm.hasLinkHelper(targetName, nextRoles, level-1) } // GetRoles gets the roles that a user inherits. func (rm *RoleManagerImpl) GetRoles(name string, domains ...string) ([]string, error) { user, created := rm.getRole(name) if created { defer rm.removeRole(user.name) } return user.getRoles(), nil } // GetUsers gets the users of a role. // domain is an unreferenced parameter here, may be used in other implementations. func (rm *RoleManagerImpl) GetUsers(name string, domain ...string) ([]string, error) { role, created := rm.getRole(name) if created { defer rm.removeRole(role.name) } return role.getUsers(), nil } func (rm *RoleManagerImpl) toString() []string { var roles []string rm.allRoles.Range(func(key, value interface{}) bool { role := value.(*Role) if text := role.toString(); text != "" { roles = append(roles, text) } return true }) return roles } // PrintRoles prints all the roles to log. func (rm *RoleManagerImpl) PrintRoles() error { if !(rm.logger).IsEnabled() { return nil } roles := rm.toString() rm.logger.LogRole(roles) return nil } // GetDomains gets domains that a user has func (rm *RoleManagerImpl) GetDomains(name string) ([]string, error) { domains := []string{defaultDomain} return domains, nil } // GetAllDomains gets all domains func (rm *RoleManagerImpl) GetAllDomains() ([]string, error) { domains := []string{defaultDomain} return domains, nil } func (rm *RoleManagerImpl) copyFrom(other *RoleManagerImpl) { other.Range(func(name1, name2 string, domain ...string) bool { _ = rm.AddLink(name1, name2, domain...) return true }) } func rangeLinks(users *sync.Map, fn func(name1, name2 string, domain ...string) bool) { users.Range(func(_, value interface{}) bool { user := value.(*Role) user.roles.Range(func(key, _ interface{}) bool { roleName := key.(string) return fn(user.name, roleName, defaultDomain) }) return true }) } func (rm *RoleManagerImpl) Range(fn func(name1, name2 string, domain ...string) bool) { rangeLinks(rm.allRoles, fn) } // Deprecated: BuildRelationship is no longer required func (rm *RoleManagerImpl) BuildRelationship(name1 string, name2 string, domain ...string) error { return nil } type DomainManager struct { rmMap *sync.Map maxHierarchyLevel int matchingFunc rbac.MatchingFunc domainMatchingFunc rbac.MatchingFunc logger log.Logger matchingFuncCache *util.SyncLRUCache } // NewDomainManager is the constructor for creating an instance of the // default DomainManager implementation. func NewDomainManager(maxHierarchyLevel int) *DomainManager { dm := &DomainManager{} _ = dm.Clear() // init rmMap and rmCache dm.maxHierarchyLevel = maxHierarchyLevel return dm } // SetLogger sets role manager's logger. func (dm *DomainManager) SetLogger(logger log.Logger) { dm.logger = logger } // AddMatchingFunc support use pattern in g func (dm *DomainManager) AddMatchingFunc(name string, fn rbac.MatchingFunc) { dm.matchingFunc = fn dm.rmMap.Range(func(key, value interface{}) bool { value.(*RoleManagerImpl).AddMatchingFunc(name, fn) return true }) } // AddDomainMatchingFunc support use domain pattern in g func (dm *DomainManager) AddDomainMatchingFunc(name string, fn rbac.MatchingFunc) { dm.domainMatchingFunc = fn dm.rmMap.Range(func(key, value interface{}) bool { value.(*RoleManagerImpl).AddDomainMatchingFunc(name, fn) return true }) dm.rebuild() } // clears the map of RoleManagers func (dm *DomainManager) rebuild() { rmMap := dm.rmMap _ = dm.Clear() rmMap.Range(func(key, value interface{}) bool { domain := key.(string) rm := value.(*RoleManagerImpl) rm.Range(func(name1, name2 string, _ ...string) bool { _ = dm.AddLink(name1, name2, domain) return true }) return true }) } // Clear clears all stored data and resets the role manager to the initial state. func (dm *DomainManager) Clear() error { dm.rmMap = &sync.Map{} dm.matchingFuncCache = util.NewSyncLRUCache(100) return nil } func (dm *DomainManager) getDomain(domains ...string) (domain string, err error) { switch len(domains) { case 0: return defaultDomain, nil default: return domains[0], nil } } func (dm *DomainManager) Match(str string, pattern string) bool { if str == pattern { return true } if dm.domainMatchingFunc != nil { return dm.domainMatchingFunc(str, pattern) } else { return false } } func (dm *DomainManager) rangeAffectedRoleManagers(domain string, fn func(rm *RoleManagerImpl)) { if dm.domainMatchingFunc != nil { dm.rmMap.Range(func(key, value interface{}) bool { domain2 := key.(string) if domain != domain2 && dm.Match(domain2, domain) { fn(value.(*RoleManagerImpl)) } return true }) } } func (dm *DomainManager) load(name interface{}) (value *RoleManagerImpl, ok bool) { if r, ok := dm.rmMap.Load(name); ok { return r.(*RoleManagerImpl), true } return nil, false } // load or create a RoleManager instance of domain func (dm *DomainManager) getRoleManager(domain string, store bool) *RoleManagerImpl { var rm *RoleManagerImpl var ok bool if rm, ok = dm.load(domain); !ok { rm = newRoleManagerWithMatchingFunc(dm.maxHierarchyLevel, dm.matchingFunc) if store { dm.rmMap.Store(domain, rm) } if dm.domainMatchingFunc != nil { dm.rmMap.Range(func(key, value interface{}) bool { domain2 := key.(string) rm2 := value.(*RoleManagerImpl) if domain != domain2 && dm.Match(domain, domain2) { rm.copyFrom(rm2) } return true }) } } return rm } // AddLink adds the inheritance link between role: name1 and role: name2. // aka role: name1 inherits role: name2. func (dm *DomainManager) AddLink(name1 string, name2 string, domains ...string) error { domain, err := dm.getDomain(domains...) if err != nil { return err } roleManager := dm.getRoleManager(domain, true) //create role manager if it does not exist _ = roleManager.AddLink(name1, name2, domains...) dm.rangeAffectedRoleManagers(domain, func(rm *RoleManagerImpl) { _ = rm.AddLink(name1, name2, domains...) }) return nil } // DeleteLink deletes the inheritance link between role: name1 and role: name2. // aka role: name1 does not inherit role: name2 any more. func (dm *DomainManager) DeleteLink(name1 string, name2 string, domains ...string) error { domain, err := dm.getDomain(domains...) if err != nil { return err } roleManager := dm.getRoleManager(domain, true) //create role manager if it does not exist _ = roleManager.DeleteLink(name1, name2, domains...) dm.rangeAffectedRoleManagers(domain, func(rm *RoleManagerImpl) { _ = rm.DeleteLink(name1, name2, domains...) }) return nil } // HasLink determines whether role: name1 inherits role: name2. func (dm *DomainManager) HasLink(name1 string, name2 string, domains ...string) (bool, error) { domain, err := dm.getDomain(domains...) if err != nil { return false, err } rm := dm.getRoleManager(domain, false) return rm.HasLink(name1, name2, domains...) } // GetRoles gets the roles that a subject inherits. func (dm *DomainManager) GetRoles(name string, domains ...string) ([]string, error) { domain, err := dm.getDomain(domains...) if err != nil { return nil, err } rm := dm.getRoleManager(domain, false) return rm.GetRoles(name, domains...) } // GetUsers gets the users of a role. func (dm *DomainManager) GetUsers(name string, domains ...string) ([]string, error) { domain, err := dm.getDomain(domains...) if err != nil { return nil, err } rm := dm.getRoleManager(domain, false) return rm.GetUsers(name, domains...) } func (dm *DomainManager) toString() []string { var roles []string dm.rmMap.Range(func(key, value interface{}) bool { domain := key.(string) rm := value.(*RoleManagerImpl) domainRoles := rm.toString() roles = append(roles, fmt.Sprintf("%s: %s", domain, strings.Join(domainRoles, ", "))) return true }) return roles } // PrintRoles prints all the roles to log. func (dm *DomainManager) PrintRoles() error { if !(dm.logger).IsEnabled() { return nil } roles := dm.toString() dm.logger.LogRole(roles) return nil } // GetDomains gets domains that a user has func (dm *DomainManager) GetDomains(name string) ([]string, error) { var domains []string dm.rmMap.Range(func(key, value interface{}) bool { domain := key.(string) rm := value.(*RoleManagerImpl) role, created := rm.getRole(name) if created { defer rm.removeRole(role.name) } if len(role.getUsers()) > 0 || len(role.getRoles()) > 0 { domains = append(domains, domain) } return true }) return domains, nil } // GetAllDomains gets all domains func (rm *DomainManager) GetAllDomains() ([]string, error) { var domains []string rm.rmMap.Range(func(key, value interface{}) bool { domains = append(domains, key.(string)) return true }) return domains, nil } // Deprecated: BuildRelationship is no longer required func (rm *DomainManager) BuildRelationship(name1 string, name2 string, domain ...string) error { return nil } type RoleManager struct { *DomainManager } func NewRoleManager(maxHierarchyLevel int) *RoleManager { rm := &RoleManager{} rm.DomainManager = NewDomainManager(maxHierarchyLevel) return rm } type ConditionalRoleManager struct { RoleManagerImpl } func (crm *ConditionalRoleManager) copyFrom(other *ConditionalRoleManager) { other.Range(func(name1, name2 string, domain ...string) bool { _ = crm.AddLink(name1, name2, domain...) return true }) } // use this constructor to avoid rebuild of AddMatchingFunc func newConditionalRoleManagerWithMatchingFunc(maxHierarchyLevel int, fn rbac.MatchingFunc) *ConditionalRoleManager { rm := NewConditionalRoleManager(maxHierarchyLevel) rm.matchingFunc = fn return rm } // NewConditionalRoleManager is the constructor for creating an instance of the // ConditionalRoleManager implementation. func NewConditionalRoleManager(maxHierarchyLevel int) *ConditionalRoleManager { rm := ConditionalRoleManager{} _ = rm.Clear() //init allRoles and matchingFuncCache rm.maxHierarchyLevel = maxHierarchyLevel rm.SetLogger(&log.DefaultLogger{}) return &rm } // HasLink determines whether role: name1 inherits role: name2. func (crm *ConditionalRoleManager) HasLink(name1 string, name2 string, domains ...string) (bool, error) { if name1 == name2 || (crm.matchingFunc != nil && crm.Match(name1, name2)) { return true, nil } user, userCreated := crm.getRole(name1) role, roleCreated := crm.getRole(name2) if userCreated { defer crm.removeRole(user.name) } if roleCreated { defer crm.removeRole(role.name) } return crm.hasLinkHelper(role.name, map[string]*Role{user.name: user}, crm.maxHierarchyLevel, domains...), nil } // hasLinkHelper use the Breadth First Search algorithm to traverse the Role tree // Judging whether the user has a role (has link) is to judge whether the role node can be reached from the user node func (crm *ConditionalRoleManager) hasLinkHelper(targetName string, roles map[string]*Role, level int, domains ...string) bool { if level < 0 || len(roles) == 0 { return false } nextRoles := map[string]*Role{} for _, role := range roles { if targetName == role.name || (crm.matchingFunc != nil && crm.Match(role.name, targetName)) { return true } role.rangeRoles(func(key, value interface{}) bool { nextRole := value.(*Role) return crm.getNextRoles(role, nextRole, domains, nextRoles) }) } return crm.hasLinkHelper(targetName, nextRoles, level-1) } func (crm *ConditionalRoleManager) getNextRoles(currentRole, nextRole *Role, domains []string, nextRoles map[string]*Role) bool { passLinkConditionFunc := true var err error // If LinkConditionFunc exists, it needs to pass the verification to get nextRole if len(domains) == 0 { if linkConditionFunc, existLinkCondition := crm.GetLinkConditionFunc(currentRole.name, nextRole.name); existLinkCondition { params, _ := crm.GetLinkConditionFuncParams(currentRole.name, nextRole.name) passLinkConditionFunc, err = linkConditionFunc(params...) } } else { if linkConditionFunc, existLinkCondition := crm.GetDomainLinkConditionFunc(currentRole.name, nextRole.name, domains[0]); existLinkCondition { params, _ := crm.GetLinkConditionFuncParams(currentRole.name, nextRole.name, domains[0]) passLinkConditionFunc, err = linkConditionFunc(params...) } } if err != nil { crm.logger.LogError(err, "hasLinkHelper LinkCondition Error") return false } if passLinkConditionFunc { nextRoles[nextRole.name] = nextRole } return true } // GetLinkConditionFunc get LinkConditionFunc based on userName, roleName func (crm *ConditionalRoleManager) GetLinkConditionFunc(userName, roleName string) (rbac.LinkConditionFunc, bool) { return crm.GetDomainLinkConditionFunc(userName, roleName, defaultDomain) } // GetDomainLinkConditionFunc get LinkConditionFunc based on userName, roleName, domain func (crm *ConditionalRoleManager) GetDomainLinkConditionFunc(userName, roleName, domain string) (rbac.LinkConditionFunc, bool) { user, userCreated := crm.getRole(userName) role, roleCreated := crm.getRole(roleName) if userCreated { crm.removeRole(user.name) return nil, false } if roleCreated { crm.removeRole(role.name) return nil, false } return user.getLinkConditionFunc(role, domain) } // GetLinkConditionFuncParams gets parameters of LinkConditionFunc based on userName, roleName, domain func (crm *ConditionalRoleManager) GetLinkConditionFuncParams(userName, roleName string, domain ...string) ([]string, bool) { user, userCreated := crm.getRole(userName) role, roleCreated := crm.getRole(roleName) if userCreated { crm.removeRole(user.name) return nil, false } if roleCreated { crm.removeRole(role.name) return nil, false } domainName := defaultDomain if len(domain) != 0 { domainName = domain[0] } if params, ok := user.getLinkConditionFuncParams(role, domainName); ok { return params, true } else { return nil, false } } // AddLinkConditionFunc is based on userName, roleName, add LinkConditionFunc func (crm *ConditionalRoleManager) AddLinkConditionFunc(userName, roleName string, fn rbac.LinkConditionFunc) { crm.AddDomainLinkConditionFunc(userName, roleName, defaultDomain, fn) } // AddDomainLinkConditionFunc is based on userName, roleName, domain, add LinkConditionFunc func (crm *ConditionalRoleManager) AddDomainLinkConditionFunc(userName, roleName, domain string, fn rbac.LinkConditionFunc) { user, _ := crm.getRole(userName) role, _ := crm.getRole(roleName) user.addLinkConditionFunc(role, domain, fn) } // SetLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName, domain func (crm *ConditionalRoleManager) SetLinkConditionFuncParams(userName, roleName string, params ...string) { crm.SetDomainLinkConditionFuncParams(userName, roleName, defaultDomain, params...) } // SetDomainLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName, domain func (crm *ConditionalRoleManager) SetDomainLinkConditionFuncParams(userName, roleName, domain string, params ...string) { user, _ := crm.getRole(userName) role, _ := crm.getRole(roleName) user.setLinkConditionFuncParams(role, domain, params...) } type ConditionalDomainManager struct { ConditionalRoleManager DomainManager } // NewConditionalDomainManager is the constructor for creating an instance of the // ConditionalDomainManager implementation. func NewConditionalDomainManager(maxHierarchyLevel int) *ConditionalDomainManager { rm := ConditionalDomainManager{} _ = rm.Clear() //init allRoles and matchingFuncCache rm.maxHierarchyLevel = maxHierarchyLevel rm.SetLogger(&log.DefaultLogger{}) return &rm } func (dm *ConditionalDomainManager) load(name interface{}) (value *ConditionalRoleManager, ok bool) { if r, ok := dm.rmMap.Load(name); ok { return r.(*ConditionalRoleManager), true } return nil, false } // load or create a ConditionalRoleManager instance of domain func (cdm *ConditionalDomainManager) getConditionalRoleManager(domain string, store bool) *ConditionalRoleManager { var rm *ConditionalRoleManager var ok bool if rm, ok = cdm.load(domain); !ok { rm = newConditionalRoleManagerWithMatchingFunc(cdm.maxHierarchyLevel, cdm.matchingFunc) if store { cdm.rmMap.Store(domain, rm) } if cdm.domainMatchingFunc != nil { cdm.rmMap.Range(func(key, value interface{}) bool { domain2 := key.(string) rm2 := value.(*ConditionalRoleManager) if domain != domain2 && cdm.Match(domain, domain2) { rm.copyFrom(rm2) } return true }) } } return rm } // HasLink determines whether role: name1 inherits role: name2. func (cdm *ConditionalDomainManager) HasLink(name1 string, name2 string, domains ...string) (bool, error) { domain, err := cdm.getDomain(domains...) if err != nil { return false, err } rm := cdm.getConditionalRoleManager(domain, false) return rm.HasLink(name1, name2, domains...) } // AddLink adds the inheritance link between role: name1 and role: name2. // aka role: name1 inherits role: name2. func (cdm *ConditionalDomainManager) AddLink(name1 string, name2 string, domains ...string) error { domain, err := cdm.getDomain(domains...) if err != nil { return err } conditionalRoleManager := cdm.getConditionalRoleManager(domain, true) //create role manager if it does not exist _ = conditionalRoleManager.AddLink(name1, name2, domain) cdm.rangeAffectedRoleManagers(domain, func(rm *RoleManagerImpl) { _ = rm.AddLink(name1, name2, domain) }) return nil } // DeleteLink deletes the inheritance link between role: name1 and role: name2. // aka role: name1 does not inherit role: name2 any more. func (cdm *ConditionalDomainManager) DeleteLink(name1 string, name2 string, domains ...string) error { domain, err := cdm.getDomain(domains...) if err != nil { return err } conditionalRoleManager := cdm.getConditionalRoleManager(domain, true) //create role manager if it does not exist _ = conditionalRoleManager.DeleteLink(name1, name2, domain) cdm.rangeAffectedRoleManagers(domain, func(rm *RoleManagerImpl) { _ = rm.DeleteLink(name1, name2, domain) }) return nil } // AddLinkConditionFunc is based on userName, roleName, add LinkConditionFunc func (cdm *ConditionalDomainManager) AddLinkConditionFunc(userName, roleName string, fn rbac.LinkConditionFunc) { cdm.rmMap.Range(func(key, value interface{}) bool { value.(*ConditionalRoleManager).AddLinkConditionFunc(userName, roleName, fn) return true }) } // AddDomainLinkConditionFunc is based on userName, roleName, domain, add LinkConditionFunc func (cdm *ConditionalDomainManager) AddDomainLinkConditionFunc(userName, roleName, domain string, fn rbac.LinkConditionFunc) { cdm.rmMap.Range(func(key, value interface{}) bool { value.(*ConditionalRoleManager).AddDomainLinkConditionFunc(userName, roleName, domain, fn) return true }) } // SetLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName func (cdm *ConditionalDomainManager) SetLinkConditionFuncParams(userName, roleName string, params ...string) { cdm.rmMap.Range(func(key, value interface{}) bool { value.(*ConditionalRoleManager).SetLinkConditionFuncParams(userName, roleName, params...) return true }) } // SetDomainLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName, domain func (cdm *ConditionalDomainManager) SetDomainLinkConditionFuncParams(userName, roleName, domain string, params ...string) { cdm.rmMap.Range(func(key, value interface{}) bool { value.(*ConditionalRoleManager).SetDomainLinkConditionFuncParams(userName, roleName, domain, params...) return true }) } golang-github-casbin-casbin-2.82.0/rbac/default-role-manager/role_manager_test.go000066400000000000000000000275571456376452400300070ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package defaultrolemanager import ( "fmt" "testing" "github.com/casbin/casbin/v2/rbac" "github.com/casbin/casbin/v2/util" ) func testRole(t *testing.T, rm rbac.RoleManager, name1 string, name2 string, res bool) { t.Helper() myRes, _ := rm.HasLink(name1, name2) t.Logf("%s, %s: %t", name1, name2, myRes) if myRes != res { t.Errorf("%s < %s: %t, supposed to be %t", name1, name2, !res, res) } } func testDomainRole(t *testing.T, rm rbac.RoleManager, name1 string, name2 string, domain string, res bool) { t.Helper() myRes, _ := rm.HasLink(name1, name2, domain) t.Logf("%s :: %s, %s: %t", domain, name1, name2, myRes) if myRes != res { t.Errorf("%s :: %s < %s: %t, supposed to be %t", domain, name1, name2, !res, res) } } func testPrintRoles(t *testing.T, rm rbac.RoleManager, name string, res []string) { t.Helper() myRes, _ := rm.GetRoles(name) t.Logf("%s: %s", name, myRes) if !util.SetEquals(myRes, res) { t.Errorf("%s: %s, supposed to be %s", name, myRes, res) } } func testPrintUsers(t *testing.T, rm rbac.RoleManager, name string, res []string) { t.Helper() myRes, _ := rm.GetUsers(name) t.Logf("%s: %s", name, myRes) if !util.SetEquals(myRes, res) { t.Errorf("%s: %s, supposed to be %s", name, myRes, res) } } func testPrintRolesWithDomain(t *testing.T, rm rbac.RoleManager, name string, domain string, res []string) { t.Helper() myRes, _ := rm.GetRoles(name, domain) if !util.SetEquals(myRes, res) { t.Errorf("%s: %s, supposed to be %s", name, myRes, res) } } func TestRole(t *testing.T) { rm := NewRoleManager(3) _ = rm.AddLink("u1", "g1") _ = rm.AddLink("u2", "g1") _ = rm.AddLink("u3", "g2") _ = rm.AddLink("u4", "g2") _ = rm.AddLink("u4", "g3") _ = rm.AddLink("g1", "g3") // Current role inheritance tree: // g3 g2 // / \ / \ // g1 u4 u3 // / \ // u1 u2 testRole(t, rm, "u1", "g1", true) testRole(t, rm, "u1", "g2", false) testRole(t, rm, "u1", "g3", true) testRole(t, rm, "u2", "g1", true) testRole(t, rm, "u2", "g2", false) testRole(t, rm, "u2", "g3", true) testRole(t, rm, "u3", "g1", false) testRole(t, rm, "u3", "g2", true) testRole(t, rm, "u3", "g3", false) testRole(t, rm, "u4", "g1", false) testRole(t, rm, "u4", "g2", true) testRole(t, rm, "u4", "g3", true) testPrintRoles(t, rm, "u1", []string{"g1"}) testPrintRoles(t, rm, "u2", []string{"g1"}) testPrintRoles(t, rm, "u3", []string{"g2"}) testPrintRoles(t, rm, "u4", []string{"g2", "g3"}) testPrintRoles(t, rm, "g1", []string{"g3"}) testPrintRoles(t, rm, "g2", []string{}) testPrintRoles(t, rm, "g3", []string{}) _ = rm.DeleteLink("g1", "g3") _ = rm.DeleteLink("u4", "g2") // Current role inheritance tree after deleting the links: // g3 g2 // \ \ // g1 u4 u3 // / \ // u1 u2 testRole(t, rm, "u1", "g1", true) testRole(t, rm, "u1", "g2", false) testRole(t, rm, "u1", "g3", false) testRole(t, rm, "u2", "g1", true) testRole(t, rm, "u2", "g2", false) testRole(t, rm, "u2", "g3", false) testRole(t, rm, "u3", "g1", false) testRole(t, rm, "u3", "g2", true) testRole(t, rm, "u3", "g3", false) testRole(t, rm, "u4", "g1", false) testRole(t, rm, "u4", "g2", false) testRole(t, rm, "u4", "g3", true) testPrintRoles(t, rm, "u1", []string{"g1"}) testPrintRoles(t, rm, "u2", []string{"g1"}) testPrintRoles(t, rm, "u3", []string{"g2"}) testPrintRoles(t, rm, "u4", []string{"g3"}) testPrintRoles(t, rm, "g1", []string{}) testPrintRoles(t, rm, "g2", []string{}) testPrintRoles(t, rm, "g3", []string{}) rm = NewRoleManager(3) rm.AddMatchingFunc("keyMatch", util.KeyMatch) _ = rm.AddLink("u1", "g1") _ = rm.AddLink("u1", "*") _ = rm.AddLink("u2", "g2") // Current role inheritance tree after deleting the links: // g1 g2 // \ / \ // * u2 // | // u1 testRole(t, rm, "u1", "g1", true) testRole(t, rm, "u1", "g2", true) testRole(t, rm, "u2", "g2", true) testRole(t, rm, "u2", "g1", false) testPrintRoles(t, rm, "u1", []string{"*", "u1", "u2", "g1", "g2"}) testPrintUsers(t, rm, "*", []string{"u1"}) } func TestDomainRole(t *testing.T) { rm := NewRoleManager(3) _ = rm.AddLink("u1", "g1", "domain1") _ = rm.AddLink("u2", "g1", "domain1") _ = rm.AddLink("u3", "admin", "domain2") _ = rm.AddLink("u4", "admin", "domain2") _ = rm.AddLink("u4", "admin", "domain1") _ = rm.AddLink("g1", "admin", "domain1") // Current role inheritance tree: // domain1:admin domain2:admin // / \ / \ // domain1:g1 u4 u3 // / \ // u1 u2 testDomainRole(t, rm, "u1", "g1", "domain1", true) testDomainRole(t, rm, "u1", "g1", "domain2", false) testDomainRole(t, rm, "u1", "admin", "domain1", true) testDomainRole(t, rm, "u1", "admin", "domain2", false) testDomainRole(t, rm, "u2", "g1", "domain1", true) testDomainRole(t, rm, "u2", "g1", "domain2", false) testDomainRole(t, rm, "u2", "admin", "domain1", true) testDomainRole(t, rm, "u2", "admin", "domain2", false) testDomainRole(t, rm, "u3", "g1", "domain1", false) testDomainRole(t, rm, "u3", "g1", "domain2", false) testDomainRole(t, rm, "u3", "admin", "domain1", false) testDomainRole(t, rm, "u3", "admin", "domain2", true) testDomainRole(t, rm, "u4", "g1", "domain1", false) testDomainRole(t, rm, "u4", "g1", "domain2", false) testDomainRole(t, rm, "u4", "admin", "domain1", true) testDomainRole(t, rm, "u4", "admin", "domain2", true) _ = rm.DeleteLink("g1", "admin", "domain1") _ = rm.DeleteLink("u4", "admin", "domain2") // Current role inheritance tree after deleting the links: // domain1:admin domain2:admin // \ \ // domain1:g1 u4 u3 // / \ // u1 u2 testDomainRole(t, rm, "u1", "g1", "domain1", true) testDomainRole(t, rm, "u1", "g1", "domain2", false) testDomainRole(t, rm, "u1", "admin", "domain1", false) testDomainRole(t, rm, "u1", "admin", "domain2", false) testDomainRole(t, rm, "u2", "g1", "domain1", true) testDomainRole(t, rm, "u2", "g1", "domain2", false) testDomainRole(t, rm, "u2", "admin", "domain1", false) testDomainRole(t, rm, "u2", "admin", "domain2", false) testDomainRole(t, rm, "u3", "g1", "domain1", false) testDomainRole(t, rm, "u3", "g1", "domain2", false) testDomainRole(t, rm, "u3", "admin", "domain1", false) testDomainRole(t, rm, "u3", "admin", "domain2", true) testDomainRole(t, rm, "u4", "g1", "domain1", false) testDomainRole(t, rm, "u4", "g1", "domain2", false) testDomainRole(t, rm, "u4", "admin", "domain1", true) testDomainRole(t, rm, "u4", "admin", "domain2", false) } func TestClear(t *testing.T) { rm := NewRoleManager(3) _ = rm.AddLink("u1", "g1") _ = rm.AddLink("u2", "g1") _ = rm.AddLink("u3", "g2") _ = rm.AddLink("u4", "g2") _ = rm.AddLink("u4", "g3") _ = rm.AddLink("g1", "g3") // Current role inheritance tree: // g3 g2 // / \ / \ // g1 u4 u3 // / \ // u1 u2 _ = rm.Clear() // All data is cleared. // No role inheritance now. testRole(t, rm, "u1", "g1", false) testRole(t, rm, "u1", "g2", false) testRole(t, rm, "u1", "g3", false) testRole(t, rm, "u2", "g1", false) testRole(t, rm, "u2", "g2", false) testRole(t, rm, "u2", "g3", false) testRole(t, rm, "u3", "g1", false) testRole(t, rm, "u3", "g2", false) testRole(t, rm, "u3", "g3", false) testRole(t, rm, "u4", "g1", false) testRole(t, rm, "u4", "g2", false) testRole(t, rm, "u4", "g3", false) } func TestDomainPatternRole(t *testing.T) { rm := NewRoleManager(10) rm.AddDomainMatchingFunc("keyMatch2", util.KeyMatch2) _ = rm.AddLink("u1", "g1", "domain1") _ = rm.AddLink("u2", "g1", "domain2") _ = rm.AddLink("u3", "g1", "*") _ = rm.AddLink("u4", "g2", "domain3") // Current role inheritance tree after deleting the links: // domain1:g1 domain2:g1 domain3:g2 // / \ / \ | // domain1:u1 *:g1 domain2:u2 domain3:u4 // | // *:u3 testDomainRole(t, rm, "u1", "g1", "domain1", true) testDomainRole(t, rm, "u2", "g1", "domain1", false) testDomainRole(t, rm, "u2", "g1", "domain2", true) testDomainRole(t, rm, "u3", "g1", "domain1", true) testDomainRole(t, rm, "u3", "g1", "domain2", true) testDomainRole(t, rm, "u1", "g2", "domain1", false) testDomainRole(t, rm, "u4", "g2", "domain3", true) testDomainRole(t, rm, "u3", "g2", "domain3", false) testPrintRolesWithDomain(t, rm, "u3", "domain1", []string{"g1"}) testPrintRolesWithDomain(t, rm, "u1", "domain1", []string{"g1"}) testPrintRolesWithDomain(t, rm, "u3", "domain2", []string{"g1"}) testPrintRolesWithDomain(t, rm, "u1", "domain2", []string{}) testPrintRolesWithDomain(t, rm, "u4", "domain3", []string{"g2"}) } func TestAllMatchingFunc(t *testing.T) { rm := NewRoleManager(10) rm.AddMatchingFunc("keyMatch2", util.KeyMatch2) rm.AddDomainMatchingFunc("keyMatch2", util.KeyMatch2) _ = rm.AddLink("/book/:id", "book_group", "*") // Current role inheritance tree after deleting the links: // *:book_group // | // *:/book/:id testDomainRole(t, rm, "/book/1", "book_group", "domain1", true) testDomainRole(t, rm, "/book/2", "book_group", "domain1", true) } func TestMatchingFuncOrder(t *testing.T) { rm := NewRoleManager(10) rm.AddMatchingFunc("regexMatch", util.RegexMatch) _ = rm.AddLink("g\\d+", "root") _ = rm.AddLink("u1", "g1") testRole(t, rm, "u1", "root", true) _ = rm.Clear() _ = rm.AddLink("u1", "g1") _ = rm.AddLink("g\\d+", "root") testRole(t, rm, "u1", "root", true) _ = rm.Clear() _ = rm.AddLink("u1", "g\\d+") testRole(t, rm, "u1", "g1", true) testRole(t, rm, "u1", "g2", true) } func TestDomainMatchingFuncWithDifferentDomain(t *testing.T) { rm := NewRoleManager(10) rm.AddDomainMatchingFunc("keyMatch", util.KeyMatch) _ = rm.AddLink("alice", "editor", "*") _ = rm.AddLink("editor", "admin", "domain1") testDomainRole(t, rm, "alice", "admin", "domain1", true) testDomainRole(t, rm, "alice", "admin", "domain2", false) } func TestTemporaryRoles(t *testing.T) { rm := NewRoleManager(10) rm.AddMatchingFunc("regexMatch", util.RegexMatch) _ = rm.AddLink("u\\d+", "user") for i := 0; i < 10; i++ { testRole(t, rm, fmt.Sprintf("u%d", i), "user", true) } testPrintUsers(t, rm, "user", []string{"u\\d+"}) testPrintRoles(t, rm, "u1", []string{"user"}) _ = rm.AddLink("u1", "manager") for i := 10; i < 20; i++ { testRole(t, rm, fmt.Sprintf("u%d", i), "user", true) } testPrintUsers(t, rm, "user", []string{"u\\d+", "u1"}) testPrintRoles(t, rm, "u1", []string{"user", "manager"}) } func TestMaxHierarchyLevel(t *testing.T) { rm := NewRoleManager(1) _ = rm.AddLink("level0", "level1") _ = rm.AddLink("level1", "level2") _ = rm.AddLink("level2", "level3") testRole(t, rm, "level0", "level0", true) testRole(t, rm, "level0", "level1", true) testRole(t, rm, "level0", "level2", false) testRole(t, rm, "level0", "level3", false) testRole(t, rm, "level1", "level2", true) testRole(t, rm, "level1", "level3", false) rm = NewRoleManager(2) _ = rm.AddLink("level0", "level1") _ = rm.AddLink("level1", "level2") _ = rm.AddLink("level2", "level3") testRole(t, rm, "level0", "level0", true) testRole(t, rm, "level0", "level1", true) testRole(t, rm, "level0", "level2", true) testRole(t, rm, "level0", "level3", false) testRole(t, rm, "level1", "level2", true) testRole(t, rm, "level1", "level3", true) } golang-github-casbin-casbin-2.82.0/rbac/role_manager.go000066400000000000000000000073211456376452400227400ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rbac import "github.com/casbin/casbin/v2/log" type MatchingFunc func(arg1 string, arg2 string) bool type LinkConditionFunc = func(args ...string) (bool, error) // RoleManager provides interface to define the operations for managing roles. type RoleManager interface { // Clear clears all stored data and resets the role manager to the initial state. Clear() error // AddLink adds the inheritance link between two roles. role: name1 and role: name2. // domain is a prefix to the roles (can be used for other purposes). AddLink(name1 string, name2 string, domain ...string) error // Deprecated: BuildRelationship is no longer required BuildRelationship(name1 string, name2 string, domain ...string) error // DeleteLink deletes the inheritance link between two roles. role: name1 and role: name2. // domain is a prefix to the roles (can be used for other purposes). DeleteLink(name1 string, name2 string, domain ...string) error // HasLink determines whether a link exists between two roles. role: name1 inherits role: name2. // domain is a prefix to the roles (can be used for other purposes). HasLink(name1 string, name2 string, domain ...string) (bool, error) // GetRoles gets the roles that a user inherits. // domain is a prefix to the roles (can be used for other purposes). GetRoles(name string, domain ...string) ([]string, error) // GetUsers gets the users that inherits a role. // domain is a prefix to the users (can be used for other purposes). GetUsers(name string, domain ...string) ([]string, error) // GetDomains gets domains that a user has GetDomains(name string) ([]string, error) // GetAllDomains gets all domains GetAllDomains() ([]string, error) // PrintRoles prints all the roles to log. PrintRoles() error // SetLogger sets role manager's logger. SetLogger(logger log.Logger) // Match matches the domain with the pattern Match(str string, pattern string) bool // AddMatchingFunc adds the matching function AddMatchingFunc(name string, fn MatchingFunc) // AddDomainMatchingFunc adds the domain matching function AddDomainMatchingFunc(name string, fn MatchingFunc) } // ConditionalRoleManager provides interface to define the operations for managing roles. // Link with conditions is supported type ConditionalRoleManager interface { RoleManager // AddLinkConditionFunc Add condition function fn for Link userName->roleName, // when fn returns true, Link is valid, otherwise invalid AddLinkConditionFunc(userName, roleName string, fn LinkConditionFunc) // SetLinkConditionFuncParams Sets the parameters of the condition function fn for Link userName->roleName SetLinkConditionFuncParams(userName, roleName string, params ...string) // AddDomainLinkConditionFunc Add condition function fn for Link userName-> {roleName, domain}, // when fn returns true, Link is valid, otherwise invalid AddDomainLinkConditionFunc(user string, role string, domain string, fn LinkConditionFunc) // SetDomainLinkConditionFuncParams Sets the parameters of the condition function fn // for Link userName->{roleName, domain} SetDomainLinkConditionFuncParams(user string, role string, domain string, params ...string) } golang-github-casbin-casbin-2.82.0/rbac_api.go000066400000000000000000000421261456376452400211400ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "strings" "github.com/casbin/casbin/v2/constant" "github.com/casbin/casbin/v2/errors" "github.com/casbin/casbin/v2/util" ) // GetRolesForUser gets the roles that a user has. func (e *Enforcer) GetRolesForUser(name string, domain ...string) ([]string, error) { res, err := e.model["g"]["g"].RM.GetRoles(name, domain...) return res, err } // GetUsersForRole gets the users that has a role. func (e *Enforcer) GetUsersForRole(name string, domain ...string) ([]string, error) { res, err := e.model["g"]["g"].RM.GetUsers(name, domain...) return res, err } // HasRoleForUser determines whether a user has a role. func (e *Enforcer) HasRoleForUser(name string, role string, domain ...string) (bool, error) { roles, err := e.GetRolesForUser(name, domain...) if err != nil { return false, err } hasRole := false for _, r := range roles { if r == role { hasRole = true break } } return hasRole, nil } // AddRoleForUser adds a role for a user. // Returns false if the user already has the role (aka not affected). func (e *Enforcer) AddRoleForUser(user string, role string, domain ...string) (bool, error) { args := []string{user, role} args = append(args, domain...) return e.AddGroupingPolicy(args) } // AddRolesForUser adds roles for a user. // Returns false if the user already has the roles (aka not affected). func (e *Enforcer) AddRolesForUser(user string, roles []string, domain ...string) (bool, error) { var rules [][]string for _, role := range roles { rule := []string{user, role} rule = append(rule, domain...) rules = append(rules, rule) } return e.AddGroupingPolicies(rules) } // DeleteRoleForUser deletes a role for a user. // Returns false if the user does not have the role (aka not affected). func (e *Enforcer) DeleteRoleForUser(user string, role string, domain ...string) (bool, error) { args := []string{user, role} args = append(args, domain...) return e.RemoveGroupingPolicy(args) } // DeleteRolesForUser deletes all roles for a user. // Returns false if the user does not have any roles (aka not affected). func (e *Enforcer) DeleteRolesForUser(user string, domain ...string) (bool, error) { var args []string if len(domain) == 0 { args = []string{user} } else if len(domain) > 1 { return false, errors.ErrDomainParameter } else { args = []string{user, "", domain[0]} } return e.RemoveFilteredGroupingPolicy(0, args...) } // DeleteUser deletes a user. // Returns false if the user does not exist (aka not affected). func (e *Enforcer) DeleteUser(user string) (bool, error) { var err error res1, err := e.RemoveFilteredGroupingPolicy(0, user) if err != nil { return res1, err } subIndex, err := e.GetFieldIndex("p", constant.SubjectIndex) if err != nil { return false, err } res2, err := e.RemoveFilteredPolicy(subIndex, user) return res1 || res2, err } // DeleteRole deletes a role. // Returns false if the role does not exist (aka not affected). func (e *Enforcer) DeleteRole(role string) (bool, error) { var err error res1, err := e.RemoveFilteredGroupingPolicy(1, role) if err != nil { return res1, err } subIndex, err := e.GetFieldIndex("p", constant.SubjectIndex) if err != nil { return false, err } res2, err := e.RemoveFilteredPolicy(subIndex, role) return res1 || res2, err } // DeletePermission deletes a permission. // Returns false if the permission does not exist (aka not affected). func (e *Enforcer) DeletePermission(permission ...string) (bool, error) { return e.RemoveFilteredPolicy(1, permission...) } // AddPermissionForUser adds a permission for a user or role. // Returns false if the user or role already has the permission (aka not affected). func (e *Enforcer) AddPermissionForUser(user string, permission ...string) (bool, error) { return e.AddPolicy(util.JoinSlice(user, permission...)) } // AddPermissionsForUser adds multiple permissions for a user or role. // Returns false if the user or role already has one of the permissions (aka not affected). func (e *Enforcer) AddPermissionsForUser(user string, permissions ...[]string) (bool, error) { var rules [][]string for _, permission := range permissions { rules = append(rules, util.JoinSlice(user, permission...)) } return e.AddPolicies(rules) } // DeletePermissionForUser deletes a permission for a user or role. // Returns false if the user or role does not have the permission (aka not affected). func (e *Enforcer) DeletePermissionForUser(user string, permission ...string) (bool, error) { return e.RemovePolicy(util.JoinSlice(user, permission...)) } // DeletePermissionsForUser deletes permissions for a user or role. // Returns false if the user or role does not have any permissions (aka not affected). func (e *Enforcer) DeletePermissionsForUser(user string) (bool, error) { subIndex, err := e.GetFieldIndex("p", constant.SubjectIndex) if err != nil { return false, err } return e.RemoveFilteredPolicy(subIndex, user) } // GetPermissionsForUser gets permissions for a user or role. func (e *Enforcer) GetPermissionsForUser(user string, domain ...string) ([][]string, error) { return e.GetNamedPermissionsForUser("p", user, domain...) } // GetNamedPermissionsForUser gets permissions for a user or role by named policy. func (e *Enforcer) GetNamedPermissionsForUser(ptype string, user string, domain ...string) ([][]string, error) { permission := make([][]string, 0) for pType, assertion := range e.model["p"] { if pType != ptype { continue } args := make([]string, len(assertion.Tokens)) subIndex, err := e.GetFieldIndex("p", constant.SubjectIndex) if err != nil { subIndex = 0 } args[subIndex] = user if len(domain) > 0 { index, err := e.GetFieldIndex(ptype, constant.DomainIndex) if err != nil { return permission, err } args[index] = domain[0] } perm := e.GetFilteredNamedPolicy(ptype, 0, args...) permission = append(permission, perm...) } return permission, nil } // HasPermissionForUser determines whether a user has a permission. func (e *Enforcer) HasPermissionForUser(user string, permission ...string) bool { return e.HasPolicy(util.JoinSlice(user, permission...)) } // GetImplicitRolesForUser gets implicit roles that a user has. // Compared to GetRolesForUser(), this function retrieves indirect roles besides direct roles. // For example: // g, alice, role:admin // g, role:admin, role:user // // GetRolesForUser("alice") can only get: ["role:admin"]. // But GetImplicitRolesForUser("alice") will get: ["role:admin", "role:user"]. func (e *Enforcer) GetImplicitRolesForUser(name string, domain ...string) ([]string, error) { res := []string{} for _, rm := range e.rmMap { roleSet := make(map[string]bool) roleSet[name] = true q := make([]string, 0) q = append(q, name) for len(q) > 0 { name := q[0] q = q[1:] roles, err := rm.GetRoles(name, domain...) if err != nil { return nil, err } for _, r := range roles { if _, ok := roleSet[r]; !ok { res = append(res, r) q = append(q, r) roleSet[r] = true } } } } return res, nil } // GetImplicitUsersForRole gets implicit users for a role. func (e *Enforcer) GetImplicitUsersForRole(name string, domain ...string) ([]string, error) { res := []string{} for _, rm := range e.rmMap { roleSet := make(map[string]bool) roleSet[name] = true q := make([]string, 0) q = append(q, name) for len(q) > 0 { name := q[0] q = q[1:] roles, err := rm.GetUsers(name, domain...) if err != nil && err.Error() != "error: name does not exist" { return nil, err } for _, r := range roles { if _, ok := roleSet[r]; !ok { res = append(res, r) q = append(q, r) roleSet[r] = true } } } } return res, nil } // GetImplicitPermissionsForUser gets implicit permissions for a user or role. // Compared to GetPermissionsForUser(), this function retrieves permissions for inherited roles. // For example: // p, admin, data1, read // p, alice, data2, read // g, alice, admin // // GetPermissionsForUser("alice") can only get: [["alice", "data2", "read"]]. // But GetImplicitPermissionsForUser("alice") will get: [["admin", "data1", "read"], ["alice", "data2", "read"]]. func (e *Enforcer) GetImplicitPermissionsForUser(user string, domain ...string) ([][]string, error) { return e.GetNamedImplicitPermissionsForUser("p", user, domain...) } // GetNamedImplicitPermissionsForUser gets implicit permissions for a user or role by named policy. // Compared to GetNamedPermissionsForUser(), this function retrieves permissions for inherited roles. // For example: // p, admin, data1, read // p2, admin, create // g, alice, admin // // GetImplicitPermissionsForUser("alice") can only get: [["admin", "data1", "read"]], whose policy is default policy "p" // But you can specify the named policy "p2" to get: [["admin", "create"]] by GetNamedImplicitPermissionsForUser("p2","alice") func (e *Enforcer) GetNamedImplicitPermissionsForUser(ptype string, user string, domain ...string) ([][]string, error) { permission := make([][]string, 0) rm := e.GetRoleManager() domainIndex, _ := e.GetFieldIndex(ptype, constant.DomainIndex) for _, rule := range e.model["p"][ptype].Policy { if len(domain) == 0 { matched, _ := rm.HasLink(user, rule[0]) if matched { permission = append(permission, deepCopyPolicy(rule)) } } else if len(domain) > 1 { return nil, errors.ErrDomainParameter } else { d := domain[0] matched := rm.Match(d, rule[domainIndex]) if !matched { continue } matched, _ = rm.HasLink(user, rule[0], d) if matched { newRule := deepCopyPolicy(rule) newRule[domainIndex] = d permission = append(permission, newRule) } } } return permission, nil } // GetImplicitUsersForPermission gets implicit users for a permission. // For example: // p, admin, data1, read // p, bob, data1, read // g, alice, admin // // GetImplicitUsersForPermission("data1", "read") will get: ["alice", "bob"]. // Note: only users will be returned, roles (2nd arg in "g") will be excluded. func (e *Enforcer) GetImplicitUsersForPermission(permission ...string) ([]string, error) { pSubjects := e.GetAllSubjects() gInherit := e.model.GetValuesForFieldInPolicyAllTypes("g", 1) gSubjects := e.model.GetValuesForFieldInPolicyAllTypes("g", 0) subjects := append(pSubjects, gSubjects...) util.ArrayRemoveDuplicates(&subjects) subjects = util.SetSubtract(subjects, gInherit) res := []string{} for _, user := range subjects { req := util.JoinSliceAny(user, permission...) allowed, err := e.Enforce(req...) if err != nil { return nil, err } if allowed { res = append(res, user) } } return res, nil } // GetDomainsForUser gets all domains func (e *Enforcer) GetDomainsForUser(user string) ([]string, error) { var domains []string for _, rm := range e.rmMap { domain, err := rm.GetDomains(user) if err != nil { return nil, err } domains = append(domains, domain...) } return domains, nil } // GetImplicitResourcesForUser returns all policies that user obtaining in domain func (e *Enforcer) GetImplicitResourcesForUser(user string, domain ...string) ([][]string, error) { permissions, err := e.GetImplicitPermissionsForUser(user, domain...) if err != nil { return nil, err } res := make([][]string, 0) for _, permission := range permissions { if permission[0] == user { res = append(res, permission) continue } resLocal := [][]string{{user}} tokensLength := len(permission) t := make([][]string, 1, tokensLength) for _, token := range permission[1:] { tokens, err := e.GetImplicitUsersForRole(token, domain...) if err != nil { return nil, err } tokens = append(tokens, token) t = append(t, tokens) } for i := 1; i < tokensLength; i++ { n := make([][]string, 0) for _, tokens := range t[i] { for _, policy := range resLocal { t := append([]string(nil), policy...) t = append(t, tokens) n = append(n, t) } } resLocal = n } res = append(res, resLocal...) } return res, nil } // deepCopyPolicy returns a deepcopy version of the policy to prevent changing policies through returned slice func deepCopyPolicy(src []string) []string { newRule := make([]string, len(src)) copy(newRule, src) return newRule } // GetAllowedObjectConditions returns a string array of object conditions that the user can access. // For example: conditions, err := e.GetAllowedObjectConditions("alice", "read", "r.obj.") // Note: // // 0. prefix: You can customize the prefix of the object conditions, and "r.obj." is commonly used as a prefix. // After removing the prefix, the remaining part is the condition of the object. // If there is an obj policy that does not meet the prefix requirement, an errors.ERR_OBJ_CONDITION will be returned. // // 1. If the 'objectConditions' array is empty, return errors.ERR_EMPTY_CONDITION // This error is returned because some data adapters' ORM return full table data by default // when they receive an empty condition, which tends to behave contrary to expectations.(e.g. GORM) // If you are using an adapter that does not behave like this, you can choose to ignore this error. func (e *Enforcer) GetAllowedObjectConditions(user string, action string, prefix string) ([]string, error) { permissions, err := e.GetImplicitPermissionsForUser(user) if err != nil { return nil, err } var objectConditions []string for _, policy := range permissions { // policy {sub, obj, act} if policy[2] == action { if !strings.HasPrefix(policy[1], prefix) { return nil, errors.ErrObjCondition } objectConditions = append(objectConditions, strings.TrimPrefix(policy[1], prefix)) } } if len(objectConditions) == 0 { return nil, errors.ErrEmptyCondition } return objectConditions, nil } // removeDuplicatePermissions Convert permissions to string as a hash to deduplicate. func removeDuplicatePermissions(permissions [][]string) [][]string { permissionsSet := make(map[string]bool) res := make([][]string, 0) for _, permission := range permissions { permissionStr := util.ArrayToString(permission) if permissionsSet[permissionStr] { continue } permissionsSet[permissionStr] = true res = append(res, permission) } return res } // GetImplicitUsersForResource return implicit user based on resource. // for example: // p, alice, data1, read // p, bob, data2, write // p, data2_admin, data2, read // p, data2_admin, data2, write // g, alice, data2_admin // GetImplicitUsersForResource("data2") will return [[bob data2 write] [alice data2 read] [alice data2 write]] // GetImplicitUsersForResource("data1") will return [[alice data1 read]] // Note: only users will be returned, roles (2nd arg in "g") will be excluded. func (e *Enforcer) GetImplicitUsersForResource(resource string) ([][]string, error) { permissions := make([][]string, 0) subjectIndex, _ := e.GetFieldIndex("p", "sub") objectIndex, _ := e.GetFieldIndex("p", "obj") rm := e.GetRoleManager() isRole := make(map[string]bool) for _, role := range e.GetAllRoles() { isRole[role] = true } for _, rule := range e.model["p"]["p"].Policy { obj := rule[objectIndex] if obj != resource { continue } sub := rule[subjectIndex] if !isRole[sub] { permissions = append(permissions, rule) } else { users, err := rm.GetUsers(sub) if err != nil { return nil, err } for _, user := range users { implicitUserRule := deepCopyPolicy(rule) implicitUserRule[subjectIndex] = user permissions = append(permissions, implicitUserRule) } } } res := removeDuplicatePermissions(permissions) return res, nil } // GetImplicitUsersForResourceByDomain return implicit user based on resource and domain. // Compared to GetImplicitUsersForResource, domain is supported func (e *Enforcer) GetImplicitUsersForResourceByDomain(resource string, domain string) ([][]string, error) { permissions := make([][]string, 0) subjectIndex, _ := e.GetFieldIndex("p", "sub") objectIndex, _ := e.GetFieldIndex("p", "obj") domIndex, _ := e.GetFieldIndex("p", "dom") rm := e.GetRoleManager() isRole := make(map[string]bool) for _, role := range e.GetAllRolesByDomain(domain) { isRole[role] = true } for _, rule := range e.model["p"]["p"].Policy { obj := rule[objectIndex] if obj != resource { continue } sub := rule[subjectIndex] if !isRole[sub] { permissions = append(permissions, rule) } else { if domain != rule[domIndex] { continue } users, err := rm.GetUsers(sub, domain) if err != nil { return nil, err } for _, user := range users { implicitUserRule := deepCopyPolicy(rule) implicitUserRule[subjectIndex] = user permissions = append(permissions, implicitUserRule) } } } res := removeDuplicatePermissions(permissions) return res, nil } golang-github-casbin-casbin-2.82.0/rbac_api_synced.go000066400000000000000000000175261456376452400225130ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin // GetRolesForUser gets the roles that a user has. func (e *SyncedEnforcer) GetRolesForUser(name string, domain ...string) ([]string, error) { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetRolesForUser(name, domain...) } // GetUsersForRole gets the users that has a role. func (e *SyncedEnforcer) GetUsersForRole(name string, domain ...string) ([]string, error) { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetUsersForRole(name, domain...) } // HasRoleForUser determines whether a user has a role. func (e *SyncedEnforcer) HasRoleForUser(name string, role string, domain ...string) (bool, error) { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.HasRoleForUser(name, role, domain...) } // AddRoleForUser adds a role for a user. // Returns false if the user already has the role (aka not affected). func (e *SyncedEnforcer) AddRoleForUser(user string, role string, domain ...string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddRoleForUser(user, role, domain...) } // AddRolesForUser adds roles for a user. // Returns false if the user already has the roles (aka not affected). func (e *SyncedEnforcer) AddRolesForUser(user string, roles []string, domain ...string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddRolesForUser(user, roles, domain...) } // DeleteRoleForUser deletes a role for a user. // Returns false if the user does not have the role (aka not affected). func (e *SyncedEnforcer) DeleteRoleForUser(user string, role string, domain ...string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.DeleteRoleForUser(user, role, domain...) } // DeleteRolesForUser deletes all roles for a user. // Returns false if the user does not have any roles (aka not affected). func (e *SyncedEnforcer) DeleteRolesForUser(user string, domain ...string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.DeleteRolesForUser(user, domain...) } // DeleteUser deletes a user. // Returns false if the user does not exist (aka not affected). func (e *SyncedEnforcer) DeleteUser(user string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.DeleteUser(user) } // DeleteRole deletes a role. // Returns false if the role does not exist (aka not affected). func (e *SyncedEnforcer) DeleteRole(role string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.DeleteRole(role) } // DeletePermission deletes a permission. // Returns false if the permission does not exist (aka not affected). func (e *SyncedEnforcer) DeletePermission(permission ...string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.DeletePermission(permission...) } // AddPermissionForUser adds a permission for a user or role. // Returns false if the user or role already has the permission (aka not affected). func (e *SyncedEnforcer) AddPermissionForUser(user string, permission ...string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddPermissionForUser(user, permission...) } // AddPermissionsForUser adds permissions for a user or role. // Returns false if the user or role already has the permissions (aka not affected). func (e *SyncedEnforcer) AddPermissionsForUser(user string, permissions ...[]string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddPermissionsForUser(user, permissions...) } // DeletePermissionForUser deletes a permission for a user or role. // Returns false if the user or role does not have the permission (aka not affected). func (e *SyncedEnforcer) DeletePermissionForUser(user string, permission ...string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.DeletePermissionForUser(user, permission...) } // DeletePermissionsForUser deletes permissions for a user or role. // Returns false if the user or role does not have any permissions (aka not affected). func (e *SyncedEnforcer) DeletePermissionsForUser(user string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.DeletePermissionsForUser(user) } // GetPermissionsForUser gets permissions for a user or role. func (e *SyncedEnforcer) GetPermissionsForUser(user string, domain ...string) ([][]string, error) { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetPermissionsForUser(user, domain...) } // GetNamedPermissionsForUser gets permissions for a user or role by named policy. func (e *SyncedEnforcer) GetNamedPermissionsForUser(ptype string, user string, domain ...string) ([][]string, error) { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetNamedPermissionsForUser(ptype, user, domain...) } // HasPermissionForUser determines whether a user has a permission. func (e *SyncedEnforcer) HasPermissionForUser(user string, permission ...string) bool { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.HasPermissionForUser(user, permission...) } // GetImplicitRolesForUser gets implicit roles that a user has. // Compared to GetRolesForUser(), this function retrieves indirect roles besides direct roles. // For example: // g, alice, role:admin // g, role:admin, role:user // // GetRolesForUser("alice") can only get: ["role:admin"]. // But GetImplicitRolesForUser("alice") will get: ["role:admin", "role:user"]. func (e *SyncedEnforcer) GetImplicitRolesForUser(name string, domain ...string) ([]string, error) { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetImplicitRolesForUser(name, domain...) } // GetImplicitPermissionsForUser gets implicit permissions for a user or role. // Compared to GetPermissionsForUser(), this function retrieves permissions for inherited roles. // For example: // p, admin, data1, read // p, alice, data2, read // g, alice, admin // // GetPermissionsForUser("alice") can only get: [["alice", "data2", "read"]]. // But GetImplicitPermissionsForUser("alice") will get: [["admin", "data1", "read"], ["alice", "data2", "read"]]. func (e *SyncedEnforcer) GetImplicitPermissionsForUser(user string, domain ...string) ([][]string, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.GetImplicitPermissionsForUser(user, domain...) } // GetNamedImplicitPermissionsForUser gets implicit permissions for a user or role by named policy. // Compared to GetNamedPermissionsForUser(), this function retrieves permissions for inherited roles. // For example: // p, admin, data1, read // p2, admin, create // g, alice, admin // // GetImplicitPermissionsForUser("alice") can only get: [["admin", "data1", "read"]], whose policy is default policy "p" // But you can specify the named policy "p2" to get: [["admin", "create"]] by GetNamedImplicitPermissionsForUser("p2","alice") func (e *SyncedEnforcer) GetNamedImplicitPermissionsForUser(ptype string, user string, domain ...string) ([][]string, error) { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetNamedImplicitPermissionsForUser(ptype, user, domain...) } // GetImplicitUsersForPermission gets implicit users for a permission. // For example: // p, admin, data1, read // p, bob, data1, read // g, alice, admin // // GetImplicitUsersForPermission("data1", "read") will get: ["alice", "bob"]. // Note: only users will be returned, roles (2nd arg in "g") will be excluded. func (e *SyncedEnforcer) GetImplicitUsersForPermission(permission ...string) ([]string, error) { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetImplicitUsersForPermission(permission...) } golang-github-casbin-casbin-2.82.0/rbac_api_test.go000066400000000000000000000604451456376452400222030ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "log" "sort" "testing" "github.com/casbin/casbin/v2/constant" "github.com/casbin/casbin/v2/errors" "github.com/casbin/casbin/v2/util" ) func testGetRoles(t *testing.T, e *Enforcer, res []string, name string, domain ...string) { t.Helper() myRes, err := e.GetRolesForUser(name, domain...) if err != nil { t.Error("Roles for ", name, " could not be fetched: ", err.Error()) } t.Log("Roles for ", name, ": ", myRes) if !util.SetEquals(res, myRes) { t.Error("Roles for ", name, ": ", myRes, ", supposed to be ", res) } } func testGetUsers(t *testing.T, e *Enforcer, res []string, name string, domain ...string) { t.Helper() myRes, err := e.GetUsersForRole(name, domain...) switch err { case nil: break case errors.ErrNameNotFound: t.Log("No name found") default: t.Error("Users for ", name, " could not be fetched: ", err.Error()) } t.Log("Users for ", name, ": ", myRes) if !util.SetEquals(res, myRes) { t.Error("Users for ", name, ": ", myRes, ", supposed to be ", res) } } func testHasRole(t *testing.T, e *Enforcer, name string, role string, res bool, domain ...string) { t.Helper() myRes, err := e.HasRoleForUser(name, role, domain...) if err != nil { t.Error("HasRoleForUser returned an error: ", err.Error()) } t.Log(name, " has role ", role, ": ", myRes) if res != myRes { t.Error(name, " has role ", role, ": ", myRes, ", supposed to be ", res) } } func TestRoleAPI(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") testGetRoles(t, e, []string{"data2_admin"}, "alice") testGetRoles(t, e, []string{}, "bob") testGetRoles(t, e, []string{}, "data2_admin") testGetRoles(t, e, []string{}, "non_exist") testHasRole(t, e, "alice", "data1_admin", false) testHasRole(t, e, "alice", "data2_admin", true) _, _ = e.AddRoleForUser("alice", "data1_admin") testGetRoles(t, e, []string{"data1_admin", "data2_admin"}, "alice") testGetRoles(t, e, []string{}, "bob") testGetRoles(t, e, []string{}, "data2_admin") _, _ = e.DeleteRoleForUser("alice", "data1_admin") testGetRoles(t, e, []string{"data2_admin"}, "alice") testGetRoles(t, e, []string{}, "bob") testGetRoles(t, e, []string{}, "data2_admin") _, _ = e.DeleteRolesForUser("alice") testGetRoles(t, e, []string{}, "alice") testGetRoles(t, e, []string{}, "bob") testGetRoles(t, e, []string{}, "data2_admin") _, _ = e.AddRoleForUser("alice", "data1_admin") _, _ = e.DeleteUser("alice") testGetRoles(t, e, []string{}, "alice") testGetRoles(t, e, []string{}, "bob") testGetRoles(t, e, []string{}, "data2_admin") _, _ = e.AddRoleForUser("alice", "data2_admin") testEnforce(t, e, "alice", "data1", "read", false) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", true) testEnforce(t, e, "alice", "data2", "write", true) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) _, _ = e.DeleteRole("data2_admin") testEnforce(t, e, "alice", "data1", "read", false) testEnforce(t, e, "alice", "data1", "write", false) testEnforce(t, e, "alice", "data2", "read", false) testEnforce(t, e, "alice", "data2", "write", false) testEnforce(t, e, "bob", "data1", "read", false) testEnforce(t, e, "bob", "data1", "write", false) testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) } func TestRoleAPI_Domains(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv") testHasRole(t, e, "alice", "admin", true, "domain1") testHasRole(t, e, "alice", "admin", false, "domain2") testGetRoles(t, e, []string{"admin"}, "alice", "domain1") testGetRoles(t, e, []string{}, "bob", "domain1") testGetRoles(t, e, []string{}, "admin", "domain1") testGetRoles(t, e, []string{}, "non_exist", "domain1") testGetRoles(t, e, []string{}, "alice", "domain2") testGetRoles(t, e, []string{"admin"}, "bob", "domain2") testGetRoles(t, e, []string{}, "admin", "domain2") testGetRoles(t, e, []string{}, "non_exist", "domain2") _, _ = e.DeleteRoleForUser("alice", "admin", "domain1") _, _ = e.AddRoleForUser("bob", "admin", "domain1") testGetRoles(t, e, []string{}, "alice", "domain1") testGetRoles(t, e, []string{"admin"}, "bob", "domain1") testGetRoles(t, e, []string{}, "admin", "domain1") testGetRoles(t, e, []string{}, "non_exist", "domain1") testGetRoles(t, e, []string{}, "alice", "domain2") testGetRoles(t, e, []string{"admin"}, "bob", "domain2") testGetRoles(t, e, []string{}, "admin", "domain2") testGetRoles(t, e, []string{}, "non_exist", "domain2") _, _ = e.AddRoleForUser("alice", "admin", "domain1") _, _ = e.DeleteRolesForUser("bob", "domain1") testGetRoles(t, e, []string{"admin"}, "alice", "domain1") testGetRoles(t, e, []string{}, "bob", "domain1") testGetRoles(t, e, []string{}, "admin", "domain1") testGetRoles(t, e, []string{}, "non_exist", "domain1") testGetRoles(t, e, []string{}, "alice", "domain2") testGetRoles(t, e, []string{"admin"}, "bob", "domain2") testGetRoles(t, e, []string{}, "admin", "domain2") testGetRoles(t, e, []string{}, "non_exist", "domain2") _, _ = e.AddRolesForUser("bob", []string{"admin", "admin1", "admin2"}, "domain1") testGetRoles(t, e, []string{"admin", "admin1", "admin2"}, "bob", "domain1") testGetPermissions(t, e, "admin", [][]string{{"admin", "domain1", "data1", "read"}, {"admin", "domain1", "data1", "write"}}, "domain1") testGetPermissions(t, e, "admin", [][]string{{"admin", "domain2", "data2", "read"}, {"admin", "domain2", "data2", "write"}}, "domain2") } func TestEnforcer_AddRolesForUser(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") _, _ = e.AddRolesForUser("alice", []string{"data1_admin", "data2_admin", "data3_admin"}) // The "alice" already has "data2_admin" , it will be return false. So "alice" just has "data2_admin". testGetRoles(t, e, []string{"data2_admin"}, "alice") // delete role _, _ = e.DeleteRoleForUser("alice", "data2_admin") _, _ = e.AddRolesForUser("alice", []string{"data1_admin", "data2_admin", "data3_admin"}) testGetRoles(t, e, []string{"data1_admin", "data2_admin", "data3_admin"}, "alice") testEnforce(t, e, "alice", "data1", "read", true) testEnforce(t, e, "alice", "data2", "read", true) testEnforce(t, e, "alice", "data2", "write", true) } func testGetPermissions(t *testing.T, e *Enforcer, name string, res [][]string, domain ...string) { t.Helper() myRes, err := e.GetPermissionsForUser(name, domain...) if err != nil { t.Error(err.Error()) } t.Log("Permissions for ", name, ": ", myRes) if !util.Array2DEquals(res, myRes) { t.Error("Permissions for ", name, ": ", myRes, ", supposed to be ", res) } } func testHasPermission(t *testing.T, e *Enforcer, name string, permission []string, res bool) { t.Helper() myRes := e.HasPermissionForUser(name, permission...) t.Log(name, " has permission ", util.ArrayToString(permission), ": ", myRes) if res != myRes { t.Error(name, " has permission ", util.ArrayToString(permission), ": ", myRes, ", supposed to be ", res) } } func testGetNamedPermissionsForUser(t *testing.T, e *Enforcer, ptype string, name string, res [][]string, domain ...string) { t.Helper() myRes, err := e.GetNamedPermissionsForUser(ptype, name, domain...) if err != nil { t.Error(err.Error()) } t.Log("Named permissions for ", name, ": ", myRes) if !util.Array2DEquals(res, myRes) { t.Error("Named permissions for ", name, ": ", myRes, ", supposed to be ", res) } } func TestPermissionAPI(t *testing.T) { e, _ := NewEnforcer("examples/basic_without_resources_model.conf", "examples/basic_without_resources_policy.csv") testEnforceWithoutUsers(t, e, "alice", "read", true) testEnforceWithoutUsers(t, e, "alice", "write", false) testEnforceWithoutUsers(t, e, "bob", "read", false) testEnforceWithoutUsers(t, e, "bob", "write", true) testGetPermissions(t, e, "alice", [][]string{{"alice", "read"}}) testGetPermissions(t, e, "bob", [][]string{{"bob", "write"}}) testHasPermission(t, e, "alice", []string{"read"}, true) testHasPermission(t, e, "alice", []string{"write"}, false) testHasPermission(t, e, "bob", []string{"read"}, false) testHasPermission(t, e, "bob", []string{"write"}, true) _, _ = e.DeletePermission("read") testEnforceWithoutUsers(t, e, "alice", "read", false) testEnforceWithoutUsers(t, e, "alice", "write", false) testEnforceWithoutUsers(t, e, "bob", "read", false) testEnforceWithoutUsers(t, e, "bob", "write", true) _, _ = e.AddPermissionForUser("bob", "read") testEnforceWithoutUsers(t, e, "alice", "read", false) testEnforceWithoutUsers(t, e, "alice", "write", false) testEnforceWithoutUsers(t, e, "bob", "read", true) testEnforceWithoutUsers(t, e, "bob", "write", true) _, _ = e.AddPermissionsForUser("jack", []string{"read"}, []string{"write"}) testEnforceWithoutUsers(t, e, "jack", "read", true) testEnforceWithoutUsers(t, e, "bob", "write", true) _, _ = e.DeletePermissionForUser("bob", "read") testEnforceWithoutUsers(t, e, "alice", "read", false) testEnforceWithoutUsers(t, e, "alice", "write", false) testEnforceWithoutUsers(t, e, "bob", "read", false) testEnforceWithoutUsers(t, e, "bob", "write", true) _, _ = e.DeletePermissionsForUser("bob") testEnforceWithoutUsers(t, e, "alice", "read", false) testEnforceWithoutUsers(t, e, "alice", "write", false) testEnforceWithoutUsers(t, e, "bob", "read", false) testEnforceWithoutUsers(t, e, "bob", "write", false) e, _ = NewEnforcer("examples/rbac_with_multiple_policy_model.conf", "examples/rbac_with_multiple_policy_policy.csv") testGetNamedPermissionsForUser(t, e, "p", "user", [][]string{{"user", "/data", "GET"}}) testGetNamedPermissionsForUser(t, e, "p2", "user", [][]string{{"user", "view"}}) } func testGetImplicitRoles(t *testing.T, e *Enforcer, name string, res []string) { t.Helper() myRes, _ := e.GetImplicitRolesForUser(name) t.Log("Implicit roles for ", name, ": ", myRes) if !util.SetEquals(res, myRes) { t.Error("Implicit roles for ", name, ": ", myRes, ", supposed to be ", res) } } func testGetImplicitRolesInDomain(t *testing.T, e *Enforcer, name string, domain string, res []string) { t.Helper() myRes, _ := e.GetImplicitRolesForUser(name, domain) t.Log("Implicit roles in domain ", domain, " for ", name, ": ", myRes) if !util.SetEquals(res, myRes) { t.Error("Implicit roles in domain ", domain, " for ", name, ": ", myRes, ", supposed to be ", res) } } func TestImplicitRoleAPI(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_with_hierarchy_policy.csv") testGetPermissions(t, e, "alice", [][]string{{"alice", "data1", "read"}}) testGetPermissions(t, e, "bob", [][]string{{"bob", "data2", "write"}}) testGetImplicitRoles(t, e, "alice", []string{"admin", "data1_admin", "data2_admin"}) testGetImplicitRoles(t, e, "bob", []string{}) e, _ = NewEnforcer("examples/rbac_with_pattern_model.conf", "examples/rbac_with_pattern_policy.csv") e.GetRoleManager().AddMatchingFunc("matcher", util.KeyMatch) e.AddNamedMatchingFunc("g2", "matcher", util.KeyMatch) //testGetImplicitRoles(t, e, "cathy", []string{"/book/1/2/3/4/5", "pen_admin", "/book/*", "book_group"}) testGetImplicitRoles(t, e, "cathy", []string{"/book/1/2/3/4/5", "pen_admin"}) testGetRoles(t, e, []string{"/book/1/2/3/4/5", "pen_admin"}, "cathy") } func testGetImplicitPermissions(t *testing.T, e *Enforcer, name string, res [][]string, domain ...string) { t.Helper() myRes, _ := e.GetImplicitPermissionsForUser(name, domain...) t.Log("Implicit permissions for ", name, ": ", myRes) if !util.Set2DEquals(res, myRes) { t.Error("Implicit permissions for ", name, ": ", myRes, ", supposed to be ", res) } } func testGetImplicitPermissionsWithDomain(t *testing.T, e *Enforcer, name string, domain string, res [][]string) { t.Helper() myRes, _ := e.GetImplicitPermissionsForUser(name, domain) t.Log("Implicit permissions for", name, "under", domain, ":", myRes) if !util.Set2DEquals(res, myRes) { t.Error("Implicit permissions for", name, "under", domain, ":", myRes, ", supposed to be ", res) } } func testGetNamedImplicitPermissions(t *testing.T, e *Enforcer, ptype string, name string, res [][]string) { t.Helper() myRes, _ := e.GetNamedImplicitPermissionsForUser(ptype, name) t.Log("Named implicit permissions for ", name, ": ", myRes) if !util.Set2DEquals(res, myRes) { t.Error("Named implicit permissions for ", name, ": ", myRes, ", supposed to be ", res) } } func TestImplicitPermissionAPI(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_with_hierarchy_policy.csv") testGetPermissions(t, e, "alice", [][]string{{"alice", "data1", "read"}}) testGetPermissions(t, e, "bob", [][]string{{"bob", "data2", "write"}}) testGetImplicitPermissions(t, e, "alice", [][]string{{"alice", "data1", "read"}, {"data1_admin", "data1", "read"}, {"data1_admin", "data1", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}}) testGetImplicitPermissions(t, e, "bob", [][]string{{"bob", "data2", "write"}}) e, _ = NewEnforcer("examples/rbac_with_domain_pattern_model.conf", "examples/rbac_with_domain_pattern_policy.csv") e.AddNamedDomainMatchingFunc("g", "KeyMatch", util.KeyMatch) testGetImplicitPermissions(t, e, "admin", [][]string{{"admin", "domain1", "data1", "read"}, {"admin", "domain1", "data1", "write"}, {"admin", "domain1", "data3", "read"}}, "domain1") _, err := e.GetImplicitPermissionsForUser("admin", "domain1", "domain2") if err == nil { t.Error("GetImplicitPermissionsForUser should not support multiple domains") } testGetImplicitPermissions(t, e, "alice", [][]string{{"admin", "domain2", "data2", "read"}, {"admin", "domain2", "data2", "write"}, {"admin", "domain2", "data3", "read"}}, "domain2") e, _ = NewEnforcer("examples/rbac_with_multiple_policy_model.conf", "examples/rbac_with_multiple_policy_policy.csv") testGetNamedImplicitPermissions(t, e, "p", "alice", [][]string{{"user", "/data", "GET"}, {"admin", "/data", "POST"}}) testGetNamedImplicitPermissions(t, e, "p2", "alice", [][]string{{"user", "view"}, {"admin", "create"}}) } func TestImplicitPermissionAPIWithDomain(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_hierarchy_with_domains_policy.csv") testGetImplicitPermissionsWithDomain(t, e, "alice", "domain1", [][]string{{"alice", "domain1", "data2", "read"}, {"role:reader", "domain1", "data1", "read"}, {"role:writer", "domain1", "data1", "write"}}) } func testGetImplicitUsers(t *testing.T, e *Enforcer, res []string, permission ...string) { t.Helper() myRes, _ := e.GetImplicitUsersForPermission(permission...) t.Log("Implicit users for permission: ", permission, ": ", myRes) sort.Strings(res) sort.Strings(myRes) if !util.ArrayEquals(res, myRes) { t.Error("Implicit users for permission: ", permission, ": ", myRes, ", supposed to be ", res) } } func TestImplicitUserAPI(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_with_hierarchy_policy.csv") testGetImplicitUsers(t, e, []string{"alice"}, "data1", "read") testGetImplicitUsers(t, e, []string{"alice"}, "data1", "write") testGetImplicitUsers(t, e, []string{"alice"}, "data2", "read") testGetImplicitUsers(t, e, []string{"alice", "bob"}, "data2", "write") e.ClearPolicy() _, _ = e.AddPolicy("admin", "data1", "read") _, _ = e.AddPolicy("bob", "data1", "read") _, _ = e.AddGroupingPolicy("alice", "admin") testGetImplicitUsers(t, e, []string{"alice", "bob"}, "data1", "read") } func testGetImplicitResourcesForUser(t *testing.T, e *Enforcer, res [][]string, user string, domain ...string) { t.Helper() myRes, _ := e.GetImplicitResourcesForUser(user, domain...) t.Log("Implicit resources for user: ", user, ": ", myRes) lessFunc := func(arr [][]string) func(int, int) bool { return func(i, j int) bool { policy1, policy2 := arr[i], arr[j] for k := range policy1 { if policy1[k] == policy2[k] { continue } return policy1[k] < policy2[k] } return true } } sort.Slice(res, lessFunc(res)) sort.Slice(myRes, lessFunc(myRes)) if !util.Array2DEquals(res, myRes) { t.Error("Implicit resources for user: ", user, ": ", myRes, ", supposed to be ", res) } } func TestGetImplicitResourcesForUser(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_pattern_model.conf", "examples/rbac_with_pattern_policy.csv") testGetImplicitResourcesForUser(t, e, [][]string{ {"alice", "/pen/1", "GET"}, {"alice", "/pen2/1", "GET"}, {"alice", "/book/:id", "GET"}, {"alice", "/book2/{id}", "GET"}, {"alice", "/book/*", "GET"}, {"alice", "book_group", "GET"}, }, "alice") testGetImplicitResourcesForUser(t, e, [][]string{ {"bob", "pen_group", "GET"}, {"bob", "/pen/:id", "GET"}, {"bob", "/pen2/{id}", "GET"}, }, "bob") testGetImplicitResourcesForUser(t, e, [][]string{ {"cathy", "pen_group", "GET"}, {"cathy", "/pen/:id", "GET"}, {"cathy", "/pen2/{id}", "GET"}, }, "cathy") } func TestImplicitUsersForRole(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_pattern_model.conf", "examples/rbac_with_pattern_policy.csv") testGetImplicitUsersForRole(t, e, "book_admin", []string{"alice"}) testGetImplicitUsersForRole(t, e, "pen_admin", []string{"cathy", "bob"}) testGetImplicitUsersForRole(t, e, "book_group", []string{"/book/*", "/book/:id", "/book2/{id}"}) testGetImplicitUsersForRole(t, e, "pen_group", []string{"/pen/:id", "/pen2/{id}"}) } func testGetImplicitUsersForRole(t *testing.T, e *Enforcer, name string, res []string) { t.Helper() myRes, _ := e.GetImplicitUsersForRole(name) t.Log("Implicit users for ", name, ": ", myRes) sort.Strings(res) sort.Strings(myRes) if !util.SetEquals(res, myRes) { t.Error("Implicit users for ", name, ": ", myRes, ", supposed to be ", res) } } func TestExplicitPriorityModify(t *testing.T) { e, _ := NewEnforcer("examples/priority_model_explicit.conf", "examples/priority_policy_explicit.csv") testEnforce(t, e, "bob", "data2", "write", true) _, err := e.AddPolicy("1", "bob", "data2", "write", "deny") if err != nil { t.Fatalf("AddPolicy: %v", err) } testEnforce(t, e, "bob", "data2", "write", false) _, err = e.DeletePermissionsForUser("bob") if err != nil { t.Fatalf("DeletePermissionForUser: %v", err) } testEnforce(t, e, "bob", "data2", "write", true) _, err = e.DeleteRole("data2_allow_group") if err != nil { t.Fatalf("DeleteRole: %v", err) } testEnforce(t, e, "bob", "data2", "write", false) } func TestCustomizedFieldIndex(t *testing.T) { e, _ := NewEnforcer("examples/priority_model_explicit_customized.conf", "examples/priority_policy_explicit_customized.csv") // Due to the customized priority token, the enforcer failed to handle the priority. testEnforce(t, e, "bob", "data2", "read", true) // set PriorityIndex and reload e.SetFieldIndex("p", constant.PriorityIndex, 0) err := e.LoadPolicy() if err != nil { t.Fatalf("LoadPolicy: %v", err) } testEnforce(t, e, "bob", "data2", "read", false) testEnforce(t, e, "bob", "data2", "write", true) _, err = e.AddPolicy("1", "data2", "write", "deny", "bob") if err != nil { t.Fatalf("AddPolicy: %v", err) } testEnforce(t, e, "bob", "data2", "write", false) // Due to the customized subject token, the enforcer will raise an error before SetFieldIndex. _, err = e.DeletePermissionsForUser("bob") if err == nil { t.Fatalf("Failed to warning SetFieldIndex") } e.SetFieldIndex("p", constant.SubjectIndex, 4) _, err = e.DeletePermissionsForUser("bob") if err != nil { t.Fatalf("DeletePermissionForUser: %v", err) } testEnforce(t, e, "bob", "data2", "write", true) _, err = e.DeleteRole("data2_allow_group") if err != nil { t.Fatalf("DeleteRole: %v", err) } testEnforce(t, e, "bob", "data2", "write", false) } func testGetAllowedObjectConditions(t *testing.T, e *Enforcer, user string, act string, prefix string, res []string, expectedErr error) { myRes, actualErr := e.GetAllowedObjectConditions(user, act, prefix) if actualErr != expectedErr { t.Error("actual Err: ", actualErr, ", supposed to be ", expectedErr) } if actualErr == nil { log.Print("Policy: ", myRes) if !util.ArrayEquals(res, myRes) { t.Error("Policy: ", myRes, ", supposed to be ", res) } } } func TestGetAllowedObjectConditions(t *testing.T) { e, _ := NewEnforcer("examples/object_conditions_model.conf", "examples/object_conditions_policy.csv") testGetAllowedObjectConditions(t, e, "alice", "read", "r.obj.", []string{"price < 25", "category_id = 2"}, nil) testGetAllowedObjectConditions(t, e, "admin", "read", "r.obj.", []string{"category_id = 2"}, nil) testGetAllowedObjectConditions(t, e, "bob", "write", "r.obj.", []string{"author = bob"}, nil) // test ErrEmptyCondition testGetAllowedObjectConditions(t, e, "alice", "write", "r.obj.", []string{}, errors.ErrEmptyCondition) testGetAllowedObjectConditions(t, e, "bob", "read", "r.obj.", []string{}, errors.ErrEmptyCondition) // test ErrObjCondition // should : e.AddPolicy("alice", "r.obj.price > 50", "read") ok, _ := e.AddPolicy("alice", "price > 50", "read") if ok { testGetAllowedObjectConditions(t, e, "alice", "read", "r.obj.", []string{}, errors.ErrObjCondition) } // test prefix e.ClearPolicy() err := e.GetRoleManager().DeleteLink("alice", "admin") if err != nil { panic(err) } ok, _ = e.AddPolicies([][]string{ {"alice", "r.book.price < 25", "read"}, {"admin", "r.book.category_id = 2", "read"}, {"bob", "r.book.author = bob", "write"}, }) if ok { testGetAllowedObjectConditions(t, e, "alice", "read", "r.book.", []string{"price < 25"}, nil) testGetAllowedObjectConditions(t, e, "admin", "read", "r.book.", []string{"category_id = 2"}, nil) testGetAllowedObjectConditions(t, e, "bob", "write", "r.book.", []string{"author = bob"}, nil) } } func testGetImplicitUsersForResource(t *testing.T, e *Enforcer, res [][]string, resource string, domain ...string) { t.Helper() myRes, err := e.GetImplicitUsersForResource(resource) if err != nil { panic(err) } if !util.Set2DEquals(res, myRes) { t.Error("Implicit users for ", resource, "in domain ", domain, " : ", myRes, ", supposed to be ", res) } else { t.Log("Implicit users for ", resource, "in domain ", domain, " : ", myRes) } } func TestGetImplicitUsersForResource(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") testGetImplicitUsersForResource(t, e, [][]string{{"alice", "data1", "read"}}, "data1") testGetImplicitUsersForResource(t, e, [][]string{{"bob", "data2", "write"}, {"alice", "data2", "read"}, {"alice", "data2", "write"}}, "data2") // test duplicate permissions _, _ = e.AddGroupingPolicy("alice", "data2_admin_2") _, _ = e.AddPolicies([][]string{{"data2_admin_2", "data2", "read"}, {"data2_admin_2", "data2", "write"}}) testGetImplicitUsersForResource(t, e, [][]string{{"bob", "data2", "write"}, {"alice", "data2", "read"}, {"alice", "data2", "write"}}, "data2") } func testGetImplicitUsersForResourceByDomain(t *testing.T, e *Enforcer, res [][]string, resource string, domain string) { t.Helper() myRes, err := e.GetImplicitUsersForResourceByDomain(resource, domain) if err != nil { panic(err) } if !util.Set2DEquals(res, myRes) { t.Error("Implicit users for ", resource, "in domain ", domain, " : ", myRes, ", supposed to be ", res) } else { t.Log("Implicit users for ", resource, "in domain ", domain, " : ", myRes) } } func TestGetImplicitUsersForResourceByDomain(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv") testGetImplicitUsersForResourceByDomain(t, e, [][]string{{"alice", "domain1", "data1", "read"}, {"alice", "domain1", "data1", "write"}}, "data1", "domain1") testGetImplicitUsersForResourceByDomain(t, e, [][]string{}, "data2", "domain1") testGetImplicitUsersForResourceByDomain(t, e, [][]string{{"bob", "domain2", "data2", "read"}, {"bob", "domain2", "data2", "write"}}, "data2", "domain2") } golang-github-casbin-casbin-2.82.0/rbac_api_with_domains.go000066400000000000000000000123371456376452400237060ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import "github.com/casbin/casbin/v2/constant" // GetUsersForRoleInDomain gets the users that has a role inside a domain. Add by Gordon func (e *Enforcer) GetUsersForRoleInDomain(name string, domain string) []string { res, _ := e.model["g"]["g"].RM.GetUsers(name, domain) return res } // GetRolesForUserInDomain gets the roles that a user has inside a domain. func (e *Enforcer) GetRolesForUserInDomain(name string, domain string) []string { res, _ := e.model["g"]["g"].RM.GetRoles(name, domain) return res } // GetPermissionsForUserInDomain gets permissions for a user or role inside a domain. func (e *Enforcer) GetPermissionsForUserInDomain(user string, domain string) [][]string { res, _ := e.GetImplicitPermissionsForUser(user, domain) return res } // AddRoleForUserInDomain adds a role for a user inside a domain. // Returns false if the user already has the role (aka not affected). func (e *Enforcer) AddRoleForUserInDomain(user string, role string, domain string) (bool, error) { return e.AddGroupingPolicy(user, role, domain) } // DeleteRoleForUserInDomain deletes a role for a user inside a domain. // Returns false if the user does not have the role (aka not affected). func (e *Enforcer) DeleteRoleForUserInDomain(user string, role string, domain string) (bool, error) { return e.RemoveGroupingPolicy(user, role, domain) } // DeleteRolesForUserInDomain deletes all roles for a user inside a domain. // Returns false if the user does not have any roles (aka not affected). func (e *Enforcer) DeleteRolesForUserInDomain(user string, domain string) (bool, error) { roles, err := e.model["g"]["g"].RM.GetRoles(user, domain) if err != nil { return false, err } var rules [][]string for _, role := range roles { rules = append(rules, []string{user, role, domain}) } return e.RemoveGroupingPolicies(rules) } // GetAllUsersByDomain would get all users associated with the domain. func (e *Enforcer) GetAllUsersByDomain(domain string) []string { m := make(map[string]struct{}) g := e.model["g"]["g"] p := e.model["p"]["p"] users := make([]string, 0) index, err := e.GetFieldIndex("p", constant.DomainIndex) if err != nil { return []string{} } getUser := func(index int, policies [][]string, domain string, m map[string]struct{}) []string { if len(policies) == 0 || len(policies[0]) <= index { return []string{} } res := make([]string, 0) for _, policy := range policies { if _, ok := m[policy[0]]; policy[index] == domain && !ok { res = append(res, policy[0]) m[policy[0]] = struct{}{} } } return res } users = append(users, getUser(2, g.Policy, domain, m)...) users = append(users, getUser(index, p.Policy, domain, m)...) return users } // DeleteAllUsersByDomain would delete all users associated with the domain. func (e *Enforcer) DeleteAllUsersByDomain(domain string) (bool, error) { g := e.model["g"]["g"] p := e.model["p"]["p"] index, err := e.GetFieldIndex("p", constant.DomainIndex) if err != nil { return false, err } getUser := func(index int, policies [][]string, domain string) [][]string { if len(policies) == 0 || len(policies[0]) <= index { return [][]string{} } res := make([][]string, 0) for _, policy := range policies { if policy[index] == domain { res = append(res, policy) } } return res } users := getUser(2, g.Policy, domain) if _, err := e.RemoveGroupingPolicies(users); err != nil { return false, err } users = getUser(index, p.Policy, domain) if _, err := e.RemovePolicies(users); err != nil { return false, err } return true, nil } // DeleteDomains would delete all associated users and roles. // It would delete all domains if parameter is not provided. func (e *Enforcer) DeleteDomains(domains ...string) (bool, error) { if len(domains) == 0 { e.ClearPolicy() return true, nil } for _, domain := range domains { if _, err := e.DeleteAllUsersByDomain(domain); err != nil { return false, err } } return true, nil } // GetAllDomains would get all domains. func (e *Enforcer) GetAllDomains() ([]string, error) { return e.model["g"]["g"].RM.GetAllDomains() } // GetAllRolesByDomain would get all roles associated with the domain. // note: Not applicable to Domains with inheritance relationship (implicit roles) func (e *Enforcer) GetAllRolesByDomain(domain string) []string { g := e.model["g"]["g"] policies := g.Policy roles := make([]string, 0) existMap := make(map[string]bool) // remove duplicates for _, policy := range policies { if policy[len(policy)-1] == domain { role := policy[len(policy)-2] if _, ok := existMap[role]; !ok { roles = append(roles, role) existMap[role] = true } } } return roles } golang-github-casbin-casbin-2.82.0/rbac_api_with_domains_synced.go000066400000000000000000000046521456376452400252540ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin // GetUsersForRoleInDomain gets the users that has a role inside a domain. Add by Gordon func (e *SyncedEnforcer) GetUsersForRoleInDomain(name string, domain string) []string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetUsersForRoleInDomain(name, domain) } // GetRolesForUserInDomain gets the roles that a user has inside a domain. func (e *SyncedEnforcer) GetRolesForUserInDomain(name string, domain string) []string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetRolesForUserInDomain(name, domain) } // GetPermissionsForUserInDomain gets permissions for a user or role inside a domain. func (e *SyncedEnforcer) GetPermissionsForUserInDomain(user string, domain string) [][]string { e.m.RLock() defer e.m.RUnlock() return e.Enforcer.GetPermissionsForUserInDomain(user, domain) } // AddRoleForUserInDomain adds a role for a user inside a domain. // Returns false if the user already has the role (aka not affected). func (e *SyncedEnforcer) AddRoleForUserInDomain(user string, role string, domain string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.AddRoleForUserInDomain(user, role, domain) } // DeleteRoleForUserInDomain deletes a role for a user inside a domain. // Returns false if the user does not have the role (aka not affected). func (e *SyncedEnforcer) DeleteRoleForUserInDomain(user string, role string, domain string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.DeleteRoleForUserInDomain(user, role, domain) } // DeleteRolesForUserInDomain deletes all roles for a user inside a domain. // Returns false if the user does not have any roles (aka not affected). func (e *SyncedEnforcer) DeleteRolesForUserInDomain(user string, domain string) (bool, error) { e.m.Lock() defer e.m.Unlock() return e.Enforcer.DeleteRolesForUserInDomain(user, domain) } golang-github-casbin-casbin-2.82.0/rbac_api_with_domains_test.go000066400000000000000000000261541456376452400247470ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package casbin import ( "sort" "testing" "github.com/casbin/casbin/v2/util" ) // testGetUsersInDomain: Add by Gordon func testGetUsersInDomain(t *testing.T, e *Enforcer, name string, domain string, res []string) { t.Helper() myRes := e.GetUsersForRoleInDomain(name, domain) t.Log("Users for ", name, " under ", domain, ": ", myRes) if !util.SetEquals(res, myRes) { t.Error("Users for ", name, " under ", domain, ": ", myRes, ", supposed to be ", res) } } func testGetRolesInDomain(t *testing.T, e *Enforcer, name string, domain string, res []string) { t.Helper() myRes := e.GetRolesForUserInDomain(name, domain) t.Log("Roles for ", name, " under ", domain, ": ", myRes) if !util.SetEquals(res, myRes) { t.Error("Roles for ", name, " under ", domain, ": ", myRes, ", supposed to be ", res) } } func TestGetImplicitRolesForDomainUser(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_hierarchy_with_domains_policy.csv") // This is only able to retrieve the first level of roles. testGetRolesInDomain(t, e, "alice", "domain1", []string{"role:global_admin"}) // Retrieve all inherit roles. It supports domains as well. testGetImplicitRolesInDomain(t, e, "alice", "domain1", []string{"role:global_admin", "role:reader", "role:writer"}) } // TestUserAPIWithDomains: Add by Gordon func TestUserAPIWithDomains(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv") testGetUsers(t, e, []string{"alice"}, "admin", "domain1") testGetUsersInDomain(t, e, "admin", "domain1", []string{"alice"}) testGetUsers(t, e, []string{}, "non_exist", "domain1") testGetUsersInDomain(t, e, "non_exist", "domain1", []string{}) testGetUsers(t, e, []string{"bob"}, "admin", "domain2") testGetUsersInDomain(t, e, "admin", "domain2", []string{"bob"}) testGetUsers(t, e, []string{}, "non_exist", "domain2") testGetUsersInDomain(t, e, "non_exist", "domain2", []string{}) _, _ = e.DeleteRoleForUserInDomain("alice", "admin", "domain1") _, _ = e.AddRoleForUserInDomain("bob", "admin", "domain1") testGetUsers(t, e, []string{"bob"}, "admin", "domain1") testGetUsersInDomain(t, e, "admin", "domain1", []string{"bob"}) testGetUsers(t, e, []string{}, "non_exist", "domain1") testGetUsersInDomain(t, e, "non_exist", "domain1", []string{}) testGetUsers(t, e, []string{"bob"}, "admin", "domain2") testGetUsersInDomain(t, e, "admin", "domain2", []string{"bob"}) testGetUsers(t, e, []string{}, "non_exist", "domain2") testGetUsersInDomain(t, e, "non_exist", "domain2", []string{}) } func TestRoleAPIWithDomains(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv") testGetRoles(t, e, []string{"admin"}, "alice", "domain1") testGetRolesInDomain(t, e, "alice", "domain1", []string{"admin"}) testGetRoles(t, e, []string{}, "bob", "domain1") testGetRolesInDomain(t, e, "bob", "domain1", []string{}) testGetRoles(t, e, []string{}, "admin", "domain1") testGetRolesInDomain(t, e, "admin", "domain1", []string{}) testGetRoles(t, e, []string{}, "non_exist", "domain1") testGetRolesInDomain(t, e, "non_exist", "domain1", []string{}) testGetRoles(t, e, []string{}, "alice", "domain2") testGetRolesInDomain(t, e, "alice", "domain2", []string{}) testGetRoles(t, e, []string{"admin"}, "bob", "domain2") testGetRolesInDomain(t, e, "bob", "domain2", []string{"admin"}) testGetRoles(t, e, []string{}, "admin", "domain2") testGetRolesInDomain(t, e, "admin", "domain2", []string{}) testGetRoles(t, e, []string{}, "non_exist", "domain2") testGetRolesInDomain(t, e, "non_exist", "domain2", []string{}) _, _ = e.DeleteRoleForUserInDomain("alice", "admin", "domain1") _, _ = e.AddRoleForUserInDomain("bob", "admin", "domain1") testGetRoles(t, e, []string{}, "alice", "domain1") testGetRolesInDomain(t, e, "alice", "domain1", []string{}) testGetRoles(t, e, []string{"admin"}, "bob", "domain1") testGetRolesInDomain(t, e, "bob", "domain1", []string{"admin"}) testGetRoles(t, e, []string{}, "admin", "domain1") testGetRolesInDomain(t, e, "admin", "domain1", []string{}) testGetRoles(t, e, []string{}, "non_exist", "domain1") testGetRolesInDomain(t, e, "non_exist", "domain1", []string{}) testGetRoles(t, e, []string{}, "alice", "domain2") testGetRolesInDomain(t, e, "alice", "domain2", []string{}) testGetRoles(t, e, []string{"admin"}, "bob", "domain2") testGetRolesInDomain(t, e, "bob", "domain2", []string{"admin"}) testGetRoles(t, e, []string{}, "admin", "domain2") testGetRolesInDomain(t, e, "admin", "domain2", []string{}) testGetRoles(t, e, []string{}, "non_exist", "domain2") testGetRolesInDomain(t, e, "non_exist", "domain2", []string{}) _, _ = e.AddRoleForUserInDomain("alice", "admin", "domain1") _, _ = e.DeleteRolesForUserInDomain("bob", "domain1") testGetRoles(t, e, []string{"admin"}, "alice", "domain1") testGetRolesInDomain(t, e, "alice", "domain1", []string{"admin"}) testGetRoles(t, e, []string{}, "bob", "domain1") testGetRolesInDomain(t, e, "bob", "domain1", []string{}) testGetRoles(t, e, []string{}, "admin", "domain1") testGetRolesInDomain(t, e, "admin", "domain1", []string{}) testGetRoles(t, e, []string{}, "non_exist", "domain1") testGetRolesInDomain(t, e, "non_exist", "domain1", []string{}) testGetRoles(t, e, []string{}, "alice", "domain2") testGetRolesInDomain(t, e, "alice", "domain2", []string{}) testGetRoles(t, e, []string{"admin"}, "bob", "domain2") testGetRolesInDomain(t, e, "bob", "domain2", []string{"admin"}) testGetRoles(t, e, []string{}, "admin", "domain2") testGetRolesInDomain(t, e, "admin", "domain2", []string{}) testGetRoles(t, e, []string{}, "non_exist", "domain2") testGetRolesInDomain(t, e, "non_exist", "domain2", []string{}) } func testGetPermissionsInDomain(t *testing.T, e *Enforcer, name string, domain string, res [][]string) { t.Helper() myRes := e.GetPermissionsForUserInDomain(name, domain) t.Log("Permissions for ", name, " under ", domain, ": ", myRes) if !util.Array2DEquals(res, myRes) { t.Error("Permissions for ", name, " under ", domain, ": ", myRes, ", supposed to be ", res) } } func TestPermissionAPIInDomain(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv") testGetPermissionsInDomain(t, e, "alice", "domain1", [][]string{{"admin", "domain1", "data1", "read"}, {"admin", "domain1", "data1", "write"}}) testGetPermissionsInDomain(t, e, "bob", "domain1", [][]string{}) testGetPermissionsInDomain(t, e, "admin", "domain1", [][]string{{"admin", "domain1", "data1", "read"}, {"admin", "domain1", "data1", "write"}}) testGetPermissionsInDomain(t, e, "non_exist", "domain1", [][]string{}) testGetPermissionsInDomain(t, e, "alice", "domain2", [][]string{}) testGetPermissionsInDomain(t, e, "bob", "domain2", [][]string{{"admin", "domain2", "data2", "read"}, {"admin", "domain2", "data2", "write"}}) testGetPermissionsInDomain(t, e, "admin", "domain2", [][]string{{"admin", "domain2", "data2", "read"}, {"admin", "domain2", "data2", "write"}}) testGetPermissionsInDomain(t, e, "non_exist", "domain2", [][]string{}) } func testGetDomainsForUser(t *testing.T, e *Enforcer, res []string, user string) { t.Helper() myRes, _ := e.GetDomainsForUser(user) sort.Strings(myRes) sort.Strings(res) if !util.SetEquals(res, myRes) { t.Error("domains for user: ", user, ": ", myRes, ", supposed to be ", res) } } func TestGetDomainsForUser(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy2.csv") testGetDomainsForUser(t, e, []string{"domain1", "domain2"}, "alice") testGetDomainsForUser(t, e, []string{"domain2", "domain3"}, "bob") testGetDomainsForUser(t, e, []string{"domain3"}, "user") } func testGetAllUsersByDomain(t *testing.T, e *Enforcer, domain string, expected []string) { if !util.SetEquals(e.GetAllUsersByDomain(domain), expected) { t.Errorf("users in %s: %v, supposed to be %v\n", domain, e.GetAllUsersByDomain(domain), expected) } } func TestGetAllUsersByDomain(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv") testGetAllUsersByDomain(t, e, "domain1", []string{"alice", "admin"}) testGetAllUsersByDomain(t, e, "domain2", []string{"bob", "admin"}) } func testDeleteAllUsersByDomain(t *testing.T, domain string, expectedPolicy, expectedGroupingPolicy [][]string) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv") _, _ = e.DeleteAllUsersByDomain(domain) if !util.Array2DEquals(e.GetPolicy(), expectedPolicy) { t.Errorf("policy in %s: %v, supposed to be %v\n", domain, e.GetPolicy(), expectedPolicy) } if !util.Array2DEquals(e.GetGroupingPolicy(), expectedGroupingPolicy) { t.Errorf("grouping policy in %s: %v, supposed to be %v\n", domain, e.GetGroupingPolicy(), expectedGroupingPolicy) } } func TestDeleteAllUsersByDomain(t *testing.T) { testDeleteAllUsersByDomain(t, "domain1", [][]string{ {"admin", "domain2", "data2", "read"}, {"admin", "domain2", "data2", "write"}, }, [][]string{ {"bob", "admin", "domain2"}, }) testDeleteAllUsersByDomain(t, "domain2", [][]string{ {"admin", "domain1", "data1", "read"}, {"admin", "domain1", "data1", "write"}, }, [][]string{ {"alice", "admin", "domain1"}, }) } // testGetAllDomains tests GetAllDomains() func testGetAllDomains(t *testing.T, e *Enforcer, res []string) { t.Helper() myRes, _ := e.GetAllDomains() sort.Strings(myRes) sort.Strings(res) if !util.ArrayEquals(res, myRes) { t.Error("domains: ", myRes, ", supposed to be ", res) } } func TestGetAllDomains(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv") testGetAllDomains(t, e, []string{"domain1", "domain2"}) } func testGetAllRolesByDomain(t *testing.T, e *Enforcer, domain string, expected []string) { if !util.SetEquals(e.GetAllRolesByDomain(domain), expected) { t.Errorf("roles in %s: %v, supposed to be %v\n", domain, e.GetAllRolesByDomain(domain), expected) } } func TestGetAllRolesByDomain(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv") testGetAllRolesByDomain(t, e, "domain1", []string{"admin"}) testGetAllRolesByDomain(t, e, "domain2", []string{"admin"}) e, _ = NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy2.csv") testGetAllRolesByDomain(t, e, "domain1", []string{"admin"}) testGetAllRolesByDomain(t, e, "domain2", []string{"admin"}) testGetAllRolesByDomain(t, e, "domain3", []string{"user"}) } golang-github-casbin-casbin-2.82.0/role_manager_b_test.go000066400000000000000000000121071456376452400233670ustar00rootroot00000000000000package casbin import ( "fmt" "testing" "github.com/casbin/casbin/v2/util" ) func BenchmarkRoleManagerSmall(b *testing.B) { e, _ := NewEnforcer("examples/rbac_model.conf", false) // Do not rebuild the role inheritance relations for every AddGroupingPolicy() call. e.EnableAutoBuildRoleLinks(false) // 100 roles, 10 resources. pPolicies := make([][]string, 0) for i := 0; i < 100; i++ { pPolicies = append(pPolicies, []string{fmt.Sprintf("group%d", i), fmt.Sprintf("data%d", i/10), "read"}) } _, err := e.AddPolicies(pPolicies) if err != nil { b.Fatal(err) } // 1000 users. gPolicies := make([][]string, 0) for i := 0; i < 1000; i++ { gPolicies = append(gPolicies, []string{fmt.Sprintf("user%d", i), fmt.Sprintf("group%d", i/10)}) } _, err = e.AddGroupingPolicies(gPolicies) if err != nil { b.Fatal(err) } rm := e.GetRoleManager() b.ResetTimer() for i := 0; i < b.N; i++ { for j := 0; j < 100; j++ { _, _ = rm.HasLink("user501", fmt.Sprintf("group%d", j)) } } } func BenchmarkRoleManagerMedium(b *testing.B) { e, _ := NewEnforcer("examples/rbac_model.conf", false) // Do not rebuild the role inheritance relations for every AddGroupingPolicy() call. e.EnableAutoBuildRoleLinks(false) // 1000 roles, 100 resources. pPolicies := make([][]string, 0) for i := 0; i < 1000; i++ { pPolicies = append(pPolicies, []string{fmt.Sprintf("group%d", i), fmt.Sprintf("data%d", i/10), "read"}) } _, err := e.AddPolicies(pPolicies) if err != nil { b.Fatal(err) } // 10000 users. gPolicies := make([][]string, 0) for i := 0; i < 10000; i++ { gPolicies = append(gPolicies, []string{fmt.Sprintf("user%d", i), fmt.Sprintf("group%d", i/10)}) } _, err = e.AddGroupingPolicies(gPolicies) if err != nil { b.Fatal(err) } err = e.BuildRoleLinks() if err != nil { b.Fatal(err) } rm := e.GetRoleManager() b.ResetTimer() for i := 0; i < b.N; i++ { for j := 0; j < 1000; j++ { _, _ = rm.HasLink("user501", fmt.Sprintf("group%d", j)) } } } func BenchmarkRoleManagerLarge(b *testing.B) { e, _ := NewEnforcer("examples/rbac_model.conf", false) // 10000 roles, 1000 resources. pPolicies := make([][]string, 0) for i := 0; i < 10000; i++ { pPolicies = append(pPolicies, []string{fmt.Sprintf("group%d", i), fmt.Sprintf("data%d", i/10), "read"}) } _, err := e.AddPolicies(pPolicies) if err != nil { b.Fatal(err) } // 100000 users. gPolicies := make([][]string, 0) for i := 0; i < 100000; i++ { gPolicies = append(gPolicies, []string{fmt.Sprintf("user%d", i), fmt.Sprintf("group%d", i/10)}) } _, err = e.AddGroupingPolicies(gPolicies) if err != nil { b.Fatal(err) } rm := e.GetRoleManager() b.ResetTimer() for i := 0; i < b.N; i++ { for j := 0; j < 10000; j++ { _, _ = rm.HasLink("user501", fmt.Sprintf("group%d", j)) } } } func BenchmarkBuildRoleLinksWithPatternLarge(b *testing.B) { e, _ := NewEnforcer("examples/performance/rbac_with_pattern_large_scale_model.conf", "examples/performance/rbac_with_pattern_large_scale_policy.csv") e.AddNamedMatchingFunc("g", "", util.KeyMatch4) b.ResetTimer() for i := 0; i < b.N; i++ { _ = e.BuildRoleLinks() } } func BenchmarkBuildRoleLinksWithDomainPatternLarge(b *testing.B) { e, _ := NewEnforcer("examples/performance/rbac_with_pattern_large_scale_model.conf", "examples/performance/rbac_with_pattern_large_scale_policy.csv") e.AddNamedDomainMatchingFunc("g", "", util.KeyMatch4) b.ResetTimer() for i := 0; i < b.N; i++ { _ = e.BuildRoleLinks() } } func BenchmarkBuildRoleLinksWithPatternAndDomainPatternLarge(b *testing.B) { e, _ := NewEnforcer("examples/performance/rbac_with_pattern_large_scale_model.conf", "examples/performance/rbac_with_pattern_large_scale_policy.csv") e.AddNamedMatchingFunc("g", "", util.KeyMatch4) e.AddNamedDomainMatchingFunc("g", "", util.KeyMatch4) b.ResetTimer() for i := 0; i < b.N; i++ { _ = e.BuildRoleLinks() } } func BenchmarkHasLinkWithPatternLarge(b *testing.B) { e, _ := NewEnforcer("examples/performance/rbac_with_pattern_large_scale_model.conf", "examples/performance/rbac_with_pattern_large_scale_policy.csv") e.AddNamedMatchingFunc("g", "", util.KeyMatch4) rm := e.rmMap["g"] b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = rm.HasLink("staffUser1001", "staff001", "/orgs/1/sites/site001") } } func BenchmarkHasLinkWithDomainPatternLarge(b *testing.B) { e, _ := NewEnforcer("examples/performance/rbac_with_pattern_large_scale_model.conf", "examples/performance/rbac_with_pattern_large_scale_policy.csv") e.AddNamedDomainMatchingFunc("g", "", util.KeyMatch4) rm := e.rmMap["g"] b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = rm.HasLink("staffUser1001", "staff001", "/orgs/1/sites/site001") } } func BenchmarkHasLinkWithPatternAndDomainPatternLarge(b *testing.B) { e, _ := NewEnforcer("examples/performance/rbac_with_pattern_large_scale_model.conf", "examples/performance/rbac_with_pattern_large_scale_policy.csv") e.AddNamedMatchingFunc("g", "", util.KeyMatch4) e.AddNamedDomainMatchingFunc("g", "", util.KeyMatch4) rm := e.rmMap["g"] b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = rm.HasLink("staffUser1001", "staff001", "/orgs/1/sites/site001") } } golang-github-casbin-casbin-2.82.0/util/000077500000000000000000000000001456376452400200215ustar00rootroot00000000000000golang-github-casbin-casbin-2.82.0/util/builtin_operators.go000066400000000000000000000317671456376452400241320ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package util import ( "errors" "fmt" "net" "path" "regexp" "strings" "sync" "time" "github.com/casbin/casbin/v2/rbac" "github.com/casbin/govaluate" ) var ( keyMatch4Re *regexp.Regexp = regexp.MustCompile(`{([^/]+)}`) ) // validate the variadic parameter size and type as string func validateVariadicArgs(expectedLen int, args ...interface{}) error { if len(args) != expectedLen { return fmt.Errorf("expected %d arguments, but got %d", expectedLen, len(args)) } for _, p := range args { _, ok := p.(string) if !ok { return errors.New("argument must be a string") } } return nil } // validate the variadic string parameter size func validateVariadicStringArgs(expectedLen int, args ...string) error { if len(args) != expectedLen { return fmt.Errorf("expected %d arguments, but got %d", expectedLen, len(args)) } return nil } // KeyMatch determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *. // For example, "/foo/bar" matches "/foo/*" func KeyMatch(key1 string, key2 string) bool { i := strings.Index(key2, "*") if i == -1 { return key1 == key2 } if len(key1) > i { return key1[:i] == key2[:i] } return key1 == key2[:i] } // KeyMatchFunc is the wrapper for KeyMatch. func KeyMatchFunc(args ...interface{}) (interface{}, error) { if err := validateVariadicArgs(2, args...); err != nil { return false, fmt.Errorf("%s: %s", "keyMatch", err) } name1 := args[0].(string) name2 := args[1].(string) return bool(KeyMatch(name1, name2)), nil } // KeyGet returns the matched part // For example, "/foo/bar/foo" matches "/foo/*" // "bar/foo" will been returned func KeyGet(key1, key2 string) string { i := strings.Index(key2, "*") if i == -1 { return "" } if len(key1) > i { if key1[:i] == key2[:i] { return key1[i:] } } return "" } // KeyGetFunc is the wrapper for KeyGet func KeyGetFunc(args ...interface{}) (interface{}, error) { if err := validateVariadicArgs(2, args...); err != nil { return false, fmt.Errorf("%s: %s", "keyGet", err) } name1 := args[0].(string) name2 := args[1].(string) return KeyGet(name1, name2), nil } // KeyMatch2 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *. // For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/:resource" func KeyMatch2(key1 string, key2 string) bool { key2 = strings.Replace(key2, "/*", "/.*", -1) re := regexp.MustCompile(`:[^/]+`) key2 = re.ReplaceAllString(key2, "$1[^/]+$2") return RegexMatch(key1, "^"+key2+"$") } // KeyMatch2Func is the wrapper for KeyMatch2. func KeyMatch2Func(args ...interface{}) (interface{}, error) { if err := validateVariadicArgs(2, args...); err != nil { return false, fmt.Errorf("%s: %s", "keyMatch2", err) } name1 := args[0].(string) name2 := args[1].(string) return bool(KeyMatch2(name1, name2)), nil } // KeyGet2 returns value matched pattern // For example, "/resource1" matches "/:resource" // if the pathVar == "resource", then "resource1" will be returned func KeyGet2(key1, key2 string, pathVar string) string { key2 = strings.Replace(key2, "/*", "/.*", -1) re := regexp.MustCompile(`:[^/]+`) keys := re.FindAllString(key2, -1) key2 = re.ReplaceAllString(key2, "$1([^/]+)$2") key2 = "^" + key2 + "$" re2 := regexp.MustCompile(key2) values := re2.FindAllStringSubmatch(key1, -1) if len(values) == 0 { return "" } for i, key := range keys { if pathVar == key[1:] { return values[0][i+1] } } return "" } // KeyGet2Func is the wrapper for KeyGet2 func KeyGet2Func(args ...interface{}) (interface{}, error) { if err := validateVariadicArgs(3, args...); err != nil { return false, fmt.Errorf("%s: %s", "keyGet2", err) } name1 := args[0].(string) name2 := args[1].(string) key := args[2].(string) return KeyGet2(name1, name2, key), nil } // KeyMatch3 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *. // For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/{resource}" func KeyMatch3(key1 string, key2 string) bool { key2 = strings.Replace(key2, "/*", "/.*", -1) re := regexp.MustCompile(`\{[^/]+\}`) key2 = re.ReplaceAllString(key2, "$1[^/]+$2") return RegexMatch(key1, "^"+key2+"$") } // KeyMatch3Func is the wrapper for KeyMatch3. func KeyMatch3Func(args ...interface{}) (interface{}, error) { if err := validateVariadicArgs(2, args...); err != nil { return false, fmt.Errorf("%s: %s", "keyMatch3", err) } name1 := args[0].(string) name2 := args[1].(string) return bool(KeyMatch3(name1, name2)), nil } // KeyGet3 returns value matched pattern // For example, "project/proj_project1_admin/" matches "project/proj_{project}_admin/" // if the pathVar == "project", then "project1" will be returned func KeyGet3(key1, key2 string, pathVar string) string { key2 = strings.Replace(key2, "/*", "/.*", -1) re := regexp.MustCompile(`\{[^/]+?\}`) // non-greedy match of `{...}` to support multiple {} in `/.../` keys := re.FindAllString(key2, -1) key2 = re.ReplaceAllString(key2, "$1([^/]+?)$2") key2 = "^" + key2 + "$" re2 := regexp.MustCompile(key2) values := re2.FindAllStringSubmatch(key1, -1) if len(values) == 0 { return "" } for i, key := range keys { if pathVar == key[1:len(key)-1] { return values[0][i+1] } } return "" } // KeyGet3Func is the wrapper for KeyGet3 func KeyGet3Func(args ...interface{}) (interface{}, error) { if err := validateVariadicArgs(3, args...); err != nil { return false, fmt.Errorf("%s: %s", "keyGet3", err) } name1 := args[0].(string) name2 := args[1].(string) key := args[2].(string) return KeyGet3(name1, name2, key), nil } // KeyMatch4 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *. // Besides what KeyMatch3 does, KeyMatch4 can also match repeated patterns: // "/parent/123/child/123" matches "/parent/{id}/child/{id}" // "/parent/123/child/456" does not match "/parent/{id}/child/{id}" // But KeyMatch3 will match both. func KeyMatch4(key1 string, key2 string) bool { key2 = strings.Replace(key2, "/*", "/.*", -1) tokens := []string{} re := keyMatch4Re key2 = re.ReplaceAllStringFunc(key2, func(s string) string { tokens = append(tokens, s[1:len(s)-1]) return "([^/]+)" }) re = regexp.MustCompile("^" + key2 + "$") matches := re.FindStringSubmatch(key1) if matches == nil { return false } matches = matches[1:] if len(tokens) != len(matches) { panic(errors.New("KeyMatch4: number of tokens is not equal to number of values")) } values := map[string]string{} for key, token := range tokens { if _, ok := values[token]; !ok { values[token] = matches[key] } if values[token] != matches[key] { return false } } return true } // KeyMatch4Func is the wrapper for KeyMatch4. func KeyMatch4Func(args ...interface{}) (interface{}, error) { if err := validateVariadicArgs(2, args...); err != nil { return false, fmt.Errorf("%s: %s", "keyMatch4", err) } name1 := args[0].(string) name2 := args[1].(string) return bool(KeyMatch4(name1, name2)), nil } // KeyMatch5 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a * // For example, // - "/foo/bar?status=1&type=2" matches "/foo/bar" // - "/parent/child1" and "/parent/child1" matches "/parent/*" // - "/parent/child1?status=1" matches "/parent/*" func KeyMatch5(key1 string, key2 string) bool { i := strings.Index(key1, "?") if i != -1 { key1 = key1[:i] } key2 = strings.Replace(key2, "/*", "/.*", -1) re := regexp.MustCompile(`\{[^/]+\}`) key2 = re.ReplaceAllString(key2, "$1[^/]+$2") return RegexMatch(key1, "^"+key2+"$") } // KeyMatch5Func is the wrapper for KeyMatch5. func KeyMatch5Func(args ...interface{}) (interface{}, error) { if err := validateVariadicArgs(2, args...); err != nil { return false, fmt.Errorf("%s: %s", "keyMatch5", err) } name1 := args[0].(string) name2 := args[1].(string) return bool(KeyMatch5(name1, name2)), nil } // RegexMatch determines whether key1 matches the pattern of key2 in regular expression. func RegexMatch(key1 string, key2 string) bool { res, err := regexp.MatchString(key2, key1) if err != nil { panic(err) } return res } // RegexMatchFunc is the wrapper for RegexMatch. func RegexMatchFunc(args ...interface{}) (interface{}, error) { if err := validateVariadicArgs(2, args...); err != nil { return false, fmt.Errorf("%s: %s", "regexMatch", err) } name1 := args[0].(string) name2 := args[1].(string) return bool(RegexMatch(name1, name2)), nil } // IPMatch determines whether IP address ip1 matches the pattern of IP address ip2, ip2 can be an IP address or a CIDR pattern. // For example, "192.168.2.123" matches "192.168.2.0/24" func IPMatch(ip1 string, ip2 string) bool { objIP1 := net.ParseIP(ip1) if objIP1 == nil { panic("invalid argument: ip1 in IPMatch() function is not an IP address.") } _, cidr, err := net.ParseCIDR(ip2) if err != nil { objIP2 := net.ParseIP(ip2) if objIP2 == nil { panic("invalid argument: ip2 in IPMatch() function is neither an IP address nor a CIDR.") } return objIP1.Equal(objIP2) } return cidr.Contains(objIP1) } // IPMatchFunc is the wrapper for IPMatch. func IPMatchFunc(args ...interface{}) (interface{}, error) { if err := validateVariadicArgs(2, args...); err != nil { return false, fmt.Errorf("%s: %s", "ipMatch", err) } ip1 := args[0].(string) ip2 := args[1].(string) return bool(IPMatch(ip1, ip2)), nil } // GlobMatch determines whether key1 matches the pattern of key2 using glob pattern func GlobMatch(key1 string, key2 string) (bool, error) { return path.Match(key2, key1) } // GlobMatchFunc is the wrapper for GlobMatch. func GlobMatchFunc(args ...interface{}) (interface{}, error) { if err := validateVariadicArgs(2, args...); err != nil { return false, fmt.Errorf("%s: %s", "globMatch", err) } name1 := args[0].(string) name2 := args[1].(string) return GlobMatch(name1, name2) } // GenerateGFunction is the factory method of the g(_, _[, _]) function. func GenerateGFunction(rm rbac.RoleManager) govaluate.ExpressionFunction { memorized := sync.Map{} return func(args ...interface{}) (interface{}, error) { // Like all our other govaluate functions, all args are strings. // Allocate and generate a cache key from the arguments... total := len(args) for _, a := range args { aStr := a.(string) total += len(aStr) } builder := strings.Builder{} builder.Grow(total) for _, arg := range args { builder.WriteByte(0) builder.WriteString(arg.(string)) } key := builder.String() // ...and see if we've already calculated this. v, found := memorized.Load(key) if found { return v, nil } // If not, do the calculation. // There are guaranteed to be exactly 2 or 3 arguments. name1, name2 := args[0].(string), args[1].(string) if rm == nil { v = name1 == name2 } else if len(args) == 2 { v, _ = rm.HasLink(name1, name2) } else { domain := args[2].(string) v, _ = rm.HasLink(name1, name2, domain) } memorized.Store(key, v) return v, nil } } // GenerateConditionalGFunction is the factory method of the g(_, _[, _]) function with conditions. func GenerateConditionalGFunction(crm rbac.ConditionalRoleManager) govaluate.ExpressionFunction { return func(args ...interface{}) (interface{}, error) { // Like all our other govaluate functions, all args are strings. var hasLink bool name1, name2 := args[0].(string), args[1].(string) if crm == nil { hasLink = name1 == name2 } else if len(args) == 2 { hasLink, _ = crm.HasLink(name1, name2) } else { domain := args[2].(string) hasLink, _ = crm.HasLink(name1, name2, domain) } return hasLink, nil } } // builtin LinkConditionFunc // TimeMatchFunc is the wrapper for TimeMatch. func TimeMatchFunc(args ...string) (bool, error) { if err := validateVariadicStringArgs(2, args...); err != nil { return false, fmt.Errorf("%s: %s", "TimeMatch", err) } return TimeMatch(args[0], args[1]) } // TimeMatch determines whether the current time is between startTime and endTime. // You can use "_" to indicate that the parameter is ignored func TimeMatch(startTime, endTime string) (bool, error) { now := time.Now() if startTime != "_" { if start, err := time.Parse("2006-01-02 15:04:05", startTime); err != nil { return false, err } else if !now.After(start) { return false, nil } } if endTime != "_" { if end, err := time.Parse("2006-01-02 15:04:05", endTime); err != nil { return false, err } else if !now.Before(end) { return false, nil } } return true, nil } golang-github-casbin-casbin-2.82.0/util/builtin_operators_test.go000066400000000000000000000555211456376452400251630ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package util import ( "testing" ) func testKeyMatch(t *testing.T, key1 string, key2 string, res bool) { t.Helper() myRes := KeyMatch(key1, key2) t.Logf("%s < %s: %t", key1, key2, myRes) if myRes != res { t.Errorf("%s < %s: %t, supposed to be %t", key1, key2, !res, res) } } func TestKeyMatch(t *testing.T) { testKeyMatch(t, "/foo", "/foo", true) testKeyMatch(t, "/foo", "/foo*", true) testKeyMatch(t, "/foo", "/foo/*", false) testKeyMatch(t, "/foo/bar", "/foo", false) testKeyMatch(t, "/foo/bar", "/foo*", true) testKeyMatch(t, "/foo/bar", "/foo/*", true) testKeyMatch(t, "/foobar", "/foo", false) testKeyMatch(t, "/foobar", "/foo*", true) testKeyMatch(t, "/foobar", "/foo/*", false) } func testKeyGet(t *testing.T, key1 string, key2 string, res string) { t.Helper() myRes := KeyGet(key1, key2) t.Logf(`%s < %s: "%s"`, key1, key2, myRes) if myRes != res { t.Errorf(`%s < %s: "%s", supposed to be "%s"`, key1, key2, myRes, res) } } func TestKeyGet(t *testing.T) { testKeyGet(t, "/foo", "/foo", "") testKeyGet(t, "/foo", "/foo*", "") testKeyGet(t, "/foo", "/foo/*", "") testKeyGet(t, "/foo/bar", "/foo", "") testKeyGet(t, "/foo/bar", "/foo*", "/bar") testKeyGet(t, "/foo/bar", "/foo/*", "bar") testKeyGet(t, "/foobar", "/foo", "") testKeyGet(t, "/foobar", "/foo*", "bar") testKeyGet(t, "/foobar", "/foo/*", "") } func testKeyMatch2(t *testing.T, key1 string, key2 string, res bool) { t.Helper() myRes := KeyMatch2(key1, key2) t.Logf("%s < %s: %t", key1, key2, myRes) if myRes != res { t.Errorf("%s < %s: %t, supposed to be %t", key1, key2, !res, res) } } func testGlobMatch(t *testing.T, key1 string, key2 string, res bool) { t.Helper() myRes, err := GlobMatch(key1, key2) if err != nil { panic(err) } t.Logf("%s < %s: %t", key1, key2, myRes) if myRes != res { t.Errorf("%s < %s: %t, supposed to be %t", key1, key2, !res, res) } } func TestKeyMatch2(t *testing.T) { testKeyMatch2(t, "/foo", "/foo", true) testKeyMatch2(t, "/foo", "/foo*", true) testKeyMatch2(t, "/foo", "/foo/*", false) testKeyMatch2(t, "/foo/bar", "/foo", false) testKeyMatch2(t, "/foo/bar", "/foo*", false) // different with KeyMatch. testKeyMatch2(t, "/foo/bar", "/foo/*", true) testKeyMatch2(t, "/foobar", "/foo", false) testKeyMatch2(t, "/foobar", "/foo*", false) // different with KeyMatch. testKeyMatch2(t, "/foobar", "/foo/*", false) testKeyMatch2(t, "/", "/:resource", false) testKeyMatch2(t, "/resource1", "/:resource", true) testKeyMatch2(t, "/myid", "/:id/using/:resId", false) testKeyMatch2(t, "/myid/using/myresid", "/:id/using/:resId", true) testKeyMatch2(t, "/proxy/myid", "/proxy/:id/*", false) testKeyMatch2(t, "/proxy/myid/", "/proxy/:id/*", true) testKeyMatch2(t, "/proxy/myid/res", "/proxy/:id/*", true) testKeyMatch2(t, "/proxy/myid/res/res2", "/proxy/:id/*", true) testKeyMatch2(t, "/proxy/myid/res/res2/res3", "/proxy/:id/*", true) testKeyMatch2(t, "/proxy/", "/proxy/:id/*", false) testKeyMatch2(t, "/alice", "/:id", true) testKeyMatch2(t, "/alice/all", "/:id/all", true) testKeyMatch2(t, "/alice", "/:id/all", false) testKeyMatch2(t, "/alice/all", "/:id", false) testKeyMatch2(t, "/alice/all", "/:/all", false) } func testKeyGet2(t *testing.T, key1 string, key2 string, pathVar string, res string) { t.Helper() myRes := KeyGet2(key1, key2, pathVar) t.Logf(`%s < %s: %s = "%s"`, key1, key2, pathVar, myRes) if myRes != res { t.Errorf(`%s < %s: %s = "%s" supposed to be "%s"`, key1, key2, pathVar, myRes, res) } } func TestKeyGet2(t *testing.T) { testKeyGet2(t, "/foo", "/foo", "id", "") testKeyGet2(t, "/foo", "/foo*", "id", "") testKeyGet2(t, "/foo", "/foo/*", "id", "") testKeyGet2(t, "/foo/bar", "/foo", "id", "") testKeyGet2(t, "/foo/bar", "/foo*", "id", "") testKeyGet2(t, "/foo/bar", "/foo/*", "id", "") testKeyGet2(t, "/foobar", "/foo", "id", "") testKeyGet2(t, "/foobar", "/foo*", "id", "") testKeyGet2(t, "/foobar", "/foo/*", "id", "") testKeyGet2(t, "/", "/:resource", "resource", "") testKeyGet2(t, "/resource1", "/:resource", "resource", "resource1") testKeyGet2(t, "/myid", "/:id/using/:resId", "id", "") testKeyGet2(t, "/myid/using/myresid", "/:id/using/:resId", "id", "myid") testKeyGet2(t, "/myid/using/myresid", "/:id/using/:resId", "resId", "myresid") testKeyGet2(t, "/proxy/myid", "/proxy/:id/*", "id", "") testKeyGet2(t, "/proxy/myid/", "/proxy/:id/*", "id", "myid") testKeyGet2(t, "/proxy/myid/res", "/proxy/:id/*", "id", "myid") testKeyGet2(t, "/proxy/myid/res/res2", "/proxy/:id/*", "id", "myid") testKeyGet2(t, "/proxy/myid/res/res2/res3", "/proxy/:id/*", "id", "myid") testKeyGet2(t, "/proxy/myid/res/res2/res3", "/proxy/:id/res/*", "id", "myid") testKeyGet2(t, "/proxy/", "/proxy/:id/*", "id", "") testKeyGet2(t, "/alice", "/:id", "id", "alice") testKeyGet2(t, "/alice/all", "/:id/all", "id", "alice") testKeyGet2(t, "/alice", "/:id/all", "id", "") testKeyGet2(t, "/alice/all", "/:id", "id", "") testKeyGet2(t, "/alice/all", "/:/all", "", "") } func testKeyMatch3(t *testing.T, key1 string, key2 string, res bool) { t.Helper() myRes := KeyMatch3(key1, key2) t.Logf("%s < %s: %t", key1, key2, myRes) if myRes != res { t.Errorf("%s < %s: %t, supposed to be %t", key1, key2, !res, res) } } func TestKeyMatch3(t *testing.T) { // keyMatch3() is similar with KeyMatch2(), except using "/proxy/{id}" instead of "/proxy/:id". testKeyMatch3(t, "/foo", "/foo", true) testKeyMatch3(t, "/foo", "/foo*", true) testKeyMatch3(t, "/foo", "/foo/*", false) testKeyMatch3(t, "/foo/bar", "/foo", false) testKeyMatch3(t, "/foo/bar", "/foo*", false) testKeyMatch3(t, "/foo/bar", "/foo/*", true) testKeyMatch3(t, "/foobar", "/foo", false) testKeyMatch3(t, "/foobar", "/foo*", false) testKeyMatch3(t, "/foobar", "/foo/*", false) testKeyMatch3(t, "/", "/{resource}", false) testKeyMatch3(t, "/resource1", "/{resource}", true) testKeyMatch3(t, "/myid", "/{id}/using/{resId}", false) testKeyMatch3(t, "/myid/using/myresid", "/{id}/using/{resId}", true) testKeyMatch3(t, "/proxy/myid", "/proxy/{id}/*", false) testKeyMatch3(t, "/proxy/myid/", "/proxy/{id}/*", true) testKeyMatch3(t, "/proxy/myid/res", "/proxy/{id}/*", true) testKeyMatch3(t, "/proxy/myid/res/res2", "/proxy/{id}/*", true) testKeyMatch3(t, "/proxy/myid/res/res2/res3", "/proxy/{id}/*", true) testKeyMatch3(t, "/proxy/", "/proxy/{id}/*", false) testKeyMatch3(t, "/myid/using/myresid", "/{id/using/{resId}", false) } func testKeyGet3(t *testing.T, key1 string, key2 string, pathVar string, res string) { t.Helper() myRes := KeyGet3(key1, key2, pathVar) t.Logf(`%s < %s: %s = "%s"`, key1, key2, pathVar, myRes) if myRes != res { t.Errorf(`%s < %s: %s = "%s" supposed to be "%s"`, key1, key2, pathVar, myRes, res) } } func TestKeyGet3(t *testing.T) { // KeyGet3() is similar with KeyGet2(), except using "/proxy/{id}" instead of "/proxy/:id". testKeyGet3(t, "/foo", "/foo", "id", "") testKeyGet3(t, "/foo", "/foo*", "id", "") testKeyGet3(t, "/foo", "/foo/*", "id", "") testKeyGet3(t, "/foo/bar", "/foo", "id", "") testKeyGet3(t, "/foo/bar", "/foo*", "id", "") testKeyGet3(t, "/foo/bar", "/foo/*", "id", "") testKeyGet3(t, "/foobar", "/foo", "id", "") testKeyGet3(t, "/foobar", "/foo*", "id", "") testKeyGet3(t, "/foobar", "/foo/*", "id", "") testKeyGet3(t, "/", "/{resource}", "resource", "") testKeyGet3(t, "/resource1", "/{resource}", "resource", "resource1") testKeyGet3(t, "/myid", "/{id}/using/{resId}", "id", "") testKeyGet3(t, "/myid/using/myresid", "/{id}/using/{resId}", "id", "myid") testKeyGet3(t, "/myid/using/myresid", "/{id}/using/{resId}", "resId", "myresid") testKeyGet3(t, "/proxy/myid", "/proxy/{id}/*", "id", "") testKeyGet3(t, "/proxy/myid/", "/proxy/{id}/*", "id", "myid") testKeyGet3(t, "/proxy/myid/res", "/proxy/{id}/*", "id", "myid") testKeyGet3(t, "/proxy/myid/res/res2", "/proxy/{id}/*", "id", "myid") testKeyGet3(t, "/proxy/myid/res/res2/res3", "/proxy/{id}/*", "id", "myid") testKeyGet3(t, "/proxy/", "/proxy/{id}/*", "id", "") testKeyGet3(t, "/api/group1_group_name/project1_admin/info", "/api/{proj}_admin/info", "proj", "") testKeyGet3(t, "/{id/using/myresid", "/{id/using/{resId}", "resId", "myresid") testKeyGet3(t, "/{id/using/myresid/status}", "/{id/using/{resId}/status}", "resId", "myresid") testKeyGet3(t, "/proxy/myid/res/res2/res3", "/proxy/{id}/*/{res}", "res", "res3") testKeyGet3(t, "/api/project1_admin/info", "/api/{proj}_admin/info", "proj", "project1") testKeyGet3(t, "/api/group1_group_name/project1_admin/info", "/api/{g}_{gn}/{proj}_admin/info", "g", "group1") testKeyGet3(t, "/api/group1_group_name/project1_admin/info", "/api/{g}_{gn}/{proj}_admin/info", "gn", "group_name") testKeyGet3(t, "/api/group1_group_name/project1_admin/info", "/api/{g}_{gn}/{proj}_admin/info", "proj", "project1") } func testKeyMatch4(t *testing.T, key1 string, key2 string, res bool) { t.Helper() myRes := KeyMatch4(key1, key2) t.Logf("%s < %s: %t", key1, key2, myRes) if myRes != res { t.Errorf("%s < %s: %t, supposed to be %t", key1, key2, !res, res) } } func TestKeyMatch4(t *testing.T) { testKeyMatch4(t, "/parent/123/child/123", "/parent/{id}/child/{id}", true) testKeyMatch4(t, "/parent/123/child/456", "/parent/{id}/child/{id}", false) testKeyMatch4(t, "/parent/123/child/123", "/parent/{id}/child/{another_id}", true) testKeyMatch4(t, "/parent/123/child/456", "/parent/{id}/child/{another_id}", true) testKeyMatch4(t, "/parent/123/child/123/book/123", "/parent/{id}/child/{id}/book/{id}", true) testKeyMatch4(t, "/parent/123/child/123/book/456", "/parent/{id}/child/{id}/book/{id}", false) testKeyMatch4(t, "/parent/123/child/456/book/123", "/parent/{id}/child/{id}/book/{id}", false) testKeyMatch4(t, "/parent/123/child/456/book/", "/parent/{id}/child/{id}/book/{id}", false) testKeyMatch4(t, "/parent/123/child/456", "/parent/{id}/child/{id}/book/{id}", false) testKeyMatch4(t, "/parent/123/child/123", "/parent/{i/d}/child/{i/d}", false) } func testRegexMatch(t *testing.T, key1 string, key2 string, res bool) { t.Helper() myRes := RegexMatch(key1, key2) t.Logf("%s < %s: %t", key1, key2, myRes) if myRes != res { t.Errorf("%s < %s: %t, supposed to be %t", key1, key2, !res, res) } } func TestRegexMatch(t *testing.T) { testRegexMatch(t, "/topic/create", "/topic/create", true) testRegexMatch(t, "/topic/create/123", "/topic/create", true) testRegexMatch(t, "/topic/delete", "/topic/create", false) testRegexMatch(t, "/topic/edit", "/topic/edit/[0-9]+", false) testRegexMatch(t, "/topic/edit/123", "/topic/edit/[0-9]+", true) testRegexMatch(t, "/topic/edit/abc", "/topic/edit/[0-9]+", false) testRegexMatch(t, "/foo/delete/123", "/topic/delete/[0-9]+", false) testRegexMatch(t, "/topic/delete/0", "/topic/delete/[0-9]+", true) testRegexMatch(t, "/topic/edit/123s", "/topic/delete/[0-9]+", false) } func testIPMatch(t *testing.T, ip1 string, ip2 string, res bool) { t.Helper() myRes := IPMatch(ip1, ip2) t.Logf("%s < %s: %t", ip1, ip2, myRes) if myRes != res { t.Errorf("%s < %s: %t, supposed to be %t", ip1, ip2, !res, res) } } func TestIPMatch(t *testing.T) { testIPMatch(t, "192.168.2.123", "192.168.2.0/24", true) testIPMatch(t, "192.168.2.123", "192.168.3.0/24", false) testIPMatch(t, "192.168.2.123", "192.168.2.0/16", true) testIPMatch(t, "192.168.2.123", "192.168.2.123", true) testIPMatch(t, "192.168.2.123", "192.168.2.123/32", true) testIPMatch(t, "10.0.0.11", "10.0.0.0/8", true) testIPMatch(t, "11.0.0.123", "10.0.0.0/8", false) } func testRegexMatchFunc(t *testing.T, res bool, err string, args ...interface{}) { t.Helper() myRes, myErr := RegexMatchFunc(args...) myErrStr := "" if myErr != nil { myErrStr = myErr.Error() } if myRes != res || err != myErrStr { t.Errorf("%v returns %v %v, supposed to be %v %v", args, myRes, myErr, res, err) } } func testKeyMatchFunc(t *testing.T, res bool, err string, args ...interface{}) { t.Helper() myRes, myErr := KeyMatchFunc(args...) myErrStr := "" if myErr != nil { myErrStr = myErr.Error() } if myRes != res || err != myErrStr { t.Errorf("%v returns %v %v, supposed to be %v %v", args, myRes, myErr, res, err) } } func testKeyMatch2Func(t *testing.T, res bool, err string, args ...interface{}) { t.Helper() myRes, myErr := KeyMatch2Func(args...) myErrStr := "" if myErr != nil { myErrStr = myErr.Error() } if myRes != res || err != myErrStr { t.Errorf("%v returns %v %v, supposed to be %v %v", args, myRes, myErr, res, err) } } func testKeyMatch3Func(t *testing.T, res bool, err string, args ...interface{}) { t.Helper() myRes, myErr := KeyMatch3Func(args...) myErrStr := "" if myErr != nil { myErrStr = myErr.Error() } if myRes != res || err != myErrStr { t.Errorf("%v returns %v %v, supposed to be %v %v", args, myRes, myErr, res, err) } } func testKeyMatch4Func(t *testing.T, res bool, err string, args ...interface{}) { t.Helper() myRes, myErr := KeyMatch4Func(args...) myErrStr := "" if myErr != nil { myErrStr = myErr.Error() } if myRes != res || err != myErrStr { t.Errorf("%v returns %v %v, supposed to be %v %v", args, myRes, myErr, res, err) } } func testKeyMatch5Func(t *testing.T, res bool, err string, args ...interface{}) { t.Helper() myRes, myErr := KeyMatch5Func(args...) myErrStr := "" if myErr != nil { myErrStr = myErr.Error() } if myRes != res || err != myErrStr { t.Errorf("%v returns %v %v, supposed to be %v %v", args, myRes, myErr, res, err) } } func testIPMatchFunc(t *testing.T, res bool, err string, args ...interface{}) { t.Helper() myRes, myErr := IPMatchFunc(args...) myErrStr := "" if myErr != nil { myErrStr = myErr.Error() } if myRes != res || err != myErrStr { t.Errorf("%v returns %v %v, supposed to be %v %v", args, myRes, myErr, res, err) } } func TestRegexMatchFunc(t *testing.T) { testRegexMatchFunc(t, false, "regexMatch: expected 2 arguments, but got 1", "/topic/create") testRegexMatchFunc(t, false, "regexMatch: expected 2 arguments, but got 3", "/topic/create/123", "/topic/create", "/topic/update") testRegexMatchFunc(t, false, "regexMatch: argument must be a string", "/topic/create", false) testRegexMatchFunc(t, true, "", "/topic/create/123", "/topic/create") } func TestKeyMatchFunc(t *testing.T) { testKeyMatchFunc(t, false, "keyMatch: expected 2 arguments, but got 1", "/foo") testKeyMatchFunc(t, false, "keyMatch: expected 2 arguments, but got 3", "/foo/create/123", "/foo/*", "/foo/update/123") testKeyMatchFunc(t, false, "keyMatch: argument must be a string", "/foo", true) testKeyMatchFunc(t, false, "", "/foo/bar", "/foo") testKeyMatchFunc(t, true, "", "/foo/bar", "/foo/*") testKeyMatchFunc(t, true, "", "/foo/bar", "/foo*") } func TestKeyMatch2Func(t *testing.T) { testKeyMatch2Func(t, false, "keyMatch2: expected 2 arguments, but got 1", "/") testKeyMatch2Func(t, false, "keyMatch2: expected 2 arguments, but got 3", "/foo/create/123", "/*", "/foo/update/123") testKeyMatch2Func(t, false, "keyMatch2: argument must be a string", "/foo", true) testKeyMatch2Func(t, false, "", "/", "/:resource") testKeyMatch2Func(t, true, "", "/resource1", "/:resource") testKeyMatch2Func(t, true, "", "/foo", "/foo") testKeyMatch2Func(t, true, "", "/foo", "/foo*") testKeyMatch2Func(t, false, "", "/foo", "/foo/*") } func TestKeyMatch3Func(t *testing.T) { testKeyMatch3Func(t, false, "keyMatch3: expected 2 arguments, but got 1", "/") testKeyMatch3Func(t, false, "keyMatch3: expected 2 arguments, but got 3", "/foo/create/123", "/*", "/foo/update/123") testKeyMatch3Func(t, false, "keyMatch3: argument must be a string", "/foo", true) testKeyMatch3Func(t, true, "", "/foo", "/foo") testKeyMatch3Func(t, true, "", "/foo", "/foo*") testKeyMatch3Func(t, false, "", "/foo", "/foo/*") testKeyMatch3Func(t, false, "", "/foo/bar", "/foo") testKeyMatch3Func(t, false, "", "/foo/bar", "/foo*") testKeyMatch3Func(t, true, "", "/foo/bar", "/foo/*") testKeyMatch3Func(t, false, "", "/foobar", "/foo") testKeyMatch3Func(t, false, "", "/foobar", "/foo*") testKeyMatch3Func(t, false, "", "/foobar", "/foo/*") testKeyMatch3Func(t, false, "", "/", "/{resource}") testKeyMatch3Func(t, true, "", "/resource1", "/{resource}") testKeyMatch3Func(t, false, "", "/myid", "/{id}/using/{resId}") testKeyMatch3Func(t, true, "", "/myid/using/myresid", "/{id}/using/{resId}") testKeyMatch3Func(t, false, "", "/proxy/myid", "/proxy/{id}/*") testKeyMatch3Func(t, true, "", "/proxy/myid/", "/proxy/{id}/*") testKeyMatch3Func(t, true, "", "/proxy/myid/res", "/proxy/{id}/*") testKeyMatch3Func(t, true, "", "/proxy/myid/res/res2", "/proxy/{id}/*") testKeyMatch3Func(t, true, "", "/proxy/myid/res/res2/res3", "/proxy/{id}/*") testKeyMatch3Func(t, false, "", "/proxy/", "/proxy/{id}/*") } func TestKeyMatch4Func(t *testing.T) { testKeyMatch4Func(t, false, "keyMatch4: expected 2 arguments, but got 1", "/parent/123/child/123") testKeyMatch4Func(t, false, "keyMatch4: expected 2 arguments, but got 3", "/parent/123/child/123", "/parent/{id}/child/{id}", true) testKeyMatch4Func(t, false, "keyMatch4: argument must be a string", "/parent/123/child/123", true) testKeyMatch4Func(t, true, "", "/parent/123/child/123", "/parent/{id}/child/{id}") testKeyMatch4Func(t, false, "", "/parent/123/child/456", "/parent/{id}/child/{id}") testKeyMatch4Func(t, true, "", "/parent/123/child/123", "/parent/{id}/child/{another_id}") testKeyMatch4Func(t, true, "", "/parent/123/child/456", "/parent/{id}/child/{another_id}") } func TestKeyMatch5Func(t *testing.T) { testKeyMatch5Func(t, false, "keyMatch5: expected 2 arguments, but got 1", "/foo") testKeyMatch5Func(t, false, "keyMatch5: expected 2 arguments, but got 3", "/foo/create/123", "/foo/*", "/foo/update/123") testKeyMatch5Func(t, false, "keyMatch5: argument must be a string", "/parent/123", true) testKeyMatch5Func(t, true, "", "/parent/child?status=1&type=2", "/parent/child") testKeyMatch5Func(t, false, "", "/parent?status=1&type=2", "/parent/child") testKeyMatch5Func(t, true, "", "/parent/child/?status=1&type=2", "/parent/child/") testKeyMatch5Func(t, false, "", "/parent/child/?status=1&type=2", "/parent/child") testKeyMatch5Func(t, false, "", "/parent/child?status=1&type=2", "/parent/child/") testKeyMatch5Func(t, true, "", "/foo", "/foo") testKeyMatch5Func(t, true, "", "/foo", "/foo*") testKeyMatch5Func(t, false, "", "/foo", "/foo/*") testKeyMatch5Func(t, false, "", "/foo/bar", "/foo") testKeyMatch5Func(t, false, "", "/foo/bar", "/foo*") testKeyMatch5Func(t, true, "", "/foo/bar", "/foo/*") testKeyMatch5Func(t, false, "", "/foobar", "/foo") testKeyMatch5Func(t, false, "", "/foobar", "/foo*") testKeyMatch5Func(t, false, "", "/foobar", "/foo/*") testKeyMatch5Func(t, false, "", "/", "/{resource}") testKeyMatch5Func(t, true, "", "/resource1", "/{resource}") testKeyMatch5Func(t, false, "", "/myid", "/{id}/using/{resId}") testKeyMatch5Func(t, true, "", "/myid/using/myresid", "/{id}/using/{resId}") testKeyMatch5Func(t, false, "", "/proxy/myid", "/proxy/{id}/*") testKeyMatch5Func(t, true, "", "/proxy/myid/", "/proxy/{id}/*") testKeyMatch5Func(t, true, "", "/proxy/myid/res", "/proxy/{id}/*") testKeyMatch5Func(t, true, "", "/proxy/myid/res/res2", "/proxy/{id}/*") testKeyMatch5Func(t, true, "", "/proxy/myid/res/res2/res3", "/proxy/{id}/*") testKeyMatch5Func(t, false, "", "/proxy/", "/proxy/{id}/*") testKeyMatch5Func(t, false, "", "/proxy/myid?status=1&type=2", "/proxy/{id}/*") testKeyMatch5Func(t, true, "", "/proxy/myid/", "/proxy/{id}/*") testKeyMatch5Func(t, true, "", "/proxy/myid/res?status=1&type=2", "/proxy/{id}/*") testKeyMatch5Func(t, true, "", "/proxy/myid/res/res2?status=1&type=2", "/proxy/{id}/*") testKeyMatch5Func(t, true, "", "/proxy/myid/res/res2/res3?status=1&type=2", "/proxy/{id}/*") testKeyMatch5Func(t, false, "", "/proxy/", "/proxy/{id}/*") } func TestIPMatchFunc(t *testing.T) { testIPMatchFunc(t, false, "ipMatch: expected 2 arguments, but got 1", "192.168.2.123") testIPMatchFunc(t, false, "ipMatch: argument must be a string", "192.168.2.123", 128) testIPMatchFunc(t, true, "", "192.168.2.123", "192.168.2.0/24") } func TestGlobMatch(t *testing.T) { testGlobMatch(t, "/foo", "/foo", true) testGlobMatch(t, "/foo", "/foo*", true) testGlobMatch(t, "/foo", "/foo/*", false) testGlobMatch(t, "/foo/bar", "/foo", false) testGlobMatch(t, "/foo/bar", "/foo*", false) testGlobMatch(t, "/foo/bar", "/foo/*", true) testGlobMatch(t, "/foobar", "/foo", false) testGlobMatch(t, "/foobar", "/foo*", true) testGlobMatch(t, "/foobar", "/foo/*", false) testGlobMatch(t, "/foo", "*/foo", true) testGlobMatch(t, "/foo", "*/foo*", true) testGlobMatch(t, "/foo", "*/foo/*", false) testGlobMatch(t, "/foo/bar", "*/foo", false) testGlobMatch(t, "/foo/bar", "*/foo*", false) testGlobMatch(t, "/foo/bar", "*/foo/*", true) testGlobMatch(t, "/foobar", "*/foo", false) testGlobMatch(t, "/foobar", "*/foo*", true) testGlobMatch(t, "/foobar", "*/foo/*", false) testGlobMatch(t, "/prefix/foo", "*/foo", false) testGlobMatch(t, "/prefix/foo", "*/foo*", false) testGlobMatch(t, "/prefix/foo", "*/foo/*", false) testGlobMatch(t, "/prefix/foo/bar", "*/foo", false) testGlobMatch(t, "/prefix/foo/bar", "*/foo*", false) testGlobMatch(t, "/prefix/foo/bar", "*/foo/*", false) testGlobMatch(t, "/prefix/foobar", "*/foo", false) testGlobMatch(t, "/prefix/foobar", "*/foo*", false) testGlobMatch(t, "/prefix/foobar", "*/foo/*", false) testGlobMatch(t, "/prefix/subprefix/foo", "*/foo", false) testGlobMatch(t, "/prefix/subprefix/foo", "*/foo*", false) testGlobMatch(t, "/prefix/subprefix/foo", "*/foo/*", false) testGlobMatch(t, "/prefix/subprefix/foo/bar", "*/foo", false) testGlobMatch(t, "/prefix/subprefix/foo/bar", "*/foo*", false) testGlobMatch(t, "/prefix/subprefix/foo/bar", "*/foo/*", false) testGlobMatch(t, "/prefix/subprefix/foobar", "*/foo", false) testGlobMatch(t, "/prefix/subprefix/foobar", "*/foo*", false) testGlobMatch(t, "/prefix/subprefix/foobar", "*/foo/*", false) } func testTimeMatch(t *testing.T, startTime string, endTime string, res bool) { t.Helper() myRes, err := TimeMatch(startTime, endTime) if err != nil { panic(err) } t.Logf("%s < %s: %t", startTime, endTime, myRes) if myRes != res { t.Errorf("%s < %s: %t, supposed to be %t", startTime, endTime, !res, res) } } func TestTestMatch(t *testing.T) { testTimeMatch(t, "0000-01-01 00:00:00", "0000-01-02 00:00:00", false) testTimeMatch(t, "0000-01-01 00:00:00", "9999-12-30 00:00:00", true) testTimeMatch(t, "_", "_", true) testTimeMatch(t, "_", "9999-12-30 00:00:00", true) testTimeMatch(t, "_", "0000-01-02 00:00:00", false) testTimeMatch(t, "0000-01-01 00:00:00", "_", true) testTimeMatch(t, "9999-12-30 00:00:00", "_", false) } golang-github-casbin-casbin-2.82.0/util/util.go000066400000000000000000000200001456376452400213150ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package util import ( "encoding/json" "regexp" "sort" "strings" "sync" ) var evalReg = regexp.MustCompile(`\beval\((?P[^)]*)\)`) var escapeAssertionRegex = regexp.MustCompile(`\b((r|p)[0-9]*)\.`) func JsonToMap(jsonStr string) (map[string]interface{}, error) { result := make(map[string]interface{}) err := json.Unmarshal([]byte(jsonStr), &result) if err != nil { return result, err } return result, nil } // EscapeAssertion escapes the dots in the assertion, because the expression evaluation doesn't support such variable names. func EscapeAssertion(s string) string { s = escapeAssertionRegex.ReplaceAllStringFunc(s, func(m string) string { return strings.Replace(m, ".", "_", 1) }) return s } // RemoveComments removes the comments starting with # in the text. func RemoveComments(s string) string { pos := strings.Index(s, "#") if pos == -1 { return s } return strings.TrimSpace(s[0:pos]) } // ArrayEquals determines whether two string arrays are identical. func ArrayEquals(a []string, b []string) bool { if len(a) != len(b) { return false } for i, v := range a { if v != b[i] { return false } } return true } // Array2DEquals determines whether two 2-dimensional string arrays are identical. func Array2DEquals(a [][]string, b [][]string) bool { if len(a) != len(b) { return false } for i, v := range a { if !ArrayEquals(v, b[i]) { return false } } return true } // SortArray2D Sorts the two-dimensional string array func SortArray2D(arr [][]string) { if len(arr) != 0 { sort.Slice(arr, func(i, j int) bool { elementLen := len(arr[0]) for k := 0; k < elementLen; k++ { if arr[i][k] < arr[j][k] { return true } else if arr[i][k] > arr[j][k] { return false } } return true }) } } // SortedArray2DEquals determines whether two 2-dimensional string arrays are identical. func SortedArray2DEquals(a [][]string, b [][]string) bool { if len(a) != len(b) { return false } copyA := make([][]string, len(a)) copy(copyA, a) copyB := make([][]string, len(b)) copy(copyB, b) SortArray2D(copyA) SortArray2D(copyB) for i, v := range copyA { if !ArrayEquals(v, copyB[i]) { return false } } return true } // ArrayRemoveDuplicates removes any duplicated elements in a string array. func ArrayRemoveDuplicates(s *[]string) { found := make(map[string]bool) j := 0 for i, x := range *s { if !found[x] { found[x] = true (*s)[j] = (*s)[i] j++ } } *s = (*s)[:j] } // ArrayToString gets a printable string for a string array. func ArrayToString(s []string) string { return strings.Join(s, ", ") } // ParamsToString gets a printable string for variable number of parameters. func ParamsToString(s ...string) string { return strings.Join(s, ", ") } // SetEquals determines whether two string sets are identical. func SetEquals(a []string, b []string) bool { if len(a) != len(b) { return false } sort.Strings(a) sort.Strings(b) for i, v := range a { if v != b[i] { return false } } return true } // SetEquals determines whether two string sets are identical. func SetEqualsInt(a []int, b []int) bool { if len(a) != len(b) { return false } sort.Ints(a) sort.Ints(b) for i, v := range a { if v != b[i] { return false } } return true } // SetEquals determines whether two string sets are identical. func Set2DEquals(a [][]string, b [][]string) bool { if len(a) != len(b) { return false } var aa []string for _, v := range a { sort.Strings(v) aa = append(aa, strings.Join(v, ", ")) } var bb []string for _, v := range b { sort.Strings(v) bb = append(bb, strings.Join(v, ", ")) } return SetEquals(aa, bb) } // JoinSlice joins a string and a slice into a new slice. func JoinSlice(a string, b ...string) []string { res := make([]string, 0, len(b)+1) res = append(res, a) res = append(res, b...) return res } // JoinSliceAny joins a string and a slice into a new interface{} slice. func JoinSliceAny(a string, b ...string) []interface{} { res := make([]interface{}, 0, len(b)+1) res = append(res, a) for _, s := range b { res = append(res, s) } return res } // SetSubtract returns the elements in `a` that aren't in `b`. func SetSubtract(a []string, b []string) []string { mb := make(map[string]struct{}, len(b)) for _, x := range b { mb[x] = struct{}{} } var diff []string for _, x := range a { if _, found := mb[x]; !found { diff = append(diff, x) } } return diff } // HasEval determine whether matcher contains function eval func HasEval(s string) bool { return evalReg.MatchString(s) } // ReplaceEval replace function eval with the value of its parameters func ReplaceEval(s string, rule string) string { return evalReg.ReplaceAllString(s, "("+rule+")") } // ReplaceEvalWithMap replace function eval with the value of its parameters via given sets. func ReplaceEvalWithMap(src string, sets map[string]string) string { return evalReg.ReplaceAllStringFunc(src, func(s string) string { subs := evalReg.FindStringSubmatch(s) if subs == nil { return s } key := subs[1] value, found := sets[key] if !found { return s } return evalReg.ReplaceAllString(s, value) }) } // GetEvalValue returns the parameters of function eval func GetEvalValue(s string) []string { subMatch := evalReg.FindAllStringSubmatch(s, -1) var rules []string for _, rule := range subMatch { rules = append(rules, rule[1]) } return rules } func RemoveDuplicateElement(s []string) []string { result := make([]string, 0, len(s)) temp := map[string]struct{}{} for _, item := range s { if _, ok := temp[item]; !ok { temp[item] = struct{}{} result = append(result, item) } } return result } type node struct { key interface{} value interface{} prev *node next *node } type LRUCache struct { capacity int m map[interface{}]*node head *node tail *node } func NewLRUCache(capacity int) *LRUCache { cache := &LRUCache{} cache.capacity = capacity cache.m = map[interface{}]*node{} head := &node{} tail := &node{} head.next = tail tail.prev = head cache.head = head cache.tail = tail return cache } func (cache *LRUCache) remove(n *node, listOnly bool) { if !listOnly { delete(cache.m, n.key) } n.prev.next = n.next n.next.prev = n.prev } func (cache *LRUCache) add(n *node, listOnly bool) { if !listOnly { cache.m[n.key] = n } headNext := cache.head.next cache.head.next = n headNext.prev = n n.next = headNext n.prev = cache.head } func (cache *LRUCache) moveToHead(n *node) { cache.remove(n, true) cache.add(n, true) } func (cache *LRUCache) Get(key interface{}) (value interface{}, ok bool) { n, ok := cache.m[key] if ok { cache.moveToHead(n) return n.value, ok } else { return nil, ok } } func (cache *LRUCache) Put(key interface{}, value interface{}) { n, ok := cache.m[key] if ok { cache.remove(n, false) } else { n = &node{key, value, nil, nil} if len(cache.m) >= cache.capacity { cache.remove(cache.tail.prev, false) } } cache.add(n, false) } type SyncLRUCache struct { rwm sync.RWMutex *LRUCache } func NewSyncLRUCache(capacity int) *SyncLRUCache { cache := &SyncLRUCache{} cache.LRUCache = NewLRUCache(capacity) return cache } func (cache *SyncLRUCache) Get(key interface{}) (value interface{}, ok bool) { cache.rwm.Lock() defer cache.rwm.Unlock() return cache.LRUCache.Get(key) } func (cache *SyncLRUCache) Put(key interface{}, value interface{}) { cache.rwm.Lock() defer cache.rwm.Unlock() cache.LRUCache.Put(key, value) } golang-github-casbin-casbin-2.82.0/util/util_test.go000066400000000000000000000211731456376452400223700ustar00rootroot00000000000000// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package util import ( "testing" ) func testEscapeAssertion(t *testing.T, s string, res string) { t.Helper() myRes := EscapeAssertion(s) t.Logf("%s: %s", s, myRes) if myRes != res { t.Errorf("%s: %s, supposed to be %s", s, myRes, res) } } func TestEscapeAssertion(t *testing.T) { testEscapeAssertion(t, "r_sub == r_obj.value", "r_sub == r_obj.value") testEscapeAssertion(t, "p_sub == r_sub.value", "p_sub == r_sub.value") testEscapeAssertion(t, "r.attr.value == p.attr", "r_attr.value == p_attr") testEscapeAssertion(t, "r.attr.value == p.attr", "r_attr.value == p_attr") testEscapeAssertion(t, "r.attp.value || p.attr", "r_attp.value || p_attr") testEscapeAssertion(t, "r2.attr.value == p2.attr", "r2_attr.value == p2_attr") testEscapeAssertion(t, "r2.attp.value || p2.attr", "r2_attp.value || p2_attr") testEscapeAssertion(t, "r.attp.value &&p.attr", "r_attp.value &&p_attr") testEscapeAssertion(t, "r.attp.value >p.attr", "r_attp.value >p_attr") testEscapeAssertion(t, "r.attp.value