├── .editorconfig ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── bug_report.yml └── workflows │ ├── close_stale_issues.yml │ ├── lint.yml │ ├── release.yml │ └── updatepot.yml ├── .gitignore ├── .phpcs.xml ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── RoboFile.php ├── composer.json ├── composer.lock ├── front ├── callback.php ├── picture.send.php ├── preference.form.php ├── provider.form.php ├── provider.php └── user.form.php ├── hook.php ├── inc ├── preference.class.php ├── provider.class.php ├── provider_user.class.php └── toolbox.class.php ├── locales ├── de_DE.mo ├── de_DE.po ├── en_GB.mo ├── en_GB.po ├── es_ES.mo ├── es_ES.po ├── fr_FR.mo ├── fr_FR.po ├── pt_BR.mo ├── pt_BR.po ├── singlesignon.pot └── tr_TR.po ├── plugin.png ├── plugin.xml ├── providers.json ├── screenshots ├── image_1.png └── image_2.png ├── setup.php ├── tools └── extract_template.sh └── transifex.yml /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = space 11 | indent_size = 3 12 | 13 | # We recommend you to keep these unchanged 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | insert_final_newline = true 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | issuehunt: edgardmessias 2 | ko_fi: edgardmessias 3 | custom: ['https://www.paypal.com/donate?hosted_button_id=5KHYY5ZDTNDSY'] 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a report to help us improve glpi-singlesignon 3 | body: 4 | - type: markdown 5 | attributes: 6 | value: | 7 | 8 | Dear GLPI plugin user. 9 | 10 | BEFORE SUBMITTING YOUR ISSUE, please make sure to read and follow these steps: 11 | 12 | * We do not guarantee any processing / resolution time for community issues. 13 | * Keep this tracker in ENGLISH. If you want support in your language, the [community forum](https://forum.glpi-project.org) is the best place. 14 | * Always try to reproduce your issue at least on latest stable release. 15 | 16 | The GLPI Single-Sign On Development team. 17 | - type: checkboxes 18 | attributes: 19 | label: Is there an existing issue for this? 20 | description: Please search to see if an issue already exists for the bug you encountered. 21 | options: 22 | - label: I have searched the existing issues 23 | validations: 24 | required: true 25 | - type: input 26 | id: glpi-version 27 | attributes: 28 | label: GLPI Version 29 | description: What version of our GLPI are you running? 30 | validations: 31 | required: true 32 | - type: input 33 | id: plugin-version 34 | attributes: 35 | label: Plugin version 36 | description: What version of `glpi-singlesignon` are you running? 37 | validations: 38 | required: true 39 | - type: textarea 40 | attributes: 41 | label: Bug description 42 | description: A concise description of the problem you are experiencing and what you expected to happen. 43 | validations: 44 | required: false 45 | - type: textarea 46 | id: logs 47 | attributes: 48 | label: Relevant log output 49 | description: | 50 | Please copy and paste any relevant log output. Find them in `*-error.log` files under `glpi/files/_log/`. 51 | 52 | Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. 53 | render: shell 54 | - type: input 55 | id: url 56 | attributes: 57 | label: Page URL 58 | description: If applicable, page URL where the bug happens. 59 | validations: 60 | required: false 61 | - type: textarea 62 | attributes: 63 | label: Steps To reproduce 64 | description: Steps to reproduce the behavior. 65 | placeholder: | 66 | 1. With this config... 67 | 2. Go to... 68 | 3. Scroll down to... 69 | 4. See error... 70 | validations: 71 | required: false 72 | - type: textarea 73 | attributes: 74 | label: Your GLPI setup information 75 | description: Copy and paste the information you will find in GLPI debug mode in the `Setup > Single-Sign on` menu, `Debug` tab of your SSO provider. 76 | validations: 77 | required: false 78 | - type: textarea 79 | attributes: 80 | label: Anything else? 81 | description: Add any other context about the problem here. 82 | validations: 83 | required: false 84 | -------------------------------------------------------------------------------- /.github/workflows/close_stale_issues.yml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues' 2 | on: 3 | schedule: 4 | - cron: '0 8 * * *' 5 | 6 | jobs: 7 | stale: 8 | if: github.repository == 'edgardmessias/glpi-singlesignon' 9 | permissions: 10 | issues: write # for actions/stale to close stale issues 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/stale@v9 14 | with: 15 | stale-issue-message: >- 16 | There has been no activity on this issue for some time and therefore it is considered stale 17 | and will be closed automatically in 10 days. 18 | 19 | 20 | If this issue is related to a bug, please try to reproduce on latest release. If the problem persist, 21 | feel free to add a comment to revive this issue. 22 | days-before-issue-stale: 60 23 | days-before-pr-stale: -1 # PR will be marked as stale manually. 24 | days-before-close: 10 25 | exempt-issue-labels: "bug,enhancement,question,security" # Issues with "bug", "enhancement", "question" or "security" labels will not be marked as stale 26 | exempt-all-milestones: true # Do not check issues/PR with defined milestone. 27 | ascending: true # First check older issues/PR. 28 | operations-per-run: 750 # Max API calls per run. 29 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | pull_request: 8 | branches: 9 | - "*" 10 | 11 | jobs: 12 | lint: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v2 17 | 18 | - name: Setup PHP 19 | uses: shivammathur/setup-php@v2 20 | with: 21 | php-version: "7.1" 22 | extensions: intl, mbstring, mcrypt, openssl 23 | tools: composer 24 | 25 | - name: Setup problem matchers 26 | run: | 27 | echo "::add-matcher::${{ runner.tool_cache }}/php.json" 28 | 29 | - name: Cache Composer packages 30 | id: composer-cache 31 | uses: actions/cache@v2 32 | with: 33 | path: vendor 34 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 35 | restore-keys: | 36 | ${{ runner.os }}-php- 37 | 38 | - name: Install dependencies 39 | if: steps.composer-cache.outputs.cache-hit != 'true' 40 | run: composer install --prefer-dist --no-progress --no-suggest 41 | 42 | - name: PHPCS check 43 | uses: chekalsky/phpcs-action@v1 44 | with: 45 | enable_warnings: true 46 | phpcs_bin_path: "./vendor/bin/phpcs" 47 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create release with tag 2 | 3 | on: 4 | push: 5 | # Sequence of patterns matched against refs/heads 6 | branches: 7 | - '*.*.*' # Push events to matching ex:20.15.10 8 | 9 | env: 10 | TAG_VALUE: ${GITHUB_REF/refs\/heads\//} 11 | PR_BRANCH: release-ci-${{ github.sha }} 12 | 13 | jobs: 14 | generate-changelog: 15 | name: Generate Changelog 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v2 20 | 21 | - name: 🏷️ Create/update tag 22 | uses: actions/github-script@v7 23 | with: 24 | script: | 25 | github.rest.git.createRef({ 26 | owner: context.repo.owner, 27 | repo: context.repo.repo, 28 | ref: 'refs/tags/${{ github.ref_name }}', 29 | sha: context.sha 30 | }).catch(err => { 31 | if (err.status !== 422) throw err; 32 | }) 33 | 34 | - name: Update CHANGELOG 35 | id: changelog 36 | uses: requarks/changelog-action@v1 37 | with: 38 | token: ${{ github.token }} 39 | tag: ${{ github.ref_name }} 40 | writeToFile: true 41 | #excludeTypes: build,docs,style 42 | #includeInvalidCommits: true 43 | 44 | - name: Commit CHANGELOG.md (master) 45 | uses: stefanzweifel/git-auto-commit-action@v4 46 | with: 47 | branch: master 48 | commit_message: 'docs: update CHANGELOG.md for ${{ github.ref_name }} [skip ci]' 49 | file_pattern: CHANGELOG.md 50 | 51 | - name: Commit CHANGELOG.md (branch) 52 | uses: stefanzweifel/git-auto-commit-action@v4 53 | with: 54 | branch: ${{ github.ref_name }} 55 | commit_message: 'docs: update CHANGELOG.md for ${{ github.ref_name }} [skip ci]' 56 | file_pattern: CHANGELOG.md 57 | 58 | build: 59 | name: Upload Release Asset 60 | permissions: write-all 61 | runs-on: ubuntu-latest 62 | steps: 63 | - name: Checkout code 64 | uses: actions/checkout@v2 65 | 66 | - name: Build project # This would actually build your project, using zip for an example artifact 67 | id: build_ 68 | env: 69 | GITHUB_NAME: ${{ github.event.repository.name }} 70 | run: sudo apt-get install libxml-xpath-perl;echo $(xpath -e '/root/versions/version[num="'${GITHUB_REF/refs\/heads\/v/}'"]/compatibility/text()' plugin.xml);echo ::set-output name=version_glpi::$(xpath -e '/root/versions/version[num="'${GITHUB_REF/refs\/heads\/v/}'"]/compatibility/text()' plugin.xml); rm -rf plugin.xml tools wiki screenshots test .git .github ISSUE_TEMPLATE.md TODO.txt $GITHUB_NAME.png;cd ..; tar jcvf $GITHUB_NAME-${GITHUB_REF/refs\/heads\//}.tar.bz2 $GITHUB_NAME;ls -al;echo ::set-output name=tag::${GITHUB_REF/refs\/heads\//};echo ${{ steps.getxml.outputs.info }}; 71 | # run: rm -rf plugin.xml tools wiki screenshots test ISSUE_TEMPLATE.md TODO.txt $GITHUB_NAME.png; tar -zcvf $GITHUB_NAME-$GITHUB_TAG.tar.gz $GITHUB_NAME 72 | 73 | - name: Create Release 74 | id: create_release 75 | uses: actions/create-release@v1 76 | env: 77 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 78 | with: 79 | tag_name: ${{ steps.build_.outputs.tag }} 80 | release_name: | 81 | GLPI ${{ steps.build_.outputs.version_glpi }} : Version ${{ steps.build_.outputs.tag }} disponible / available 82 | body : Version ${{ steps.build_.outputs.tag }} released for GLPI ${{ steps.build_.outputs.version_glpi }}. The [full changelog is available](https://github.com/edgardmessias/glpi-singlesignon/blob/master/CHANGELOG.md) for more details. 83 | draft: false 84 | prerelease: false 85 | 86 | - name: Upload Release Asset 87 | id: upload-release-asset 88 | uses: actions/upload-release-asset@v1 89 | env: 90 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 91 | GITHUB_NAME: ${{ github.event.repository.name }} 92 | with: 93 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 94 | asset_path: /home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}-${{ steps.build_.outputs.tag }}.tar.bz2 95 | asset_name: ${{ github.event.repository.name }}-${{ steps.build_.outputs.tag }}.tar.bz2 96 | asset_content_type: application/zip 97 | -------------------------------------------------------------------------------- /.github/workflows/updatepot.yml: -------------------------------------------------------------------------------- 1 | name: Update POT 2 | on: 3 | push: 4 | branches: [ master ] 5 | paths-ignore: 6 | - 'locales/**' 7 | 8 | env: 9 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 10 | jobs: 11 | run: 12 | 13 | name: Update POT 14 | permissions: write-all 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout repo 18 | uses: actions/checkout@v2 19 | 20 | - name: install xgettext 21 | 22 | run: sudo apt-get install gettext; 23 | - name: Update POT 24 | run: sh tools/extract_template.sh; 25 | 26 | 27 | - name: Commit changes 28 | uses: EndBug/add-and-commit@v5.1.0 29 | with: 30 | message: "Update POT" 31 | - name: Push changes 32 | 33 | uses: actions-go/push@v1 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject 2 | /vendor 3 | /*.tar 4 | /*.tar.gz 5 | /*.tgz 6 | /*.zip 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /.phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | . 23 | vendor 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "bmewburn.vscode-intelephense-client", 4 | "valeryanm.vscode-phpsab", 5 | "xdebug.php-debug" 6 | ] 7 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use o IntelliSense para aprender sobre possíveis atributos. 3 | // Passe o mouse para ver as descrições dos atributos existentes. 4 | // Para obter mais informações, visite: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Listen for XDebug", 9 | "type": "php", 10 | "request": "launch", 11 | "port": 9000, 12 | "ignore": [ 13 | "**/vendor/**/*.php" 14 | ] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "phpsab.snifferEnable": true, 3 | "phpsab.standard": ".phpcs.xml", 4 | "phpsab.composerJsonPath": "composer.json", 5 | "intelephense.environment.includePaths": [ 6 | "../../" 7 | ], 8 | "intelephense.format.braces": "k&r", 9 | "[json]": { 10 | "editor.defaultFormatter": "vscode.json-language-features", 11 | "editor.detectIndentation": false, 12 | "editor.tabSize": 4 13 | }, 14 | "[jsonc]": { 15 | "editor.defaultFormatter": "vscode.json-language-features", 16 | "editor.detectIndentation": false, 17 | "editor.tabSize": 4 18 | }, 19 | "[php]": { 20 | "editor.defaultFormatter": "bmewburn.vscode-intelephense-client" 21 | } 22 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Run PHP Server", 8 | "type": "shell", 9 | "command": "php", 10 | "isBackground": true, 11 | "group": "build", 12 | "args": [ 13 | "-S", 14 | "localhost:8000", 15 | "-t", 16 | "../.." 17 | ], 18 | "problemMatcher": [ 19 | { 20 | "pattern": [ 21 | { 22 | "regexp": ".", 23 | "file": 1, 24 | "location": 2, 25 | "message": 3 26 | } 27 | ], 28 | "background": { 29 | "activeOnStart": true, 30 | "beginsPattern": ".", 31 | "endsPattern": "." 32 | } 33 | } 34 | ] 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [v1.4.0] - 2025-02-04 8 | ### :sparkles: New Features 9 | - [`4901caf`](https://github.com/edgardmessias/glpi-singlesignon/commit/4901cafe3c5d3eb0f7f41e93bff4a50912c166ae) - Added es_ES translation *(commit by [@edgardmessias](https://github.com/edgardmessias))* 10 | - [`7552525`](https://github.com/edgardmessias/glpi-singlesignon/commit/755252547056d650a3e5a4c394c2b6af865e4ede) - Added lint to provider page from plugin page *(commit by [@edgardmessias](https://github.com/edgardmessias))* 11 | - [`135975f`](https://github.com/edgardmessias/glpi-singlesignon/commit/135975fa192032871d5bbb10e13d300a05884506) - Added license (close [#36](https://github.com/edgardmessias/glpi-singlesignon/pull/36)) *(commit by [@edgardmessias](https://github.com/edgardmessias))* 12 | - [`51999b4`](https://github.com/edgardmessias/glpi-singlesignon/commit/51999b40dfe7fafa9473755820acdd60cdc808fe) - **locales**: french translation *(PR [#65](https://github.com/edgardmessias/glpi-singlesignon/pull/65) by [@ternium1](https://github.com/ternium1))* 13 | - [`cae95d8`](https://github.com/edgardmessias/glpi-singlesignon/commit/cae95d8a43c9c375368c10b46c640926b690e5df) - Added create new user for Google *(PR [#70](https://github.com/edgardmessias/glpi-singlesignon/pull/70) by [@ch-tm](https://github.com/ch-tm))* 14 | - [`144c3cd`](https://github.com/edgardmessias/glpi-singlesignon/commit/144c3cdc9c4d7915c64b21e8b64f56c522b40d90) - Improve Reverse Proxy and Plugin folder support *(PR [#103](https://github.com/edgardmessias/glpi-singlesignon/pull/103) by [@eduardomozart](https://github.com/eduardomozart))* 15 | - [`ebff864`](https://github.com/edgardmessias/glpi-singlesignon/commit/ebff8646f933038ad94969cc1a424135381f9f9f) - Sync GLPI photo with Azure AD *(PR [#101](https://github.com/edgardmessias/glpi-singlesignon/pull/101) by [@eduardomozart](https://github.com/eduardomozart))* 16 | - [`d45b759`](https://github.com/edgardmessias/glpi-singlesignon/commit/d45b759bc760ca1314f070796dd8cf1d51fc1368) - Automatically close stale issues *(commit by [@eduardomozart](https://github.com/eduardomozart))* 17 | - [`cc09ff3`](https://github.com/edgardmessias/glpi-singlesignon/commit/cc09ff3e9be37856e11e1f1a5c58a2a673ef0aa4) - Automatically close stale issues *(commit by [@eduardomozart](https://github.com/eduardomozart))* 18 | - [`fb7eae6`](https://github.com/edgardmessias/glpi-singlesignon/commit/fb7eae6ebeb95de90613ed7167995045b6f9c6dd) - Add 'debug' tab to copy SSO provider info *(commit by [@eduardomozart](https://github.com/eduardomozart))* 19 | 20 | ### :bug: Bug Fixes 21 | - [`c7b1be1`](https://github.com/edgardmessias/glpi-singlesignon/commit/c7b1be17b3c2d80c6df31d334d3c7d3de1214a74) - Fixed redirect for default provider *(commit by [@edgardmessias](https://github.com/edgardmessias))* 22 | - [`be22981`](https://github.com/edgardmessias/glpi-singlesignon/commit/be229815a7f026e40903c588dc1c1eb3d7fef972) - Fixed show buttons for GLPI >= 10.0 *(commit by [@edgardmessias](https://github.com/edgardmessias))* 23 | - [`2c83a54`](https://github.com/edgardmessias/glpi-singlesignon/commit/2c83a54f013e596d28b7d52beb56145d2555f4b0) - Could not add picture see [#88](https://github.com/edgardmessias/glpi-singlesignon/pull/88) *(PR [#89](https://github.com/edgardmessias/glpi-singlesignon/pull/89) by [@invisiblemarcel](https://github.com/invisiblemarcel))* 24 | - [`ddc9c2d`](https://github.com/edgardmessias/glpi-singlesignon/commit/ddc9c2ddc1a3dd7fe1f76c44c844b1d3e690c9d7) - Add missing string to translation *(PR [#93](https://github.com/edgardmessias/glpi-singlesignon/pull/93) by [@eduardomozart](https://github.com/eduardomozart))* 25 | - [`8c63d47`](https://github.com/edgardmessias/glpi-singlesignon/commit/8c63d47f64f5ab190b39ce39ecf0743ad5d23e05) - Add missing translation strings *(PR [#102](https://github.com/edgardmessias/glpi-singlesignon/pull/102) by [@eduardomozart](https://github.com/eduardomozart))* 26 | - [`1a1a7e8`](https://github.com/edgardmessias/glpi-singlesignon/commit/1a1a7e8c7a4f8786bfdbc275b09efeae63cf3cc1) - Add API token date *(PR [#100](https://github.com/edgardmessias/glpi-singlesignon/pull/100) by [@eduardomozart](https://github.com/eduardomozart))* 27 | - [`6a2be8e`](https://github.com/edgardmessias/glpi-singlesignon/commit/6a2be8e9e7116513ebc640c634b4a36dc5bb5306) - Add support for "Extra Options" field *(commit by [@eduardomozart](https://github.com/eduardomozart))* 28 | 29 | ### :wrench: Chores 30 | - [`bd01b9c`](https://github.com/edgardmessias/glpi-singlesignon/commit/bd01b9c045b7dec749f51ccd608e0badbdd22fe7) - Updated vscode configs *(commit by [@edgardmessias](https://github.com/edgardmessias))* 31 | 32 | [v1.4.0]: https://github.com/edgardmessias/glpi-singlesignon/compare/v1.3.3...v1.4.0 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ### GNU GENERAL PUBLIC LICENSE 2 | 3 | Version 3, 29 June 2007 4 | 5 | Copyright (C) 2007 Free Software Foundation, Inc. 6 | 7 | 8 | Everyone is permitted to copy and distribute verbatim copies of this 9 | license document, but changing it is not allowed. 10 | 11 | ### Preamble 12 | 13 | The GNU General Public License is a free, copyleft license for 14 | software and other kinds of works. 15 | 16 | The licenses for most software and other practical works are designed 17 | to take away your freedom to share and change the works. By contrast, 18 | the GNU General Public License is intended to guarantee your freedom 19 | to share and change all versions of a program--to make sure it remains 20 | free software for all its users. We, the Free Software Foundation, use 21 | the GNU General Public License for most of our software; it applies 22 | also to any other work released this way by its authors. You can apply 23 | it to your programs, too. 24 | 25 | When we speak of free software, we are referring to freedom, not 26 | price. Our General Public Licenses are designed to make sure that you 27 | have the freedom to distribute copies of free software (and charge for 28 | them if you wish), that you receive source code or can get it if you 29 | want it, that you can change the software or use pieces of it in new 30 | free programs, and that you know you can do these things. 31 | 32 | To protect your rights, we need to prevent others from denying you 33 | these rights or asking you to surrender the rights. Therefore, you 34 | have certain responsibilities if you distribute copies of the 35 | software, or if you modify it: responsibilities to respect the freedom 36 | of others. 37 | 38 | For example, if you distribute copies of such a program, whether 39 | gratis or for a fee, you must pass on to the recipients the same 40 | freedoms that you received. You must make sure that they, too, receive 41 | or can get the source code. And you must show them these terms so they 42 | know their rights. 43 | 44 | Developers that use the GNU GPL protect your rights with two steps: 45 | (1) assert copyright on the software, and (2) offer you this License 46 | giving you legal permission to copy, distribute and/or modify it. 47 | 48 | For the developers' and authors' protection, the GPL clearly explains 49 | that there is no warranty for this free software. For both users' and 50 | authors' sake, the GPL requires that modified versions be marked as 51 | changed, so that their problems will not be attributed erroneously to 52 | authors of previous versions. 53 | 54 | Some devices are designed to deny users access to install or run 55 | modified versions of the software inside them, although the 56 | manufacturer can do so. This is fundamentally incompatible with the 57 | aim of protecting users' freedom to change the software. The 58 | systematic pattern of such abuse occurs in the area of products for 59 | individuals to use, which is precisely where it is most unacceptable. 60 | Therefore, we have designed this version of the GPL to prohibit the 61 | practice for those products. If such problems arise substantially in 62 | other domains, we stand ready to extend this provision to those 63 | domains in future versions of the GPL, as needed to protect the 64 | freedom of users. 65 | 66 | Finally, every program is threatened constantly by software patents. 67 | States should not allow patents to restrict development and use of 68 | software on general-purpose computers, but in those that do, we wish 69 | to avoid the special danger that patents applied to a free program 70 | could make it effectively proprietary. To prevent this, the GPL 71 | assures that patents cannot be used to render the program non-free. 72 | 73 | The precise terms and conditions for copying, distribution and 74 | modification follow. 75 | 76 | ### TERMS AND CONDITIONS 77 | 78 | #### 0. Definitions. 79 | 80 | "This License" refers to version 3 of the GNU General Public License. 81 | 82 | "Copyright" also means copyright-like laws that apply to other kinds 83 | of works, such as semiconductor masks. 84 | 85 | "The Program" refers to any copyrightable work licensed under this 86 | License. Each licensee is addressed as "you". "Licensees" and 87 | "recipients" may be individuals or organizations. 88 | 89 | To "modify" a work means to copy from or adapt all or part of the work 90 | in a fashion requiring copyright permission, other than the making of 91 | an exact copy. The resulting work is called a "modified version" of 92 | the earlier work or a work "based on" the earlier work. 93 | 94 | A "covered work" means either the unmodified Program or a work based 95 | on the Program. 96 | 97 | To "propagate" a work means to do anything with it that, without 98 | permission, would make you directly or secondarily liable for 99 | infringement under applicable copyright law, except executing it on a 100 | computer or modifying a private copy. Propagation includes copying, 101 | distribution (with or without modification), making available to the 102 | public, and in some countries other activities as well. 103 | 104 | To "convey" a work means any kind of propagation that enables other 105 | parties to make or receive copies. Mere interaction with a user 106 | through a computer network, with no transfer of a copy, is not 107 | conveying. 108 | 109 | An interactive user interface displays "Appropriate Legal Notices" to 110 | the extent that it includes a convenient and prominently visible 111 | feature that (1) displays an appropriate copyright notice, and (2) 112 | tells the user that there is no warranty for the work (except to the 113 | extent that warranties are provided), that licensees may convey the 114 | work under this License, and how to view a copy of this License. If 115 | the interface presents a list of user commands or options, such as a 116 | menu, a prominent item in the list meets this criterion. 117 | 118 | #### 1. Source Code. 119 | 120 | The "source code" for a work means the preferred form of the work for 121 | making modifications to it. "Object code" means any non-source form of 122 | a work. 123 | 124 | A "Standard Interface" means an interface that either is an official 125 | standard defined by a recognized standards body, or, in the case of 126 | interfaces specified for a particular programming language, one that 127 | is widely used among developers working in that language. 128 | 129 | The "System Libraries" of an executable work include anything, other 130 | than the work as a whole, that (a) is included in the normal form of 131 | packaging a Major Component, but which is not part of that Major 132 | Component, and (b) serves only to enable use of the work with that 133 | Major Component, or to implement a Standard Interface for which an 134 | implementation is available to the public in source code form. A 135 | "Major Component", in this context, means a major essential component 136 | (kernel, window system, and so on) of the specific operating system 137 | (if any) on which the executable work runs, or a compiler used to 138 | produce the work, or an object code interpreter used to run it. 139 | 140 | The "Corresponding Source" for a work in object code form means all 141 | the source code needed to generate, install, and (for an executable 142 | work) run the object code and to modify the work, including scripts to 143 | control those activities. However, it does not include the work's 144 | System Libraries, or general-purpose tools or generally available free 145 | programs which are used unmodified in performing those activities but 146 | which are not part of the work. For example, Corresponding Source 147 | includes interface definition files associated with source files for 148 | the work, and the source code for shared libraries and dynamically 149 | linked subprograms that the work is specifically designed to require, 150 | such as by intimate data communication or control flow between those 151 | subprograms and other parts of the work. 152 | 153 | The Corresponding Source need not include anything that users can 154 | regenerate automatically from other parts of the Corresponding Source. 155 | 156 | The Corresponding Source for a work in source code form is that same 157 | work. 158 | 159 | #### 2. Basic Permissions. 160 | 161 | All rights granted under this License are granted for the term of 162 | copyright on the Program, and are irrevocable provided the stated 163 | conditions are met. This License explicitly affirms your unlimited 164 | permission to run the unmodified Program. The output from running a 165 | covered work is covered by this License only if the output, given its 166 | content, constitutes a covered work. This License acknowledges your 167 | rights of fair use or other equivalent, as provided by copyright law. 168 | 169 | You may make, run and propagate covered works that you do not convey, 170 | without conditions so long as your license otherwise remains in force. 171 | You may convey covered works to others for the sole purpose of having 172 | them make modifications exclusively for you, or provide you with 173 | facilities for running those works, provided that you comply with the 174 | terms of this License in conveying all material for which you do not 175 | control copyright. Those thus making or running the covered works for 176 | you must do so exclusively on your behalf, under your direction and 177 | control, on terms that prohibit them from making any copies of your 178 | copyrighted material outside their relationship with you. 179 | 180 | Conveying under any other circumstances is permitted solely under the 181 | conditions stated below. Sublicensing is not allowed; section 10 makes 182 | it unnecessary. 183 | 184 | #### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 185 | 186 | No covered work shall be deemed part of an effective technological 187 | measure under any applicable law fulfilling obligations under article 188 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 189 | similar laws prohibiting or restricting circumvention of such 190 | measures. 191 | 192 | When you convey a covered work, you waive any legal power to forbid 193 | circumvention of technological measures to the extent such 194 | circumvention is effected by exercising rights under this License with 195 | respect to the covered work, and you disclaim any intention to limit 196 | operation or modification of the work as a means of enforcing, against 197 | the work's users, your or third parties' legal rights to forbid 198 | circumvention of technological measures. 199 | 200 | #### 4. Conveying Verbatim Copies. 201 | 202 | You may convey verbatim copies of the Program's source code as you 203 | receive it, in any medium, provided that you conspicuously and 204 | appropriately publish on each copy an appropriate copyright notice; 205 | keep intact all notices stating that this License and any 206 | non-permissive terms added in accord with section 7 apply to the code; 207 | keep intact all notices of the absence of any warranty; and give all 208 | recipients a copy of this License along with the Program. 209 | 210 | You may charge any price or no price for each copy that you convey, 211 | and you may offer support or warranty protection for a fee. 212 | 213 | #### 5. Conveying Modified Source Versions. 214 | 215 | You may convey a work based on the Program, or the modifications to 216 | produce it from the Program, in the form of source code under the 217 | terms of section 4, provided that you also meet all of these 218 | conditions: 219 | 220 | - a) The work must carry prominent notices stating that you modified 221 | it, and giving a relevant date. 222 | - b) The work must carry prominent notices stating that it is 223 | released under this License and any conditions added under 224 | section 7. This requirement modifies the requirement in section 4 225 | to "keep intact all notices". 226 | - c) You must license the entire work, as a whole, under this 227 | License to anyone who comes into possession of a copy. This 228 | License will therefore apply, along with any applicable section 7 229 | additional terms, to the whole of the work, and all its parts, 230 | regardless of how they are packaged. This License gives no 231 | permission to license the work in any other way, but it does not 232 | invalidate such permission if you have separately received it. 233 | - d) If the work has interactive user interfaces, each must display 234 | Appropriate Legal Notices; however, if the Program has interactive 235 | interfaces that do not display Appropriate Legal Notices, your 236 | work need not make them do so. 237 | 238 | A compilation of a covered work with other separate and independent 239 | works, which are not by their nature extensions of the covered work, 240 | and which are not combined with it such as to form a larger program, 241 | in or on a volume of a storage or distribution medium, is called an 242 | "aggregate" if the compilation and its resulting copyright are not 243 | used to limit the access or legal rights of the compilation's users 244 | beyond what the individual works permit. Inclusion of a covered work 245 | in an aggregate does not cause this License to apply to the other 246 | parts of the aggregate. 247 | 248 | #### 6. Conveying Non-Source Forms. 249 | 250 | You may convey a covered work in object code form under the terms of 251 | sections 4 and 5, provided that you also convey the machine-readable 252 | Corresponding Source under the terms of this License, in one of these 253 | ways: 254 | 255 | - a) Convey the object code in, or embodied in, a physical product 256 | (including a physical distribution medium), accompanied by the 257 | Corresponding Source fixed on a durable physical medium 258 | customarily used for software interchange. 259 | - b) Convey the object code in, or embodied in, a physical product 260 | (including a physical distribution medium), accompanied by a 261 | written offer, valid for at least three years and valid for as 262 | long as you offer spare parts or customer support for that product 263 | model, to give anyone who possesses the object code either (1) a 264 | copy of the Corresponding Source for all the software in the 265 | product that is covered by this License, on a durable physical 266 | medium customarily used for software interchange, for a price no 267 | more than your reasonable cost of physically performing this 268 | conveying of source, or (2) access to copy the Corresponding 269 | Source from a network server at no charge. 270 | - c) Convey individual copies of the object code with a copy of the 271 | written offer to provide the Corresponding Source. This 272 | alternative is allowed only occasionally and noncommercially, and 273 | only if you received the object code with such an offer, in accord 274 | with subsection 6b. 275 | - d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | - e) Convey the object code using peer-to-peer transmission, 288 | provided you inform other peers where the object code and 289 | Corresponding Source of the work are being offered to the general 290 | public at no charge under subsection 6d. 291 | 292 | A separable portion of the object code, whose source code is excluded 293 | from the Corresponding Source as a System Library, need not be 294 | included in conveying the object code work. 295 | 296 | A "User Product" is either (1) a "consumer product", which means any 297 | tangible personal property which is normally used for personal, 298 | family, or household purposes, or (2) anything designed or sold for 299 | incorporation into a dwelling. In determining whether a product is a 300 | consumer product, doubtful cases shall be resolved in favor of 301 | coverage. For a particular product received by a particular user, 302 | "normally used" refers to a typical or common use of that class of 303 | product, regardless of the status of the particular user or of the way 304 | in which the particular user actually uses, or expects or is expected 305 | to use, the product. A product is a consumer product regardless of 306 | whether the product has substantial commercial, industrial or 307 | non-consumer uses, unless such uses represent the only significant 308 | mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to 312 | install and execute modified versions of a covered work in that User 313 | Product from a modified version of its Corresponding Source. The 314 | information must suffice to ensure that the continued functioning of 315 | the modified object code is in no case prevented or interfered with 316 | solely because modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or 331 | updates for a work that has been modified or installed by the 332 | recipient, or for the User Product in which it has been modified or 333 | installed. Access to a network may be denied when the modification 334 | itself materially and adversely affects the operation of the network 335 | or violates the rules and protocols for communication across the 336 | network. 337 | 338 | Corresponding Source conveyed, and Installation Information provided, 339 | in accord with this section must be in a format that is publicly 340 | documented (and with an implementation available to the public in 341 | source code form), and must require no special password or key for 342 | unpacking, reading or copying. 343 | 344 | #### 7. Additional Terms. 345 | 346 | "Additional permissions" are terms that supplement the terms of this 347 | License by making exceptions from one or more of its conditions. 348 | Additional permissions that are applicable to the entire Program shall 349 | be treated as though they were included in this License, to the extent 350 | that they are valid under applicable law. If additional permissions 351 | apply only to part of the Program, that part may be used separately 352 | under those permissions, but the entire Program remains governed by 353 | this License without regard to the additional permissions. 354 | 355 | When you convey a copy of a covered work, you may at your option 356 | remove any additional permissions from that copy, or from any part of 357 | it. (Additional permissions may be written to require their own 358 | removal in certain cases when you modify the work.) You may place 359 | additional permissions on material, added by you to a covered work, 360 | for which you have or can give appropriate copyright permission. 361 | 362 | Notwithstanding any other provision of this License, for material you 363 | add to a covered work, you may (if authorized by the copyright holders 364 | of that material) supplement the terms of this License with terms: 365 | 366 | - a) Disclaiming warranty or limiting liability differently from the 367 | terms of sections 15 and 16 of this License; or 368 | - b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | - c) Prohibiting misrepresentation of the origin of that material, 372 | or requiring that modified versions of such material be marked in 373 | reasonable ways as different from the original version; or 374 | - d) Limiting the use for publicity purposes of names of licensors 375 | or authors of the material; or 376 | - e) Declining to grant rights under trademark law for use of some 377 | trade names, trademarks, or service marks; or 378 | - f) Requiring indemnification of licensors and authors of that 379 | material by anyone who conveys the material (or modified versions 380 | of it) with contractual assumptions of liability to the recipient, 381 | for any liability that these contractual assumptions directly 382 | impose on those licensors and authors. 383 | 384 | All other non-permissive additional terms are considered "further 385 | restrictions" within the meaning of section 10. If the Program as you 386 | received it, or any part of it, contains a notice stating that it is 387 | governed by this License along with a term that is a further 388 | restriction, you may remove that term. If a license document contains 389 | a further restriction but permits relicensing or conveying under this 390 | License, you may add to a covered work material governed by the terms 391 | of that license document, provided that the further restriction does 392 | not survive such relicensing or conveying. 393 | 394 | If you add terms to a covered work in accord with this section, you 395 | must place, in the relevant source files, a statement of the 396 | additional terms that apply to those files, or a notice indicating 397 | where to find the applicable terms. 398 | 399 | Additional terms, permissive or non-permissive, may be stated in the 400 | form of a separately written license, or stated as exceptions; the 401 | above requirements apply either way. 402 | 403 | #### 8. Termination. 404 | 405 | You may not propagate or modify a covered work except as expressly 406 | provided under this License. Any attempt otherwise to propagate or 407 | modify it is void, and will automatically terminate your rights under 408 | this License (including any patent licenses granted under the third 409 | paragraph of section 11). 410 | 411 | However, if you cease all violation of this License, then your license 412 | from a particular copyright holder is reinstated (a) provisionally, 413 | unless and until the copyright holder explicitly and finally 414 | terminates your license, and (b) permanently, if the copyright holder 415 | fails to notify you of the violation by some reasonable means prior to 416 | 60 days after the cessation. 417 | 418 | Moreover, your license from a particular copyright holder is 419 | reinstated permanently if the copyright holder notifies you of the 420 | violation by some reasonable means, this is the first time you have 421 | received notice of violation of this License (for any work) from that 422 | copyright holder, and you cure the violation prior to 30 days after 423 | your receipt of the notice. 424 | 425 | Termination of your rights under this section does not terminate the 426 | licenses of parties who have received copies or rights from you under 427 | this License. If your rights have been terminated and not permanently 428 | reinstated, you do not qualify to receive new licenses for the same 429 | material under section 10. 430 | 431 | #### 9. Acceptance Not Required for Having Copies. 432 | 433 | You are not required to accept this License in order to receive or run 434 | a copy of the Program. Ancillary propagation of a covered work 435 | occurring solely as a consequence of using peer-to-peer transmission 436 | to receive a copy likewise does not require acceptance. However, 437 | nothing other than this License grants you permission to propagate or 438 | modify any covered work. These actions infringe copyright if you do 439 | not accept this License. Therefore, by modifying or propagating a 440 | covered work, you indicate your acceptance of this License to do so. 441 | 442 | #### 10. Automatic Licensing of Downstream Recipients. 443 | 444 | Each time you convey a covered work, the recipient automatically 445 | receives a license from the original licensors, to run, modify and 446 | propagate that work, subject to this License. You are not responsible 447 | for enforcing compliance by third parties with this License. 448 | 449 | An "entity transaction" is a transaction transferring control of an 450 | organization, or substantially all assets of one, or subdividing an 451 | organization, or merging organizations. If propagation of a covered 452 | work results from an entity transaction, each party to that 453 | transaction who receives a copy of the work also receives whatever 454 | licenses to the work the party's predecessor in interest had or could 455 | give under the previous paragraph, plus a right to possession of the 456 | Corresponding Source of the work from the predecessor in interest, if 457 | the predecessor has it or can get it with reasonable efforts. 458 | 459 | You may not impose any further restrictions on the exercise of the 460 | rights granted or affirmed under this License. For example, you may 461 | not impose a license fee, royalty, or other charge for exercise of 462 | rights granted under this License, and you may not initiate litigation 463 | (including a cross-claim or counterclaim in a lawsuit) alleging that 464 | any patent claim is infringed by making, using, selling, offering for 465 | sale, or importing the Program or any portion of it. 466 | 467 | #### 11. Patents. 468 | 469 | A "contributor" is a copyright holder who authorizes use under this 470 | License of the Program or a work on which the Program is based. The 471 | work thus licensed is called the contributor's "contributor version". 472 | 473 | A contributor's "essential patent claims" are all patent claims owned 474 | or controlled by the contributor, whether already acquired or 475 | hereafter acquired, that would be infringed by some manner, permitted 476 | by this License, of making, using, or selling its contributor version, 477 | but do not include claims that would be infringed only as a 478 | consequence of further modification of the contributor version. For 479 | purposes of this definition, "control" includes the right to grant 480 | patent sublicenses in a manner consistent with the requirements of 481 | this License. 482 | 483 | Each contributor grants you a non-exclusive, worldwide, royalty-free 484 | patent license under the contributor's essential patent claims, to 485 | make, use, sell, offer for sale, import and otherwise run, modify and 486 | propagate the contents of its contributor version. 487 | 488 | In the following three paragraphs, a "patent license" is any express 489 | agreement or commitment, however denominated, not to enforce a patent 490 | (such as an express permission to practice a patent or covenant not to 491 | sue for patent infringement). To "grant" such a patent license to a 492 | party means to make such an agreement or commitment not to enforce a 493 | patent against the party. 494 | 495 | If you convey a covered work, knowingly relying on a patent license, 496 | and the Corresponding Source of the work is not available for anyone 497 | to copy, free of charge and under the terms of this License, through a 498 | publicly available network server or other readily accessible means, 499 | then you must either (1) cause the Corresponding Source to be so 500 | available, or (2) arrange to deprive yourself of the benefit of the 501 | patent license for this particular work, or (3) arrange, in a manner 502 | consistent with the requirements of this License, to extend the patent 503 | license to downstream recipients. "Knowingly relying" means you have 504 | actual knowledge that, but for the patent license, your conveying the 505 | covered work in a country, or your recipient's use of the covered work 506 | in a country, would infringe one or more identifiable patents in that 507 | country that you have reason to believe are valid. 508 | 509 | If, pursuant to or in connection with a single transaction or 510 | arrangement, you convey, or propagate by procuring conveyance of, a 511 | covered work, and grant a patent license to some of the parties 512 | receiving the covered work authorizing them to use, propagate, modify 513 | or convey a specific copy of the covered work, then the patent license 514 | you grant is automatically extended to all recipients of the covered 515 | work and works based on it. 516 | 517 | A patent license is "discriminatory" if it does not include within the 518 | scope of its coverage, prohibits the exercise of, or is conditioned on 519 | the non-exercise of one or more of the rights that are specifically 520 | granted under this License. You may not convey a covered work if you 521 | are a party to an arrangement with a third party that is in the 522 | business of distributing software, under which you make payment to the 523 | third party based on the extent of your activity of conveying the 524 | work, and under which the third party grants, to any of the parties 525 | who would receive the covered work from you, a discriminatory patent 526 | license (a) in connection with copies of the covered work conveyed by 527 | you (or copies made from those copies), or (b) primarily for and in 528 | connection with specific products or compilations that contain the 529 | covered work, unless you entered into that arrangement, or that patent 530 | license was granted, prior to 28 March 2007. 531 | 532 | Nothing in this License shall be construed as excluding or limiting 533 | any implied license or other defenses to infringement that may 534 | otherwise be available to you under applicable patent law. 535 | 536 | #### 12. No Surrender of Others' Freedom. 537 | 538 | If conditions are imposed on you (whether by court order, agreement or 539 | otherwise) that contradict the conditions of this License, they do not 540 | excuse you from the conditions of this License. If you cannot convey a 541 | covered work so as to satisfy simultaneously your obligations under 542 | this License and any other pertinent obligations, then as a 543 | consequence you may not convey it at all. For example, if you agree to 544 | terms that obligate you to collect a royalty for further conveying 545 | from those to whom you convey the Program, the only way you could 546 | satisfy both those terms and this License would be to refrain entirely 547 | from conveying the Program. 548 | 549 | #### 13. Use with the GNU Affero General Public License. 550 | 551 | Notwithstanding any other provision of this License, you have 552 | permission to link or combine any covered work with a work licensed 553 | under version 3 of the GNU Affero General Public License into a single 554 | combined work, and to convey the resulting work. The terms of this 555 | License will continue to apply to the part which is the covered work, 556 | but the special requirements of the GNU Affero General Public License, 557 | section 13, concerning interaction through a network will apply to the 558 | combination as such. 559 | 560 | #### 14. Revised Versions of this License. 561 | 562 | The Free Software Foundation may publish revised and/or new versions 563 | of the GNU General Public License from time to time. Such new versions 564 | will be similar in spirit to the present version, but may differ in 565 | detail to address new problems or concerns. 566 | 567 | Each version is given a distinguishing version number. If the Program 568 | specifies that a certain numbered version of the GNU General Public 569 | License "or any later version" applies to it, you have the option of 570 | following the terms and conditions either of that numbered version or 571 | of any later version published by the Free Software Foundation. If the 572 | Program does not specify a version number of the GNU General Public 573 | License, you may choose any version ever published by the Free 574 | Software Foundation. 575 | 576 | If the Program specifies that a proxy can decide which future versions 577 | of the GNU General Public License can be used, that proxy's public 578 | statement of acceptance of a version permanently authorizes you to 579 | choose that version for the Program. 580 | 581 | Later license versions may give you additional or different 582 | permissions. However, no additional obligations are imposed on any 583 | author or copyright holder as a result of your choosing to follow a 584 | later version. 585 | 586 | #### 15. Disclaimer of Warranty. 587 | 588 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 589 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 590 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT 591 | WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT 592 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 593 | A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND 594 | PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE 595 | DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR 596 | CORRECTION. 597 | 598 | #### 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR 602 | CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 603 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES 604 | ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT 605 | NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR 606 | LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM 607 | TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER 608 | PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 609 | 610 | #### 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | ### How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these 626 | terms. 627 | 628 | To do so, attach the following notices to the program. It is safest to 629 | attach them to the start of each source file to most effectively state 630 | the exclusion of warranty; and each file should have at least the 631 | "copyright" line and a pointer to where the full notice is found. 632 | 633 | 634 | Copyright (C) 635 | 636 | This program is free software: you can redistribute it and/or modify 637 | it under the terms of the GNU General Public License as published by 638 | the Free Software Foundation, either version 3 of the License, or 639 | (at your option) any later version. 640 | 641 | This program is distributed in the hope that it will be useful, 642 | but WITHOUT ANY WARRANTY; without even the implied warranty of 643 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 644 | GNU General Public License for more details. 645 | 646 | You should have received a copy of the GNU General Public License 647 | along with this program. If not, see . 648 | 649 | Also add information on how to contact you by electronic and paper 650 | mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands \`show w' and \`show c' should show the 661 | appropriate parts of the General Public License. Of course, your 662 | program's commands might be different; for a GUI interface, you would 663 | use an "about box". 664 | 665 | You should also get your employer (if you work as a programmer) or 666 | school, if any, to sign a "copyright disclaimer" for the program, if 667 | necessary. For more information on this, and how to apply and follow 668 | the GNU GPL, see . 669 | 670 | The GNU General Public License does not permit incorporating your 671 | program into proprietary programs. If your program is a subroutine 672 | library, you may consider it more useful to permit linking proprietary 673 | applications with the library. If this is what you want to do, use the 674 | GNU Lesser General Public License instead of this License. But first, 675 | please read . -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Single Sign-On for GLPI 2 | 3 | ![Lint](https://github.com/edgardmessias/glpi-singlesignon/workflows/Lint/badge.svg) 4 | [![CodeFactor](https://www.codefactor.io/repository/github/edgardmessias/glpi-singlesignon/badge)](https://www.codefactor.io/repository/github/edgardmessias/glpi-singlesignon) 5 | [![Total Downloads](https://img.shields.io/github/downloads/edgardmessias/glpi-singlesignon/total.svg)](https://github.com/edgardmessias/glpi-singlesignon/releases) 6 | [![Current Release](https://img.shields.io/github/release/edgardmessias/glpi-singlesignon.svg)](https://github.com/edgardmessias/glpi-singlesignon/releases/latest) 7 | 8 | Single sign-on (SSO) is a property of access control of multiple related, yet independent, software systems. With this property, a user logs in with a single ID and password to gain access to any of several related systems. 9 | 10 | # Installation 11 | * Uncompress the archive to the `/plugins/singlesignon` directory 12 | * Navigate to the Configuration > Plugins page, 13 | * Install and activate the plugin. 14 | 15 | # Usage 16 | * Go to `Configuration > Single Sign-On` and add a provider. You can find an explanation of the main configuration parameters [here](https://github.com/edgardmessias/glpi-singlesignon/wiki/Plugin-Provider-Options). 17 | * To test, do logout and try login with links below login page `Login with ` 18 | 19 | # Available providers 20 | * Azure - https://docs.microsoft.com/azure/app-service/configure-authentication-provider-aad 21 | * Facebook - https://developers.facebook.com/docs/apps/ 22 | * GitHub - https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/ 23 | * Google - https://developers.google.com/identity/protocols/OpenIDConnect 24 | * Instagram - https://www.instagram.com/developer/authentication/ 25 | * LinkedIn - https://docs.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow?context=linkedin/context 26 | * Generic - Allow to define custom URLs 27 | * Zitadel - use _Generic_ and see parameters in [Generic Examples - Zitadel](https://github.com/edgardmessias/glpi-singlesignon/wiki/Generic-Examples-%E2%80%90-Zitadel) 28 | 29 | # Adding translations 30 | If your preferred language is missing. You can add your own [translation on Transifex service](https://app.transifex.com/eduardomozart/glpi-singlesignon/languages/). 31 | 32 | # Adding a new release 33 | To create a new release of this plugin automatically through GitHub Actions (Workflow), edit the file ``plugin.xml`` to include the new version tag, GLPI compatible version and download URL and create a new branch. Remember to edit the ``setup.php`` file for the new plugin version. 34 | 35 | # Screenshots 36 | 37 | ![image 1](./screenshots/image_1.png) 38 | ![image 2](./screenshots/image_2.png) 39 | 40 | # Donation 41 | 42 | 43 | 49 | 54 | 55 |
44 | PayPal
45 | 46 | 47 | 48 |
50 | Pix (Brazil)
51 |
52 | 28571c52-8b9b-416c-a18f-8e5246060881 53 |
56 | -------------------------------------------------------------------------------- /RoboFile.php: -------------------------------------------------------------------------------- 1 | . 21 | * --------------------------------------------------------------------- 22 | * @copyright Copyright © 2021 - 2022 Edgard 23 | * @license http://www.gnu.org/licenses/gpl.txt GPLv3+ 24 | * @link https://github.com/edgardmessias/glpi-singlesignon/ 25 | * --------------------------------------------------------------------- 26 | */ 27 | 28 | use Symfony\Component\Finder\Finder; 29 | 30 | /** 31 | * This is project's console commands configuration for Robo task runner. 32 | * 33 | * @see http://robo.li/ 34 | */ 35 | class RoboFile extends \Robo\Tasks { 36 | 37 | protected $name = "singlesignon"; 38 | protected $issues = "https://github.com/edgardmessias/glpi-singlesignon/issues"; 39 | 40 | protected function getLocaleFiles() { 41 | $finder = new Finder(); 42 | $finder 43 | ->files() 44 | ->name('*.po') 45 | ->in('locales'); 46 | 47 | $files = []; 48 | foreach ($finder as $file) { 49 | $files[] = str_replace('\\', '/', $file->getRelativePathname()); 50 | } 51 | 52 | return $files; 53 | } 54 | 55 | public function compile_locales() { 56 | $files = $this->getLocaleFiles(); 57 | 58 | foreach ($files as $file) { 59 | $lang = basename($file, ".po"); 60 | 61 | $this->taskExec('msgfmt')->args([ 62 | "locales/$lang.po", 63 | "-o", 64 | "locales/$lang.mo", 65 | ])->run(); 66 | } 67 | } 68 | 69 | public function update_locales() { 70 | $finder = new Finder(); 71 | $finder 72 | ->files() 73 | ->name('*.php') 74 | ->in(__DIR__) 75 | ->exclude([ 76 | 'vendor' 77 | ]) 78 | ->sortByName(); 79 | 80 | if (!$finder->hasResults()) { 81 | return false; 82 | } 83 | 84 | $args = []; 85 | 86 | foreach ($finder as $file) { 87 | $args[] = str_replace('\\', '/', $file->getRelativePathname()); 88 | } 89 | 90 | $args[] = '-D'; 91 | $args[] = '.'; 92 | $args[] = '-o'; 93 | $args[] = "locales/{$this->name}.pot"; 94 | $args[] = '-L'; 95 | $args[] = 'PHP'; 96 | $args[] = '--add-comments=TRANS'; 97 | $args[] = '--from-code=UTF-8'; 98 | $args[] = '--force-po'; 99 | $args[] = '--keyword=__sso'; 100 | $args[] = "--package-name={$this->name}"; 101 | 102 | if ($this->issues) { 103 | $args[] = "--msgid-bugs-address={$this->issues}"; 104 | } 105 | 106 | try { 107 | $content = file_get_contents('setup.php'); 108 | $name = 'PLUGIN_' . strtoupper($this->name) . '_VERSION'; 109 | preg_match("/'$name',\s*'([\w\.]+)'/", $content, $matches); 110 | $args[] = '--package-version=' . $matches[1]; 111 | } catch (\Exception $ex) { 112 | echo $ex->getMessage(); 113 | } 114 | 115 | putenv("LANG=C"); 116 | 117 | $this->taskExec('xgettext')->args($args)->run(); 118 | 119 | $this->taskReplaceInFile("locales/{$this->name}.pot") 120 | ->from('CHARSET') 121 | ->to('UTF-8') 122 | ->run(); 123 | 124 | $this->taskExec('msginit')->args([ 125 | '--no-translator', 126 | '-i', 127 | "locales/{$this->name}.pot", 128 | '-l', 129 | 'en_GB.UTF8', 130 | '-o', 131 | 'locales/en_GB.po', 132 | ])->run(); 133 | 134 | $files = $this->getLocaleFiles(); 135 | 136 | foreach ($files as $file) { 137 | $lang = basename($file, ".po"); 138 | 139 | if ($lang === "en_GB") { 140 | continue; 141 | } 142 | 143 | $this->taskExec('msgmerge')->args([ 144 | "--update", 145 | "locales/$lang.po", 146 | "locales/{$this->name}.pot", 147 | "--lang=$lang", 148 | "--backup=off", 149 | ])->run(); 150 | } 151 | 152 | $this->compile_locales(); 153 | } 154 | 155 | public function build() { 156 | $this->_remove(["$this->name.zip", "$this->name.tgz"]); 157 | 158 | $this->compile_locales(); 159 | 160 | $tmpPath = $this->_tmpDir(); 161 | 162 | $exclude = glob(__DIR__ . '/.*'); 163 | $exclude[] = 'plugin.xml'; 164 | $exclude[] = 'RoboFile.php'; 165 | $exclude[] = 'screenshots'; 166 | $exclude[] = 'tools'; 167 | $exclude[] = 'vendor'; 168 | $exclude[] = "$this->name.zip"; 169 | $exclude[] = "$this->name.tgz"; 170 | 171 | $this->taskCopyDir([__DIR__ => $tmpPath]) 172 | ->exclude($exclude) 173 | ->run(); 174 | 175 | $composer_file = "$tmpPath/composer.json"; 176 | if (file_exists($composer_file)) { 177 | $hasDep = false; 178 | try { 179 | $data = json_decode(file_get_contents($composer_file), true); 180 | $hasDep = isset($data['require']) && count($data['require']) > 0; 181 | } catch (\Exception $ex) { 182 | $hasDep = true; 183 | } 184 | 185 | if ($hasDep) { 186 | $this->taskComposerInstall() 187 | ->workingDir($tmpPath) 188 | ->noDev() 189 | ->run(); 190 | } 191 | } 192 | 193 | $this->_remove("$tmpPath/composer.lock"); 194 | 195 | // Pack 196 | $this->taskPack("$this->name.zip") 197 | ->addDir($this->name, $tmpPath) 198 | ->run(); 199 | 200 | $this->taskPack("$this->name.tgz") 201 | ->addDir($this->name, $tmpPath) 202 | ->run(); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edgardmessias/glpi-singlesignon", 3 | "description": "Single Sign-On (OAuth) for GLPI", 4 | "license": "GPL-3.0-or-later", 5 | "require-dev": { 6 | "consolidation/robo": "^1.4", 7 | "glpi-project/coding-standard": "^0.8", 8 | "pear/archive_tar": "^1.4", 9 | "phpcompatibility/php-compatibility": "^9.3", 10 | "squizlabs/php_codesniffer": "^3.5" 11 | }, 12 | "scripts": { 13 | "lint": "phpcs", 14 | "lint:fix": "phpcbf", 15 | "make-release": "robo build", 16 | "update-locales": "robo update_locales" 17 | }, 18 | "config": { 19 | "allow-plugins": { 20 | "dealerdirect/phpcodesniffer-composer-installer": true 21 | }, 22 | "platform": { 23 | "php": "7.1.2" 24 | }, 25 | "sort-packages": true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /front/callback.php: -------------------------------------------------------------------------------- 1 | . 21 | * --------------------------------------------------------------------- 22 | * @copyright Copyright © 2021 - 2022 Edgard 23 | * @license http://www.gnu.org/licenses/gpl.txt GPLv3+ 24 | * @link https://github.com/edgardmessias/glpi-singlesignon/ 25 | * --------------------------------------------------------------------- 26 | */ 27 | 28 | //Disable CSRF token 29 | define('GLPI_USE_CSRF_CHECK', 0); 30 | 31 | ini_set('display_errors', 1); 32 | ini_set('display_startup_errors', 1); 33 | error_reporting(E_ALL); 34 | 35 | include('../../../inc/includes.php'); 36 | 37 | $provider_id = PluginSinglesignonToolbox::getCallbackParameters('provider'); 38 | 39 | if (!$provider_id) { 40 | Html::displayErrorAndDie(__sso("Provider not defined."), false); 41 | } 42 | 43 | $signon_provider = new PluginSinglesignonProvider(); 44 | 45 | if (!$signon_provider->getFromDB($provider_id)) { 46 | Html::displayErrorAndDie(__sso("Provider not found."), true); 47 | } 48 | 49 | if (!$signon_provider->fields['is_active']) { 50 | Html::displayErrorAndDie(__sso("Provider not active."), true); 51 | } 52 | 53 | $signon_provider->checkAuthorization(); 54 | 55 | $test = PluginSinglesignonToolbox::getCallbackParameters('test'); 56 | 57 | if ($test) { 58 | $signon_provider->debug = true; 59 | Html::nullHeader("Login", PluginSinglesignonToolbox::getBaseURL() . '/index.php'); 60 | echo '
'; 61 | echo '
';
 62 |    echo "### BEGIN ###\n";
 63 |    $signon_provider->getResourceOwner();
 64 |    echo "### END ###";
 65 |    echo '
'; 66 | Html::nullFooter(); 67 | exit(); 68 | } 69 | 70 | $user_id = Session::getLoginUserID(); 71 | 72 | $REDIRECT = ""; 73 | 74 | if ($user_id || $signon_provider->login()) { 75 | 76 | $user_id = $user_id ?: Session::getLoginUserID(); 77 | 78 | if ($user_id) { 79 | $signon_provider->linkUser($user_id); 80 | } 81 | 82 | $params = PluginSinglesignonToolbox::getCallbackParameters('q'); 83 | 84 | if (isset($params['redirect'])) { 85 | $REDIRECT = '?redirect=' . $params['redirect']; 86 | } else if (isset($_GET['state']) && is_integer(strpos($_GET['state'], ";redirect="))) { 87 | $REDIRECT = '?' . substr($_GET['state'], strpos($_GET['state'], ";redirect=") + 1); 88 | } 89 | 90 | $url_redirect = ''; 91 | 92 | if ($_SESSION["glpiactiveprofile"]["interface"] == "helpdesk") { 93 | if ($_SESSION['glpiactiveprofile']['create_ticket_on_login'] && empty($REDIRECT)) { 94 | $url_redirect = PluginSinglesignonToolbox::getBaseURL() . "/front/helpdesk.public.php?create_ticket=1"; 95 | } else { 96 | $url_redirect = PluginSinglesignonToolbox::getBaseURL() . "/front/helpdesk.public.php$REDIRECT"; 97 | } 98 | } else { 99 | if ($_SESSION['glpiactiveprofile']['create_ticket_on_login'] && empty($REDIRECT)) { 100 | $url_redirect = PluginSinglesignonToolbox::getBaseURL() . "/front/ticket.form.php"; 101 | } else { 102 | $url_redirect = PluginSinglesignonToolbox::getBaseURL() . "/front/central.php$REDIRECT"; 103 | } 104 | } 105 | 106 | Html::nullHeader("Login", PluginSinglesignonToolbox::getBaseURL() . '/index.php'); 107 | echo '
' . 108 | __sso('Automatic redirection, else click') . ''; 109 | echo '
'; 117 | Html::nullFooter(); 118 | exit(); 119 | 120 | // Auth::redirectIfAuthenticated(); 121 | 122 | } 123 | 124 | // we have done at least a good login? No, we exit. 125 | Html::nullHeader("Login", PluginSinglesignonToolbox::getBaseURL() . '/index.php'); 126 | echo '
' . __('User not authorized to connect in GLPI') . '

'; 127 | // Logout whit noAUto to manage auto_login with errors 128 | echo '' . __('Log in again') . '
'; 130 | echo ''; 140 | Html::nullFooter(); 141 | exit(); 142 | -------------------------------------------------------------------------------- /front/picture.send.php: -------------------------------------------------------------------------------- 1 | . 21 | * --------------------------------------------------------------------- 22 | * @copyright Copyright © 2021 - 2022 Edgard 23 | * @license http://www.gnu.org/licenses/gpl.txt GPLv3+ 24 | * @link https://github.com/edgardmessias/glpi-singlesignon/ 25 | * --------------------------------------------------------------------- 26 | */ 27 | 28 | include('../../../inc/includes.php'); 29 | 30 | $provider = new PluginSinglesignonProvider(); 31 | $path = false; 32 | 33 | if (isset($_GET['id'])) { // docid for document 34 | if (!$provider->getFromDB($_GET['id'])) { 35 | Html::displayErrorAndDie(__('Unknown file'), true); 36 | } 37 | 38 | $path = $provider->fields['picture']; 39 | } else if (isset($_GET['path'])) { 40 | $path = $_GET['path']; 41 | } else { 42 | Html::displayErrorAndDie(__('Invalid filename'), true); 43 | } 44 | 45 | $path = GLPI_PLUGIN_DOC_DIR . "/singlesignon/" . $path; 46 | 47 | if (!file_exists($path)) { 48 | Html::displayErrorAndDie(__('File not found'), true); // Not found 49 | } 50 | 51 | Toolbox::sendFile($path, "logo.png", null, true); 52 | -------------------------------------------------------------------------------- /front/preference.form.php: -------------------------------------------------------------------------------- 1 | . 21 | * --------------------------------------------------------------------- 22 | * @copyright Copyright © 2021 - 2022 Edgard 23 | * @license http://www.gnu.org/licenses/gpl.txt GPLv3+ 24 | * @link https://github.com/edgardmessias/glpi-singlesignon/ 25 | * --------------------------------------------------------------------- 26 | */ 27 | 28 | include ('../../../inc/includes.php'); 29 | 30 | Session::checkLoginUser(); 31 | 32 | if (isset($_POST["update"])) { 33 | 34 | $prefer = new PluginSinglesignonPreference(Session::getLoginUserID()); 35 | $prefer->loadProviders(); 36 | 37 | $prefer->update($_POST); 38 | 39 | Html::back(); 40 | } else { 41 | Html::back(); 42 | } 43 | -------------------------------------------------------------------------------- /front/provider.form.php: -------------------------------------------------------------------------------- 1 | . 21 | * --------------------------------------------------------------------- 22 | * @copyright Copyright © 2021 - 2022 Edgard 23 | * @license http://www.gnu.org/licenses/gpl.txt GPLv3+ 24 | * @link https://github.com/edgardmessias/glpi-singlesignon/ 25 | * --------------------------------------------------------------------- 26 | */ 27 | 28 | use Glpi\Event; 29 | 30 | include ('../../../inc/includes.php'); 31 | 32 | Session::checkRight("config", UPDATE); 33 | 34 | if (!isset($_GET["id"])) { 35 | $_GET["id"] = ""; 36 | } 37 | 38 | $provider = new PluginSinglesignonProvider(); 39 | 40 | if (isset($_POST["add"])) { 41 | $provider->check(-1, CREATE, $_POST); 42 | 43 | if ($newID = $provider->add($_POST)) { 44 | Event::log($newID, "singlesignon", 4, "provider", 45 | sprintf(__('%1$s adds the item %2$s'), $_SESSION["glpiname"], $_POST["name"])); 46 | if ($_SESSION['glpibackcreated']) { 47 | Html::redirect($provider->getLinkURL()); 48 | } 49 | } 50 | Html::back(); 51 | } else if (isset($_POST["delete"])) { 52 | $provider->check($_POST["id"], DELETE); 53 | $provider->delete($_POST); 54 | 55 | Event::log($_POST["id"], "singlesignon", 4, "provider", 56 | //TRANS: %s is the user login 57 | sprintf(__('%s deletes an item'), $_SESSION["glpiname"])); 58 | 59 | $provider->redirectToList(); 60 | } else if (isset($_POST["restore"])) { 61 | $provider->check($_POST["id"], DELETE); 62 | 63 | $provider->restore($_POST); 64 | Event::log($_POST["id"], "singlesignon", 4, "provider", 65 | //TRANS: %s is the user login 66 | sprintf(__('%s restores an item'), $_SESSION["glpiname"])); 67 | $provider->redirectToList(); 68 | } else if (isset($_POST["purge"])) { 69 | $provider->check($_POST["id"], PURGE); 70 | 71 | $provider->delete($_POST, 1); 72 | Event::log($_POST["id"], "singlesignon", 4, "provider", 73 | //TRANS: %s is the user login 74 | sprintf(__('%s purges an item'), $_SESSION["glpiname"])); 75 | $provider->redirectToList(); 76 | } else if (isset($_POST["update"])) { 77 | $provider->check($_POST["id"], UPDATE); 78 | 79 | $provider->update($_POST); 80 | Event::log($_POST["id"], "singlesignon", 4, "provider", 81 | //TRANS: %s is the user login 82 | sprintf(__('%s updates an item'), $_SESSION["glpiname"])); 83 | Html::back(); 84 | } else { 85 | if ($_SESSION["glpiactiveprofile"]["interface"] == "central") { 86 | Html::header(__sso('Single Sign-on'), $_SERVER['PHP_SELF'], "config", "pluginsinglesignonprovider", ""); 87 | } else { 88 | Html::helpHeader(__sso('Single Sign-on'), $_SERVER['PHP_SELF']); 89 | } 90 | 91 | $provider->display($_GET); 92 | } 93 | 94 | 95 | Html::footer(); 96 | -------------------------------------------------------------------------------- /front/provider.php: -------------------------------------------------------------------------------- 1 | . 21 | * --------------------------------------------------------------------- 22 | * @copyright Copyright © 2021 - 2022 Edgard 23 | * @license http://www.gnu.org/licenses/gpl.txt GPLv3+ 24 | * @link https://github.com/edgardmessias/glpi-singlesignon/ 25 | * --------------------------------------------------------------------- 26 | */ 27 | 28 | include ('../../../inc/includes.php'); 29 | 30 | Session::checkRight("config", UPDATE); 31 | 32 | if ($_SESSION["glpiactiveprofile"]["interface"] == "central") { 33 | Html::header(__sso('Single Sign-on'), $_SERVER['PHP_SELF'], "config", "pluginsinglesignonprovider", ""); 34 | } else { 35 | Html::helpHeader(__sso('Single Sign-on'), $_SERVER['PHP_SELF']); 36 | } 37 | 38 | 39 | //checkTypeRight('PluginExampleExample',"r"); 40 | 41 | Search::show('PluginSinglesignonProvider'); 42 | 43 | Html::footer(); 44 | -------------------------------------------------------------------------------- /front/user.form.php: -------------------------------------------------------------------------------- 1 | . 21 | * --------------------------------------------------------------------- 22 | * @copyright Copyright © 2021 - 2022 Edgard 23 | * @license http://www.gnu.org/licenses/gpl.txt GPLv3+ 24 | * @link https://github.com/edgardmessias/glpi-singlesignon/ 25 | * --------------------------------------------------------------------- 26 | */ 27 | 28 | include('../../../inc/includes.php'); 29 | 30 | Session::checkRight(User::$rightname, UPDATE); 31 | 32 | if (isset($_POST["update"]) && isset($_POST["user_id"])) { 33 | 34 | $prefer = new PluginSinglesignonPreference((int) $_POST["user_id"]); 35 | $prefer->loadProviders(); 36 | 37 | $prefer->update($_POST); 38 | 39 | Html::back(); 40 | } else { 41 | Html::back(); 42 | } 43 | -------------------------------------------------------------------------------- /hook.php: -------------------------------------------------------------------------------- 1 | . 21 | * --------------------------------------------------------------------- 22 | * @copyright Copyright © 2021 - 2022 Edgard 23 | * @license http://www.gnu.org/licenses/gpl.txt GPLv3+ 24 | * @link https://github.com/edgardmessias/glpi-singlesignon/ 25 | * --------------------------------------------------------------------- 26 | */ 27 | 28 | function plugin_singlesignon_display_login() { 29 | global $CFG_GLPI; 30 | 31 | $signon_provider = new PluginSinglesignonProvider(); 32 | 33 | $condition = '`is_active` = 1'; 34 | if (version_compare(GLPI_VERSION, '9.4', '>=')) { 35 | $condition = [$condition]; 36 | } 37 | $rows = $signon_provider->find($condition); 38 | 39 | $html = []; 40 | 41 | foreach ($rows as $row) { 42 | $query = []; 43 | 44 | if (isset($_REQUEST['redirect'])) { 45 | $query['redirect'] = $_REQUEST['redirect']; 46 | } 47 | 48 | $url = PluginSinglesignonToolbox::getCallbackUrl($row['id'], $query); 49 | $isDefault = PluginSinglesignonToolbox::isDefault($row); 50 | if ($isDefault && !isset($_GET["noAUTO"])) { 51 | Html::redirect($url); 52 | return; 53 | } 54 | $html[] = PluginSinglesignonToolbox::renderButton($url, $row); 55 | } 56 | 57 | if (!empty($html)) { 58 | echo '
'; 59 | echo implode(" \n", $html); 60 | echo PluginSinglesignonToolbox::renderButton('#', ['name' => __('GLPI')], 'vsubmit old-login'); 61 | echo '
'; 62 | ?> 63 | 91 | 191 | $value) { 210 | if (!isset($current[$key])) { 211 | $current[$key] = $value; 212 | } 213 | } 214 | 215 | Config::setConfigurationValues('singlesignon', $current); 216 | 217 | if (!sso_TableExists("glpi_plugin_singlesignon_providers")) { 218 | $query = "CREATE TABLE `glpi_plugin_singlesignon_providers` ( 219 | `id` int(11) NOT NULL auto_increment, 220 | `is_default` tinyint(1) NOT NULL DEFAULT '0', 221 | `popup` tinyint(1) NOT NULL DEFAULT '0', 222 | `split_domain` tinyint(1) NOT NULL DEFAULT '0', 223 | `authorized_domains` varchar(255) COLLATE utf8_unicode_ci NULL, 224 | `type` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 225 | `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 226 | `client_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 227 | `client_secret` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 228 | `scope` varchar(255) COLLATE utf8_unicode_ci NULL, 229 | `extra_options` varchar(255) COLLATE utf8_unicode_ci NULL, 230 | `url_authorize` varchar(255) COLLATE utf8_unicode_ci NULL, 231 | `url_access_token` varchar(255) COLLATE utf8_unicode_ci NULL, 232 | `url_resource_owner_details` varchar(255) COLLATE utf8_unicode_ci NULL, 233 | `is_active` tinyint(1) NOT NULL DEFAULT '0', 234 | `use_email_for_login` tinyint(1) NOT NULL DEFAULT '0', 235 | `split_name` tinyint(1) NOT NULL DEFAULT '0', 236 | `is_deleted` tinyint(1) NOT NULL default '0', 237 | `comment` text COLLATE utf8_unicode_ci, 238 | `date_mod` timestamp NULL DEFAULT NULL, 239 | `date_creation` timestamp NULL DEFAULT NULL, 240 | PRIMARY KEY (`id`), 241 | KEY `date_mod` (`date_mod`), 242 | KEY `date_creation` (`date_creation`) 243 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci"; 244 | 245 | $DB->query($query) or die("error creating glpi_plugin_singlesignon_providers " . $DB->error()); 246 | } else { 247 | $query = "SHOW COLUMNS FROM glpi_plugin_singlesignon_providers LIKE 'is_default'"; 248 | $result = $DB->query($query) or die($DB->error()); 249 | if ($DB->numrows($result) != 1) { 250 | $DB->query("ALTER TABLE glpi_plugin_singlesignon_providers ADD is_default tinyint(1) NOT NULL DEFAULT '0'") or die($DB->error()); 251 | } 252 | 253 | $query = "SHOW COLUMNS FROM glpi_plugin_singlesignon_providers LIKE 'popup'"; 254 | $result = $DB->query($query) or die($DB->error()); 255 | if ($DB->numrows($result) != 1) { 256 | $DB->query("ALTER TABLE glpi_plugin_singlesignon_providers ADD popup tinyint(1) NOT NULL DEFAULT '0'") or die($DB->error()); 257 | } 258 | $query = "SHOW COLUMNS FROM glpi_plugin_singlesignon_providers LIKE 'split_domain'"; 259 | $result = $DB->query($query) or die($DB->error()); 260 | if ($DB->numrows($result) != 1) { 261 | $DB->query("ALTER TABLE glpi_plugin_singlesignon_providers ADD split_domain tinyint(1) NOT NULL DEFAULT '0'") or die($DB->error()); 262 | } 263 | $query = "SHOW COLUMNS FROM glpi_plugin_singlesignon_providers LIKE 'authorized_domains'"; 264 | $result = $DB->query($query) or die($DB->error()); 265 | if ($DB->numrows($result) != 1) { 266 | $DB->query("ALTER TABLE glpi_plugin_singlesignon_providers ADD authorized_domains varchar(255) COLLATE utf8_unicode_ci NULL") or die($DB->error()); 267 | } 268 | $query = "SHOW COLUMNS FROM glpi_plugin_singlesignon_providers LIKE 'use_email_for_login'"; 269 | $result = $DB->query($query) or die($DB->error()); 270 | if ($DB->numrows($result) != 1) { 271 | $DB->query("ALTER TABLE glpi_plugin_singlesignon_providers ADD use_email_for_login tinyint(1) NOT NULL DEFAULT '0'") or die($DB->error()); 272 | } 273 | $query = "SHOW COLUMNS FROM glpi_plugin_singlesignon_providers LIKE 'split_name'"; 274 | $result = $DB->query($query) or die($DB->error()); 275 | if ($DB->numrows($result) != 1) { 276 | $DB->query("ALTER TABLE glpi_plugin_singlesignon_providers ADD split_name tinyint(1) NOT NULL DEFAULT '0'") or die($DB->error()); 277 | } 278 | } 279 | 280 | // add display preferences 281 | $query_display_pref = "SELECT id 282 | FROM glpi_displaypreferences 283 | WHERE itemtype = 'PluginSinglesignonProvider'"; 284 | $res_display_pref = $DB->query($query_display_pref); 285 | if ($DB->numrows($res_display_pref) == 0) { 286 | $DB->query("INSERT INTO `glpi_displaypreferences` VALUES (NULL,'PluginSinglesignonProvider','2','1','0');"); 287 | $DB->query("INSERT INTO `glpi_displaypreferences` VALUES (NULL,'PluginSinglesignonProvider','3','2','0');"); 288 | $DB->query("INSERT INTO `glpi_displaypreferences` VALUES (NULL,'PluginSinglesignonProvider','5','4','0');"); 289 | $DB->query("INSERT INTO `glpi_displaypreferences` VALUES (NULL,'PluginSinglesignonProvider','6','5','0');"); 290 | $DB->query("INSERT INTO `glpi_displaypreferences` VALUES (NULL,'PluginSinglesignonProvider','10','6','0');"); 291 | } 292 | 293 | if (!sso_TableExists("glpi_plugin_singlesignon_providers_users") && version_compare($currentVersion, "1.2.0", '<')) { 294 | $query = "ALTER TABLE `glpi_plugin_singlesignon_providers` 295 | ADD `picture` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 296 | ADD `bgcolor` varchar(7) DEFAULT NULL, 297 | ADD `color` varchar(7) DEFAULT NULL"; 298 | $DB->query($query) or die("error adding picture column " . $DB->error()); 299 | } 300 | if (!sso_TableExists("glpi_plugin_singlesignon_providers_users") && version_compare($currentVersion, "1.3.0", '<')) { 301 | $query = "CREATE TABLE `glpi_plugin_singlesignon_providers_users` ( 302 | `id` int(11) NOT NULL AUTO_INCREMENT, 303 | `plugin_singlesignon_providers_id` int(11) NOT NULL DEFAULT '0', 304 | `users_id` int(11) NOT NULL DEFAULT '0', 305 | `remote_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 306 | PRIMARY KEY (`id`), 307 | UNIQUE KEY `unicity` (`plugin_singlesignon_providers_id`,`users_id`), 308 | UNIQUE KEY `unicity_remote` (`plugin_singlesignon_providers_id`,`remote_id`) 309 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;"; 310 | $DB->query($query) or die("error creating glpi_plugin_singlesignon_providers_users " . $DB->error()); 311 | } 312 | 313 | Config::setConfigurationValues('singlesignon', [ 314 | 'version' => PLUGIN_SINGLESIGNON_VERSION, 315 | ]); 316 | return true; 317 | } 318 | 319 | function plugin_singlesignon_uninstall() { 320 | global $DB; 321 | 322 | $config = new Config(); 323 | $condition = "`context` LIKE 'singlesignon%'"; 324 | if (version_compare(GLPI_VERSION, '9.4', '>=')) { 325 | $condition = [$condition]; 326 | } 327 | $rows = $config->find($condition); 328 | 329 | foreach ($rows as $id => $row) { 330 | $config->delete(['id' => $id]); 331 | } 332 | 333 | // Old version tables 334 | if (sso_TableExists("glpi_plugin_singlesignon_providers")) { 335 | $query = "DROP TABLE `glpi_plugin_singlesignon_providers`"; 336 | $DB->query($query) or die("error deleting glpi_plugin_singlesignon_providers"); 337 | } 338 | 339 | return true; 340 | } 341 | -------------------------------------------------------------------------------- /inc/preference.class.php: -------------------------------------------------------------------------------- 1 | . 21 | * --------------------------------------------------------------------- 22 | * @copyright Copyright © 2021 - 2022 Edgard 23 | * @license http://www.gnu.org/licenses/gpl.txt GPLv3+ 24 | * @link https://github.com/edgardmessias/glpi-singlesignon/ 25 | * --------------------------------------------------------------------- 26 | */ 27 | 28 | class PluginSinglesignonPreference extends CommonDBTM { 29 | 30 | static protected $notable = true; 31 | static $rightname = ''; 32 | 33 | // Provider data 34 | public $user_id = null; 35 | public $providers = []; 36 | public $providers_users = []; 37 | 38 | public function __construct($user_id = null) { 39 | parent::__construct(); 40 | 41 | $this->user_id = $user_id; 42 | } 43 | 44 | public function loadProviders() { 45 | $signon_provider = new PluginSinglesignonProvider(); 46 | 47 | $condition = '`is_active` = 1'; 48 | if (version_compare(GLPI_VERSION, '9.4', '>=')) { 49 | $condition = [$condition]; 50 | } 51 | $this->providers = $signon_provider->find($condition); 52 | 53 | $provider_user = new PluginSinglesignonProvider_User(); 54 | 55 | $condition = "`users_id` = {$this->user_id}"; 56 | if (version_compare(GLPI_VERSION, '9.4', '>=')) { 57 | $condition = [$condition]; 58 | } 59 | $this->providers_users = $provider_user->find($condition); 60 | } 61 | 62 | public function update(array $input, $history = 1, $options = []) { 63 | if (!isset($input['_remove_sso']) || !is_array($input['_remove_sso'])) { 64 | return false; 65 | } 66 | 67 | $ids = $input['_remove_sso']; 68 | if (empty($ids)) { 69 | return false; 70 | } 71 | 72 | $provider_user = new PluginSinglesignonProvider_User(); 73 | $condition = "`users_id` = {$this->user_id} AND `id` IN (" . implode(',', $ids) . ")"; 74 | if (version_compare(GLPI_VERSION, '9.4', '>=')) { 75 | $condition = [$condition]; 76 | } 77 | 78 | $providers_users = $provider_user->find($condition); 79 | 80 | foreach ($providers_users as $pu) { 81 | $provider_user->delete($pu); 82 | } 83 | } 84 | 85 | function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) { 86 | switch (get_class($item)) { 87 | case 'Preference': 88 | case 'User': 89 | return [1 => __sso('Single Sign-on')]; 90 | default: 91 | return ''; 92 | } 93 | } 94 | 95 | static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) { 96 | switch (get_class($item)) { 97 | case 'User': 98 | $prefer = new self($item->fields['id']); 99 | $prefer->loadProviders(); 100 | $prefer->showFormUser($item); 101 | break; 102 | case 'Preference': 103 | $prefer = new self(Session::getLoginUserID()); 104 | $prefer->loadProviders(); 105 | $prefer->showFormPreference($item); 106 | break; 107 | } 108 | return true; 109 | } 110 | 111 | function showFormUser(CommonGLPI $item) { 112 | global $CFG_GLPI; 113 | 114 | if (!User::canView()) { 115 | return false; 116 | } 117 | $canedit = Session::haveRight(User::$rightname, UPDATE); 118 | if ($canedit) { 119 | echo "
"; 120 | } 121 | echo Html::hidden('user_id', ['value' => $this->user_id]); 122 | 123 | echo "
"; 124 | echo ""; 125 | 126 | echo ""; 127 | 128 | $this->showFormDefault($item); 129 | 130 | if ($canedit) { 131 | echo ""; 132 | echo ""; 135 | } 136 | 137 | echo "
" . __('Settings') . "
"; 133 | echo ""; 134 | echo "
"; 138 | Html::closeForm(); 139 | } 140 | 141 | function showFormPreference(CommonGLPI $item) { 142 | $user = new User(); 143 | if (!$user->can($this->user_id, READ) && ($this->user_id != Session::getLoginUserID())) { 144 | return false; 145 | } 146 | $canedit = $this->user_id == Session::getLoginUserID(); 147 | 148 | if ($canedit) { 149 | echo ""; 150 | } 151 | 152 | echo "
"; 153 | echo ""; 154 | 155 | echo ""; 156 | 157 | $this->showFormDefault($item); 158 | 159 | if ($canedit) { 160 | echo ""; 161 | echo ""; 164 | } 165 | 166 | echo "
" . __('Settings') . "
"; 162 | echo ""; 163 | echo "
"; 167 | Html::closeForm(); 168 | } 169 | 170 | function showFormDefault(CommonGLPI $item) { 171 | echo ""; 172 | echo " " . __sso('Single Sign-on Provider') . ""; 173 | 174 | foreach ($this->providers as $p) { 175 | switch (get_class($item)) { 176 | case 'User': 177 | $redirect = $item->getFormURLWithID($this->user_id, true); 178 | break; 179 | case 'Preference': 180 | $redirect = $item->getSearchURL(false); 181 | break; 182 | default: 183 | $redirect = ''; 184 | } 185 | 186 | $url = PluginSinglesignonToolbox::getCallbackUrl($p['id'], ['redirect' => $redirect]); 187 | 188 | echo PluginSinglesignonToolbox::renderButton($url, $p); 189 | echo " "; 190 | } 191 | 192 | echo ""; 193 | 194 | echo ""; 195 | 196 | if (!empty($this->providers_users)) { 197 | echo "" . __sso('Linked accounts') . ""; 198 | 199 | foreach ($this->providers_users as $pu) { 200 | /** @var PluginSinglesignonProvider */ 201 | $provider = PluginSinglesignonProvider::getById($pu['plugin_singlesignon_providers_id']); 202 | 203 | echo ""; 204 | echo $provider->fields['name'] . ' (ID:' . $pu['remote_id'] . ')'; 205 | echo ""; 206 | echo Html::getCheckbox([ 207 | 'title' => __('Clear'), 208 | 'name' => "_remove_sso[]", 209 | 'value' => $pu['id'], 210 | ]); 211 | echo " " . __('Clear'); 212 | echo ""; 213 | } 214 | } 215 | 216 | ?> 217 | 234 | . 21 | * --------------------------------------------------------------------- 22 | * @copyright Copyright © 2021 - 2022 Edgard 23 | * @license http://www.gnu.org/licenses/gpl.txt GPLv3+ 24 | * @link https://github.com/edgardmessias/glpi-singlesignon/ 25 | * --------------------------------------------------------------------- 26 | */ 27 | 28 | class PluginSinglesignonProvider_User extends CommonDBRelation { 29 | 30 | // From CommonDBRelation 31 | static public $itemtype_1 = 'PluginSinglesignonProvider'; 32 | static public $items_id_1 = 'plugin_singlesignon_providers_id'; 33 | 34 | static public $itemtype_2 = 'User'; 35 | static public $items_id_2 = 'users_id'; 36 | } 37 | -------------------------------------------------------------------------------- /inc/toolbox.class.php: -------------------------------------------------------------------------------- 1 | . 21 | * --------------------------------------------------------------------- 22 | * @copyright Copyright © 2021 - 2022 Edgard 23 | * @license http://www.gnu.org/licenses/gpl.txt GPLv3+ 24 | * @link https://github.com/edgardmessias/glpi-singlesignon/ 25 | * --------------------------------------------------------------------- 26 | */ 27 | 28 | class PluginSinglesignonToolbox { 29 | /** 30 | * Generate a URL to callback 31 | * Some providers don't accept query string, it convert to PATH 32 | * @global array $CFG_GLPI 33 | * @param integer $id 34 | * @param array $query 35 | * @return string 36 | */ 37 | public static function getCallbackUrl($row, $query = []) { 38 | global $CFG_GLPI; 39 | 40 | $url = Plugin::getPhpDir("singlesignon", false) . '/front/callback.php'; 41 | 42 | $url .= "/provider/".$row; 43 | 44 | if (!empty($query)) { 45 | $_SESSION['redirect'] = $query['redirect']; 46 | } 47 | 48 | return $url; 49 | } 50 | 51 | public static function isDefault($row, $query = []) { 52 | 53 | if ($row['is_default'] == 1) { 54 | return true; 55 | } 56 | return false; 57 | } 58 | 59 | public static function getCallbackParameters($name = null) { 60 | $data = []; 61 | 62 | if (isset($_SERVER['PATH_INFO'])) { 63 | $path_info = trim($_SERVER['PATH_INFO'], '/'); 64 | 65 | $parts = explode('/', $path_info); 66 | 67 | $key = null; 68 | 69 | foreach ($parts as $part) { 70 | if ($key === null) { 71 | $key = $part; 72 | } else { 73 | if ($key === "provider" || $key === "test") { 74 | $part = intval($part); 75 | } else { 76 | $tmp = base64_decode($part); 77 | parse_str($tmp, $part); 78 | } 79 | 80 | if ($key === $name) { 81 | return $part; 82 | } 83 | 84 | $data[$key] = $part; 85 | $key = null; 86 | } 87 | } 88 | } 89 | 90 | if (!isset($data[$name])) { 91 | return null; 92 | } 93 | 94 | return $data; 95 | } 96 | 97 | public static function startsWith($haystack, $needle) { 98 | $length = strlen($needle); 99 | return (substr($haystack, 0, $length) === $needle); 100 | } 101 | 102 | static function getPictureUrl($path) { 103 | global $CFG_GLPI; 104 | 105 | $path = Html::cleanInputText($path); // prevent xss 106 | 107 | if (empty($path)) { 108 | return null; 109 | } 110 | 111 | return PluginSinglesignonToolbox::getBaseURL() . Plugin::getPhpDir("singlesignon", false) . '/front/picture.send.php?path=' . $path; 112 | } 113 | 114 | public static function savePicture($src, $uniq_prefix = "") { 115 | 116 | if (function_exists('Document::isImage') && !Document::isImage($src)) { 117 | return false; 118 | } 119 | 120 | $filename = uniqid($uniq_prefix); 121 | $ext = pathinfo($src, PATHINFO_EXTENSION); 122 | $subdirectory = substr($filename, -2); // subdirectory based on last 2 hex digit 123 | 124 | $basePath = GLPI_PLUGIN_DOC_DIR . "/singlesignon"; 125 | $i = 0; 126 | do { 127 | // Iterate on possible suffix while dest exists. 128 | // This case will almost never exists as dest is based on an unique id. 129 | $dest = $basePath 130 | . '/' . $subdirectory 131 | . '/' . $filename . ($i > 0 ? '_' . $i : '') . '.' . $ext; 132 | $i++; 133 | } while (file_exists($dest)); 134 | // If the base directory does not exists, create it 135 | if (!is_dir($basePath) && !mkdir($basePath)) { 136 | return false; 137 | } 138 | // If the sub directory does not exists, create the sub directory 139 | if (!is_dir($basePath . '/' . $subdirectory) && !mkdir($basePath . '/' . $subdirectory)) { 140 | return false; 141 | } 142 | 143 | if (!rename($src, $dest)) { 144 | return false; 145 | } 146 | 147 | return substr($dest, strlen($basePath . '/')); // Return dest relative to GLPI_PICTURE_DIR 148 | } 149 | 150 | public static function deletePicture($path) { 151 | $basePath = GLPI_PLUGIN_DOC_DIR . "/singlesignon"; 152 | $fullpath = $basePath . '/' . $path; 153 | 154 | if (!file_exists($fullpath)) { 155 | return false; 156 | } 157 | 158 | $fullpath = realpath($fullpath); 159 | if (!static::startsWith($fullpath, realpath($basePath))) { 160 | return false; 161 | } 162 | 163 | return @unlink($fullpath); 164 | } 165 | 166 | public static function renderButton($url, $data, $class = 'oauth-login') { 167 | $popupClass = ""; 168 | if (isset($data['popup']) && $data['popup'] == 1) { 169 | $popupClass = "popup"; 170 | } 171 | $btn = ' 'max-height: 20px;margin-right: 4px', 190 | ] 191 | ); 192 | $btn .= ' '; 193 | } 194 | 195 | $btn .= sprintf(__sso('Login with %s'), $data['name']); 196 | $btn .= ''; 197 | return $btn; 198 | } 199 | 200 | /** 201 | * Get base URL without query string 202 | * @return string 203 | */ 204 | public static function getBaseURL() { 205 | global $CFG_GLPI; 206 | 207 | if (!empty($CFG_GLPI['url_base'])) { 208 | return $CFG_GLPI['url_base']; 209 | } 210 | 211 | $baseURL = ""; 212 | if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) { 213 | $baseURL = ($_SERVER["HTTP_X_FORWARDED_PROTO"] == "https") ? "https://" : "http://"; 214 | } else if (isset($_SERVER["HTTPS"])) { 215 | $baseURL = ($_SERVER["HTTPS"] == "on") ? "https://" : "http://"; 216 | } else { 217 | $baseURL = "http://"; 218 | } 219 | 220 | if (isset($_SERVER["HTTP_X_FORWARDED_HOST"])) { 221 | $baseURL .= $_SERVER["HTTP_X_FORWARDED_HOST"]; 222 | } else if (isset($_SERVER["HTTP_X_FORWARDED_HOST"])) { 223 | $baseURL .= $_SERVER["HTTP_X_FORWARDED_HOST"]; 224 | } else { 225 | $baseURL .= $_SERVER["SERVER_NAME"]; 226 | } 227 | 228 | $port = $_SERVER["SERVER_PORT"]; 229 | if (isset($_SERVER["HTTP_X_FORWARDED_PORT"])) { 230 | $port = $_SERVER["HTTP_X_FORWARDED_PORT"]; 231 | } 232 | 233 | if ($port != "80" && $port != "443") { 234 | $baseURL .= ":" . $_SERVER["SERVER_PORT"]; 235 | } 236 | return $baseURL; 237 | } 238 | 239 | /** 240 | * Get current URL without query string 241 | * @return string 242 | */ 243 | public static function getCurrentURL() { 244 | $currentURL = PluginSinglesignonToolbox::getBaseURL(); 245 | 246 | // $currentURL .= $_SERVER["REQUEST_URI"]; 247 | // Ignore Query String 248 | if (isset($_SERVER["SCRIPT_NAME"])) { 249 | $currentURL .= $_SERVER["SCRIPT_NAME"]; 250 | } 251 | if (isset($_SERVER["PATH_INFO"])) { 252 | $currentURL .= $_SERVER["PATH_INFO"]; 253 | } 254 | return $currentURL; 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /locales/de_DE.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edgardmessias/glpi-singlesignon/e3edebe6c8f3cc3733544ba965a33c54d85693b0/locales/de_DE.mo -------------------------------------------------------------------------------- /locales/de_DE.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR glpi-singlesignon Development Team 3 | # This file is distributed under the same license as the singlesignon package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Eduardo Mozart de Oliveira , 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: singlesignon\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-02-04 19:54+0000\n" 15 | "PO-Revision-Date: 2025-02-04 19:31+0000\n" 16 | "Last-Translator: Eduardo Mozart de Oliveira , 2025\n" 17 | "Language-Team: German (Germany) (https://app.transifex.com/eduardomozart/teams/204814/de_DE/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: de_DE\n" 22 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 23 | 24 | #: setup.php:33 25 | #, php-format 26 | msgid "Please, rename the plugin folder \"%s\" to \"singlesignon\"" 27 | msgstr "Bitte benenne das Plugin Verzeichnis von \"%s\" in \"singlesignon\" um." 28 | 29 | #: setup.php:67 front/provider.form.php:86 front/provider.form.php:88 30 | #: front/provider.php:33 front/provider.php:35 inc/preference.class.php:89 31 | #: inc/provider.class.php:84 32 | msgid "Single Sign-on" 33 | msgstr "Single Sign-on" 34 | 35 | #: setup.php:84 36 | msgid "This plugin requires GLPI >= 0.85" 37 | msgstr "Das Plugin benötigt mindestens GLPI Version 0.85" 38 | 39 | #: front/callback.php:40 40 | msgid "Provider not defined." 41 | msgstr "Provider nicht definiert." 42 | 43 | #: front/callback.php:46 44 | msgid "Provider not found." 45 | msgstr "Provider nicht gefunden." 46 | 47 | #: front/callback.php:50 48 | msgid "Provider not active." 49 | msgstr "Provider nicht aktiv." 50 | 51 | #: front/callback.php:108 52 | msgid "Automatic redirection, else click" 53 | msgstr "" 54 | 55 | #: inc/preference.class.php:172 inc/provider.class.php:77 56 | msgid "Single Sign-on Provider" 57 | msgstr "Single Sign-on Provider" 58 | 59 | #: inc/preference.class.php:197 60 | msgid "Linked accounts" 61 | msgstr "Verlinkte Accounts" 62 | 63 | #: inc/provider.class.php:126 64 | msgid "JSON SSO provider representation" 65 | msgstr "" 66 | 67 | #: inc/provider.class.php:127 68 | msgid "Copy provider information" 69 | msgstr "" 70 | 71 | #: inc/provider.class.php:158 72 | msgid "SSO Type" 73 | msgstr "SSO Typ" 74 | 75 | #: inc/provider.class.php:166 inc/provider.class.php:509 76 | msgid "Client ID" 77 | msgstr "Client ID" 78 | 79 | #: inc/provider.class.php:168 inc/provider.class.php:517 80 | msgid "Client Secret" 81 | msgstr "Client Secret" 82 | 83 | #: inc/provider.class.php:179 inc/provider.class.php:525 84 | msgid "Scope" 85 | msgstr "Scope" 86 | 87 | #: inc/provider.class.php:181 inc/provider.class.php:533 88 | msgid "Extra Options" 89 | msgstr "Weitere Optionen" 90 | 91 | #: inc/provider.class.php:183 92 | #, php-format 93 | msgid "" 94 | "Allows you to specify custom parameters for the SSO provider %1$s. Example: " 95 | "%2$s to force login or %3$s to force account selection (supported URL " 96 | "settings may vary by provider). You can specify additional parameters with " 97 | "the \"&\" delimiter." 98 | msgstr "" 99 | 100 | #: inc/provider.class.php:183 inc/provider.class.php:190 101 | #: inc/provider.class.php:541 102 | msgid "Authorize URL" 103 | msgstr "URL Autorisierung" 104 | 105 | #: inc/provider.class.php:195 inc/provider.class.php:549 106 | msgid "Access Token URL" 107 | msgstr "URL Access Token" 108 | 109 | #: inc/provider.class.php:200 inc/provider.class.php:557 110 | msgid "Resource Owner Details URL" 111 | msgstr "URL Benutzerinfornmationen" 112 | 113 | #: inc/provider.class.php:205 114 | msgid "IsDefault" 115 | msgstr "Ist Standard" 116 | 117 | #: inc/provider.class.php:207 118 | msgid "PopupAuth" 119 | msgstr "PopupAuth" 120 | 121 | #: inc/provider.class.php:213 122 | msgid "SplitDomain" 123 | msgstr "Domäne abtrennen" 124 | 125 | #: inc/provider.class.php:217 126 | msgid "AuthorizedDomains" 127 | msgstr "Erlaubte Domänen" 128 | 129 | #: inc/provider.class.php:219 130 | msgid "" 131 | "Provide a list of domains allowed to log in through this provider (separated" 132 | " by commas, no spaces)." 133 | msgstr "" 134 | 135 | #: inc/provider.class.php:225 136 | msgid "Use Email as Login" 137 | msgstr "E-Mail als Login benutzen" 138 | 139 | #: inc/provider.class.php:228 140 | msgid "Split Name" 141 | msgstr "Name abtrennen" 142 | 143 | #: inc/provider.class.php:316 144 | msgid "Callback URL" 145 | msgstr "Callback URL" 146 | 147 | #: inc/provider.class.php:320 148 | msgid "Test Single Sign-on" 149 | msgstr "Teste Single Sign-on" 150 | 151 | #: inc/provider.class.php:379 152 | msgid "A Name is required" 153 | msgstr "Es wird ein Name benötigt." 154 | 155 | #: inc/provider.class.php:385 156 | #, php-format 157 | msgid "The \"%s\" is a Invalid type" 158 | msgstr "Der Typ \"%s\" ist nicht gültig" 159 | 160 | #: inc/provider.class.php:389 161 | msgid "A Client ID is required" 162 | msgstr "Eine Client ID ist notwendig" 163 | 164 | #: inc/provider.class.php:393 165 | msgid "A Client Secret is required" 166 | msgstr "Ein Client Secret ist notwendig" 167 | 168 | #: inc/provider.class.php:398 169 | msgid "An Authorize URL is required" 170 | msgstr "Eine Autorisierungs URL ist notwendig" 171 | 172 | #: inc/provider.class.php:400 173 | msgid "The Authorize URL is invalid" 174 | msgstr "Die Autorisierungs URL ist nicht gültig" 175 | 176 | #: inc/provider.class.php:404 177 | msgid "An Access Token URL is required" 178 | msgstr "Eine Access Token URL ist notwendig" 179 | 180 | #: inc/provider.class.php:406 181 | msgid "The Access Token URL is invalid" 182 | msgstr "Die Access Token URL ist nicht gültig" 183 | 184 | #: inc/provider.class.php:410 185 | msgid "A Resource Owner Details URL is required" 186 | msgstr "Eine Benutzerinfornmationen URL ist notwendig" 187 | 188 | #: inc/provider.class.php:412 189 | msgid "The Resource Owner Details URL is invalid" 190 | msgstr "Die Benutzerinfornmationen URL ist nicht gültig" 191 | 192 | #: inc/provider.class.php:634 193 | msgid "Generic" 194 | msgstr "Generisch" 195 | 196 | #: inc/provider.class.php:635 197 | msgid "Azure" 198 | msgstr "Azure" 199 | 200 | #: inc/provider.class.php:636 201 | msgid "Facebook" 202 | msgstr "Facebook" 203 | 204 | #: inc/provider.class.php:637 205 | msgid "GitHub" 206 | msgstr "GitHub" 207 | 208 | #: inc/provider.class.php:638 209 | msgid "Google" 210 | msgstr "Google" 211 | 212 | #: inc/provider.class.php:639 213 | msgid "Instagram" 214 | msgstr "Instagram" 215 | 216 | #: inc/provider.class.php:640 217 | msgid "LinkdeIn" 218 | msgstr "LinkdeIn" 219 | 220 | #: inc/toolbox.class.php:195 221 | #, php-format 222 | msgid "Login with %s" 223 | msgstr "Login mit %s" 224 | -------------------------------------------------------------------------------- /locales/en_GB.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edgardmessias/glpi-singlesignon/e3edebe6c8f3cc3733544ba965a33c54d85693b0/locales/en_GB.mo -------------------------------------------------------------------------------- /locales/en_GB.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR glpi-singlesignon Development Team 3 | # This file is distributed under the same license as the singlesignon package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Eduardo Mozart de Oliveira , 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: singlesignon\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-02-04 19:54+0000\n" 15 | "PO-Revision-Date: 2025-02-04 19:31+0000\n" 16 | "Last-Translator: Eduardo Mozart de Oliveira , 2025\n" 17 | "Language-Team: English (United Kingdom) (https://app.transifex.com/eduardomozart/teams/204814/en_GB/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: en_GB\n" 22 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 23 | 24 | #: setup.php:33 25 | #, php-format 26 | msgid "Please, rename the plugin folder \"%s\" to \"singlesignon\"" 27 | msgstr "Please, rename the plugin folder \"%s\" to \"singlesignon\"" 28 | 29 | #: setup.php:67 front/provider.form.php:86 front/provider.form.php:88 30 | #: front/provider.php:33 front/provider.php:35 inc/preference.class.php:89 31 | #: inc/provider.class.php:84 32 | msgid "Single Sign-on" 33 | msgstr "Single Sign-on" 34 | 35 | #: setup.php:84 36 | msgid "This plugin requires GLPI >= 0.85" 37 | msgstr "This plugin requires GLPI >= 0.85" 38 | 39 | #: front/callback.php:40 40 | msgid "Provider not defined." 41 | msgstr "Provider not defined." 42 | 43 | #: front/callback.php:46 44 | msgid "Provider not found." 45 | msgstr "Provider not found." 46 | 47 | #: front/callback.php:50 48 | msgid "Provider not active." 49 | msgstr "Provider not active." 50 | 51 | #: front/callback.php:108 52 | msgid "Automatic redirection, else click" 53 | msgstr "" 54 | 55 | #: inc/preference.class.php:172 inc/provider.class.php:77 56 | msgid "Single Sign-on Provider" 57 | msgstr "Single Sign-on Provider" 58 | 59 | #: inc/preference.class.php:197 60 | msgid "Linked accounts" 61 | msgstr "Linked accounts" 62 | 63 | #: inc/provider.class.php:126 64 | msgid "JSON SSO provider representation" 65 | msgstr "" 66 | 67 | #: inc/provider.class.php:127 68 | msgid "Copy provider information" 69 | msgstr "" 70 | 71 | #: inc/provider.class.php:158 72 | msgid "SSO Type" 73 | msgstr "SSO Type" 74 | 75 | #: inc/provider.class.php:166 inc/provider.class.php:509 76 | msgid "Client ID" 77 | msgstr "Client ID" 78 | 79 | #: inc/provider.class.php:168 inc/provider.class.php:517 80 | msgid "Client Secret" 81 | msgstr "Client Secret" 82 | 83 | #: inc/provider.class.php:179 inc/provider.class.php:525 84 | msgid "Scope" 85 | msgstr "Scope" 86 | 87 | #: inc/provider.class.php:181 inc/provider.class.php:533 88 | msgid "Extra Options" 89 | msgstr "Extra Options" 90 | 91 | #: inc/provider.class.php:183 92 | #, php-format 93 | msgid "" 94 | "Allows you to specify custom parameters for the SSO provider %1$s. Example: " 95 | "%2$s to force login or %3$s to force account selection (supported URL " 96 | "settings may vary by provider). You can specify additional parameters with " 97 | "the \"&\" delimiter." 98 | msgstr "" 99 | 100 | #: inc/provider.class.php:183 inc/provider.class.php:190 101 | #: inc/provider.class.php:541 102 | msgid "Authorize URL" 103 | msgstr "Authorize URL" 104 | 105 | #: inc/provider.class.php:195 inc/provider.class.php:549 106 | msgid "Access Token URL" 107 | msgstr "Access Token URL" 108 | 109 | #: inc/provider.class.php:200 inc/provider.class.php:557 110 | msgid "Resource Owner Details URL" 111 | msgstr "Resource Owner Details URL" 112 | 113 | #: inc/provider.class.php:205 114 | msgid "IsDefault" 115 | msgstr "" 116 | 117 | #: inc/provider.class.php:207 118 | msgid "PopupAuth" 119 | msgstr "PopupAuth" 120 | 121 | #: inc/provider.class.php:213 122 | msgid "SplitDomain" 123 | msgstr "SplitDomain" 124 | 125 | #: inc/provider.class.php:217 126 | msgid "AuthorizedDomains" 127 | msgstr "AuthorizedDomains" 128 | 129 | #: inc/provider.class.php:219 130 | msgid "" 131 | "Provide a list of domains allowed to log in through this provider (separated" 132 | " by commas, no spaces)." 133 | msgstr "" 134 | 135 | #: inc/provider.class.php:225 136 | msgid "Use Email as Login" 137 | msgstr "" 138 | 139 | #: inc/provider.class.php:228 140 | msgid "Split Name" 141 | msgstr "" 142 | 143 | #: inc/provider.class.php:316 144 | msgid "Callback URL" 145 | msgstr "Callback URL" 146 | 147 | #: inc/provider.class.php:320 148 | msgid "Test Single Sign-on" 149 | msgstr "Test Single Sign-on" 150 | 151 | #: inc/provider.class.php:379 152 | msgid "A Name is required" 153 | msgstr "A Name is required" 154 | 155 | #: inc/provider.class.php:385 156 | #, php-format 157 | msgid "The \"%s\" is a Invalid type" 158 | msgstr "The \"%s\" is a Invalid type" 159 | 160 | #: inc/provider.class.php:389 161 | msgid "A Client ID is required" 162 | msgstr "A Client ID is required" 163 | 164 | #: inc/provider.class.php:393 165 | msgid "A Client Secret is required" 166 | msgstr "A Client Secret is required" 167 | 168 | #: inc/provider.class.php:398 169 | msgid "An Authorize URL is required" 170 | msgstr "An Authorize URL is required" 171 | 172 | #: inc/provider.class.php:400 173 | msgid "The Authorize URL is invalid" 174 | msgstr "The Authorize URL is invalid" 175 | 176 | #: inc/provider.class.php:404 177 | msgid "An Access Token URL is required" 178 | msgstr "An Access Token URL is required" 179 | 180 | #: inc/provider.class.php:406 181 | msgid "The Access Token URL is invalid" 182 | msgstr "The Access Token URL is invalid" 183 | 184 | #: inc/provider.class.php:410 185 | msgid "A Resource Owner Details URL is required" 186 | msgstr "A Resource Owner Details URL is required" 187 | 188 | #: inc/provider.class.php:412 189 | msgid "The Resource Owner Details URL is invalid" 190 | msgstr "The Resource Owner Details URL is invalid" 191 | 192 | #: inc/provider.class.php:634 193 | msgid "Generic" 194 | msgstr "Generic" 195 | 196 | #: inc/provider.class.php:635 197 | msgid "Azure" 198 | msgstr "Azure" 199 | 200 | #: inc/provider.class.php:636 201 | msgid "Facebook" 202 | msgstr "Facebook" 203 | 204 | #: inc/provider.class.php:637 205 | msgid "GitHub" 206 | msgstr "GitHub" 207 | 208 | #: inc/provider.class.php:638 209 | msgid "Google" 210 | msgstr "Google" 211 | 212 | #: inc/provider.class.php:639 213 | msgid "Instagram" 214 | msgstr "Instagram" 215 | 216 | #: inc/provider.class.php:640 217 | msgid "LinkdeIn" 218 | msgstr "LinkdeIn" 219 | 220 | #: inc/toolbox.class.php:195 221 | #, php-format 222 | msgid "Login with %s" 223 | msgstr "Login with %s" 224 | -------------------------------------------------------------------------------- /locales/es_ES.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edgardmessias/glpi-singlesignon/e3edebe6c8f3cc3733544ba965a33c54d85693b0/locales/es_ES.mo -------------------------------------------------------------------------------- /locales/es_ES.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR glpi-singlesignon Development Team 3 | # This file is distributed under the same license as the singlesignon package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Eduardo Mozart de Oliveira , 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: singlesignon\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-02-04 19:54+0000\n" 15 | "PO-Revision-Date: 2025-02-04 19:31+0000\n" 16 | "Last-Translator: Eduardo Mozart de Oliveira , 2025\n" 17 | "Language-Team: Spanish (Spain) (https://app.transifex.com/eduardomozart/teams/204814/es_ES/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: es_ES\n" 22 | "Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" 23 | 24 | #: setup.php:33 25 | #, php-format 26 | msgid "Please, rename the plugin folder \"%s\" to \"singlesignon\"" 27 | msgstr "" 28 | "Por favor, cambie el nombre de la carpeta del plugin \"%s\" para " 29 | "\"singlesignon\"" 30 | 31 | #: setup.php:67 front/provider.form.php:86 front/provider.form.php:88 32 | #: front/provider.php:33 front/provider.php:35 inc/preference.class.php:89 33 | #: inc/provider.class.php:84 34 | msgid "Single Sign-on" 35 | msgstr "Single Sign-on" 36 | 37 | #: setup.php:84 38 | msgid "This plugin requires GLPI >= 0.85" 39 | msgstr "Este plugin requiere GLPI >= 0.85" 40 | 41 | #: front/callback.php:40 42 | msgid "Provider not defined." 43 | msgstr "Proveedor no definido." 44 | 45 | #: front/callback.php:46 46 | msgid "Provider not found." 47 | msgstr "Proveedor no encontrado." 48 | 49 | #: front/callback.php:50 50 | msgid "Provider not active." 51 | msgstr "Proveedor no activado." 52 | 53 | #: front/callback.php:108 54 | msgid "Automatic redirection, else click" 55 | msgstr "" 56 | 57 | #: inc/preference.class.php:172 inc/provider.class.php:77 58 | msgid "Single Sign-on Provider" 59 | msgstr "Proveedor de Single Sign-on" 60 | 61 | #: inc/preference.class.php:197 62 | msgid "Linked accounts" 63 | msgstr "Cuentas vinculadas" 64 | 65 | #: inc/provider.class.php:126 66 | msgid "JSON SSO provider representation" 67 | msgstr "" 68 | 69 | #: inc/provider.class.php:127 70 | msgid "Copy provider information" 71 | msgstr "" 72 | 73 | #: inc/provider.class.php:158 74 | msgid "SSO Type" 75 | msgstr "Tipo de SSO" 76 | 77 | #: inc/provider.class.php:166 inc/provider.class.php:509 78 | msgid "Client ID" 79 | msgstr "Identificación del cliente" 80 | 81 | #: inc/provider.class.php:168 inc/provider.class.php:517 82 | msgid "Client Secret" 83 | msgstr "Secreto del cliente" 84 | 85 | #: inc/provider.class.php:179 inc/provider.class.php:525 86 | msgid "Scope" 87 | msgstr "Alcance" 88 | 89 | #: inc/provider.class.php:181 inc/provider.class.php:533 90 | msgid "Extra Options" 91 | msgstr "Opciones adicionales" 92 | 93 | #: inc/provider.class.php:183 94 | #, php-format 95 | msgid "" 96 | "Allows you to specify custom parameters for the SSO provider %1$s. Example: " 97 | "%2$s to force login or %3$s to force account selection (supported URL " 98 | "settings may vary by provider). You can specify additional parameters with " 99 | "the \"&\" delimiter." 100 | msgstr "" 101 | 102 | #: inc/provider.class.php:183 inc/provider.class.php:190 103 | #: inc/provider.class.php:541 104 | msgid "Authorize URL" 105 | msgstr "URL de autorización" 106 | 107 | #: inc/provider.class.php:195 inc/provider.class.php:549 108 | msgid "Access Token URL" 109 | msgstr "URL del token de acceso" 110 | 111 | #: inc/provider.class.php:200 inc/provider.class.php:557 112 | msgid "Resource Owner Details URL" 113 | msgstr "URL de detalles del propietario del recurso" 114 | 115 | #: inc/provider.class.php:205 116 | msgid "IsDefault" 117 | msgstr "" 118 | 119 | #: inc/provider.class.php:207 120 | msgid "PopupAuth" 121 | msgstr "" 122 | 123 | #: inc/provider.class.php:213 124 | msgid "SplitDomain" 125 | msgstr "" 126 | 127 | #: inc/provider.class.php:217 128 | msgid "AuthorizedDomains" 129 | msgstr "" 130 | 131 | #: inc/provider.class.php:219 132 | msgid "" 133 | "Provide a list of domains allowed to log in through this provider (separated" 134 | " by commas, no spaces)." 135 | msgstr "" 136 | 137 | #: inc/provider.class.php:225 138 | msgid "Use Email as Login" 139 | msgstr "" 140 | 141 | #: inc/provider.class.php:228 142 | msgid "Split Name" 143 | msgstr "" 144 | 145 | #: inc/provider.class.php:316 146 | msgid "Callback URL" 147 | msgstr "URL de devolución de llamada" 148 | 149 | #: inc/provider.class.php:320 150 | msgid "Test Single Sign-on" 151 | msgstr "Pruebar Single Sign-on" 152 | 153 | #: inc/provider.class.php:379 154 | msgid "A Name is required" 155 | msgstr "Se requiere un nombre" 156 | 157 | #: inc/provider.class.php:385 158 | #, php-format 159 | msgid "The \"%s\" is a Invalid type" 160 | msgstr "El \"%s\" es un tipo no válido" 161 | 162 | #: inc/provider.class.php:389 163 | msgid "A Client ID is required" 164 | msgstr "Se requiere una identificación de cliente" 165 | 166 | #: inc/provider.class.php:393 167 | msgid "A Client Secret is required" 168 | msgstr "Se requiere un secreto de cliente" 169 | 170 | #: inc/provider.class.php:398 171 | msgid "An Authorize URL is required" 172 | msgstr "Se requiere una URL de autorización" 173 | 174 | #: inc/provider.class.php:400 175 | msgid "The Authorize URL is invalid" 176 | msgstr "La URL de autorización no es válida" 177 | 178 | #: inc/provider.class.php:404 179 | msgid "An Access Token URL is required" 180 | msgstr "Se requiere una URL de token de acceso" 181 | 182 | #: inc/provider.class.php:406 183 | msgid "The Access Token URL is invalid" 184 | msgstr "La URL del token de acceso no es válida" 185 | 186 | #: inc/provider.class.php:410 187 | msgid "A Resource Owner Details URL is required" 188 | msgstr "Se requiere una URL de detalles del propietario del recurso" 189 | 190 | #: inc/provider.class.php:412 191 | msgid "The Resource Owner Details URL is invalid" 192 | msgstr "La URL de detalles del propietario del recurso no es válida" 193 | 194 | #: inc/provider.class.php:634 195 | msgid "Generic" 196 | msgstr "Genérico" 197 | 198 | #: inc/provider.class.php:635 199 | msgid "Azure" 200 | msgstr "Azure" 201 | 202 | #: inc/provider.class.php:636 203 | msgid "Facebook" 204 | msgstr "Facebook" 205 | 206 | #: inc/provider.class.php:637 207 | msgid "GitHub" 208 | msgstr "GitHub" 209 | 210 | #: inc/provider.class.php:638 211 | msgid "Google" 212 | msgstr "Google" 213 | 214 | #: inc/provider.class.php:639 215 | msgid "Instagram" 216 | msgstr "Instagram" 217 | 218 | #: inc/provider.class.php:640 219 | msgid "LinkdeIn" 220 | msgstr "LinkdeIn" 221 | 222 | #: inc/toolbox.class.php:195 223 | #, php-format 224 | msgid "Login with %s" 225 | msgstr "Iniciar con %s" 226 | -------------------------------------------------------------------------------- /locales/fr_FR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edgardmessias/glpi-singlesignon/e3edebe6c8f3cc3733544ba965a33c54d85693b0/locales/fr_FR.mo -------------------------------------------------------------------------------- /locales/fr_FR.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR glpi-singlesignon Development Team 3 | # This file is distributed under the same license as the singlesignon package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Eduardo Mozart de Oliveira , 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: singlesignon\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-02-04 19:54+0000\n" 15 | "PO-Revision-Date: 2025-02-04 19:31+0000\n" 16 | "Last-Translator: Eduardo Mozart de Oliveira , 2025\n" 17 | "Language-Team: French (France) (https://app.transifex.com/eduardomozart/teams/204814/fr_FR/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: fr_FR\n" 22 | "Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" 23 | 24 | #: setup.php:33 25 | #, php-format 26 | msgid "Please, rename the plugin folder \"%s\" to \"singlesignon\"" 27 | msgstr "Renommez le dossier \"%s\" à \"singlesignon\"" 28 | 29 | #: setup.php:67 front/provider.form.php:86 front/provider.form.php:88 30 | #: front/provider.php:33 front/provider.php:35 inc/preference.class.php:89 31 | #: inc/provider.class.php:84 32 | msgid "Single Sign-on" 33 | msgstr "Identification unique" 34 | 35 | #: setup.php:84 36 | msgid "This plugin requires GLPI >= 0.85" 37 | msgstr "Ce plugin requiert une version de GLPI >= 0.85" 38 | 39 | #: front/callback.php:40 40 | msgid "Provider not defined." 41 | msgstr "Fournisseur non défini" 42 | 43 | #: front/callback.php:46 44 | msgid "Provider not found." 45 | msgstr "Fournisseur introuvable" 46 | 47 | #: front/callback.php:50 48 | msgid "Provider not active." 49 | msgstr "Fournisseur désactivé" 50 | 51 | #: front/callback.php:108 52 | msgid "Automatic redirection, else click" 53 | msgstr "" 54 | 55 | #: inc/preference.class.php:172 inc/provider.class.php:77 56 | msgid "Single Sign-on Provider" 57 | msgstr "Fournisseur d'identification unique" 58 | 59 | #: inc/preference.class.php:197 60 | msgid "Linked accounts" 61 | msgstr "Comptes liés" 62 | 63 | #: inc/provider.class.php:126 64 | msgid "JSON SSO provider representation" 65 | msgstr "" 66 | 67 | #: inc/provider.class.php:127 68 | msgid "Copy provider information" 69 | msgstr "" 70 | 71 | #: inc/provider.class.php:158 72 | msgid "SSO Type" 73 | msgstr "Type de SSO" 74 | 75 | #: inc/provider.class.php:166 inc/provider.class.php:509 76 | msgid "Client ID" 77 | msgstr "Identificateur du client" 78 | 79 | #: inc/provider.class.php:168 inc/provider.class.php:517 80 | msgid "Client Secret" 81 | msgstr "Secret du client" 82 | 83 | #: inc/provider.class.php:179 inc/provider.class.php:525 84 | msgid "Scope" 85 | msgstr "Étendue" 86 | 87 | #: inc/provider.class.php:181 inc/provider.class.php:533 88 | msgid "Extra Options" 89 | msgstr "Options supplémentaires" 90 | 91 | #: inc/provider.class.php:183 92 | #, php-format 93 | msgid "" 94 | "Allows you to specify custom parameters for the SSO provider %1$s. Example: " 95 | "%2$s to force login or %3$s to force account selection (supported URL " 96 | "settings may vary by provider). You can specify additional parameters with " 97 | "the \"&\" delimiter." 98 | msgstr "" 99 | 100 | #: inc/provider.class.php:183 inc/provider.class.php:190 101 | #: inc/provider.class.php:541 102 | msgid "Authorize URL" 103 | msgstr "URL d'autorisation" 104 | 105 | #: inc/provider.class.php:195 inc/provider.class.php:549 106 | msgid "Access Token URL" 107 | msgstr "URL du jeton d'accès" 108 | 109 | #: inc/provider.class.php:200 inc/provider.class.php:557 110 | msgid "Resource Owner Details URL" 111 | msgstr "Resource Owner Details URL" 112 | 113 | #: inc/provider.class.php:205 114 | msgid "IsDefault" 115 | msgstr "Authentification par défaut" 116 | 117 | #: inc/provider.class.php:207 118 | msgid "PopupAuth" 119 | msgstr "Authentification via Pop-up" 120 | 121 | #: inc/provider.class.php:213 122 | msgid "SplitDomain" 123 | msgstr "" 124 | "Ne pas prendre en compte le domaine pour la correspondance de l'identifiant " 125 | "GLPI" 126 | 127 | #: inc/provider.class.php:217 128 | msgid "AuthorizedDomains" 129 | msgstr "Domaines autorisés" 130 | 131 | #: inc/provider.class.php:219 132 | msgid "" 133 | "Provide a list of domains allowed to log in through this provider (separated" 134 | " by commas, no spaces)." 135 | msgstr "" 136 | 137 | #: inc/provider.class.php:225 138 | msgid "Use Email as Login" 139 | msgstr "" 140 | 141 | #: inc/provider.class.php:228 142 | msgid "Split Name" 143 | msgstr "" 144 | 145 | #: inc/provider.class.php:316 146 | msgid "Callback URL" 147 | msgstr "URL de callback" 148 | 149 | #: inc/provider.class.php:320 150 | msgid "Test Single Sign-on" 151 | msgstr "Tester l'identification unique" 152 | 153 | #: inc/provider.class.php:379 154 | msgid "A Name is required" 155 | msgstr "Un nom est requis" 156 | 157 | #: inc/provider.class.php:385 158 | #, php-format 159 | msgid "The \"%s\" is a Invalid type" 160 | msgstr "Le type \"%s\" est invalide" 161 | 162 | #: inc/provider.class.php:389 163 | msgid "A Client ID is required" 164 | msgstr "L'identifiant de client est requis" 165 | 166 | #: inc/provider.class.php:393 167 | msgid "A Client Secret is required" 168 | msgstr "Le secret du client est requis" 169 | 170 | #: inc/provider.class.php:398 171 | msgid "An Authorize URL is required" 172 | msgstr "L'URL d'autorisation est requise" 173 | 174 | #: inc/provider.class.php:400 175 | msgid "The Authorize URL is invalid" 176 | msgstr "L'URL d'autorisation est invalide" 177 | 178 | #: inc/provider.class.php:404 179 | msgid "An Access Token URL is required" 180 | msgstr "L'URL du jeton d'accès est requise" 181 | 182 | #: inc/provider.class.php:406 183 | msgid "The Access Token URL is invalid" 184 | msgstr "L'URL du jeton d'accès est invalide" 185 | 186 | #: inc/provider.class.php:410 187 | msgid "A Resource Owner Details URL is required" 188 | msgstr "L'URL d'information sur l'utilisateur est requise" 189 | 190 | #: inc/provider.class.php:412 191 | msgid "The Resource Owner Details URL is invalid" 192 | msgstr "L'URL d'information sur l'utilisateur est invalide" 193 | 194 | #: inc/provider.class.php:634 195 | msgid "Generic" 196 | msgstr "Generique" 197 | 198 | #: inc/provider.class.php:635 199 | msgid "Azure" 200 | msgstr "Azure" 201 | 202 | #: inc/provider.class.php:636 203 | msgid "Facebook" 204 | msgstr "Facebook" 205 | 206 | #: inc/provider.class.php:637 207 | msgid "GitHub" 208 | msgstr "GitHub" 209 | 210 | #: inc/provider.class.php:638 211 | msgid "Google" 212 | msgstr "Google" 213 | 214 | #: inc/provider.class.php:639 215 | msgid "Instagram" 216 | msgstr "Instagram" 217 | 218 | #: inc/provider.class.php:640 219 | msgid "LinkdeIn" 220 | msgstr "LinkdeIn" 221 | 222 | #: inc/toolbox.class.php:195 223 | #, php-format 224 | msgid "Login with %s" 225 | msgstr "Se connecter avec %s" 226 | -------------------------------------------------------------------------------- /locales/pt_BR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edgardmessias/glpi-singlesignon/e3edebe6c8f3cc3733544ba965a33c54d85693b0/locales/pt_BR.mo -------------------------------------------------------------------------------- /locales/pt_BR.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR glpi-singlesignon Development Team 3 | # This file is distributed under the same license as the singlesignon package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Eduardo Mozart de Oliveira , 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: singlesignon\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-02-04 20:31+0000\n" 15 | "PO-Revision-Date: 2025-02-04 20:49+0000\n" 16 | "Last-Translator: Eduardo Mozart de Oliveira , 2025\n" 17 | "Language-Team: Portuguese (Brazil) (https://app.transifex.com/eduardomozart/teams/204814/pt_BR/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: pt_BR\n" 22 | "Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" 23 | 24 | #: setup.php:33 25 | #, php-format 26 | msgid "Please, rename the plugin folder \"%s\" to \"singlesignon\"" 27 | msgstr "Por favor, renomeie a pasta do plugin \"%s\" para \"singlesignon\"" 28 | 29 | #: setup.php:67 front/provider.form.php:86 front/provider.form.php:88 30 | #: front/provider.php:33 front/provider.php:35 inc/preference.class.php:89 31 | #: inc/provider.class.php:84 32 | msgid "Single Sign-on" 33 | msgstr "Logon Único" 34 | 35 | #: setup.php:84 36 | msgid "This plugin requires GLPI >= 0.85" 37 | msgstr "Este plugin requer GLPI >= 0.85" 38 | 39 | #: front/callback.php:40 40 | msgid "Provider not defined." 41 | msgstr "Provedor não definido." 42 | 43 | #: front/callback.php:46 44 | msgid "Provider not found." 45 | msgstr "Provedor não encontrado." 46 | 47 | #: front/callback.php:50 48 | msgid "Provider not active." 49 | msgstr "Provedor não ativo." 50 | 51 | #: front/callback.php:108 52 | msgid "Automatic redirection, else click" 53 | msgstr "Redirecionamento automático, caso contrário, clique" 54 | 55 | #: inc/preference.class.php:172 inc/provider.class.php:77 56 | msgid "Single Sign-on Provider" 57 | msgstr "Provedor de Logon Único" 58 | 59 | #: inc/preference.class.php:197 60 | msgid "Linked accounts" 61 | msgstr "Contas vinculadas" 62 | 63 | #: inc/provider.class.php:126 64 | msgid "JSON SSO provider representation" 65 | msgstr "Representação do provedor SSO em JSON" 66 | 67 | #: inc/provider.class.php:127 68 | msgid "Copy provider information" 69 | msgstr "Copiar informação do provedor" 70 | 71 | #: inc/provider.class.php:158 72 | msgid "SSO Type" 73 | msgstr "Tipo SSO" 74 | 75 | #: inc/provider.class.php:166 inc/provider.class.php:509 76 | msgid "Client ID" 77 | msgstr "ID de Cliente" 78 | 79 | #: inc/provider.class.php:168 inc/provider.class.php:517 80 | msgid "Client Secret" 81 | msgstr "Segredo do Cliente" 82 | 83 | #: inc/provider.class.php:179 inc/provider.class.php:525 84 | msgid "Scope" 85 | msgstr "Escopo" 86 | 87 | #: inc/provider.class.php:181 inc/provider.class.php:533 88 | msgid "Extra Options" 89 | msgstr "Opções Extras" 90 | 91 | #: inc/provider.class.php:183 92 | #, php-format 93 | msgid "" 94 | "Allows you to specify custom parameters for the SSO provider %1$s. Example: " 95 | "%2$s to force login or %3$s to force account selection (supported URL " 96 | "settings may vary by provider). You can specify additional parameters with " 97 | "the \"&\" delimiter." 98 | msgstr "" 99 | "Permite especificar parâmetros personalizados para a %1$s do provedor de " 100 | "SSO. Exemplo: %2$s para forçar o login ou %3$s para forçar a seleção de " 101 | "conta (as configurações de URL suportadas podem variar de acordo com o " 102 | "provedor). Você pode especificar parâmetros adicionais com o delimitador " 103 | "\"&\"." 104 | 105 | #: inc/provider.class.php:183 inc/provider.class.php:190 106 | #: inc/provider.class.php:541 107 | msgid "Authorize URL" 108 | msgstr "URL de Autorização" 109 | 110 | #: inc/provider.class.php:195 inc/provider.class.php:549 111 | msgid "Access Token URL" 112 | msgstr "URL de Token de Acesso" 113 | 114 | #: inc/provider.class.php:200 inc/provider.class.php:557 115 | msgid "Resource Owner Details URL" 116 | msgstr "URL de Detalhes do Proprietário do Recurso" 117 | 118 | #: inc/provider.class.php:205 119 | msgid "IsDefault" 120 | msgstr "É Padrão" 121 | 122 | #: inc/provider.class.php:207 123 | msgid "PopupAuth" 124 | msgstr "Autenticação Pop-up" 125 | 126 | #: inc/provider.class.php:213 127 | msgid "SplitDomain" 128 | msgstr "Dividir Domínio" 129 | 130 | #: inc/provider.class.php:217 131 | msgid "AuthorizedDomains" 132 | msgstr "Domínios Autorizados" 133 | 134 | #: inc/provider.class.php:219 135 | msgid "" 136 | "Provide a list of domains allowed to log in through this provider (separated" 137 | " by commas, no spaces) (optional)." 138 | msgstr "" 139 | "Forneça uma lista de domínios permitidos para fazer login por meio deste " 140 | "provedor (separados por vírgulas, sem espaços) (opcional)." 141 | 142 | #: inc/provider.class.php:225 143 | msgid "Use Email as Login" 144 | msgstr "Usar E-mail como Login" 145 | 146 | #: inc/provider.class.php:228 147 | msgid "Split Name" 148 | msgstr "Dividir Nome" 149 | 150 | #: inc/provider.class.php:316 151 | msgid "Callback URL" 152 | msgstr "URL de Retorno" 153 | 154 | #: inc/provider.class.php:320 155 | msgid "Test Single Sign-on" 156 | msgstr "Testar Logon Único" 157 | 158 | #: inc/provider.class.php:379 159 | msgid "A Name is required" 160 | msgstr "O Nome é obrigatório" 161 | 162 | #: inc/provider.class.php:385 163 | #, php-format 164 | msgid "The \"%s\" is a Invalid type" 165 | msgstr "O \"%s\" é um tipo inválido" 166 | 167 | #: inc/provider.class.php:389 168 | msgid "A Client ID is required" 169 | msgstr "O ID de Cliente é obrigatório" 170 | 171 | #: inc/provider.class.php:393 172 | msgid "A Client Secret is required" 173 | msgstr "O Segredo do Cliente é obrigatório" 174 | 175 | #: inc/provider.class.php:398 176 | msgid "An Authorize URL is required" 177 | msgstr "A URL de Autorização é obrigatória" 178 | 179 | #: inc/provider.class.php:400 180 | msgid "The Authorize URL is invalid" 181 | msgstr "A URL de Autorização é inválida" 182 | 183 | #: inc/provider.class.php:404 184 | msgid "An Access Token URL is required" 185 | msgstr "A URL de Token de Acesso é obrigatória" 186 | 187 | #: inc/provider.class.php:406 188 | msgid "The Access Token URL is invalid" 189 | msgstr "A URL de Token de Acesso é inválida" 190 | 191 | #: inc/provider.class.php:410 192 | msgid "A Resource Owner Details URL is required" 193 | msgstr "A URL de Detalhes do Proprietário do Recurso é obrigatória" 194 | 195 | #: inc/provider.class.php:412 196 | msgid "The Resource Owner Details URL is invalid" 197 | msgstr "A URL de Detalhes do Proprietário do Recurso é inválida" 198 | 199 | #: inc/provider.class.php:634 200 | msgid "Generic" 201 | msgstr "Genérico" 202 | 203 | #: inc/provider.class.php:635 204 | msgid "Azure" 205 | msgstr "Azure" 206 | 207 | #: inc/provider.class.php:636 208 | msgid "Facebook" 209 | msgstr "Facebook" 210 | 211 | #: inc/provider.class.php:637 212 | msgid "GitHub" 213 | msgstr "GitHub" 214 | 215 | #: inc/provider.class.php:638 216 | msgid "Google" 217 | msgstr "Google" 218 | 219 | #: inc/provider.class.php:639 220 | msgid "Instagram" 221 | msgstr "Instagram" 222 | 223 | #: inc/provider.class.php:640 224 | msgid "LinkdeIn" 225 | msgstr "LinkdeIn" 226 | 227 | #: inc/toolbox.class.php:195 228 | #, php-format 229 | msgid "Login with %s" 230 | msgstr "Entrar com %s" 231 | -------------------------------------------------------------------------------- /locales/singlesignon.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR glpi-singlesignon Development Team 3 | # This file is distributed under the same license as the singlesignon package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: singlesignon\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2025-02-04 21:23+0000\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=CHARSET\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: setup.php:33 21 | #, php-format 22 | msgid "Please, rename the plugin folder \"%s\" to \"singlesignon\"" 23 | msgstr "" 24 | 25 | #: setup.php:67 front/provider.form.php:86 front/provider.form.php:88 26 | #: front/provider.php:33 front/provider.php:35 inc/preference.class.php:89 27 | #: inc/provider.class.php:84 28 | msgid "Single Sign-on" 29 | msgstr "" 30 | 31 | #: setup.php:84 32 | msgid "This plugin requires GLPI >= 0.85" 33 | msgstr "" 34 | 35 | #: front/callback.php:40 36 | msgid "Provider not defined." 37 | msgstr "" 38 | 39 | #: front/callback.php:46 40 | msgid "Provider not found." 41 | msgstr "" 42 | 43 | #: front/callback.php:50 44 | msgid "Provider not active." 45 | msgstr "" 46 | 47 | #: front/callback.php:108 48 | msgid "Automatic redirection, else click" 49 | msgstr "" 50 | 51 | #: inc/preference.class.php:172 inc/provider.class.php:77 52 | msgid "Single Sign-on Provider" 53 | msgstr "" 54 | 55 | #: inc/preference.class.php:197 56 | msgid "Linked accounts" 57 | msgstr "" 58 | 59 | #: inc/provider.class.php:126 60 | msgid "JSON SSO provider representation" 61 | msgstr "" 62 | 63 | #: inc/provider.class.php:127 64 | msgid "Copy provider information" 65 | msgstr "" 66 | 67 | #: inc/provider.class.php:158 68 | msgid "SSO Type" 69 | msgstr "" 70 | 71 | #: inc/provider.class.php:166 inc/provider.class.php:509 72 | msgid "Client ID" 73 | msgstr "" 74 | 75 | #: inc/provider.class.php:168 inc/provider.class.php:517 76 | msgid "Client Secret" 77 | msgstr "" 78 | 79 | #: inc/provider.class.php:179 inc/provider.class.php:525 80 | msgid "Scope" 81 | msgstr "" 82 | 83 | #: inc/provider.class.php:181 inc/provider.class.php:533 84 | msgid "Extra Options" 85 | msgstr "" 86 | 87 | #: inc/provider.class.php:183 88 | #, php-format 89 | msgid "" 90 | "Allows you to specify custom parameters for the SSO provider %1$s. Example: " 91 | "%2$s to force login or %3$s to force account selection (supported URL " 92 | "settings may vary by provider). You can specify additional parameters with " 93 | "the \"&\" delimiter." 94 | msgstr "" 95 | 96 | #: inc/provider.class.php:183 inc/provider.class.php:190 97 | #: inc/provider.class.php:541 98 | msgid "Authorize URL" 99 | msgstr "" 100 | 101 | #: inc/provider.class.php:195 inc/provider.class.php:549 102 | msgid "Access Token URL" 103 | msgstr "" 104 | 105 | #: inc/provider.class.php:200 inc/provider.class.php:557 106 | msgid "Resource Owner Details URL" 107 | msgstr "" 108 | 109 | #: inc/provider.class.php:205 110 | msgid "IsDefault" 111 | msgstr "" 112 | 113 | #: inc/provider.class.php:207 114 | msgid "PopupAuth" 115 | msgstr "" 116 | 117 | #: inc/provider.class.php:213 118 | msgid "SplitDomain" 119 | msgstr "" 120 | 121 | #: inc/provider.class.php:217 122 | msgid "AuthorizedDomains" 123 | msgstr "" 124 | 125 | #: inc/provider.class.php:219 126 | msgid "" 127 | "Provide a list of domains allowed to log in through this provider (separated " 128 | "by commas, no spaces) (optional)." 129 | msgstr "" 130 | 131 | #: inc/provider.class.php:225 132 | msgid "Use Email as Login" 133 | msgstr "" 134 | 135 | #: inc/provider.class.php:228 136 | msgid "Split Name" 137 | msgstr "" 138 | 139 | #: inc/provider.class.php:316 140 | msgid "Callback URL" 141 | msgstr "" 142 | 143 | #: inc/provider.class.php:320 144 | msgid "Test Single Sign-on" 145 | msgstr "" 146 | 147 | #: inc/provider.class.php:379 148 | msgid "A Name is required" 149 | msgstr "" 150 | 151 | #: inc/provider.class.php:385 152 | #, php-format 153 | msgid "The \"%s\" is a Invalid type" 154 | msgstr "" 155 | 156 | #: inc/provider.class.php:389 157 | msgid "A Client ID is required" 158 | msgstr "" 159 | 160 | #: inc/provider.class.php:393 161 | msgid "A Client Secret is required" 162 | msgstr "" 163 | 164 | #: inc/provider.class.php:398 165 | msgid "An Authorize URL is required" 166 | msgstr "" 167 | 168 | #: inc/provider.class.php:400 169 | msgid "The Authorize URL is invalid" 170 | msgstr "" 171 | 172 | #: inc/provider.class.php:404 173 | msgid "An Access Token URL is required" 174 | msgstr "" 175 | 176 | #: inc/provider.class.php:406 177 | msgid "The Access Token URL is invalid" 178 | msgstr "" 179 | 180 | #: inc/provider.class.php:410 181 | msgid "A Resource Owner Details URL is required" 182 | msgstr "" 183 | 184 | #: inc/provider.class.php:412 185 | msgid "The Resource Owner Details URL is invalid" 186 | msgstr "" 187 | 188 | #: inc/provider.class.php:634 189 | msgid "Generic" 190 | msgstr "" 191 | 192 | #: inc/provider.class.php:635 193 | msgid "Azure" 194 | msgstr "" 195 | 196 | #: inc/provider.class.php:636 197 | msgid "Facebook" 198 | msgstr "" 199 | 200 | #: inc/provider.class.php:637 201 | msgid "GitHub" 202 | msgstr "" 203 | 204 | #: inc/provider.class.php:638 205 | msgid "Google" 206 | msgstr "" 207 | 208 | #: inc/provider.class.php:639 209 | msgid "Instagram" 210 | msgstr "" 211 | 212 | #: inc/provider.class.php:640 213 | msgid "LinkdeIn" 214 | msgstr "" 215 | 216 | #: inc/toolbox.class.php:195 217 | #, php-format 218 | msgid "Login with %s" 219 | msgstr "" 220 | -------------------------------------------------------------------------------- /locales/tr_TR.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR glpi-singlesignon Development Team 3 | # This file is distributed under the same license as the singlesignon package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Sinan Doğan, 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: singlesignon\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-02-04 21:23+0000\n" 15 | "PO-Revision-Date: 2025-02-04 20:49+0000\n" 16 | "Last-Translator: Sinan Doğan, 2025\n" 17 | "Language-Team: Turkish (Turkey) (https://app.transifex.com/eduardomozart/teams/204814/tr_TR/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: tr_TR\n" 22 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 23 | 24 | #: setup.php:33 25 | #, php-format 26 | msgid "Please, rename the plugin folder \"%s\" to \"singlesignon\"" 27 | msgstr "Lütfen, \"%s\" eklenti klasörünü “singlesignon” olarak yeniden adlandırın" 28 | 29 | #: setup.php:67 front/provider.form.php:86 front/provider.form.php:88 30 | #: front/provider.php:33 front/provider.php:35 inc/preference.class.php:89 31 | #: inc/provider.class.php:84 32 | msgid "Single Sign-on" 33 | msgstr "" 34 | "Single Sign-on\n" 35 | " \n" 36 | " \n" 37 | " " 38 | 39 | #: setup.php:84 40 | msgid "This plugin requires GLPI >= 0.85" 41 | msgstr "Bu eklenti GLPI >= 0.85 gerektirir" 42 | 43 | #: front/callback.php:40 44 | msgid "Provider not defined." 45 | msgstr "Sağlayıcı tanımlanmamış." 46 | 47 | #: front/callback.php:46 48 | msgid "Provider not found." 49 | msgstr "Sağlayıcı bulunamadı." 50 | 51 | #: front/callback.php:50 52 | msgid "Provider not active." 53 | msgstr "Sağlayıcı aktif değil." 54 | 55 | #: front/callback.php:108 56 | msgid "Automatic redirection, else click" 57 | msgstr "Otomatik yönlendirme, aksi takdirde tıklayın" 58 | 59 | #: inc/preference.class.php:172 inc/provider.class.php:77 60 | msgid "Single Sign-on Provider" 61 | msgstr "Single Sign-on Sağlayıcı" 62 | 63 | #: inc/preference.class.php:197 64 | msgid "Linked accounts" 65 | msgstr "Bağlı hesaplar" 66 | 67 | #: inc/provider.class.php:126 68 | msgid "JSON SSO provider representation" 69 | msgstr "JSON SSO sağlayıcı temsili" 70 | 71 | #: inc/provider.class.php:127 72 | msgid "Copy provider information" 73 | msgstr "Sağlayıcı bilgilerini kopyala" 74 | 75 | #: inc/provider.class.php:158 76 | msgid "SSO Type" 77 | msgstr "SSO Tipi" 78 | 79 | #: inc/provider.class.php:166 inc/provider.class.php:509 80 | msgid "Client ID" 81 | msgstr "Client ID" 82 | 83 | #: inc/provider.class.php:168 inc/provider.class.php:517 84 | msgid "Client Secret" 85 | msgstr "Client Secret" 86 | 87 | #: inc/provider.class.php:179 inc/provider.class.php:525 88 | msgid "Scope" 89 | msgstr "Kapsam" 90 | 91 | #: inc/provider.class.php:181 inc/provider.class.php:533 92 | msgid "Extra Options" 93 | msgstr "Ekstra Seçenekler" 94 | 95 | #: inc/provider.class.php:183 96 | #, php-format 97 | msgid "" 98 | "Allows you to specify custom parameters for the SSO provider %1$s. Example: " 99 | "%2$s to force login or %3$s to force account selection (supported URL " 100 | "settings may vary by provider). You can specify additional parameters with " 101 | "the \"&\" delimiter." 102 | msgstr "" 103 | "%1$s SSO sağlayıcısı için özel parametreler belirlemenizi sağlar. Örnek: " 104 | "%2$s oturum açmaya zorlamak veya %3$s hesap seçimini zorlamak için " 105 | "(desteklenen URL ayarları sağlayıcıya göre değişebilir). “&” sınırlayıcısı " 106 | "ile ek parametreler belirtebilirsiniz." 107 | 108 | #: inc/provider.class.php:183 inc/provider.class.php:190 109 | #: inc/provider.class.php:541 110 | msgid "Authorize URL" 111 | msgstr "Yetkilendirme URL'si" 112 | 113 | #: inc/provider.class.php:195 inc/provider.class.php:549 114 | msgid "Access Token URL" 115 | msgstr "Erişim Belirteci URL'si" 116 | 117 | #: inc/provider.class.php:200 inc/provider.class.php:557 118 | msgid "Resource Owner Details URL" 119 | msgstr "Kaynak Sahibi Ayrıntıları URL" 120 | 121 | #: inc/provider.class.php:205 122 | msgid "IsDefault" 123 | msgstr "Varsayılan" 124 | 125 | #: inc/provider.class.php:207 126 | msgid "PopupAuth" 127 | msgstr "Açılır Pencere Giriş Ekranı" 128 | 129 | #: inc/provider.class.php:213 130 | msgid "SplitDomain" 131 | msgstr "Domaini Ayır" 132 | 133 | #: inc/provider.class.php:217 134 | msgid "AuthorizedDomains" 135 | msgstr "YetkiliAlanAdları" 136 | 137 | #: inc/provider.class.php:219 138 | msgid "" 139 | "Provide a list of domains allowed to log in through this provider (separated" 140 | " by commas, no spaces) (optional)." 141 | msgstr "" 142 | "Bu sağlayıcı üzerinden oturum açmasına izin verilen alan adlarının bir " 143 | "listesini sağlayın (virgülle ayrılmış, boşluksuz) (isteğe bağlı)." 144 | 145 | #: inc/provider.class.php:225 146 | msgid "Use Email as Login" 147 | msgstr "Giriş Olarak E-posta Kullanın" 148 | 149 | #: inc/provider.class.php:228 150 | msgid "Split Name" 151 | msgstr "Bölünmüş İsim" 152 | 153 | #: inc/provider.class.php:316 154 | msgid "Callback URL" 155 | msgstr "Geri Çağırma URL'si" 156 | 157 | #: inc/provider.class.php:320 158 | msgid "Test Single Sign-on" 159 | msgstr "Test Single Sign-on" 160 | 161 | #: inc/provider.class.php:379 162 | msgid "A Name is required" 163 | msgstr "Bir İsim Gerekli" 164 | 165 | #: inc/provider.class.php:385 166 | #, php-format 167 | msgid "The \"%s\" is a Invalid type" 168 | msgstr "\"%s\" Geçersiz bir türdür" 169 | 170 | #: inc/provider.class.php:389 171 | msgid "A Client ID is required" 172 | msgstr "Bir ClientID gereklidir." 173 | 174 | #: inc/provider.class.php:393 175 | msgid "A Client Secret is required" 176 | msgstr "Bir Client Secret gereklidir." 177 | 178 | #: inc/provider.class.php:398 179 | msgid "An Authorize URL is required" 180 | msgstr "Yetkilendirme URL'si gereklidir" 181 | 182 | #: inc/provider.class.php:400 183 | msgid "The Authorize URL is invalid" 184 | msgstr "Yetkilendirme URL'si geçersiz" 185 | 186 | #: inc/provider.class.php:404 187 | msgid "An Access Token URL is required" 188 | msgstr "Bir Erişim Belirteci URL'si gereklidir" 189 | 190 | #: inc/provider.class.php:406 191 | msgid "The Access Token URL is invalid" 192 | msgstr "Erişim Belirteci URL'si geçersiz" 193 | 194 | #: inc/provider.class.php:410 195 | msgid "A Resource Owner Details URL is required" 196 | msgstr "Bir Kaynak Sahibi Ayrıntıları URL'si gereklidir" 197 | 198 | #: inc/provider.class.php:412 199 | msgid "The Resource Owner Details URL is invalid" 200 | msgstr "Kaynak Sahibi Ayrıntıları URL'si geçersiz" 201 | 202 | #: inc/provider.class.php:634 203 | msgid "Generic" 204 | msgstr "Genel" 205 | 206 | #: inc/provider.class.php:635 207 | msgid "Azure" 208 | msgstr "Azure" 209 | 210 | #: inc/provider.class.php:636 211 | msgid "Facebook" 212 | msgstr "Facebook" 213 | 214 | #: inc/provider.class.php:637 215 | msgid "GitHub" 216 | msgstr "GitHub" 217 | 218 | #: inc/provider.class.php:638 219 | msgid "Google" 220 | msgstr "Google" 221 | 222 | #: inc/provider.class.php:639 223 | msgid "Instagram" 224 | msgstr "Instagram" 225 | 226 | #: inc/provider.class.php:640 227 | msgid "LinkdeIn" 228 | msgstr "LinkdeIn" 229 | 230 | #: inc/toolbox.class.php:195 231 | #, php-format 232 | msgid "Login with %s" 233 | msgstr "%s ile giriş yap" 234 | -------------------------------------------------------------------------------- /plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edgardmessias/glpi-singlesignon/e3edebe6c8f3cc3733544ba965a33c54d85693b0/plugin.png -------------------------------------------------------------------------------- /plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Single Sign-On 4 | singlesignon 5 | stable 6 | https://raw.githubusercontent.com/edgardmessias/glpi-singlesignon/master/plugin.png 7 | 8 | 9 | 10 | 11 | 12 | /plugins/singlesignon` directory 24 | * Navigate to the Configuration > Plugins page, 25 | * Install and activate the plugin. 26 | 27 | # Usage 28 | * Go to `Configuration > Single Sign-On` and add a provider 29 | * To test, do logout and try login with links below login page `[ Login with ]` 30 | 31 | # Available providers 32 | * Facebook - https://developers.facebook.com/docs/apps/ 33 | * GitHub - https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/ 34 | * Google - https://developers.google.com/identity/protocols/OpenIDConnect 35 | * Instagram - https://www.instagram.com/developer/authentication/ 36 | * LinkedIn - https://docs.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow?context=linkedin/context 37 | * Generic - Allow to define custom URLs 38 | ]]> 39 | 40 | 41 | https://github.com/edgardmessias/glpi-singlesignon 42 | https://github.com/edgardmessias/glpi-singlesignon/releases 43 | https://github.com/edgardmessias/glpi-singlesignon/issues 44 | https://github.com/edgardmessias/glpi-singlesignon/wiki 45 | 46 | Edgard Lorraine Messias 47 | 48 | 49 | 50 | 1.0.0 51 | 0.85 52 | 0.90 53 | 9.1 54 | 9.2 55 | 9.3 56 | 9.4 57 | 58 | 59 | 1.1.0 60 | 0.85 61 | 0.90 62 | 9.1 63 | 9.2 64 | 9.3 65 | 9.4 66 | 67 | 68 | 1.2.0 69 | 0.85 70 | 0.90 71 | 9.1 72 | 9.2 73 | 9.3 74 | 9.4 75 | 76 | 77 | 1.3.0 78 | 0.85 79 | 0.90 80 | 9.1 81 | 9.2 82 | 9.3 83 | 9.4 84 | 85 | 86 | 1.3.1 87 | 0.85 88 | 0.90 89 | 9.1 90 | 9.2 91 | 9.3 92 | 9.4 93 | 94 | 95 | 1.3.3 96 | 0.85 97 | 0.90 98 | 9.1 99 | 9.2 100 | 9.3 101 | 9.4 102 | 103 | 104 | 1.4.0 105 | ~10.0.5 106 | https://github.com/edgardmessias/glpi-singlesignon/releases/download/v1.4.0/glpi-singlesignon-v1.4.0.tar.bz2 107 | 108 | 109 | 110 | de_DE 111 | en_GB 112 | es_ES 113 | fr_FR 114 | pt_BR 115 | 116 | 117 | 118 | 119 | Authentication 120 | Login 121 | Single Sign On 122 | SSO 123 | 124 | 125 | 126 | https://raw.githubusercontent.com/edgardmessias/glpi-singlesignon/master/screenshots/image_1.png 127 | https://raw.githubusercontent.com/edgardmessias/glpi-singlesignon/master/screenshots/image_1.png 128 | 129 | 130 | -------------------------------------------------------------------------------- /providers.json: -------------------------------------------------------------------------------- 1 | { 2 | "azure": { 3 | "url_authorize": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize", 4 | "url_access_token": "https://login.microsoftonline.com/common/oauth2/v2.0/token", 5 | "url_resource_owner_details": "https://graph.microsoft.com/v1.0/me", 6 | "scope": "User.Read" 7 | }, 8 | "facebook": { 9 | "url_authorize": "https://www.facebook.com/v9.0/dialog/oauth", 10 | "url_access_token": "https://graph.facebook.com/v9.0/oauth/access_token", 11 | "url_resource_owner_details": "https://graph.facebook.com/v9.0/me?fields=id,email&access_token=&appsecret_proof=", 12 | "scope": "public_profile,email" 13 | }, 14 | "github": { 15 | "url_authorize": "https://github.com/login/oauth/authorize", 16 | "url_access_token": "https://github.com/login/oauth/access_token", 17 | "url_resource_owner_details": "https://api.github.com/user", 18 | "scope": "user:email" 19 | }, 20 | "google": { 21 | "url_authorize": "https://accounts.google.com/o/oauth2/v2/auth", 22 | "url_access_token": "https://www.googleapis.com/oauth2/v4/token", 23 | "url_resource_owner_details": "https://openidconnect.googleapis.com/v1/userinfo", 24 | "scope": "openid email profile" 25 | }, 26 | "instagram": { 27 | "url_authorize": "https://api.instagram.com/oauth/authorize", 28 | "url_access_token": "https://api.instagram.com/oauth/access_token", 29 | "url_resource_owner_details": "https://api.instagram.com/v1/users/self?access_token=", 30 | "scope": "basic" 31 | }, 32 | "linkedin": { 33 | "url_authorize": "https://www.linkedin.com/oauth/v2/authorization", 34 | "url_access_token": "https://www.linkedin.com/oauth/v2/accessToken", 35 | "url_resource_owner_details": "https://api.linkedin.com/v2/me", 36 | "scope": "r_liteprofile r_emailaddress" 37 | } 38 | } -------------------------------------------------------------------------------- /screenshots/image_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edgardmessias/glpi-singlesignon/e3edebe6c8f3cc3733544ba965a33c54d85693b0/screenshots/image_1.png -------------------------------------------------------------------------------- /screenshots/image_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edgardmessias/glpi-singlesignon/e3edebe6c8f3cc3733544ba965a33c54d85693b0/screenshots/image_2.png -------------------------------------------------------------------------------- /setup.php: -------------------------------------------------------------------------------- 1 | . 21 | * --------------------------------------------------------------------- 22 | * @copyright Copyright © 2021 - 2022 Edgard 23 | * @license http://www.gnu.org/licenses/gpl.txt GPLv3+ 24 | * @link https://github.com/edgardmessias/glpi-singlesignon/ 25 | * --------------------------------------------------------------------- 26 | */ 27 | 28 | define('PLUGIN_SINGLESIGNON_VERSION', '1.4.0'); 29 | 30 | $folder = basename(dirname(__FILE__)); 31 | 32 | if ($folder !== "singlesignon") { 33 | $msg = sprintf(__sso("Please, rename the plugin folder \"%s\" to \"singlesignon\""), $folder); 34 | Session::addMessageAfterRedirect($msg, true, ERROR); 35 | } 36 | 37 | // Init the hooks of the plugins -Needed 38 | function plugin_init_singlesignon() { 39 | global $PLUGIN_HOOKS, $CFG_GLPI, $CFG_SSO; 40 | 41 | $autoload = __DIR__ . '/vendor/autoload.php'; 42 | 43 | if (file_exists($autoload)) { 44 | include_once $autoload; 45 | } 46 | 47 | Plugin::registerClass('PluginSinglesignonPreference', [ 48 | 'addtabon' => ['Preference', 'User'] 49 | ]); 50 | 51 | $PLUGIN_HOOKS['csrf_compliant']['singlesignon'] = true; 52 | 53 | $PLUGIN_HOOKS['config_page']['singlesignon'] = 'front/provider.php'; 54 | 55 | $CFG_SSO = Config::getConfigurationValues('singlesignon'); 56 | 57 | $PLUGIN_HOOKS['display_login']['singlesignon'] = "plugin_singlesignon_display_login"; 58 | 59 | $PLUGIN_HOOKS['menu_toadd']['singlesignon'] = [ 60 | 'config' => 'PluginSinglesignonProvider', 61 | ]; 62 | } 63 | 64 | // Get the name and the version of the plugin - Needed 65 | function plugin_version_singlesignon() { 66 | return [ 67 | 'name' => __sso('Single Sign-on'), 68 | 'version' => PLUGIN_SINGLESIGNON_VERSION, 69 | 'author' => 'Edgard Lorraine Messias', 70 | 'homepage' => 'https://github.com/edgardmessias/glpi-singlesignon', 71 | 'minGlpiVersion' => '0.85' 72 | ]; 73 | } 74 | 75 | // Optional : check prerequisites before install : may print errors or add to message after redirect 76 | function plugin_singlesignon_check_prerequisites() { 77 | $autoload = __DIR__ . '/vendor/autoload.php'; 78 | 79 | // if (!file_exists($autoload)) { 80 | // echo __sso("Run first: composer install"); 81 | // return false; 82 | // } 83 | if (version_compare(GLPI_VERSION, '0.85', 'lt')) { 84 | echo __sso("This plugin requires GLPI >= 0.85"); 85 | return false; 86 | } else { 87 | return true; 88 | } 89 | } 90 | 91 | function plugin_singlesignon_check_config() { 92 | return true; 93 | } 94 | 95 | function __sso($str) { 96 | return __($str, 'singlesignon'); 97 | } 98 | 99 | function sso_TableExists($table) { 100 | if (function_exists("TableExists")) { 101 | return TableExists($table); 102 | } 103 | 104 | global $DB; 105 | return $DB->TableExists($table); 106 | } 107 | 108 | function sso_FieldExists($table, $field, $usecache = true) { 109 | if (function_exists("FieldExists")) { 110 | return FieldExists($table); 111 | } 112 | 113 | global $DB; 114 | return $DB->FieldExists($table, $field, $usecache); 115 | } 116 | -------------------------------------------------------------------------------- /tools/extract_template.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | soft='singlesignon' 4 | email='@edgardmessias' 5 | copyright='glpi-singlesignon Development Team' 6 | 7 | # Only strings with domain specified are extracted (use Xt args of keyword param to set number of args needed) 8 | 9 | xgettext *.php */*.php --copyright-holder="$copyright" --package-name="$soft" -o locales/singlesignon.pot -L PHP --add-comments=TRANS --from-code=UTF-8 --force-po \ 10 | --keyword=_n:1,2,4t --keyword=__s:1,2t --keyword=__:1,2t --keyword=_e:1,2t --keyword=_x:1c,2,3t \ 11 | --keyword=_ex:1c,2,3t --keyword=_nx:1c,2,3,5t --keyword=_sx:1c,2,3t --keyword=__sso:1 12 | -------------------------------------------------------------------------------- /transifex.yml: -------------------------------------------------------------------------------- 1 | git: 2 | filters: 3 | - filter_type: file 4 | file_format: PO 5 | source_file: locales/singlesignon.pot 6 | source_language: en 7 | translation_files_expression: locales/.po 8 | --------------------------------------------------------------------------------