diff --git a/.github/workflows/phpunit_on_pull_request.yml b/.github/workflows/phpunit_on_pull_request.yml index 473d8f9c..d48ca906 100644 --- a/.github/workflows/phpunit_on_pull_request.yml +++ b/.github/workflows/phpunit_on_pull_request.yml @@ -1,12 +1,107 @@ on: pull_request name: PHPUnit jobs: - runPHPCSInspection: - name: Run PHPUnit test + unit: #----------------------------------------------------------------------- + name: Unit test / PHP ${{ matrix.php }} + strategy: + fail-fast: false + matrix: + php: ['7.3', '7.4', '8.0'] + runs-on: ubuntu-20.04 + + steps: + - name: Check out source code + uses: actions/checkout@v2 + + - name: Check existence of composer.json file + id: check_files + uses: andstor/file-existence-action@v1 + with: + files: "composer.json, phpunit.xml.dist" + + - name: Set up PHP environment + if: steps.check_files.outputs.files_exists == 'true' + uses: shivammathur/setup-php@v2 + with: + php-version: '${{ matrix.php }}' + coverage: xdebug + tools: composer,cs2pr + + - name: Get Composer cache Directory + if: steps.check_files.outputs.files_exists == 'true' + id: composer-cache + run: | + echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: Use Composer cache + if: steps.check_files.outputs.files_exists == 'true' + uses: actions/cache@master + with: + path: ${{ steps['composer-cache'].outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + - name: Install dependencies + if: steps.check_files.outputs.files_exists == 'true' + run: COMPOSER_ROOT_VERSION=dev-master composer install --prefer-dist --no-progress --no-suggest + + - name: Setup problem matcher to provide annotations for PHPUnit + if: steps.check_files.outputs.files_exists == 'true' + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Run PHPUnit + if: steps.check_files.outputs.files_exists == 'true' + run: composer update && composer tests:unit + + - name: Archive code coverage results + uses: actions/upload-artifact@v2 + with: + name: code-coverage-report + path: /tmp/report/html + + artifact-upload: #----------------------------------------------------------------------- + name: Push artifact to static site runs-on: ubuntu-latest + needs: [unit] + steps: - - uses: actions/checkout@v2 - with: - ref: ${{ github.event.pull_request.head.sha }} - - name: Run PHPUnit test - uses: docker://rtcamp/action-run-phpunit:v1.0.0 + - name: Check out gh-reports repository + uses: actions/checkout@v2 + with: + repository: rtCamp/gh-reports.rt.gw + token: ${{ secrets.RTBOT_TOKEN }} + + - name: Download artifact + uses: actions/download-artifact@v2 + with: + name: code-coverage-report + path: /tmp/report/html + + - name: Move report into correct location + run: | + repo=$(echo ${GITHUB_REPOSITORY#rtCamp/}) + rm -rf "$repo/$GITHUB_REF" + mkdir -p "$repo/$GITHUB_REF" + rsync -avhP /tmp/report/html/ "$repo/$GITHUB_REF/" + + - name: Commit files + run: | + repo=$(echo ${GITHUB_REPOSITORY#rtCamp/}) + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add "$repo" + git commit -m "artifcat report build: $GITHUB_REPOSITORY@$GITHUB_SHA" + + - name: Push changes + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.RTBOT_TOKEN }} + branch: main + repository: rtCamp/gh-reports.rt.gw + + - name: Comment URL on PR + uses: unsplash/comment-on-pr@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + msg: "Code coverage report: https://gh-reports.rt.gw/login-with-google/${{ github.ref }}/" + check_for_duplicate_msg: true diff --git a/.github/workflows/release_plugin_on_tag.yml b/.github/workflows/release_plugin_on_tag.yml index 29f857bb..d307d5bd 100644 --- a/.github/workflows/release_plugin_on_tag.yml +++ b/.github/workflows/release_plugin_on_tag.yml @@ -11,7 +11,6 @@ jobs: - name: WordPress Plugin Deploy uses: rtCamp/action-wordpress-org-plugin-deploy@master env: - CUSTOM_COMMAND: composer install --no-dev --optimize-autoloader && composer update EXCLUDE_LIST: .github .gitignore .eslintignore phpunit.xml phpcs.xml tests README.md SLUG: login-with-google ASSETS_DIR: wp-assets diff --git a/.gitignore b/.gitignore index e647fd33..19b7c6ec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -/vendor/ .idea node_modules/ .DS_Store +.phpunit.result.cache diff --git a/LICENSE b/LICENSE index d159169d..50e720ec 100644 --- a/LICENSE +++ b/LICENSE @@ -1,82 +1,85 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 +### GNU GENERAL PUBLIC LICENSE - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. +Version 2, June 1991 - Preamble + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +### Preamble + +The licenses for most software are designed to take away your freedom +to share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to your programs, too. - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. - To protect your rights, we need to make restrictions that forbid +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. +These restrictions translate to certain responsibilities for you if +you distribute copies of the software, or if you modify it. - For example, if you distribute copies of such a program, whether +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their rights. - We protect your rights with two steps: (1) copyright the software, and +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. - Also, for each author's protection and ours, we want to make certain +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. +software. If the software is modified by someone else and passed on, +we want its recipients to know that what they have is not the +original, so that any problems introduced by others will not reflect +on the original authors' reputations. - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free +Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at +all. - The precise terms and conditions for copying, distribution and +The precise terms and conditions for copying, distribution and modification follow. - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". +**0.** This License applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work +based on the Program" means either the Program or any derivative work +under copyright law: that is to say, a work containing the Program or +a portion of it, either verbatim or with modifications and/or +translated into another language. (Hereinafter, translation is +included without limitation in the term "modification".) Each licensee +is addressed as "you". Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of +covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. +is covered only if its contents constitute a work based on the Program +(independent of having been made by running the Program). Whether that +is true depends on what the Program does. - 1. You may copy and distribute verbatim copies of the Program's +**1.** You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the @@ -85,41 +88,46 @@ and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. +you may at your option offer warranty protection in exchange for a +fee. - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and +**2.** You may modify your copy or copies of the Program or any +portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If + +**a)** You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. + + +**b)** You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any part +thereof, to be licensed as a whole at no charge to all third parties +under the terms of this License. + + +**c)** If the modified program normally reads commands interactively +when run, you must cause it, when started running for such interactive +use in the most ordinary way, to print or display an announcement +including an appropriate copyright notice and a notice that there is +no warranty (or else, saying that you provide a warranty) and that +users may redistribute the program under these conditions, and telling +the user how to view a copy of this License. (Exception: if the +Program itself is interactive but does not normally print such an +announcement, your work based on the Program is not required to print +an announcement.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you +sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. +entire whole, and thus to each and every part regardless of who wrote +it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to @@ -131,32 +139,35 @@ with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. - 3. You may copy and distribute the Program (or a work based on it, +**3.** You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, +**a)** Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections 1 +and 2 above on a medium customarily used for software interchange; or, + + +**b)** Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your cost of +physically performing source distribution, a complete machine-readable +copy of the corresponding source code, to be distributed under the +terms of Sections 1 and 2 above on a medium customarily used for +software interchange; or, + - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) +**c)** Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is allowed +only for noncommercial distribution and only if you received the +program in object code or executable form with such an offer, in +accord with Subsection b above.) The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source +making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a +control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the @@ -169,43 +180,44 @@ access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such +**4.** You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt otherwise +to copy, modify, sublicense or distribute the Program is void, and +will automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such parties remain in full compliance. - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by +**5.** You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the +**6.** Each time you redistribute the Program (or any work based on +the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further +these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. +**7.** If, as a consequence of a court judgment or allegation of +patent infringement or for any other reason (not limited to patent +issues), conditions are imposed on you (whether by court order, +agreement or otherwise) that contradict the conditions of this +License, they do not excuse you from the conditions of this License. +If you cannot distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, +then as a consequence you may not distribute the Program at all. For +example, if a patent license would not permit royalty-free +redistribution of the Program by all those who receive copies directly +or indirectly through you, then the only way you could satisfy both it +and this License would be to refrain entirely from distribution of the +Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to @@ -216,7 +228,7 @@ It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is -implemented by public license practices. Many people have made +implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing @@ -226,114 +238,124 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - 8. If the distribution and/or use of the Program is restricted in +**8.** If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates +countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest +**9.** The Free Software Foundation may publish revised and/or new +versions of the General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Program does not specify a +version number of this License, you may choose any version ever +published by the Free Software Foundation. + +**10.** If you wish to incorporate parts of the Program into other +free programs whose distribution conditions are different, write to +the author to ask for permission. For software which is copyrighted by +the Free Software Foundation, write to the Free Software Foundation; +we sometimes make exceptions for this. Our decision will be guided by +the two goals of preserving the free status of all derivatives of our +free software and of promoting the sharing and reuse of software +generally. + +**NO WARRANTY** + +**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +### END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. +free software which everyone can redistribute and change under these +terms. - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - - Copyright (C) + one line to give the program's name and an idea of what it does. + Copyright (C) yyyy name of author - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -Also add information on how to contact you by electronic and paper mail. +Also add information on how to contact you by electronic and paper +mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details + type `show w'. This is free software, and you are welcome + to redistribute it under certain conditions; type `show c' + for details. + +The hypothetical commands \`show w' and \`show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than \`show w' and +\`show c'; they could even be mouse-clicks or menu items--whatever +suits your program. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the program, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright + interest in the program `Gnomovision' + (which makes passes at compilers) written + by James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, +you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +[GNU Lesser General Public +License](https://www.gnu.org/licenses/lgpl.html) instead of this +License. diff --git a/README.md b/README.md index ba928e2d..2bdedbed 100644 --- a/README.md +++ b/README.md @@ -1,99 +1,98 @@ -

- -

+# Login with Google -# Log in with Google +> WordPress plugin to login/register with google -Project Status: Active – The project has reached a stable, usable state and is being actively developed. +1. [Overview](#overview) +2. [Installation](#installation) +3. [Usage Instructions](#usage-instructions) + + [Plugin Constants](#plugin-constants) -Minimal plugin that allows WordPress users to log in using Google. +4. [Shortcode](#shortcode) +5. [Minimum Requirements](#minimum-requirements) +6. [License](#license) -**Author:** rtCamp +## Overview -**Tags:** Google login, sign in, sso, oauth, authentication, sign-in, single sign-on, log in +Login with google provides seamless experience for users to login in to WordPress +sites using their google account. No need to manually create accounts, no need to remember quirky +passwords. Just one click and land into the site! -**Requires at least:** 5.0 +## Installation -**Tested up to:** 5.8.1 +1. Clone this repository. +2. Run `composer install --no-dev` from inside the cloned directory. +3. Upload the directory to `wp-content/plugins` directory. +4. Activate the plugin from WordPress dashboard. -**Requires PHP version:** 7.0 +## Browser support +[These browsers are supported](https://developers.google.com/identity/gsi/web/guides/supported-browsers). Note, for example, that One Tap Login is not supported in Safari. -**Stable tag:** 1.0.10 +## Usage Instructions -**License:** GPLv2 or later (of course!) +1. You will need to register a new application at https://console.cloud.google.com/apis/dashboard -**License URI:** http://www.gnu.org/licenses/gpl-2.0.html +2. `Authorization callback URL` should be like `https://yourdomain.com/wp-login.php`, where +`https://yourdomain.com` will be replaced by your site URL. -## Setup +3. Once you create the app, you will receive the `Client ID` and `Client Secret`, add these credentials +in `Settings > Login with google` settings page in their respective fields. + +4. `Create new user` enables new user registration irrespective of `Membership` settings in + `Settings > General`; as sometimes enabling user registration can lead to lots of spam users. + Plugin will take this setting as first priority and membership setting as second priority, so if + any one of them is enabled, new users will be registered by this plugin after successful authorization. + +5. `Whitelisted Domains` allows users from specific domains (domain in email) to get registered on site. +This will prevent unwanted registration on website. +**For Example:** If you want users only from your organization (`myorg.com`) to get registered on the +website, you enter `myorg.com` in whitelisted domains. Users with google +email like `abc@myorg.com` will be able to register on website. Contrary to this, users with emails like +`something@gmail.com` would not be able to register here. + -1. If you're cloning repo, then after cloning run `composer install --no-dev` to install dependencies. GitHub release zip and WordPress.org download can skip this step. -2. Create a project from [Google Developers Console](https://console.developers.google.com/apis/dashboard) if none exists. -3. Go to **Credentials** tab, then create credential for OAuth client. - * Application type will be **Web Application** - * Add `YOUR_DOMAIN/wp-login.php` in **Authorized redirect URIs** -4. This will give you **Client ID** and **Secret key**. -5. Input these values either in `WP Admin > Settings > WP Google Login`, or in `wp-config.php` using the following code snippet: +### Plugin Constants -```php -define( 'WP_GOOGLE_LOGIN_CLIENT_ID', 'YOUR_GOOGLE_CLIENT_ID' ); -define( 'WP_GOOGLE_LOGIN_SECRET', 'YOUR_SECRET_KEY' ); -``` +Above mentioned settings can also be configured via PHP constants by defining them in wp-config.php +file. -### How to enable automatic user registration? +Refer following list of constants. -You can enable user registration either by -- Checking `Settings > WP Google Login > Enable Google Login Registration` -OR -- Adding `define( 'WP_GOOGLE_LOGIN_USER_REGISTRATION', 'true' );` in wp-config.php file. +| | Type | Description | +|-----------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| WP_GOOGLE_LOGIN_CLIENT_ID | String | Google client ID of your application. | +| WP_GOOGLE_LOGIN_SECRET | String | Secret key of your application | +| WP_GOOGLE_LOGIN_USER_REGISTRATION | Boolean | (Optional) Set True If you want to enable new user registration. By default, user registration defers to `Settings > General Settings > Membership` if constant is not set. | +| WP_GOOGLE_LOGIN_WHITELIST_DOMAINS | String | (Optional) Domain name, if you want to restrict login with your custom domain. By default, It will allow all domains. You can whitelist multiple domains. | -Note: If the checkbox is ON then, it will register valid Google users even when WordPress default setting, under `Settings > General Settings > Membership > Anyone can register` checkbox is OFF. +These constants can also be configured +via [wp-cli](https://developer.wordpress.org/cli/commands/config/). -### How to restrict user registration to one or more domain(s)? +**Note:** If you have defined the constant in wp-config.php file, corresponding settings field will be disable +(locked for editing) on the settings page. -By default, when you enable user registration via constant `WP_GOOGLE_LOGIN_USER_REGISTRATION` or enable `Settings > WP Google Login > Enable Google Login Registration`, it will create a user for any Google login (including gmail.com users). If you are planning to use this plugin on a private, internal site, then you may like to restrict user registration to users under a single Google Suite organization. This configuration variable does that. +## Shortcode -Add your domain name, without any schema prefix and `www,` as the value of `WP_GOOGLE_LOGIN_WHITELIST_DOMAINS` constant or in the settings `Settings > WP Google Login > Whitelisted Domains`. You can whitelist multiple domains. Please separate domains with commas. See the below example to know how to do it via constants: +You can add the google login button to any page/post using shortcode: `google_login` -```php -define( 'WP_GOOGLE_LOGIN_WHITELIST_DOMAINS', 'example.com,sample.com' ); +**Example:** +```php +[google_login button_text="Google Login" force_display="yes" /] ``` -**Note:** If a user already exists, they **will be allowed to login with Google** regardless of whether their domain is whitelisted or not. Whitelisting will only prevent users from **registering** with email addresses from non-whitelisted domains. - - -## Hooks - -### 1. Action `wp_google_login_token` - -This action provides access token received after Google login. -**Parameters:** -* `token` (Array): Converted token using `fetchAccessTokenWithAuthCode` method of `Google_Client` class. -* `user_info` (Array): Details of user after login. -* `client` (Object): `Google_Client` object in use. - -### 2. Filter `wp_google_login_scopes` - -This filter can be used to filter existing scope used in Google Sign in. -You can ask for additional permission while user logs in. - -This filter will provide 1 parameter `scopes` in callback, which contains array of scopes. - - -## wp-config.php parameters list - -| | Type | Description | -|-----------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| WP_GOOGLE_LOGIN_CLIENT_ID | String | Google client ID of your application. | -| WP_GOOGLE_LOGIN_SECRET | String | Secret key of your application | -| WP_GOOGLE_LOGIN_USER_REGISTRATION | Boolean | (Optional) Set True If you want to enable new user registration. By default, user registration defers to `Settings > General Settings > Membership` if constant is not set. | -| WP_GOOGLE_LOGIN_WHITELIST_DOMAINS | String | (Optional) Domain name, if you want to restrict login with your custom domain. By default, It will allow all domains. You can whitelist multiple domains. | +**Supported attributes for shortcode** +| Parameter | Description | Values | Default | +| -------------- | --------------------------------------------------------------| -------| ------------------ | +| button_text | Text to show for login button | string | Login with google | +| force_display | Whether to display button when user is already logged in | yes/no | no | +| redirect_to | URL where user should be redirected post login | URL | `wp-admin` | ## Contribute ### Reporting a bug 🐞 -Before creating a new issue, do browse through the [existing issues](https://github.com/rtCamp/login-with-google/issues) for resolution or upcoming fixes. +Before creating a new issue, do browse through the [existing issues](https://github.com/rtCamp/login-with-google/issues) for resolution or upcoming fixes. If you still need to [log an issue](https://github.com/rtCamp/login-with-google/issues/new), making sure to include as much detail as you can, including clear steps to reproduce your issue if possible. @@ -101,7 +100,7 @@ If you still need to [log an issue](https://github.com/rtCamp/login-with-google/ Want to contribute a new feature? Start a conversation by logging an [issue](https://github.com/rtCamp/login-with-google/issues). -Once you're ready to send a pull request, please run through the following checklist: +Once you're ready to send a pull request, please run through the following checklist: 1. Browse through the [existing issues](https://github.com/rtCamp/login-with-google/issues) for anything related to what you want to work on. If you don't find any related issues, open a new one. @@ -115,17 +114,27 @@ Once you're ready to send a pull request, please run through the following check 1. Once your pull request has passed final code review and tests, it will be merged into `develop` and be in the pipeline for the next release. Props to you! 🎉 + ## Unit testing -- Setup local unit test environment by running script from terminal +Unit tests can be run with simple command `composer tests:unit`. +Please note that you'll need to do `composer install` (need to install dev dependencies) for running +unit tests. + +You should have PHP CLI > 7.1 installed. If you have Xdebug enabled with php, code coverage report will be +generated at `/tmp/report/html` + +## Minimum Requirements -```./bin/install-wp-tests.sh [db-host] [wp-version] [skip-database-creation]``` +WordPress >= 5.4.0 -- Execute `phpunit` in terminal from repository to run all test cases. +PHP >= 7.3 -- Execute `phpunit ./tests/inc/test-class.php` in terminal with file path to run specific tests. +## License +This library is released under +["GPL 2.0 or later" License](LICENSE). ## BTW, We're Hiring! -Join us at rtCamp, we specialize in providing high performance enterprise WordPress solutions +Join us at rtCamp, we specialize in providing high performance enterprise WordPress solutions diff --git a/assets/build/js/login.js b/assets/build/js/login.js index fa7972a2..54ec6bf2 100644 --- a/assets/build/js/login.js +++ b/assets/build/js/login.js @@ -1 +1 @@ -!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=0)}([function(e,t,n){n(1),e.exports=n(2)},function(e,t){({init:function(){document.addEventListener("DOMContentLoaded",this.onContentLoaded)},onContentLoaded:function(){this.form=document.getElementById("loginform")||document.getElementById("registerform"),null!==this.form&&(this.googleLoginButton=this.form.querySelector(".wp_google_login").cloneNode(!0),this.googleLoginButton.classList.remove("hidden"),this.form.append(this.googleLoginButton))}}).init()},function(e,t){}]); \ No newline at end of file +!function(e){var t={};function o(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,o),r.l=!0,r.exports}o.m=e,o.c=t,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(e,t){if(1&t&&(e=o(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)o.d(n,r,function(t){return e[t]}.bind(null,r));return n},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="/",o(o.s=0)}([function(e,t,o){o(1),e.exports=o(2)},function(e,t){({init:function(){document.addEventListener("DOMContentLoaded",this.onContentLoaded)},onContentLoaded:function(){this.form=document.getElementById("loginform")||document.getElementById("registerform"),document.querySelector(".wp_google_login")&&null===this.form&&(document.cookie="wp-login-with-google=1;path="+window.location.pathname+";"),null!==this.form&&(this.googleLoginButton=this.form.querySelector(".wp_google_login"),this.googleLoginButton.classList.remove("hidden"),this.form.append(this.googleLoginButton))}}).init()},function(e,t){}]); \ No newline at end of file diff --git a/assets/build/js/onetap.js b/assets/build/js/onetap.js new file mode 100644 index 00000000..2fa41276 --- /dev/null +++ b/assets/build/js/onetap.js @@ -0,0 +1,35 @@ +window.LoginWithGoogleDataCallBack = function( response ) { + var xhr = new XMLHttpRequest(); + xhr.open('POST', TempAccessOneTap.ajaxurl, true); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xhr.onreadystatechange = function() { + if(xhr.readyState === XMLHttpRequest.DONE) { + var status = xhr.status; + if ( status === 200 ) { + var response = JSON.parse( xhr.responseText ); + + if ( ! response.success ) { + alert( response.data ); + return; + } + + try { + + var redirect_to = new URL( response.data.redirect ); + var homeurl = new URL( TempAccessOneTap.homeurl ); + + if ( redirect_to.host !== homeurl.host ) { + throw new URIError( wp.i18n.__( 'Invalid URL for Redirection', 'login-with-google' ) ); + } + + } catch ( e ) { + alert( e.message ); + return; + } + + window.location = response.data.redirect; + } + } + }; + xhr.send( 'action=validate_id_token&token=' + response.credential + '&state=' + TempAccessOneTap.state ); +}; diff --git a/assets/build/js/onetap.min.js b/assets/build/js/onetap.min.js new file mode 100644 index 00000000..a7eef91b --- /dev/null +++ b/assets/build/js/onetap.min.js @@ -0,0 +1 @@ +window.LoginWithGoogleDataCallBack=function(e){var t=new XMLHttpRequest;t.open("POST",TempAccessOneTap.ajaxurl,!0),t.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),t.onreadystatechange=function(){if(t.readyState===XMLHttpRequest.DONE&&200===t.status){var e=JSON.parse(t.responseText);if(!e.success)return void alert(e.data);try{var a=new URL(e.data.redirect),n=new URL(TempAccessOneTap.homeurl);if(a.host!==n.host)throw new URIError(wp.i18n.__("Invalid URL for Redirection","login-with-google"))}catch(e){return void alert(e.message)}window.location=e.data.redirect}},t.send("action=validate_id_token&token="+e.credential+"&state="+TempAccessOneTap.state)}; diff --git a/assets/mix-manifest.json b/assets/mix-manifest.json index c444db49..2d8efe2e 100644 --- a/assets/mix-manifest.json +++ b/assets/mix-manifest.json @@ -1,4 +1,6 @@ { "/build/js/login.js": "/build/js/login.js", - "/build/css/login.css": "/build/css/login.css" + "/build/css/login.css": "/build/css/login.css", + "/build/js/onetap.js": "/build/js/onetap.js", + "/build/js/onetap.min.js": "/build/js/onetap.min.js" } diff --git a/assets/package.json b/assets/package.json index 720154f1..cd3e94ef 100644 --- a/assets/package.json +++ b/assets/package.json @@ -8,11 +8,11 @@ "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", "prod": "npm run production", "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", - "language": "wp i18n make-pot ../ --exclude=\"assets\",\"tests\",\"vendor\" ../languages/login-with-google.po" + "language": "wp i18n make-pot ../ --exclude=\"assets\",\"tests\",\"vendor\" ../languages/login-with-google.pot" }, "devDependencies": { "cross-env": "^5.1", - "laravel-mix": "^4.0.15", + "laravel-mix": "^4.1.0", "lodash": "^4.17.15", "sass": "^1.26.5", "sass-loader": "^7.1.0", diff --git a/assets/src/js/login.js b/assets/src/js/login.js index 6bd85beb..7dc7df80 100644 --- a/assets/src/js/login.js +++ b/assets/src/js/login.js @@ -19,6 +19,9 @@ const wpGoogleLogin = { * Callback function when content is load. * To render the google login button at after login form. * + * Set cookie if "Login with Google" button displayed to bypass page cache + * Do not set on wp login or registration page. + * * @return void */ onContentLoaded() { @@ -26,13 +29,18 @@ const wpGoogleLogin = { // Form either can be login or register form. this.form = document.getElementById( 'loginform' ) || document.getElementById( 'registerform' ); + // Set cookie if "Login with Google" button displayed to bypass page cache + // Do not set on wp login or registration page. + if ( document.querySelector( '.wp_google_login' ) && null === this.form ) { + document.cookie = 'wp-login-with-google=1;path=' + window.location.pathname + ';'; + } + if ( null === this.form ) { return; } - this.googleLoginButton = this.form.querySelector( '.wp_google_login' ).cloneNode( true ); + this.googleLoginButton = this.form.querySelector( '.wp_google_login' ); this.googleLoginButton.classList.remove( 'hidden' ); - // HTML is cloned from existing HTML node. this.form.append( this.googleLoginButton ); } diff --git a/assets/src/js/onetap.js b/assets/src/js/onetap.js new file mode 100644 index 00000000..2fa41276 --- /dev/null +++ b/assets/src/js/onetap.js @@ -0,0 +1,35 @@ +window.LoginWithGoogleDataCallBack = function( response ) { + var xhr = new XMLHttpRequest(); + xhr.open('POST', TempAccessOneTap.ajaxurl, true); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xhr.onreadystatechange = function() { + if(xhr.readyState === XMLHttpRequest.DONE) { + var status = xhr.status; + if ( status === 200 ) { + var response = JSON.parse( xhr.responseText ); + + if ( ! response.success ) { + alert( response.data ); + return; + } + + try { + + var redirect_to = new URL( response.data.redirect ); + var homeurl = new URL( TempAccessOneTap.homeurl ); + + if ( redirect_to.host !== homeurl.host ) { + throw new URIError( wp.i18n.__( 'Invalid URL for Redirection', 'login-with-google' ) ); + } + + } catch ( e ) { + alert( e.message ); + return; + } + + window.location = response.data.redirect; + } + } + }; + xhr.send( 'action=validate_id_token&token=' + response.credential + '&state=' + TempAccessOneTap.state ); +}; diff --git a/assets/webpack.mix.js b/assets/webpack.mix.js index 7cf589cf..8a423f03 100644 --- a/assets/webpack.mix.js +++ b/assets/webpack.mix.js @@ -11,5 +11,7 @@ mix.options( { } ); mix.copy( 'src/images', 'build/images' ) + .copy( 'src/js/onetap.js', 'build/js' ) + .minify( 'build/js/onetap.js' ) .js( 'src/js/login.js', 'build/js' ) - .sass( 'src/scss/login.scss', 'build/css' ); \ No newline at end of file + .sass( 'src/scss/login.scss', 'build/css' ); diff --git a/autoloader.php b/autoloader.php deleted file mode 100644 index dca7fb9a..00000000 --- a/autoloader.php +++ /dev/null @@ -1,72 +0,0 @@ - [db-host] [wp-version] [skip-database-creation]" - exit 1 -fi - -DB_NAME=$1 -DB_USER=$2 -DB_PASS=$3 -DB_HOST=${4-localhost} -WP_VERSION=${5-latest} -SKIP_DB_CREATE=${6-false} - -TMPDIR=${TMPDIR-/tmp} -TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") -WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} -WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/} - -download() { - if [ `which curl` ]; then - curl -s "$1" > "$2"; - elif [ `which wget` ]; then - wget -nv -O "$2" "$1" - fi -} - -if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then - WP_BRANCH=${WP_VERSION%\-*} - WP_TESTS_TAG="branches/$WP_BRANCH" - -elif [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then - WP_TESTS_TAG="branches/$WP_VERSION" -elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then - if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then - # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x - WP_TESTS_TAG="tags/${WP_VERSION%??}" - else - WP_TESTS_TAG="tags/$WP_VERSION" - fi -elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then - WP_TESTS_TAG="trunk" -else - # http serves a single offer, whereas https serves multiple. we only want one - download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json - grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json - LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') - if [[ -z "$LATEST_VERSION" ]]; then - echo "Latest WordPress version could not be found" - exit 1 - fi - WP_TESTS_TAG="tags/$LATEST_VERSION" -fi -set -ex - -install_wp() { - - if [ -d $WP_CORE_DIR ]; then - return; - fi - - mkdir -p $WP_CORE_DIR - - if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then - mkdir -p $TMPDIR/wordpress-nightly - download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip - unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/ - mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR - else - if [ $WP_VERSION == 'latest' ]; then - local ARCHIVE_NAME='latest' - elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then - # https serves multiple offers, whereas http serves single. - download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json - if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then - # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x - LATEST_VERSION=${WP_VERSION%??} - else - # otherwise, scan the releases and get the most up to date minor version of the major release - local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'` - LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) - fi - if [[ -z "$LATEST_VERSION" ]]; then - local ARCHIVE_NAME="wordpress-$WP_VERSION" - else - local ARCHIVE_NAME="wordpress-$LATEST_VERSION" - fi - else - local ARCHIVE_NAME="wordpress-$WP_VERSION" - fi - download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz - tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR - fi - - download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php -} - -install_test_suite() { - # portable in-place argument for both GNU sed and Mac OSX sed - if [[ $(uname -s) == 'Darwin' ]]; then - local ioption='-i.bak' - else - local ioption='-i' - fi - - # set up testing suite if it doesn't yet exist - if [ ! -d $WP_TESTS_DIR ]; then - # set up testing suite - mkdir -p $WP_TESTS_DIR - svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes - svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data - fi - - if [ ! -f wp-tests-config.php ]; then - download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php - # remove all forward slashes in the end - WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") - sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php - sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php - sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php - sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php - sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php - fi - -} - -install_db() { - - if [ ${SKIP_DB_CREATE} = "true" ]; then - return 0 - fi - - # parse DB_HOST for port or socket references - local PARTS=(${DB_HOST//\:/ }) - local DB_HOSTNAME=${PARTS[0]}; - local DB_SOCK_OR_PORT=${PARTS[1]}; - local EXTRA="" - - if ! [ -z $DB_HOSTNAME ] ; then - if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then - EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" - elif ! [ -z $DB_SOCK_OR_PORT ] ; then - EXTRA=" --socket=$DB_SOCK_OR_PORT" - elif ! [ -z $DB_HOSTNAME ] ; then - EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" - fi - fi - - # create database - mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA -} - -install_wp -install_test_suite -install_db diff --git a/composer.json b/composer.json index f0a10346..262b81a0 100644 --- a/composer.json +++ b/composer.json @@ -1,22 +1,61 @@ { "name": "rtcamp/login-with-google", - "description": "Minimal plugin which allows WP user to login with google.", - "license": "GPLv2", + "description": "WordPress plugin to let users login with google.", + "license": "GPL 2.0", "authors": [ + { + "name": "rtCamp", + "email": "contact@rtcamp.com", + "homepage": "https://rtcamp.com/", + "role": "Developer" + }, { "name": "Utkarsh Patel", "email": "itismeutkarsh@gmail.com" + }, + { + "name": "Paul Clark", + "role": "Developer" + }, + { + "name": "Ankit Gade", + "email": "ankit.gade@rtcamp.com", + "homepage": "https://iamank.it/", + "role": "Developer" } ], + "minimum-stability": "stable", "require": { - "google/apiclient": "^2.0" + "php": ">=7.1", + "pimple/pimple": "3.*" + }, + "require-dev": { + "squizlabs/php_codesniffer": "^3.5", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.1", + "wp-coding-standards/wpcs": "^2.3", + "sirbrillig/phpcs-variable-analysis": "^2.10", + "automattic/vipwpcs": "^2.2", + "phpcompatibility/phpcompatibility-wp": "^2.1", + "phpunit/phpunit": "9.5", + "10up/wp_mock": "0.4.2" }, - "scripts": { - "post-update-cmd": "Google_Task_Composer::cleanup" + "autoload": { + "psr-4": { + "RtCamp\\GoogleLogin\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "RtCamp\\GoogleLogin\\Tests\\": "tests/php" + } }, - "extra": { - "google/apiclient-services": [ - "Oauth2" + "scripts": { + "cs": "@php ./vendor/bin/phpcs", + "cs:fix": "@php ./vendor/bin/phpcbf", + "tests:unit": "@php ./vendor/bin/phpunit tests/php/Unit/", + "qa": [ + "@cs", + "@tests" ] } } diff --git a/composer.lock b/composer.lock index d6aaaa26..b106c288 100644 --- a/composer.lock +++ b/composer.lock @@ -4,627 +4,2493 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "43275de4c7e7779e3156ca4886d5d1a6", + "content-hash": "68a3dc0fe0a42aa8319c92c284fb3843", "packages": [ { - "name": "firebase/php-jwt", - "version": "v5.0.0", + "name": "pimple/pimple", + "version": "v3.4.0", "source": { "type": "git", - "url": "https://github.com/firebase/php-jwt.git", - "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e" + "url": "https://github.com/silexphp/Pimple.git", + "reference": "86406047271859ffc13424a048541f4531f53601" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/9984a4d3a32ae7673d6971ea00bae9d0a1abba0e", - "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/86406047271859ffc13424a048541f4531f53601", + "reference": "86406047271859ffc13424a048541f4531f53601", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.2.5", + "psr/container": "^1.1" }, "require-dev": { - "phpunit/phpunit": " 4.8.35" + "symfony/phpunit-bridge": "^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Pimple": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Pimple, a simple Dependency Injection Container", + "homepage": "https://pimple.symfony.com", + "keywords": [ + "container", + "dependency injection" + ], + "time": "2021-03-06T08:28:00+00:00" + }, + { + "name": "psr/container", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2021-03-05T17:36:06+00:00" + } + ], + "packages-dev": [ + { + "name": "10up/wp_mock", + "version": "0.4.2", + "source": { + "type": "git", + "url": "https://github.com/10up/wp_mock.git", + "reference": "9019226eb50df275aa86bb15bfc98a619601ee49" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/10up/wp_mock/zipball/9019226eb50df275aa86bb15bfc98a619601ee49", + "reference": "9019226eb50df275aa86bb15bfc98a619601ee49", + "shasum": "" + }, + "require": { + "antecedent/patchwork": "^2.1", + "mockery/mockery": "^1.0", + "php": ">=7.1", + "phpunit/phpunit": ">=7.0" + }, + "require-dev": { + "behat/behat": "^3.0", + "php-coveralls/php-coveralls": "^2.1", + "sebastian/comparator": ">=1.2.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "WP_Mock\\": "./php/WP_Mock" + }, + "classmap": [ + "php/WP_Mock.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "description": "A mocking library to take the pain out of unit testing for WordPress", + "time": "2019-03-16T03:44:39+00:00" + }, + { + "name": "antecedent/patchwork", + "version": "2.1.12", + "source": { + "type": "git", + "url": "https://github.com/antecedent/patchwork.git", + "reference": "b98e046dd4c0acc34a0846604f06f6111654d9ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antecedent/patchwork/zipball/b98e046dd4c0acc34a0846604f06f6111654d9ea", + "reference": "b98e046dd4c0acc34a0846604f06f6111654d9ea", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": ">=4" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignas Rudaitis", + "email": "ignas.rudaitis@gmail.com" + } + ], + "description": "Method redefinition (monkey-patching) functionality for PHP.", + "homepage": "http://patchwork2.org/", + "keywords": [ + "aop", + "aspect", + "interception", + "monkeypatching", + "redefinition", + "runkit", + "testing" + ], + "time": "2019-12-22T17:52:09+00:00" + }, + { + "name": "automattic/vipwpcs", + "version": "2.3.2", + "source": { + "type": "git", + "url": "https://github.com/Automattic/VIP-Coding-Standards.git", + "reference": "efacebef421334d54b99afa92fb8fa645336a8a7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Automattic/VIP-Coding-Standards/zipball/efacebef421334d54b99afa92fb8fa645336a8a7", + "reference": "efacebef421334d54b99afa92fb8fa645336a8a7", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7", + "php": ">=5.4", + "sirbrillig/phpcs-variable-analysis": "^2.8.3", + "squizlabs/php_codesniffer": "^3.5.5", + "wp-coding-standards/wpcs": "^2.3" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^0.5", + "php-parallel-lint/php-parallel-lint": "^1.0", + "phpcompatibility/php-compatibility": "^9", + "phpcsstandards/phpcsdevtools": "^1.0", + "phpunit/phpunit": "^4 || ^5 || ^6 || ^7" + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Contributors", + "homepage": "https://github.com/Automattic/VIP-Coding-Standards/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress VIP minimum coding conventions", + "keywords": [ + "phpcs", + "standards", + "wordpress" + ], + "time": "2021-04-28T16:41:50+00:00" + }, + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v0.7.1", + "source": { + "type": "git", + "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", + "reference": "fe390591e0241955f22eb9ba327d137e501c771c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/fe390591e0241955f22eb9ba327d137e501c771c", + "reference": "fe390591e0241955f22eb9ba327d137e501c771c", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.0 || ^3.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "*", + "phpcompatibility/php-compatibility": "^9.0", + "sensiolabs/security-checker": "^4.1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "homepage": "http://www.dealerdirect.com", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "time": "2020-12-07T18:04:37+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^8.0", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, "type": "library", "autoload": { "psr-4": { - "Firebase\\JWT\\": "src" + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2020-11-10T18:47:58+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "shasum": "" + }, + "require": { + "php": "^5.3|^7.0|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "time": "2020-07-09T08:09:16+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.4.3", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "d1339f64479af1bee0e82a0413813fe5345a54ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/d1339f64479af1bee0e82a0413813fe5345a54ea", + "reference": "d1339f64479af1bee0e82a0413813fe5345a54ea", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": "^7.3 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Mockery": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "http://blog.astrumfutura.com" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "http://davedevelopment.co.uk" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "time": "2021-02-24T09:51:49+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.10.2", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2020-11-13T09:40:50+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.10.5", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "4432ba399e47c66624bc73c8c0f811e5c109576f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4432ba399e47c66624bc73c8c0f811e5c109576f", + "reference": "4432ba399e47c66624bc73c8c0f811e5c109576f", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2021-05-03T19:11:20+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2020-06-27T14:33:11+00:00" + }, + { + "name": "phar-io/version", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "bae7c545bef187884426f042434e561ab1ddb182" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", + "reference": "bae7c545bef187884426f042434e561ab1ddb182", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2021-02-23T14:00:09+00:00" + }, + { + "name": "phpcompatibility/php-compatibility", + "version": "9.3.5", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.3 || ^3.0.2" + }, + "conflict": { + "squizlabs/php_codesniffer": "2.6.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "homepage": "https://github.com/wimg", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" + } + ], + "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", + "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", + "keywords": [ + "compatibility", + "phpcs", + "standards" + ], + "time": "2019-12-27T09:44:58+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-paragonie", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", + "reference": "ddabec839cc003651f2ce695c938686d1086cf43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/ddabec839cc003651f2ce695c938686d1086cf43", + "reference": "ddabec839cc003651f2ce695c938686d1086cf43", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7", + "paragonie/random_compat": "dev-master", + "paragonie/sodium_compat": "dev-master" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "lead" + } + ], + "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.", + "homepage": "http://phpcompatibility.com/", + "keywords": [ + "compatibility", + "paragonie", + "phpcs", + "polyfill", + "standards" + ], + "time": "2021-02-15T10:24:51+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-wp", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", + "reference": "b7dc0cd7a8f767ccac5e7637550ea1c50a67b09e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/b7dc0cd7a8f767ccac5e7637550ea1c50a67b09e", + "reference": "b7dc0cd7a8f767ccac5e7637550ea1c50a67b09e", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0", + "phpcompatibility/phpcompatibility-paragonie": "^1.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "lead" + } + ], + "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.", + "homepage": "http://phpcompatibility.com/", + "keywords": [ + "compatibility", + "phpcs", + "standards", + "wordpress" + ], + "time": "2021-02-15T12:58:46+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.2.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", + "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2020-09-03T19:13:55+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "time": "2020-09-17T18:55:26+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "1.13.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea", + "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.1", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2021-03-17T13:42:18+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "f6293e1b30a2354e8428e004689671b83871edde" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde", + "reference": "f6293e1b30a2354e8428e004689671b83871edde", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.10.2", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "*", + "ext-xdebug": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-03-28T07:26:59+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:57:25+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "8e16c225d57c3d6808014df6b1dd7598d0a5bbbe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8e16c225d57c3d6808014df6b1dd7598d0a5bbbe", + "reference": "8e16c225d57c3d6808014df6b1dd7598d0a5bbbe", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.1", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.3", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^2.3", + "sebastian/version": "^3.0.2" + }, + "require-dev": { + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ], + "files": [ + "src/Framework/Assert/Functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "funding": [ + { + "url": "https://phpunit.de/donate.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-12-04T05:05:53+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:49:45+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:10:38+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:52:38+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65", + "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { - "name": "Neuman Vong", - "email": "neuman+pear@twilio.com", - "role": "Developer" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Anant Narayanan", - "email": "anant@php.net", - "role": "Developer" + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", - "homepage": "https://github.com/firebase/php-jwt", - "time": "2017-06-27T22:17:23+00:00" + "time": "2020-09-28T05:24:23+00:00" }, { - "name": "google/apiclient", - "version": "v2.2.2", + "name": "sebastian/global-state", + "version": "5.0.2", "source": { "type": "git", - "url": "https://github.com/googleapis/google-api-php-client.git", - "reference": "4e0fd83510e579043e10e565528b323b7c2b3c81" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "a90ccbddffa067b51f574dea6eb25d5680839455" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/google-api-php-client/zipball/4e0fd83510e579043e10e565528b323b7c2b3c81", - "reference": "4e0fd83510e579043e10e565528b323b7c2b3c81", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a90ccbddffa067b51f574dea6eb25d5680839455", + "reference": "a90ccbddffa067b51f574dea6eb25d5680839455", "shasum": "" }, "require": { - "firebase/php-jwt": "~2.0|~3.0|~4.0|~5.0", - "google/apiclient-services": "~0.13", - "google/auth": "^1.0", - "guzzlehttp/guzzle": "~5.3.1|~6.0", - "guzzlehttp/psr7": "^1.2", - "monolog/monolog": "^1.17", - "php": ">=5.4", - "phpseclib/phpseclib": "~0.3.10|~2.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "cache/filesystem-adapter": "^0.3.2", - "phpunit/phpunit": "~4.8.36", - "squizlabs/php_codesniffer": "~2.3", - "symfony/css-selector": "~2.1", - "symfony/dom-crawler": "~2.1" + "ext-dom": "*", + "phpunit/phpunit": "^9.3" }, "suggest": { - "cache/filesystem-adapter": "For caching certs and tokens (using Google_Client::setCache)" + "ext-uopz": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-master": "5.0-dev" } }, "autoload": { - "psr-0": { - "Google_": "src/" - }, "classmap": [ - "src/Google/Service/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } ], - "description": "Client library for Google APIs", - "homepage": "http://developers.google.com/api-client-library/php", + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", "keywords": [ - "google" + "global state" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2018-06-20T15:52:20+00:00" + "time": "2020-10-26T15:55:19+00:00" }, { - "name": "google/apiclient-services", - "version": "v0.94", + "name": "sebastian/lines-of-code", + "version": "1.0.3", "source": { "type": "git", - "url": "https://github.com/googleapis/google-api-php-client-services.git", - "reference": "9686fc7dfd5b92dd9ff075bdcd674cd5c15294be" + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/9686fc7dfd5b92dd9ff075bdcd674cd5c15294be", - "reference": "9686fc7dfd5b92dd9ff075bdcd674cd5c15294be", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", "shasum": "" }, "require": { - "php": ">=5.4" + "nikic/php-parser": "^4.6", + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "~4.8" + "phpunit/phpunit": "^9.3" }, "type": "library", - "autoload": { - "psr-0": { - "Google_Service_": "src" + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" + "BSD-3-Clause" ], - "description": "Client library for Google APIs", - "homepage": "http://developers.google.com/api-client-library/php", - "keywords": [ - "google" + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2019-04-13T00:23:08+00:00" + "time": "2020-11-28T06:42:11+00:00" }, { - "name": "google/auth", - "version": "v1.5.1", + "name": "sebastian/object-enumerator", + "version": "4.0.4", "source": { "type": "git", - "url": "https://github.com/googleapis/google-auth-library-php.git", - "reference": "0f75e20e7392e863f5550ed2c2d3d50af21710fb" + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/google-auth-library-php/zipball/0f75e20e7392e863f5550ed2c2d3d50af21710fb", - "reference": "0f75e20e7392e863f5550ed2c2d3d50af21710fb", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", "shasum": "" }, "require": { - "firebase/php-jwt": "~2.0|~3.0|~4.0|~5.0", - "guzzlehttp/guzzle": "~5.3.1|~6.0", - "guzzlehttp/psr7": "^1.2", - "php": ">=5.4", - "psr/cache": "^1.0", - "psr/http-message": "^1.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^1.11", - "guzzlehttp/promises": "0.1.1|^1.3", - "phpseclib/phpseclib": "^2", - "phpunit/phpunit": "^4.8.36|^5.7", - "sebastian/comparator": ">=1.2.3" - }, - "suggest": { - "phpseclib/phpseclib": "May be used in place of OpenSSL for signing strings. Please require version ^2." + "phpunit/phpunit": "^9.3" }, "type": "library", - "autoload": { - "psr-4": { - "Google\\Auth\\": "src" + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" + "BSD-3-Clause" ], - "description": "Google Auth Library for PHP", - "homepage": "http://github.com/google/google-auth-library-php", - "keywords": [ - "Authentication", - "google", - "oauth2" + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2019-04-16T18:48:28+00:00" + "time": "2020-10-26T13:12:34+00:00" }, { - "name": "guzzlehttp/guzzle", - "version": "6.3.3", + "name": "sebastian/object-reflector", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4", - "php": ">=5.5" + "php": ">=7.3" }, "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.0" - }, - "suggest": { - "psr/log": "Required for using the Log middleware" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.3-dev" + "dev-master": "2.0-dev" } }, "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2018-04-22T15:46:56+00:00" + "time": "2020-10-26T13:14:26+00:00" }, { - "name": "guzzlehttp/promises", - "version": "v1.3.1", + "name": "sebastian/recursion-context", + "version": "4.0.4", "source": { "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", "shasum": "" }, "require": { - "php": ">=5.5.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^4.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "4.0-dev" } }, "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Guzzle promises library", - "keywords": [ - "promise" + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2016-12-20T10:07:11+00:00" + "time": "2020-10-26T13:17:30+00:00" }, { - "name": "guzzlehttp/psr7", - "version": "1.5.2", + "name": "sebastian/resource-operations", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "9f83dded91781a01c63574e387eaa769be769115" + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115", - "reference": "9f83dded91781a01c63574e387eaa769be769115", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "shasum": "" }, "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0", - "ralouphie/getallheaders": "^2.0.5" - }, - "provide": { - "psr/http-message-implementation": "1.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.5-dev" + "dev-master": "3.0-dev" } }, "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - }, - "files": [ - "src/functions_include.php" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Schultze", - "homepage": "https://github.com/Tobion" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2018-12-04T20:46:45+00:00" + "time": "2020-09-28T06:45:17+00:00" }, { - "name": "monolog/monolog", - "version": "1.24.0", + "name": "sebastian/type", + "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", - "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/81cd61ab7bbf2de744aba0ea61fae32f721df3d2", + "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2", "shasum": "" }, "require": { - "php": ">=5.3.0", - "psr/log": "~1.0" - }, - "provide": { - "psr/log-implementation": "1.0.0" + "php": ">=7.3" }, "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", - "doctrine/couchdb": "~1.0@dev", - "graylog2/gelf-php": "~1.0", - "jakub-onderka/php-parallel-lint": "0.9", - "php-amqplib/php-amqplib": "~2.4", - "php-console/php-console": "^3.1.3", - "phpunit/phpunit": "~4.5", - "phpunit/phpunit-mock-objects": "2.3.0", - "ruflin/elastica": ">=0.90 <3.0", - "sentry/sentry": "^0.13", - "swiftmailer/swiftmailer": "^5.3|^6.0" - }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "sentry/sentry": "Allow sending log messages to a Sentry server" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "2.3-dev" } }, "autoload": { - "psr-4": { - "Monolog\\": "src/Monolog" + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:18:59+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "http://github.com/Seldaek/monolog", - "keywords": [ - "log", - "logging", - "psr-3" + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2018-11-05T09:00:11+00:00" + "time": "2020-09-28T06:39:44+00:00" }, { - "name": "phpseclib/phpseclib", - "version": "2.0.15", + "name": "sirbrillig/phpcs-variable-analysis", + "version": "v2.11.0", "source": { "type": "git", - "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "11cf67cf78dc4acb18dc9149a57be4aee5036ce0" + "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", + "reference": "e76e816236f401458dd8e16beecab905861b5867" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/11cf67cf78dc4acb18dc9149a57be4aee5036ce0", - "reference": "11cf67cf78dc4acb18dc9149a57be4aee5036ce0", + "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/e76e816236f401458dd8e16beecab905861b5867", + "reference": "e76e816236f401458dd8e16beecab905861b5867", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.4.0", + "squizlabs/php_codesniffer": "^3.5" }, "require-dev": { - "phing/phing": "~2.7", - "phpunit/phpunit": "^4.8.35|^5.7|^6.0", - "sami/sami": "~2.0", - "squizlabs/php_codesniffer": "~2.0" + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "limedeck/phpunit-detailed-printer": "^3.1 || ^4.0 || ^5.0", + "phpstan/phpstan": "^0.11.8", + "phpunit/phpunit": "^5.0 || ^6.5 || ^7.0 || ^8.0", + "sirbrillig/phpcs-import-detection": "^1.1" }, - "suggest": { - "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", - "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", - "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", - "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." - }, - "type": "library", + "type": "phpcodesniffer-standard", "autoload": { - "files": [ - "phpseclib/bootstrap.php" - ], "psr-4": { - "phpseclib\\": "phpseclib/" + "VariableAnalysis\\": "VariableAnalysis/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-2-Clause" ], "authors": [ { - "name": "Jim Wigginton", - "email": "terrafrost@php.net", - "role": "Lead Developer" - }, - { - "name": "Patrick Monnerat", - "email": "pm@datasphere.ch", - "role": "Developer" - }, - { - "name": "Andreas Fischer", - "email": "bantu@phpbb.com", - "role": "Developer" + "name": "Sam Graham", + "email": "php-codesniffer-variableanalysis@illusori.co.uk" }, { - "name": "Hans-Jürgen Petrich", - "email": "petrich@tronic-media.com", - "role": "Developer" - }, - { - "name": "Graham Campbell", - "email": "graham@alt-three.com", - "role": "Developer" + "name": "Payton Swick", + "email": "payton@foolord.com" } ], - "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", - "homepage": "http://phpseclib.sourceforge.net", - "keywords": [ - "BigInteger", - "aes", - "asn.1", - "asn1", - "blowfish", - "crypto", - "cryptography", - "encryption", - "rsa", - "security", - "sftp", - "signature", - "signing", - "ssh", - "twofish", - "x.509", - "x509" - ], - "time": "2019-03-10T16:53:45+00:00" - }, - { - "name": "psr/cache", - "version": "1.0.1", + "description": "A PHPCS sniff to detect problems with variables.", + "time": "2021-03-09T22:32:14+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.6.0", "source": { "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "ffced0d2c8fa8e6cdc4d695a743271fab6c38625" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ffced0d2c8fa8e6cdc4d695a743271fab6c38625", + "reference": "ffced0d2c8fa8e6cdc4d695a743271fab6c38625", "shasum": "" }, "require": { - "php": ">=5.3.0" + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" + "dev-master": "3.x-dev" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Greg Sherwood", + "role": "lead" } ], - "description": "Common interface for caching libraries", + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", "keywords": [ - "cache", - "psr", - "psr-6" + "phpcs", + "standards" ], - "time": "2016-08-06T20:24:11+00:00" + "time": "2021-04-09T00:54:41+00:00" }, { - "name": "psr/http-message", - "version": "1.0.1", + "name": "symfony/polyfill-ctype", + "version": "v1.23.0", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.1" + }, + "suggest": { + "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" - } + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -632,48 +2498,118 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-02-19T12:13:01+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "75a63c33a8577608444246075ea0af0d052e452a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", + "reference": "75a63c33a8577608444246075ea0af0d052e452a", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } ], - "time": "2016-08-06T14:39:51+00:00" + "time": "2020-07-12T23:59:07+00:00" }, { - "name": "psr/log", - "version": "1.1.0", + "name": "webmozart/assert", + "version": "1.10.0", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" + "url": "https://github.com/webmozarts/assert.git", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.2 || ^8.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.10-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Webmozart\\Assert\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -682,66 +2618,73 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "Assertions to validate method input/output with nice error messages.", "keywords": [ - "log", - "psr", - "psr-3" + "assert", + "check", + "validate" ], - "time": "2018-11-20T15:27:04+00:00" + "time": "2021-03-09T10:59:23+00:00" }, { - "name": "ralouphie/getallheaders", - "version": "2.0.5", + "name": "wp-coding-standards/wpcs", + "version": "2.3.0", "source": { "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa" + "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", + "reference": "7da1894633f168fe244afc6de00d141f27517b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa", - "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa", + "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/7da1894633f168fe244afc6de00d141f27517b62", + "reference": "7da1894633f168fe244afc6de00d141f27517b62", "shasum": "" }, "require": { - "php": ">=5.3" + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.3.1" }, "require-dev": { - "phpunit/phpunit": "~3.7.0", - "satooshi/php-coveralls": ">=1.0" + "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || ^0.6", + "phpcompatibility/php-compatibility": "^9.0", + "phpcsstandards/phpcsdevtools": "^1.0", + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.6 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically." }, + "type": "phpcodesniffer-standard", "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" + "name": "Contributors", + "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors" } ], - "description": "A polyfill for getallheaders.", - "time": "2016-02-11T07:05:27+00:00" + "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions", + "keywords": [ + "phpcs", + "standards", + "wordpress" + ], + "time": "2020-05-13T23:57:56+00:00" } ], - "packages-dev": [], "aliases": [], "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, - "platform": [], - "platform-dev": [] + "platform": { + "php": ">=7.1" + }, + "platform-dev": [], + "plugin-api-version": "1.1.0" } diff --git a/inc/classes/class-google-auth.php b/inc/classes/class-google-auth.php deleted file mode 100644 index 9e45ee3a..00000000 --- a/inc/classes/class-google-auth.php +++ /dev/null @@ -1,475 +0,0 @@ - - * - * @package login-with-google - */ - -namespace WP_Google_Login\Inc; - -use WP_Google_Login\Inc\Traits\Singleton; - -/** - * Class Google_Auth - */ -class Google_Auth { - - use Singleton; - - /** - * Google client instance. - * - * @var \Google_Client - */ - protected $_client = false; - - /** - * To store after login redirect URL. - * - * @var string - */ - protected $_redirect_to = ''; - - /** - * Google_Auth constructor. - */ - protected function __construct() { - - $this->_include_vendor(); - - $this->_client = $this->_get_client(); - - add_filter( 'authenticate', [ $this, 'authenticate_user' ] ); - add_filter( 'login_redirect', [ $this, 'get_login_redirect' ] ); - add_filter( 'registration_redirect', [ $this, 'get_login_redirect' ] ); - add_filter( 'allowed_redirect_hosts', [ $this, 'maybe_whitelist_subdomain' ] ); - - } - - /** - * To include vendor file. - * - * @return void - */ - protected function _include_vendor() { - - $vendor_autoload = sprintf( '%s/vendor/autoload.php', WP_GOOGLE_LOGIN_PATH ); - - $validate_file = validate_file( $vendor_autoload ); - // Function validate_file returns 2 for Windows drive path, so we check that as well. - if ( ! empty( $vendor_autoload ) && file_exists( $vendor_autoload ) && ( 0 === $validate_file || 2 === $validate_file ) ) { - require_once( $vendor_autoload ); // phpcs:ignore - } - - } - - /** - * To get instance of Google Client. - * - * @return \Google_Client - */ - protected function _get_client() { - $client_id = wp_google_login_get_client_id(); - $client_secret = wp_google_login_get_client_secret(); - - // If we don't have client id and secret then bail out, plugin won't work. - if ( empty( $client_id ) || empty( $client_secret ) ) { - return; - } - - $client = new \Google_Client(); - $client->setApplicationName( 'WP Google Login' ); - - $client->setClientId( $client_id ); - $client->setClientSecret( $client_secret ); - - $redirect_to = filter_input( INPUT_GET, 'redirect_to', FILTER_SANITIZE_URL ); - $redirect_to = ( ! empty( $redirect_to ) ) ? $redirect_to : admin_url(); - - // If redirect_to url don't have host name then add that. - $redirect_to = ( ! wp_parse_url( $redirect_to, PHP_URL_HOST ) ) ? home_url( $redirect_to ) : $redirect_to; - - $state = [ - 'redirect_to' => $redirect_to, - 'blog_id' => get_current_blog_id(), - ]; - $state = urlencode_deep( implode( '|', $state ) ); - - $client->setState( $state ); - - $login_url = $this->_get_login_url(); - - $client->setRedirectUri( $login_url ); - - return $client; - - } - - /** - * Get login URL, on which user will redirect after authenticated from google. - * - * @return string Redirect URL. - */ - protected function _get_login_url() { - - // By default we will use current site's login URL. - $login_url = wp_login_url(); - - // If it's multisite setup. - // Then check if plugin is activate on network wide or if plugin is activate on main site - // Then use main site login url. - if ( is_multisite() && defined( 'BLOG_ID_CURRENT_SITE' ) ) { - - $mu_plugins = get_site_option( 'active_sitewide_plugins', [] ); - - $plugins_activate_on_main_site = get_blog_option( BLOG_ID_CURRENT_SITE, 'active_plugins' ); - - if ( ! empty( $mu_plugins[ WP_GOOGLE_LOGIN_PLUGIN_NAME ] ) || in_array( WP_GOOGLE_LOGIN_PLUGIN_NAME, $plugins_activate_on_main_site, true ) ) { - $login_url = network_site_url( 'wp-login.php' ); // @codeCoverageIgnore - } - } - - return $login_url; - } - - /** - * To get user info from google auth token. - * - * @param string $token Auth token. - * - * @return array|\Exception|\Google_Service_Exception User info - */ - protected function _get_user_from_token( $token ) { - - if ( empty( $token ) ) { - return []; - } - - $token = urldecode( $token ); - - try { - - // @codeCoverageIgnoreStart - // Ignoring because we cannot mock token and associate it with a user in test cases. - $token = $this->_client->fetchAccessTokenWithAuthCode( $token ); - - $oauthservice = new \Google_Service_Oauth2( $this->_client ); - - $google_userinfo = $oauthservice->userinfo->get(); - - $user_info = [ - 'user_email' => $google_userinfo->getEmail(), - 'display_name' => $google_userinfo->getName(), - 'first_name' => $google_userinfo->getGivenName(), - 'last_name' => $google_userinfo->getFamilyName(), - 'picture' => $google_userinfo->getPicture(), - ]; - - /** - * This hook provides access token fetched by google sign-in. - * - * @since 1.0 - * - * @param array $token Converted access token. - * @param array $user_info User details fetched from this token. - * @param object $client Google_Client object. - */ - do_action( 'wp_google_login_token', $token, $user_info, $this->_client ); - - return $user_info; - - // @codeCoverageIgnoreEnd - } catch ( \Google_Service_Exception $exception ) { - return $exception; - } - - } - - /** - * To get scopes. - * - * @return string - */ - protected function _get_scopes() { - - $scopes = [ - 'email', - 'profile', - 'openid', - ]; - - /** - * This hook can be used to add/change google API scope. - * By setting different scopes, you can ask different permissions. - * - * @since 1.0 - * - * @param array $scopes Scopes array. - * - * @return array Modified scopes. - */ - $scopes = apply_filters( 'wp_google_login_scopes', $scopes ); - - return implode( ' ', $scopes ); - - } - - /** - * Create user base on provided data. - * - * @param array $user_info User info, - * user_email : User email address - * display_name : Display name - * first_name : First name - * last_name : Last name. - * - * @return int - */ - protected function _create_user( $user_info = [] ) { - - if ( empty( $user_info['user_email'] ) || ! is_email( $user_info['user_email'] ) ) { - return 0; - } - - $email = $user_info['user_email']; - - $user_login = sanitize_user( current( explode( '@', $email ) ), true ); - - // Ensure username is unique. - $append = 1; - $o_user_login = $user_login; - - while ( username_exists( $user_login ) ) { - $user_login = $o_user_login . $append; - $append++; - } - - $user_info['user_login'] = $user_login; - $user_info['user_pass'] = wp_generate_password( 18 ); - - $user_id = wp_insert_user( $user_info ); - - return ( ! empty( $user_id ) && ! is_wp_error( $user_id ) ) ? $user_id : 0; - - } - - /** - * To check if user can register or not. - * - * @return bool - */ - protected function _can_users_register() { - $options = get_option( 'wp_google_login_settings' ); - - if ( defined( 'WP_GOOGLE_LOGIN_USER_REGISTRATION' ) ) { - return (bool) WP_GOOGLE_LOGIN_USER_REGISTRATION; - } - - $registration_enabled = ! empty( $options['registration_enabled'] ) ? (bool) $options['registration_enabled'] : false; - - if ( $registration_enabled ) { - return true; - } - - $can_user_register = get_option( 'users_can_register' ); - - return ( ! empty( $can_user_register ) ) ? true : false; - } - - /** - * To check if given email address can be register or not. - * - * @param string $email Email address. - * - * @return bool True if it can register, Otherwise False. - */ - protected function _can_register_with_email( $email ) { - - if ( empty( $email ) ) { - return false; - } - - $whitelisted_domains = wp_google_login_get_whitelisted_domains(); - - /** - * If Const is not defined or empty, - * then allow all domain. - */ - if ( empty( $whitelisted_domains ) ) { - return true; - } - - $email_parts = explode( '@', $email ); - $email_domain = ( ! empty( $email_parts[1] ) ) ? strtolower( trim( $email_parts[1] ) ) : ''; - - $whitelisted_domains = explode( ',', $whitelisted_domains ); - $whitelisted_domains = array_map( 'trim', $whitelisted_domains ); - - $count = ( ! empty( $whitelisted_domains ) ) && is_array( $whitelisted_domains ) ? count( $whitelisted_domains ) : 1; - - for ( $i = 0; $i < ( $count - 1 ); $i++ ) { - - $whitelisted_domains[ $i ] = strtolower( trim( $whitelisted_domains[ $i ] ) ); - $whitelisted_domains[ $i ] = str_replace( 'www.', '', $whitelisted_domains[ $i ] ); - - } - - $whitelisted_domains = array_unique( $whitelisted_domains ); - - return ( ! empty( $email_domain ) && in_array( $email_domain, $whitelisted_domains, true ) ) ? true : false; - } - - /** - * To get google authentication URL. - * - * @return string - */ - public function get_login_url() { - - $scopes = $this->_get_scopes(); - if ( ! is_null( $this->_client ) ) { - $url = $this->_client->createAuthUrl( $scopes ); - } - - return $url; - } - - /** - * To authenticate user. - * - * @param null|\WP_User|\WP_Error $user WP_User if the user is authenticated. - * WP_Error or null otherwise. - * - * @return null|\WP_User|\WP_Error WP_User if the user is authenticated. - * WP_Error or null otherwise. - */ - public function authenticate_user( $user = null ) { - - $is_mu_site = is_multisite(); - - $token = Helper::filter_input( INPUT_GET, 'code', FILTER_SANITIZE_STRING ); - $state = Helper::filter_input( INPUT_GET, 'state', FILTER_SANITIZE_STRING ); - $state = urldecode( $state ); - $state = explode( '|', $state ); - - $redirect_to = ( ! empty( $state[0] ) ) ? esc_url_raw( $state[0] ) : ''; - $blog_id = ( ! empty( $state[1] ) && 0 < intval( $state[1] ) ) ? intval( $state[1] ) : 0; - - if ( empty( $token ) ) { - return $user; - } - - // Set redirect URL. so we can redirect after login. - $this->_redirect_to = $redirect_to; - - /** - * If blog_id in state does not match current blog ID. - * Then redirect to login page of request blog. - * So that can take care of authentication. - */ - if ( $is_mu_site && $blog_id !== get_current_blog_id() ) { - - $query_string = filter_input( INPUT_SERVER, 'QUERY_STRING', FILTER_SANITIZE_STRING ); - - $blog_url = get_blog_option( $blog_id, 'siteurl' ); - $blog_login_url = sprintf( '%s/wp-login.php?%s', $blog_url, $query_string ); - - wp_safe_redirect( $blog_login_url ); - // @codeCoverageIgnoreStart - // Ignoring because cannot test exit. - exit(); - // @codeCoverageIgnoreEnd - } - - $user_info = $this->_get_user_from_token( $token ); - - if ( ! is_array( $user_info ) || empty( $user_info['user_email'] ) || ! is_email( $user_info['user_email'] ) ) { - return $user; - } - - // @codeCoverageIgnoreStart - // Ignoring because we cannot mock token and associate it with a user in test cases. - $user = get_user_by( 'email', $user_info['user_email'] ); - - // We found the user. - if ( ! empty( $user ) && $user instanceof \WP_User ) { - - if ( ! $is_mu_site ) { - return $user; - } - - // Check for MU site. - if ( ! empty( $blog_id ) && is_user_member_of_blog( $user->ID, $blog_id ) ) { - return $user; - } - } - - // Check if user registration is allow or not. - if ( ! $this->_can_users_register() ) { - return new \WP_Error( - 'wp_google_login_error', - // translators: %s: User email. - sprintf( __( 'User %s not registered in WordPress.', 'login-with-google' ), $user_info['user_email'] ) - ); - } - - // Check if email address is allowed or not. - if ( ! $this->_can_register_with_email( $user_info['user_email'] ) ) { - return new \WP_Error( - 'wp_google_login_error', - // translators: %s: User email. - sprintf( __( 'User can not register with %s email address.', 'login-with-google' ), $user_info['user_email'] ) - ); - } - - // Let's create WP user first. - if ( empty( $user ) || ! $user instanceof \WP_User ) { - $user_id = $this->_create_user( $user_info ); - $user = get_user_by( 'id', $user_id ); - } - - if ( $is_mu_site ) { - $default_user_role = get_blog_option( $blog_id, 'default_role', 'subscriber' ); - add_user_to_blog( $blog_id, $user->ID, $default_user_role ); - } - - return $user; - // @codeCoverageIgnoreEnd - } - - /** - * To redirect to appropriate URL after auth with google. - * - * @param string $redirect_to Redirect to URL. - * - * @return string Redirect to URL. - */ - public function get_login_redirect( $redirect_to ) { - return ( ! empty( $this->_redirect_to ) ) ? $this->_redirect_to : $redirect_to; - } - - /** - * To whitelist domain where we going to redirect after authentication user with google. - * - * @param array $hosts Whitelisted domains. - * - * @return array Whitelisted domains. - */ - public function maybe_whitelist_subdomain( $hosts = [] ) { - - $hosts = ( ! empty( $hosts ) && is_array( $hosts ) ) ? $hosts : []; - - if ( ! empty( $this->_redirect_to ) ) { - $subdomain = wp_parse_url( $this->_redirect_to, PHP_URL_HOST ); - - $hosts[] = $subdomain; - } - - $hosts = array_unique( $hosts ); - - return $hosts; - } - -} diff --git a/inc/classes/class-plugin.php b/inc/classes/class-plugin.php deleted file mode 100644 index 0fa8db21..00000000 --- a/inc/classes/class-plugin.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * @package login-with-google - */ - -namespace WP_Google_Login\Inc; - -use WP_Google_Login\Inc\Traits\Singleton; - -/** - * Class Plugin - */ -class Plugin { - - use Singleton; - - /** - * Instance of Google_Auth class. - * - * @var \WP_Google_Login\Inc\Google_Auth - */ - protected $_google_auth = false; - - /** - * Plugin constructor. - */ - protected function __construct() { - \WP_Google_Login\Inc\Settings::get_instance(); - - $this->_google_auth = Google_Auth::get_instance(); - - $this->_setup_hooks(); - } - - /** - * To setup actions/filters. - * - * @return void - */ - protected function _setup_hooks() { - - /** - * Actions - */ - add_action( 'login_enqueue_scripts', [ $this, 'login_enqueue_scripts' ] ); - add_action( 'login_form', [ $this, 'add_google_login_button' ] ); - add_action( 'register_form', [ $this, 'add_google_login_button' ] ); - - } - - /** - * To enqueue style and script for login page. - * - * @return void - */ - public function login_enqueue_scripts() { - - wp_enqueue_script( 'wp_google_login_script', sprintf( '%s/assets/build/js/login.js', WP_GOOGLE_LOGIN_URL ), [], WP_GOOGLE_LOGIN_VERSION ); - wp_enqueue_style( 'wp_google_login_style', sprintf( '%s/assets/build/css/login.css', WP_GOOGLE_LOGIN_URL ), [], WP_GOOGLE_LOGIN_VERSION ); - - } - - /** - * To render google login button. - * - * @return void - */ - public function add_google_login_button() { - - $template_path = sprintf( '%s/template/google-login-button.php', WP_GOOGLE_LOGIN_PATH ); - $login_url = $this->_google_auth->get_login_url(); - - Helper::render_template( $template_path, [ - 'login_url' => $login_url, - ] ); - } - -} \ No newline at end of file diff --git a/inc/classes/class-settings.php b/inc/classes/class-settings.php deleted file mode 100644 index 64ae06e3..00000000 --- a/inc/classes/class-settings.php +++ /dev/null @@ -1,237 +0,0 @@ -setup_hooks(); - } - - /** - * To setup actions/filters. - * - * @return void - */ - public function setup_hooks() { - - /** - * Actions - */ - add_action( 'admin_menu', [ $this, 'add_admin_menu' ] ); - add_action( 'admin_init', [ $this, 'settings_init' ] ); - } - - /** - * Add admin menu. - * - * @return void - */ - public function add_admin_menu() { - if ( current_user_can( 'manage_options' ) ) { - add_filter( 'plugin_action_links', [ $this, 'plugin_action_links' ], 10, 2 ); - } - add_options_page( __( 'Log in with Google', 'login-with-google' ), __( 'Log in with Google', 'login-with-google' ), 'manage_options', 'login-with-google', [ - $this, - 'options_page' - ] ); - } - - /** - * Adds a "Settings" link to this plugin's entry on the plugin list. - * - * @param array $links Array of links for plugin actions. - * @param string $file Path to the plugin file relative to the plugins directory. - * - * @return array $links Array of links for plugin actions. - */ - public function plugin_action_links( $links, $file ) { - if ( 'login-with-google/login-with-google.php' === $file ) { - $links[] = "" . __( 'Settings', 'login-with-google' ) . ''; - } - - return $links; - } - - /** - * Add admin menu. - * - * @return void - */ - public function settings_init() { - register_setting( 'wp_google_login', 'wp_google_login_settings' ); - - add_settings_section( - 'wp_google_login_section', - __( 'Log in with Google Settings', 'login-with-google' ), - [ $this, 'settings_section_callback' ], - 'wp_google_login' - ); - - add_settings_field( - 'wp_google_login_client_id', - __( 'Client ID', 'login-with-google' ), - [ $this, 'wp_google_login_client_id_render' ], - 'wp_google_login', - 'wp_google_login_section', - [ 'label_for' => 'client-id' ] - ); - - add_settings_field( - 'wp_google_login_client_secret', - __( 'Client Secret', 'login-with-google' ), - [ $this, 'wp_google_login_client_secret_render' ], - 'wp_google_login', - 'wp_google_login_section', - [ 'label_for' => 'client-secret' ] - ); - - add_settings_field( - 'wp_google_login_whitelisted_domains', - __( 'Whitelisted Domains', 'login-with-google' ), - [ $this, 'wp_google_login_whitelisted_domains_render' ], - 'wp_google_login', - 'wp_google_login_section', - [ 'label_for' => 'whitelisted-domains' ] - ); - - add_settings_field( - 'wp_google_login_enable_registration', - __( 'Create new user', 'login-with-google' ), - [ $this, 'wp_google_login_enable_registrationr' ], - 'wp_google_login', - 'wp_google_login_section', - [ 'label_for' => 'enable-registration' ] - ); - } - - /** - * Render Client ID settings field. - * - * @return void - */ - public function wp_google_login_client_id_render() { - $client_id = wp_google_login_get_client_id(); - $disabled = ''; - if ( defined( 'WP_GOOGLE_LOGIN_CLIENT_ID' ) ) { - $disabled = 'disabled'; - } - ?> - value=''> -

- %1s %3s.

', - esc_html__( 'Create oAuth Client ID and Client Secret at', 'login-with-google' ), - 'https://console.developers.google.com/apis/dashboard', - 'console.developers.google.com' - ) ); - ?> -

- - value=''> - - value=''> -

- - > - -

membership setting is off.', 'login-with-google' ), - is_multisite() ? 'network/settings.php' : 'options-general.php' - ) ); - ?> -

- -
-
- -
-
- value pair for each `classname => instance` in self::$_instance - * for each sub-class. - */ - $called_class = get_called_class(); - - if ( ! isset( $instance[ $called_class ] ) ) { - - $instance[ $called_class ] = new $called_class(); - - /** - * Dependent items can use the `wp_google_login_extend_singleton_init_{$called_class}` hook to execute code - */ - do_action( sprintf( 'wp_google_login_extend_singleton_init_%s', $called_class ) ); - - } - - return $instance[ $called_class ]; - - } - -} // End trait diff --git a/languages/login-with-google.pot b/languages/login-with-google.pot new file mode 100644 index 00000000..40f3f62f --- /dev/null +++ b/languages/login-with-google.pot @@ -0,0 +1,115 @@ +# Copyright (C) 2021 rtCamp +# This file is distributed under the same license as the Login with Google plugin. +msgid "" +msgstr "" +"Project-Id-Version: Login with Google 1.0.15\n" +"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/login-with-google\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"POT-Creation-Date: 2021-06-29T16:19:14+05:30\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"X-Generator: WP-CLI 2.4.0\n" +"X-Domain: login-with-google\n" + +#. Plugin Name of the plugin +#: src/Modules/Settings.php:228 +msgid "Login with Google" +msgstr "" + +#. Description of the plugin +msgid "Allow users to login/register via Google." +msgstr "" + +#. Author of the plugin +msgid "rtCamp" +msgstr "" + +#. Author URI of the plugin +msgid "https://rtcamp.com" +msgstr "" + +#: login-with-google.php:40 +msgid "Login with google Plugin requires PHP version 7.3 or higher.
Please ask your server administrator to update your environment to latest PHP version" +msgstr "" + +#: login-with-google.php:47 +msgid "The plugin Login with google has been deactivated" +msgstr "" + +#. translators: %$s is replaced with requested service name. +#: src/Container.php:61 +msgid "Invalid Service %s Passed to the container" +msgstr "" + +#: src/Modules/Login.php:172 +msgid "Registration is not allowed." +msgstr "" + +#. translators: %s is replaced with email ID of user trying to register +#: src/Modules/Login.php:199 +msgid "Cannot register with this email: %s" +msgstr "" + +#: src/Modules/Settings.php:93 +msgid "Log in with Google Settings" +msgstr "" + +#: src/Modules/Settings.php:101 +msgid "Client ID" +msgstr "" + +#: src/Modules/Settings.php:110 +msgid "Client Secret" +msgstr "" + +#: src/Modules/Settings.php:119 +msgid "Create new user" +msgstr "" + +#: src/Modules/Settings.php:128 +msgid "Whitelisted Domains" +msgstr "" + +#: src/Modules/Settings.php:148 +msgid "Create oAuth Client ID and Client Secret at" +msgstr "" + +#: src/Modules/Settings.php:185 +msgid "Create a new user account if it does not exist already" +msgstr "" + +#. translators: %1s will be replaced by page link +#: src/Modules/Settings.php:192 +msgid "If this setting is checked, a new user will be created even if membership setting is off." +msgstr "" + +#: src/Modules/Settings.php:215 +msgid "Add each domain comma separated" +msgstr "" + +#: src/Modules/Settings.php:227 +msgid "Login with Google settings" +msgstr "" + +#: src/Modules/Shortcode.php:91 +msgid "Login with google" +msgstr "" + +#: src/Utils/GoogleClient.php:98 +msgid "Access token must be set to make this API call" +msgstr "" + +#: src/Utils/GoogleClient.php:173 +msgid "Could not retrieve the access token, please try again." +msgstr "" + +#: src/Utils/GoogleClient.php:199 +msgid "Could not retrieve the user information, please try again." +msgstr "" + +#: templates/google-login-button.php:9 +msgid "Log in with Google" +msgstr "" diff --git a/languages/wp-google-login.po b/languages/wp-google-login.po deleted file mode 100644 index 8b9f83e9..00000000 --- a/languages/wp-google-login.po +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (C) 2020 rtCamp -# This file is distributed under the same license as the WP Google Login plugin. -msgid "" -msgstr "" -"Project-Id-Version: WP Google Login 1.0\n" -"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/login-with-google\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2020-04-29T09:17:01+00:00\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"X-Generator: WP-CLI 2.4.0\n" -"X-Domain: login-with-google\n" - -#. Plugin Name of the plugin -msgid "Log in with Google" -msgstr "" - -#. Plugin URI of the plugin -msgid "https://github.com/rtCamp/login-with-google" -msgstr "" - -#. Description of the plugin -msgid "Minimal plugin which allows WP user to login with google." -msgstr "" - -#. Author of the plugin -msgid "rtCamp" -msgstr "" - -#. Author URI of the plugin -msgid "https://rtcamp.com" -msgstr "" - -#. translators: %s: User email. -#: inc/classes/class-google-auth.php:398 -msgid "User %s not registered in WordPress." -msgstr "" - -#. translators: %s: User email. -#: inc/classes/class-google-auth.php:407 -msgid "User can not register with %s email address." -msgstr "" - -#: template/google-login-button.php:10 -msgid "Log in with Google" -msgstr "" diff --git a/login-with-google.php b/login-with-google.php index 2433016a..851d8496 100644 --- a/login-with-google.php +++ b/login-with-google.php @@ -1,35 +1,133 @@ Please ask your server administrator to update your environment to latest PHP version', + 'login-with-google' + ); + + printf( + '
%1$s

%2$s

', + esc_html__( + 'The plugin Login with google has been deactivated', + 'login-with-google' + ), + wp_kses( $message, [ 'br' => true ] ) + ); + + deactivate_plugins( plugin_basename( __FILE__ ) ); + } + ); + } -// Missing vendor autoload file or invalid file path. -$validate_file = validate_file( $vendor_autoload ); -// Function validate_file returns 2 for Windows drive path, so we check that as well. -if ( empty( $vendor_autoload ) || ! file_exists( $vendor_autoload ) || ( 0 !== $validate_file && 2 !== $validate_file ) ) { return; } +/** + * Autoload the dependencies. + * + * @return bool + */ +function autoload(): bool { + static $done; + if ( is_bool( $done ) ) { + return $done; + } + + if ( is_readable( __DIR__ . '/vendor/autoload.php' ) ) { + require_once __DIR__ . '/vendor/autoload.php'; + $done = true; -// We already making sure that file is exists and valid. -require_once plugin_dir_path( __FILE__ ) . 'autoloader.php'; -require_once plugin_dir_path( __FILE__ ) . 'inc/functions.php'; + return true; + } + $done = false; + + return false; +} -\WP_Google_Login\Inc\Plugin::get_instance(); +/** + * Do not do anything if composer install + * is not run. + */ +if ( ! autoload() ) { + return; +} + +/** + * Return the container instance. + */ +function container(): Container { + static $container; + + if ( null !== $container ) { + return $container; + } + + $container = new Container( new PimpleContainer() ); + + return $container; +} + +/** + * Return the Plugin instance. + * + * @return Plugin + */ +function plugin(): Plugin { + static $plugin; + + if ( null !== $plugin ) { + return $plugin; + } + + $plugin = new Plugin( container() ); + return $plugin; +} + +/** + * Let the magic happen by + * running the plugin. + */ +add_action( + 'plugins_loaded', + function() { + plugin()->run(); + }, + 100 +); diff --git a/phpcs.xml b/phpcs.xml index 0fa987fe..9fea8e34 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,12 +1,60 @@ - - - - - - warning + + + + + + + + src + login-with-google.php + + + + + + + + + + + + + + + tests/* + + + + + + - */node_modules/* - */vendor/* - */assets/build/* - */tests/* + + + tests/* + + + + + + + + + + + + + + + + + + + + */vendor/* + */node_modules/* + /lib/* + */tests/* + */.github/* + */.scripts/* diff --git a/phpunit.xml b/phpunit.xml deleted file mode 100644 index badcdd32..00000000 --- a/phpunit.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - ./tests/ - - - - - ./inc/classes/ - - - - - - diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 00000000..1e843f61 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,22 @@ + + + + + src + + + vendor/ + templates/ + src/Utils/Helper.php + + + + + + + + tests/php/Unit + + + + diff --git a/readme.txt b/readme.txt index 155ee684..6bda3c07 100644 --- a/readme.txt +++ b/readme.txt @@ -4,95 +4,116 @@ Donate link: https://rtcamp.com/ Tags: Google login, sign in, sso, oauth, authentication, sign-in, single sign-on, log in Requires at least: 5.0 Tested up to: 5.8.1 -Requires PHP: 7.0 -Stable tag: 1.0.10 +Requires PHP: 7.3 +Stable tag: 1.2.2 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html - + Minimal plugin that allows WordPress users to log in using Google. - + == Description == -Minimal plugin that allows WordPress users to log in using Google. +Ultra minimal plugin to let your users login to WordPress applications using their Google accounts. No more remembering hefty passwords! -= Setup = +### Initial Setup 1. Create a project from [Google Developers Console](https://console.developers.google.com/apis/dashboard) if none exists. + + 2. Go to **Credentials** tab, then create credential for OAuth client. * Application type will be **Web Application** * Add `YOUR_DOMAIN/wp-login.php` in **Authorized redirect URIs** + + 3. This will give you **Client ID** and **Secret key**. + + 4. Input these values either in `WP Admin > Settings > WP Google Login`, or in `wp-config.php` using the following code snippet: -` +``` define( 'WP_GOOGLE_LOGIN_CLIENT_ID', 'YOUR_GOOGLE_CLIENT_ID' ); define( 'WP_GOOGLE_LOGIN_SECRET', 'YOUR_SECRET_KEY' ); -` +```` -= How to enable automatic user registration = +### Browser support +[These browsers are supported](https://developers.google.com/identity/gsi/web/guides/supported-browsers). Note, for example, that One Tap Login is not supported in Safari. + +### How to enable automatic user registration You can enable user registration either by -- Checking `Settings > WP Google Login > Enable Google Login Registration` +- Enabling *Settings > WP Google Login > Enable Google Login Registration* + + OR -- Adding `define( 'WP_GOOGLE_LOGIN_USER_REGISTRATION', 'true' );` in wp-config.php file. -Note: If the checkbox is ON then, it will register valid Google users even when WordPress default setting, under `Settings > General Settings > Membership > Anyone can register` checkbox is OFF. -= How to restrict user registration to one or more domain(s) = +- Adding +``` +define( 'WP_GOOGLE_LOGIN_USER_REGISTRATION', 'true' ); +``` +in wp-config.php file. -By default, when you enable user registration via constant `WP_GOOGLE_LOGIN_USER_REGISTRATION` or enable `Settings > WP Google Login > Enable Google Login Registration`, it will create a user for any Google login (including gmail.com users). If you are planning to use this plugin on a private, internal site, then you may like to restrict user registration to users under a single Google Suite organization. This configuration variable does that. +**Note:** If the checkbox is ON then, it will register valid Google users even when WordPress default setting, under -Add your domain name, without any schema prefix and `www,` as the value of `WP_GOOGLE_LOGIN_WHITELIST_DOMAINS` constant or in the settings `Settings > WP Google Login > Whitelisted Domains`. You can whitelist multiple domains. Please separate domains with commas. See the below example to know how to do it via constants: +*Settings > General Settings > Membership > Anyone can register* checkbox -`define( 'WP_GOOGLE_LOGIN_WHITELIST_DOMAINS', 'example.com,sample.com' );` +is OFF. -**Note:** If a user already exists, they **will be allowed to login with Google** regardless of whether their domain is whitelisted or not. Whitelisting will only prevent users from **registering** with email addresses from non-whitelisted domains. +### Restrict user registration to one or more domain(s) -= Hooks = +By default, when you enable user registration via constant `WP_GOOGLE_LOGIN_USER_REGISTRATION` or enable *Settings > WP Google Login > Enable Google Login Registration*, it will create a user for any Google login (including gmail.com users). If you are planning to use this plugin on a private, internal site, then you may like to restrict user registration to users under a single Google Suite organization. This configuration variable does that. -Action `wp_google_login_token` -This action provides access token received after Google login. -**Parameters:** +Add your domain name, without any schema prefix and `www,` as the value of `WP_GOOGLE_LOGIN_WHITELIST_DOMAINS` constant or in the settings `Settings > WP Google Login > Whitelisted Domains`. You can whitelist multiple domains. Please separate domains with commas. See the below example to know how to do it via constants: + +``` +define( 'WP_GOOGLE_LOGIN_WHITELIST_DOMAINS', 'example.com,sample.com' ); +``` -* `token` (Array): Converted token using `fetchAccessTokenWithAuthCode` method of `Google_Client` class. -* `user_info` (Array): Details of user after login. -* `client` (Object): `Google_Client` object in use. +**Note:** If a user already exists, they **will be allowed to login with Google** regardless of whether their domain is whitelisted or not. Whitelisting will only prevent users from **registering** with email addresses from non-whitelisted domains. + +### Hooks Filter `wp_google_login_scopes` This filter can be used to filter existing scope used in Google Sign in. You can ask for additional permission while user logs in. This filter will provide 1 parameter `scopes` in callback, which contains array of scopes. -= wp-config.php parameters list = +#### wp-config.php parameters list * `WP_GOOGLE_LOGIN_CLIENT_ID` (string): Google client ID of your application. + + * `WP_GOOGLE_LOGIN_SECRET` (string): Secret key of your application + + * `WP_GOOGLE_LOGIN_USER_REGISTRATION` (boolean) (optional): Set `true` If you want to enable new user registration. By default, user registration defers to `Settings > General Settings > Membership` if constant is not set. + + * `WP_GOOGLE_LOGIN_WHITELIST_DOMAINS` (string) (optional): Domain names, if you want to restrict login with your custom domain. By default, it will allow all domains. You can whitelist multiple domains. -= BTW, We're Hiring! = +### BTW, We're Hiring! -[Join us at rtCamp, we specialize in providing high performance enterprise WordPress solutions](https://rtcamp.com/) +[](https://rtcamp.com/careers/) == Installation == - + 1. Upload `plugin-name.php` to the `/wp-content/plugins/` directory 2. Activate the plugin through the 'Plugins' menu in WordPress 3. Follow "Setup" instructions in ReadMe to configure credentials from Google Developers Console. - + == Frequently Asked Questions == - + = Reporting a bug 🐞 = - -Before creating a new issue, do browse through the [existing issues](https://github.com/rtCamp/login-with-google/issues) for resolution or upcoming fixes. + +Before creating a new issue, do browse through the [existing issues](https://github.com/rtCamp/login-with-google/issues) for resolution or upcoming fixes. If you still need to [log an issue](https://github.com/rtCamp/login-with-google/issues/new), making sure to include as much detail as you can, including clear steps to reproduce the issue, if possible. - + = Creating a pull request = - + Want to contribute a new feature? Start a conversation by [logging an issue](https://github.com/rtCamp/login-with-google/issues). -Once you're ready to send a pull request, please run through the following checklist: +Once you're ready to send a pull request, please run through the following checklist: 1. Browse through the [existing issues](https://github.com/rtCamp/login-with-google/issues) for anything related to what you want to work on. If you don't find any related issues, open a new one. @@ -108,29 +129,52 @@ Once you're ready to send a pull request, please run through the following check = Unit testing = -- Setup local unit test environment by running script from terminal - -`./bin/install-wp-tests.sh [db-host] [wp-version] [skip-database-creation]` +- Clone the plugin from [repository](https://github.com/rtCamp/login-with-google). -- Execute `phpunit` in terminal from repository to run all test cases. +- Run `composer install && composer tests:unit` to run unit tests. -- Execute `phpunit ./tests/inc/test-class.php` in terminal with file path to run specific tests. - == Screenshots == - + 1. Login screen with Google option added. 2. Plugin settings screen. 3. Settings within Google Developer Console. - + == Changelog == += 1.2.2 = +* Maintenance release. + += 1.2.1 = +* Feature: Provide filter for client arguments: rtcamp.google_client_args + += 1.2 = +* Feature: One-Tap Login setting for supported browsers. + += 1.1 = +* Feature: Add shortcode `[google_login]` with optional attributes: `[google_login button_text="Login with Google" force_display="no" redirect_to="https://example.url/page"]`. +* Feature: Replace third-party oAuth client with custom GoogleClient class. +* Fix: Identification of state value, whether a given oAuth login is relevant to this plugin. +* Remove: Google oAuth library from composer. + += 1.0.14 = +* Revert Login with GitHub state fix. + += 1.0.13 = +* Fix login issue related to oAuth state. + += 1.0.12 = +* Fix conflict with Login with GitHub plugin. + += 1.0.11 = +* Add 'login_with_google/client_arguments' filter for Google_Client arguments. + = 1.0.10 = -* Fix issue where JS/CSS were not loding. +* Fix issue where JS/CSS were not loading. = 1.0.9 = * Initial release. - + == Upgrade Notice == -= 1.0.10 = -* Fix issue where JS/CSS were not loding. += 1.2.1 = +* Feature: Provide filter for client arguments: rtcamp.google_client_args diff --git a/src/Container.php b/src/Container.php new file mode 100644 index 00000000..0eaf5bbd --- /dev/null +++ b/src/Container.php @@ -0,0 +1,188 @@ +container = $container; + } + + /** + * Get the service object. + * + * @param string $service Service object in need. + * + * @return object + * + * @throws InvalidArgumentException Exception for invalid service. + */ + public function get( string $service ) { + if ( ! in_array( $service, $this->container->keys() ) ) { + /* translators: %$s is replaced with requested service name. */ + throw new InvalidArgumentException( sprintf( __( 'Invalid Service %s Passed to the container', 'login-with-google' ), $service ) ); + } + + return $this->container[ $service ]; + } + + /** + * Define common services in container. + * + * All the module specific services will be defined inside + * respective module's container. + * + * @codeCoverageIgnore + * + * @return void + */ + public function define_services(): void { + /** + * Define Settings service to add settings page and retrieve setting values. + * + * @param PimpleContainer $c Pimple container object. + * + * @return Settings + */ + $this->container['settings'] = function( PimpleContainer $c ) { + return new Settings(); + }; + + /** + * Define the login flow service. + * + * @param PimpleContainer $c Pimple container object. + * + * @return Login + */ + $this->container['login_flow'] = function( PimpleContainer $c ) { + return new Login( $c['gh_client'], $c['authenticator'] ); + }; + + /** + * Define a service for Google OAuth client. + * + * @param PimpleContainer $c Pimple container instance. + * + * @return GoogleClient + */ + $this->container['gh_client'] = function ( PimpleContainer $c ) { + $settings = $c['settings']; + + return new GoogleClient( + [ + 'client_id' => $settings->client_id, + 'client_secret' => $settings->client_secret, + 'redirect_uri' => wp_login_url(), + ] + ); + }; + + /** + * Define Assets service to add styles or script. + * + * @param PimpleContainer $c Pimple container object. + * + * @return Assets + */ + $this->container['assets'] = function ( PimpleContainer $c ) { + return new Assets(); + }; + + /** + * Define Shortcode service to register shortcode for google login. + * + * @param PimpleContainer $c Pimple container object. + * + * @return Shortcode + */ + $this->container['shortcode'] = function ( PimpleContainer $c ) { + return new Shortcode( $c['gh_client'], $c['assets'] ); + }; + + /** + * Define Token Verifier Service. + * + * Useful in verifying JWT Auth token. + * + * @param PimpleContainer $c Pimple container object. + * + * @return TokenVerifier + */ + $this->container['token_verifier'] = function ( PimpleContainer $c ) { + return new TokenVerifier( $c['settings'] ); + }; + + /** + * One Tap Login Service. + * + * @param PimpleContainer $c Pimple container object. + * + * @return OneTapLogin + */ + $this->container['one_tap_login'] = function ( PimpleContainer $c ) { + return new OneTapLogin( $c['settings'], $c['token_verifier'], $c['gh_client'], $c['authenticator'] ); + }; + + /** + * Authenticator utility. + * + * @param PimpleContainer $c Pimple container object. + * + * @return Authenticator + */ + $this->container['authenticator'] = function ( PimpleContainer $c ) { + return new Authenticator( $c['settings'] ); + }; + + /** + * Define any additional services. + * + * @param ContainerInterface $container Container object. + * + * @since 1.0.0 + */ + do_action( 'rtcamp.google_login_services', $this ); + } +} diff --git a/src/Interfaces/Container.php b/src/Interfaces/Container.php new file mode 100644 index 00000000..b83e9613 --- /dev/null +++ b/src/Interfaces/Container.php @@ -0,0 +1,25 @@ +register_style( 'login-with-google', 'build/css/login.css' ); + } + + /** + * Enqueue the login stylesheet. + * + * @return void + */ + public function enqueue_login_styles(): void { + /** + * If style is not registered, register it. + */ + if ( ! wp_style_is( 'login-with-google', 'registered' ) ) { + $this->register_login_styles(); + } + + if ( ! wp_script_is( 'login-with-google-script', 'registered' ) ) { + $this->register_script( 'login-with-google-script', 'build/js/login.js' ); + } + + wp_enqueue_script( 'login-with-google-script' ); + wp_enqueue_style( 'login-with-google' ); + } + + /** + * Register a new script. + * + * @param string $handle Name of the script. Should be unique. + * @param string|bool $file script file, path of the script relative to the assets/build/ directory. + * @param array $deps Optional. An array of registered script handles this script depends on. Default empty array. + * @param string|bool|null $ver Optional. String specifying script version number, if not set, filetime will be used as version number. + * @param bool $in_footer Optional. Whether to enqueue the script before instead of in the . + * Default 'false'. + * @return bool Whether the script has been registered. True on success, false on failure. + */ + public function register_script( $handle, $file, $deps = [], $ver = false, $in_footer = true ) { + $src = sprintf( '%1$sassets/%2$s', plugin()->url, $file ); + $version = $this->get_file_version( $file, $ver ); + + return wp_register_script( $handle, $src, $deps, $version, $in_footer ); + } + + /** + * Register a CSS stylesheet. + * + * @param string $handle Name of the stylesheet. Should be unique. + * @param string|bool $file style file, path of the script relative to the assets/build/ directory. + * @param array $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array. + * @param string|bool|null $ver Optional. String specifying script version number, if not set, filetime will be used as version number. + * @param string $media Optional. The media for which this stylesheet has been defined. + * Default 'all'. Accepts media types like 'all', 'print' and 'screen', or media queries like + * '(orientation: portrait)' and '(max-width: 640px)'. + * + * @return bool Whether the style has been registered. True on success, false on failure. + */ + public function register_style( $handle, $file, $deps = [], $ver = false, $media = 'all' ) { + $src = sprintf( '%1$sassets/%2$s', plugin()->url, $file ); + $version = $this->get_file_version( $file, $ver ); + + return wp_register_style( $handle, $src, $deps, $version, $media ); + } + + /** + * Get file version. + * + * @param string $file File path. + * @param int|string|boolean $ver File version. + * + * @return bool|false|int + */ + private function get_file_version( $file, $ver = false ) { + if ( ! empty( $ver ) ) { + return $ver; + } + + $file_path = sprintf( '%s/%s', plugin()->assets_dir, $file ); + + return file_exists( $file_path ) ? filemtime( $file_path ) : false; + } +} diff --git a/src/Modules/Login.php b/src/Modules/Login.php new file mode 100644 index 00000000..f62e5c4c --- /dev/null +++ b/src/Modules/Login.php @@ -0,0 +1,223 @@ +gh_client = $client; + $this->authenticator = $authenticator; + } + + /** + * Module name. + * + * @return string + */ + public function name(): string { + return 'login_flow'; + } + + /** + * Initialize login flow. + * + * @return void + */ + public function init(): void { + add_action( 'login_form', [ $this, 'login_button' ] ); + add_action( 'authenticate', [ $this, 'authenticate' ] ); + add_action( 'rtcamp.google_register_user', [ $this->authenticator, 'register' ] ); + add_action( 'rtcamp.google_redirect_url', [ $this, 'redirect_url' ] ); + add_action( 'rtcamp.google_user_created', [ $this, 'user_meta' ] ); + add_filter( 'rtcamp.google_login_state', [ $this, 'state_redirect' ] ); + add_action( 'wp_login', [ $this, 'login_redirect' ] ); + } + + /** + * Add the login button to login form. + * + * @return void + */ + public function login_button(): void { + $template = trailingslashit( plugin()->template_dir ) . 'google-login-button.php'; + $login_url = plugin()->container()->get( 'gh_client' )->authorization_url(); + + Helper::render_template( + $template, + [ + 'login_url' => $login_url, + ] + ); + } + + /** + * Authenticate the user. + * + * @param WP_User|null $user User object. Default is null. + * + * @return WP_User|WP_Error + * @throws Exception During authentication. + */ + public function authenticate( $user = null ) { + if ( $user instanceof WP_User ) { + return $user; + } + + $code = Helper::filter_input( INPUT_GET, 'code', FILTER_SANITIZE_STRING ); + + if ( ! $code ) { + return $user; + } + + $state = Helper::filter_input( INPUT_GET, 'state', FILTER_SANITIZE_STRING ); + $decoded_state = $state ? (array) ( json_decode( base64_decode( $state ) ) ) : null; + + if ( ! is_array( $decoded_state ) || empty( $decoded_state['provider'] ) || 'google' !== $decoded_state['provider'] ) { + return $user; + } + + if ( empty( $decoded_state['nonce'] ) || ! wp_verify_nonce( $decoded_state['nonce'], 'login_with_google' ) ) { + return $user; + } + + try { + $this->gh_client->set_access_token( $code ); + $user = $this->gh_client->user(); + $user = $this->authenticator->authenticate( $user ); + + if ( $user instanceof WP_User ) { + $this->authenticated = true; + + return $user; + } + + throw new Exception( __( 'Could not authenticate the user, please try again.', 'login-with-google' ) ); + + } catch ( Throwable $e ) { + return new WP_Error( 'google_login_failed', $e->getMessage() ); + } + } + + /** + * Add extra meta information about user. + * + * @param int $uid User ID. + * + * @return void + */ + public function user_meta( int $uid ) { + add_user_meta( $uid, 'oauth_user', 1, true ); + add_user_meta( $uid, 'oauth_provider', 'google', true ); + } + + /** + * Redirect URL. + * + * This is useful when redirect URL is present when + * trying to login to wp-admin. + * + * @param string $url Redirect URL address. + * + * @return string + */ + public function redirect_url( string $url ): string { + + return remove_query_arg( 'redirect_to', $url ); + } + + /** + * Add redirect_to location in state. + * + * @param array $state State data. + * + * @return array + */ + public function state_redirect( array $state ): array { + $redirect_to = Helper::filter_input( INPUT_GET, 'redirect_to', FILTER_SANITIZE_STRING ); + /** + * Filter the default redirect URL in case redirect_to param is not available. + * Default to admin URL. + * + * @param string $admin_url Admin URL address. + */ + $state['redirect_to'] = $redirect_to ?? apply_filters( 'rtcamp.google_default_redirect', admin_url() ); + + return $state; + } + + /** + * Add a redirect once user has been authenticated successfully. + * + * @return void + */ + public function login_redirect(): void { + $state = Helper::filter_input( INPUT_GET, 'state', FILTER_SANITIZE_STRING ); + + if ( ! $state || ! $this->authenticated ) { + return; + } + + $state = base64_decode( $state ); + $state = $state ? json_decode( $state ) : null; + + if ( ( $state instanceof stdClass ) && ! empty( $state->provider ) && 'google' === $state->provider && ! empty( $state->redirect_to ) ) { + wp_safe_redirect( $state->redirect_to ); + exit; + } + } +} diff --git a/src/Modules/OneTapLogin.php b/src/Modules/OneTapLogin.php new file mode 100644 index 00000000..a17fe428 --- /dev/null +++ b/src/Modules/OneTapLogin.php @@ -0,0 +1,221 @@ +settings = $settings; + $this->token_verifier = $verifier; + $this->google_client = $client; + $this->authenticator = $authenticator; + } + + /** + * Module name. + * + * @return string + */ + public function name(): string { + return 'one_tap_login'; + } + + /** + * Module Initialization activity. + * + * Everything will happen if and only if one tap is active in settings. + */ + public function init(): void { + if ( $this->settings->one_tap_login ) { + add_action( 'login_enqueue_scripts', [ $this, 'one_tap_scripts' ] ); + add_action( 'login_footer', [ $this, 'one_tap_prompt' ] ); + add_action( 'wp_ajax_nopriv_validate_id_token', [ $this, 'validate_token' ] ); + add_action( 'rtcamp.id_token_verified', [ $this, 'authenticate' ] ); + add_action( + 'init', + function () { + if ( ! is_user_logged_in() ) { + $hook_prefix = ( 'sitewide' === $this->settings->one_tap_login_screen ) ? 'wp' : 'login'; + add_action( $hook_prefix . '_enqueue_scripts', [ $this, 'one_tap_scripts' ] ); + add_action( $hook_prefix . '_footer', [ $this, 'one_tap_prompt' ], 10000 ); + } + } + ); + } + } + + /** + * Show one tap prompt markup. + * + * @return void + */ + public function one_tap_prompt(): void { ?> +
+ path ) . 'assets/build/js/onetap.js' ), + true + ); + + $data = [ + 'ajaxurl' => admin_url( 'admin-ajax.php' ), + 'state' => $this->google_client->state(), + 'homeurl' => get_option( 'home', '' ), + ]; + + wp_register_script( + 'login-with-google-one-tap-js', + trailingslashit( plugin()->url ) . 'assets/build/js/' . $filename, + [ + 'wp-i18n', + ], + filemtime( trailingslashit( plugin()->path ) . 'assets/build/js/onetap.js' ), + true + ); + + wp_add_inline_script( + 'login-with-google-one-tap-js', + 'var TempAccessOneTap=' . json_encode( $data ), //phpcs:disable WordPress.WP.AlternativeFunctions.json_encode_json_encode + 'before' + ); + + wp_enqueue_script( 'login-with-google-one-tap-js' ); + } + + /** + * Validate the ID token. + * + * @return void + * @throws Exception Credential verification failure exception. + */ + public function validate_token(): void { + try { + $token = Helper::filter_input( INPUT_POST, 'token', FILTER_SANITIZE_STRING ); + $verified = $this->token_verifier->verify_token( $token ); + + if ( ! $verified ) { + throw new Exception( __( 'Cannot verify the credentials', 'login-with-google' ) ); + } + + /** + * Do something when token has been verified successfully. + * + * If we are here that means ID token has been verified. + * + * @since 1.0.16 + */ + do_action( 'rtcamp.id_token_verified' ); + + $redirect_to = apply_filters( 'rtcamp.google_default_redirect', admin_url() ); + $state = Helper::filter_input( INPUT_POST, 'state', FILTER_SANITIZE_STRING ); + $decoded_state = $state ? (array) ( json_decode( base64_decode( $state ) ) ) : null; + + if ( is_array( $decoded_state ) && ! empty( $decoded_state['provider'] ) && 'google' === $decoded_state['provider'] ) { + $redirect_to = $decoded_state['redirect_to'] ?? $redirect_to; + } + + wp_send_json_success( + [ + 'redirect' => $redirect_to, + ] + ); + die; + + } catch ( Exception $e ) { + wp_send_json_error( $e->getMessage() ); + } + } + + /** + * Authenticate the user in WordPress. + * + * @return void + * @throws Exception Authentication exception. + */ + public function authenticate(): void { + $user = $this->token_verifier->current_user(); + + if ( is_null( $user ) ) { + throw new Exception( __( 'User not found to authenticate', 'login-with-google' ) ); + } + + $wp_user = $this->authenticator->authenticate( $user ); + $this->authenticator->set_auth_cookies( $wp_user ); + } +} diff --git a/src/Modules/Settings.php b/src/Modules/Settings.php new file mode 100644 index 00000000..f63e1637 --- /dev/null +++ b/src/Modules/Settings.php @@ -0,0 +1,361 @@ + + */ + +declare(strict_types=1); + +namespace RtCamp\GoogleLogin\Modules; + +use RtCamp\GoogleLogin\Interfaces\Module as ModuleInterface; + +/** + * Class Settings. + * + * @property string|null whitelisted_domains + * @property string|null client_id + * @property string|null client_secret + * @property bool|null registration_enabled + * @property bool|null one_tap_login + * @property string one_tap_login_screen + * + * @package RtCamp\GoogleLogin\Modules + */ +class Settings implements ModuleInterface { + + /** + * Settings values. + * + * @var array + */ + public $options; + + /** + * Getters for settings values. + * + * @var string[] + */ + private $getters = [ + 'WP_GOOGLE_LOGIN_CLIENT_ID' => 'client_id', + 'WP_GOOGLE_LOGIN_SECRET' => 'client_secret', + 'WP_GOOGLE_LOGIN_USER_REGISTRATION' => 'registration_enabled', + 'WP_GOOGLE_LOGIN_WHITELIST_DOMAINS' => 'whitelisted_domains', + 'WP_GOOGLE_ONE_TAP_LOGIN' => 'one_tap_login', + 'WP_GOOGLE_ONE_TAP_LOGIN_SCREEN' => 'one_tap_login_screen', + ]; + + /** + * Getter method. + * + * @param string $name Name of option to fetch. + */ + public function __get( string $name ) { + if ( in_array( $name, $this->getters, true ) ) { + $constant_name = array_search( $name, $this->getters, true ); + + return defined( $constant_name ) ? constant( $constant_name ) : ( $this->options[ $name ] ?? '' ); + } + + return null; + } + + /** + * Return module name. + * + * @return string + */ + public function name(): string { + return 'settings'; + } + + /** + * Initialization of module. + * + * @return void + */ + public function init(): void { + $this->options = get_option( 'wp_google_login_settings', [] ); + add_action( 'admin_init', [ $this, 'register_settings' ] ); + add_action( 'admin_menu', [ $this, 'settings_page' ] ); + } + + /** + * Register the settings, section and fields. + * + * @return void + */ + public function register_settings(): void { + register_setting( 'wp_google_login', 'wp_google_login_settings' ); + + add_settings_section( + 'wp_google_login_section', + __( 'Log in with Google Settings', 'login-with-google' ), + function () { + }, + 'login-with-google' + ); + + add_settings_field( + 'wp_google_login_client_id', + __( 'Client ID', 'login-with-google' ), + [ $this, 'client_id_field' ], + 'login-with-google', + 'wp_google_login_section', + [ 'label_for' => 'client-id' ] + ); + + add_settings_field( + 'wp_google_login_client_secret', + __( 'Client Secret', 'login-with-google' ), + [ $this, 'client_secret_field' ], + 'login-with-google', + 'wp_google_login_section', + [ 'label_for' => 'client-secret' ] + ); + + add_settings_field( + 'wp_google_allow_registration', + __( 'Create New User', 'login-with-google' ), + [ $this, 'user_registration' ], + 'login-with-google', + 'wp_google_login_section', + [ 'label_for' => 'user-registration' ] + ); + + add_settings_field( + 'wp_google_one_tap_login', + __( 'Enable One Tap Login', 'login-with-google' ), + [ $this, 'one_tap_login' ], + 'login-with-google', + 'wp_google_login_section', + [ 'label_for' => 'one-tap-login' ] + ); + + add_settings_field( + 'wp_google_one_tap_login_screen', + __( 'One Tap Login Locations', 'login-with-google' ), + [ $this, 'one_tap_login_screens' ], + 'login-with-google', + 'wp_google_login_section', + [ 'label_for' => 'one-tap-login-screen' ] + ); + + add_settings_field( + 'wp_google_whitelisted_domain', + __( 'Whitelisted Domains', 'login-with-google' ), + [ $this, 'whitelisted_domains' ], + 'login-with-google', + 'wp_google_login_section', + [ 'label_for' => 'whitelisted-domains' ] + ); + } + + /** + * Render client ID field. + * + * @return void + */ + public function client_id_field(): void { ?> + disabled( 'client_id' ); ?> /> +

+ %1s %3s.

', + esc_html__( 'Create oAuth Client ID and Client Secret at', 'login-with-google' ), + 'https://console.developers.google.com/apis/dashboard', + 'console.developers.google.com' + ) + ); + ?> +

+ + disabled( 'client_secret' ); ?> /> + General + * + * @return void + */ + public function user_registration(): void { + ?> + +

+ membership setting is off.', 'login-with-google' ), + is_multisite() ? 'network/settings.php' : 'options-general.php' + ) + ); + ?> +

+ + + one_tap_login_screen ?? ''; + ?> + + + + + + disabled( 'whitelisted_domains' ); ?> type='text' name='wp_google_login_settings[whitelisted_domains]' id="whitelisted-domains" value='whitelisted_domains ); ?>' autocomplete="off" /> +

+ +

+ +
+
+ +
+
+ getters, true ); + + if ( false !== $constant_name ) { + if ( defined( $constant_name ) ) { + echo esc_attr( 'disabled="disabled"' ); + } + } + } +} diff --git a/src/Modules/Shortcode.php b/src/Modules/Shortcode.php new file mode 100644 index 00000000..0d9c87a5 --- /dev/null +++ b/src/Modules/Shortcode.php @@ -0,0 +1,181 @@ +gh_client = $client; + $this->assets = $assets; + } + + /** + * Module name. + * + * @return string + */ + public function name(): string { + return 'shortcode'; + } + + /** + * Initialization actions. + */ + public function init(): void { + add_shortcode( self::TAG, [ $this, 'callback' ] ); + add_filter( 'do_shortcode_tag', [ $this, 'scan_shortcode' ], 10, 3 ); + } + + /** + * Callback function for shortcode rendering. + * + * @param array $attrs Shortcode attributes. + * + * @return string + */ + public function callback( $attrs = [] ): string { + $attrs = shortcode_atts( + [ + 'button_text' => __( 'Login with google', 'login-with-google' ), + 'force_display' => 'no', + 'redirect_to' => get_permalink(), + ], + $attrs, + self::TAG + ); + + if ( ! $this->should_display( $attrs ) ) { + return ''; + } + + $this->redirect_uri = $attrs['redirect_to']; + + add_filter( 'rtcamp.google_redirect_url', [ $this, 'redirect_url' ] ); + add_filter( 'rtcamp.google_login_state', [ $this, 'state_redirect' ] ); + + $attrs['login_url'] = $this->gh_client->authorization_url(); + + remove_filter( 'rtcamp.google_login_state', [ $this, 'state_redirect' ] ); + remove_filter( 'rtcamp.google_redirect_url', [ $this, 'redirect_url' ] ); + $template = trailingslashit( plugin()->template_dir ) . 'google-login-button.php'; + + return Helper::render_template( $template, $attrs, false ); + } + + /** + * Check if the current single post or page contains + * shortcode. If it does, enqueue the relevant style. + * + * @param string $output Shortcode output. + * @param string $tag Shortcode tag being processed. + * @param array|string $attrs Shortcode attributes. + * + * @return string + */ + public function scan_shortcode( string $output, string $tag, $attrs ): string { + if ( ( ! is_single() && ! is_page() ) || self::TAG !== $tag || ! $this->should_display( (array) $attrs ) ) { + return $output; + } + + $this->assets->enqueue_login_styles(); + + return $output; + } + + + /** + * Filter redirect URL as per shortcode param. + * + * @param string $url Login URL. + * + * @return string + */ + public function redirect_url( string $url ): string { + + return remove_query_arg( 'redirect_to', $url ); + } + + /** + * Add redirect_to location in state. + * + * @param array $state State data. + * + * @return array + */ + public function state_redirect( array $state ): array { + if ( is_null( $this->redirect_uri ) ) { + return $state; + } + + $state['redirect_to'] = $this->redirect_uri; + + return $state; + } + + /** + * Determines whether to process the shortcode. + * + * @param array $attrs Shortcode attributes. + * + * @return bool + */ + private function should_display( array $attrs ): bool { + if ( ! is_user_logged_in() || ( ! empty( $attrs['force_display'] ) && 'yes' === (string) $attrs['force_display'] ) ) { + return true; + } + + return false; + } +} diff --git a/src/Plugin.php b/src/Plugin.php new file mode 100644 index 00000000..34208f7f --- /dev/null +++ b/src/Plugin.php @@ -0,0 +1,143 @@ +container = $container; + } + + /** + * Run the plugin + * + * @return void + */ + public function run(): void { + $this->path = dirname( __FILE__, 2 ); + $this->url = plugin_dir_url( trailingslashit( dirname( __FILE__, 2 ) ) . 'login-with-google.php' ); + $this->template_dir = trailingslashit( $this->path ) . 'templates/'; + $this->assets_dir = trailingslashit( $this->path ) . 'assets/'; + + /** + * Filter out active modules before modules are initialized. + * + * @param array $active_modules Active modules list. + * + * @since 1.0.0 + */ + $this->active_modules = apply_filters( 'rtcamp.google_login_modules', $this->active_modules ); + + $this->container()->define_services(); + $this->activate_modules(); + + add_action( 'init', [ $this, 'load_translations' ] ); + } + + /** + * Load the plugin translation if available. + * + * @return void + */ + public function load_translations(): void { + load_plugin_textdomain( 'login-with-google', false, basename( plugin()->path ) . '/languages/' . get_locale() ); + } + + /** + * Return container object + * + * @return ContainerInterface + */ + public function container(): ContainerInterface { + return $this->container; + } + + /** + * Activate individual modules. + * + * @return void + */ + private function activate_modules(): void { + foreach ( $this->active_modules as $module ) { + $module_instance = $this->container()->get( $module ); + $module_instance->init(); + } + } +} diff --git a/src/Utils/Authenticator.php b/src/Utils/Authenticator.php new file mode 100644 index 00000000..b7d0abfa --- /dev/null +++ b/src/Utils/Authenticator.php @@ -0,0 +1,169 @@ +settings = $settings; + } + + /** + * Authenticate the user. + * + * If registration setting is on, user will be created if + * that user does not exist in the application. + * + * @param stdClass $user User data object returned by Google. + * + * @return WP_User + * @throws InvalidArgumentException For invalid registrations. + */ + public function authenticate( stdClass $user ): WP_User { + if ( ! property_exists( $user, 'email' ) ) { + throw new InvalidArgumentException( __( 'Email needs to be present for the user.', 'login-with-google' ) ); + } + + if ( email_exists( $user->email ) ) { + return get_user_by( 'email', $user->email ); + } + + /** + * Check if we need to register the user. + * + * @param stdClass $user User object from google. + * @since 1.0.0 + */ + return apply_filters( 'rtcamp.google_register_user', $this->maybe_create_username( $user ) ); + } + + /** + * Register the new user if setting is on for registration. + * + * @param stdClass $user User object from google. + * + * @return WP_User|null + * @throws Throwable Invalid email registration. + * @throws Exception Registration is off. + */ + public function register( stdClass $user ): ?WP_User { + $register = true === (bool) $this->settings->registration_enabled || (bool) get_option( 'users_can_register', false ); + + if ( ! $register ) { + throw new Exception( __( 'Registration is not allowed.', 'login-with-google' ) ); + } + + try { + $whitelisted_domains = $this->settings->whitelisted_domains; + if ( empty( $whitelisted_domains ) || $this->can_register_with_email( $user->email ) ) { + $uid = wp_insert_user( + [ + 'user_login' => Helper::unique_username( $user->login ), + 'user_pass' => wp_generate_password( 18 ), + 'user_email' => $user->email, + ] + ); + + /** + * Fires once the user has been registered successfully. + */ + do_action( 'rtcamp.google_user_created', $uid, $user ); + + return get_user_by( 'id', $uid ); + } + + /* translators: %s is replaced with email ID of user trying to register */ + throw new Exception( sprintf( __( 'Cannot register with this email: %s', 'login-with-google' ), $user->email ) ); + + } catch ( Throwable $e ) { + + throw $e; + } + + } + + /** + * Set auth cookies for WordPress login. + * + * @param WP_User $user WP User object. + * + * @return void + */ + public function set_auth_cookies( WP_User $user ) { + wp_clear_auth_cookie(); + wp_set_current_user( $user->ID, $user->user_login ); + wp_set_auth_cookie( $user->ID ); + } + + /** + * Assign the `login` property to user object + * if it doesn't exists. + * + * @param stdClass $user User object. + * + * @return stdClass + */ + private function maybe_create_username( stdClass $user ): stdClass { + if ( property_exists( $user, 'login' ) || ! property_exists( $user, 'email' ) ) { + return $user; + } + + $email = $user->email; + $user_login = sanitize_user( current( explode( '@', $email ) ), true ); + $user_login = Helper::unique_username( $user_login ); + $user->login = $user_login; + + return $user; + } + + /** + * Check if given email can be used for registration. + * + * @param string $email Email ID. + * + * @return bool + */ + private function can_register_with_email( string $email ): bool { + $whitelisted_domains = explode( ',', $this->settings->whitelisted_domains ); + $whitelisted_domains = array_map( 'strtolower', $whitelisted_domains ); + $whitelisted_domains = array_map( 'trim', $whitelisted_domains ); + $email_parts = explode( '@', $email ); + $email_parts = array_map( 'strtolower', $email_parts ); + + return in_array( $email_parts[1], $whitelisted_domains, true ); + } +} diff --git a/src/Utils/GoogleClient.php b/src/Utils/GoogleClient.php new file mode 100644 index 00000000..ea78f887 --- /dev/null +++ b/src/Utils/GoogleClient.php @@ -0,0 +1,254 @@ +client_id = $config['client_id'] ?? ''; + $this->client_secret = $config['client_secret'] ?? ''; + $this->redirect_uri = $config['redirect_uri'] ?? ''; + } + + /** + * Check if access token is set before calling API methods. + * + * @param string $name Name of method called. + * @param mixed $args Arguments for method. + * + * @throws Exception Empty access token. + */ + public function __call( string $name, $args ) { + $methods = [ + 'user', + 'emails', + ]; + + if ( in_array( $name, $methods, true ) && empty( $this->access_token ) ) { + throw new Exception( __( 'Access token must be set to make this API call', 'login-with-google' ) ); + } + } + + /** + * Set access token. + * + * @param string $code Token. + * + * @return self + * @throws \Throwable Exception for fetching access token. + */ + public function set_access_token( string $code ): self { + try { + $this->access_token = $this->access_token( $code )->access_token; + + return $this; + } catch ( \Throwable $e ) { + throw $e; + } + } + + /** + * Return redirect url. + * + * @return string + */ + public function gt_redirect_url(): string { + return apply_filters( 'rtcamp.google_redirect_url', $this->redirect_uri ); + } + + /** + * Get the authorize URL + * + * @return string + */ + public function authorization_url(): string { + $plugin_scope = [ + 'email', + 'profile', + 'openid', + ]; + + $scope = apply_filters_deprecated( + 'wp_google_login_scopes', + [ + $plugin_scope, + ], + '1.0.15', + 'rtcamp.google_scope' + ); + + /** + * Filter the scopes. + * + * @param array $scope List of scopes. + */ + $scope = apply_filters( 'rtcamp.google_scope', $scope ); + + $client_args = [ + 'client_id' => $this->client_id, + 'redirect_uri' => $this->gt_redirect_url(), + 'state' => $this->state(), + 'scope' => implode( ' ', $scope ), + 'access_type' => 'online', + 'response_type' => 'code', + ]; + + /** + * Filter the arguments for sending in query. + * + * This is useful in cases for example: choosing the correct prompt. + * + * @param array $client_args List of query arguments to send to Google OAuth. + */ + $client_args = apply_filters( 'rtcamp.google_client_args', $client_args ); + + return self::AUTHORIZE_URL . '?' . http_build_query( $client_args ); + } + + /** + * Get the access token. + * + * @param string $code Response code received during authorization. + * + * @return \stdClass + * @throws Exception For access token errors. + */ + public function access_token( string $code ): \stdClass { + $response = wp_remote_post( + self::TOKEN_URL, + [ + 'headers' => [ + 'Accept' => 'application/json', + ], + 'body' => [ + 'client_id' => $this->client_id, + 'client_secret' => $this->client_secret, + 'redirect_uri' => $this->gt_redirect_url(), + 'code' => $code, + 'grant_type' => 'authorization_code', + ], + ] + ); + + if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { + throw new Exception( __( 'Could not retrieve the access token, please try again.', 'login-with-google' ) ); + } + + return json_decode( wp_remote_retrieve_body( $response ) ); + } + + /** + * Make an API request. + * + * @return \stdClass + * @throws \Throwable Exception during API. + * @throws Exception API Exception. + */ + public function user(): \stdClass { + try { + //phpcs:disable WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get + $user = wp_remote_get( + trailingslashit( self::API_BASE ) . 'oauth2/v2/userinfo?access_token=' . $this->access_token, + [ + 'headers' => [ + 'Accept' => 'application/json', + ], + ] + ); + + if ( 200 !== wp_remote_retrieve_response_code( $user ) ) { + throw new Exception( __( 'Could not retrieve the user information, please try again.', 'login-with-google' ) ); + } + + return json_decode( wp_remote_retrieve_body( $user ) ); + + } catch ( \Throwable $e ) { + + throw $e; + } + } + + /** + * State to pass in GH API. + * + * @return string + */ + public function state(): string { + $state_data['nonce'] = wp_create_nonce( 'login_with_google' ); + $state_data = apply_filters( 'rtcamp.google_login_state', $state_data ); + $state_data['provider'] = 'google'; + + return base64_encode( wp_json_encode( $state_data ) ); + } + +} diff --git a/inc/classes/class-helper.php b/src/Utils/Helper.php similarity index 88% rename from inc/classes/class-helper.php rename to src/Utils/Helper.php index 8ebbc031..3961660b 100644 --- a/inc/classes/class-helper.php +++ b/src/Utils/Helper.php @@ -2,12 +2,15 @@ /** * Helper class for all helper function. * - * @author Dhaval Parekh + * This class has been taken from Login with Google plugin. * - * @package login-with-google + * @package RtCamp\GoogleLogin + * @since 1.0.0 */ -namespace WP_Google_Login\Inc; +declare(strict_types=1); + +namespace RtCamp\GoogleLogin\Utils; /** * Class Helper @@ -172,4 +175,22 @@ public static function filter_input( $type, $variable_name, $filter = FILTER_DEF } -} \ No newline at end of file + /** + * Checks if username exists, if it does, creates a + * unique username by appending digits. + * + * @param string $username Username. + * + * @return string + */ + public static function unique_username( string $username ): string { + $uname = $username; + $count = 1; + + while ( username_exists( $uname ) ) { + $uname = $uname . '' . $count; + } + + return $uname; + } +} diff --git a/src/Utils/TokenVerifier.php b/src/Utils/TokenVerifier.php new file mode 100644 index 00000000..b9fdddc2 --- /dev/null +++ b/src/Utils/TokenVerifier.php @@ -0,0 +1,334 @@ + OPENSSL_ALGO_SHA256, + 'RS384' => OPENSSL_ALGO_SHA384, + 'RS512' => OPENSSL_ALGO_SHA512, + 'ES384' => OPENSSL_ALGO_SHA384, + 'ES256' => OPENSSL_ALGO_SHA512, + ]; + + /** + * ID Token Sent via Google. + * + * @var string + */ + private $token = ''; + + /** + * User who needs to be authenticated. + * + * @var stdClass + */ + private $current_user; + + /** + * Settings instance. + * + * @var Settings + */ + private $settings; + + /** + * TokenVerifier constructor. + * + * @param Settings $settings Settings instance. + */ + public function __construct( Settings $settings ) { + $this->settings = $settings; + } + + /** + * Get supported algorithms value. + * + * @param string $algo Algorithm. + */ + public static function get_supported_algorithm( string $algo = '' ) { + $find_algo = array_key_exists( $algo, self::SUPPORTED_ALGORITHMS ); + + if ( ! $find_algo ) { + return apply_filters( 'rtcamp.default_algorithm', OPENSSL_ALGO_SHA256, $algo ); + } + + return self::SUPPORTED_ALGORITHMS[ $algo ]; + } + + /** + * Verify if a token is valid or not. + * + * @param string $token Received ID token from Google. + * + * @return bool + * @throws Exception Token verification failure exception. + */ + public function verify_token( string $token ): bool { + $this->token = $token; + + try { + $this->is_valid_jwt(); + $this->is_valid_signature(); + $this->valid_data(); + + return true; + } catch ( Exception $e ) { + + do_action( 'rtcamp.login_with_google_exception', $e ); + + throw $e; + } + } + + /** + * Base64 URL Encode a string. + * + * @param string $string Input string to encode. + * + * @return array|string|string[] + */ + public function base64_encode_url( $string ) { + return str_replace( [ '+', '/', '=' ], [ '-', '_', '' ], base64_encode( $string ) ); + } + + /** + * Base64 URL Encode a string. + * + * @param string $string Input string to decode. + * + * @return false|string + */ + public function base64_decode_url( string $string ) { + return base64_decode( str_replace( [ '-', '_' ], [ '+', '/' ], $string ) ); + } + + /** + * Retrieve current user's data. + * + * Current user is Google user, not WP user. + * + * @return stdClass|null + */ + public function current_user(): ?stdClass { + + return $this->current_user; + } + + /** + * Get public key based on key ID. + * + * @param string|null $key_id Key ID. + * + * @return string|null + */ + public function get_public_key( $key_id = null ): ?string { + if ( ! $key_id ) { + return null; + } + + $transient_key = 'lwg_pk_' . $key_id; + $cached_pk = $this->get_transient( $transient_key ); + + if ( ! empty( $cached_pk ) ) { + return (string) $cached_pk; + } + + //phpcs:disable WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get + $certs = wp_remote_get( self::CERTS_URL ); + + if ( 200 !== wp_remote_retrieve_response_code( $certs ) ) { + return null; + } + + $headers = wp_remote_retrieve_headers( $certs ); + $keys = wp_remote_retrieve_body( $certs ); + $keys = json_decode( $keys ); + + if ( property_exists( $keys, $key_id ) ) { + $max_age = is_object( $headers ) && is_a( $headers, Requests_Utility_CaseInsensitiveDictionary::class ) ? $this->get_max_age( $headers ) : 0; + + /** + * Cache public key in transient. + * + * We will cache it for 5 mins less than the actual expiration time, + * so that it should be cleared on time. + */ + if ( $max_age ) { + $max_age = $max_age - 300; + $this->set_transient( $transient_key, $keys->{$key_id}, max( 5, $max_age ) ); + } + + return $keys->{$key_id}; + } + + return null; + } + + /** + * Checks whether received token is valid JWT token or not. + * + * @return array|null Decoded informational array with Header|Payload|Signature form. + * @throws Exception ID token invalid. + */ + private function is_valid_jwt(): ?array { + $parts = explode( '.', $this->token ); + + if ( ! is_array( $parts ) || 3 !== count( $parts ) ) { + throw new Exception( __( 'ID token is invalid', 'login-with-google' ) ); + } + + list( $header, $payload, $obtained_signature ) = $parts; + $header = $this->base64_decode_url( $header ); + $payload = $this->base64_decode_url( $payload ); + + if ( ! $header || ! $payload ) { + throw new Exception( __( 'ID token is invalid', 'login-with-google' ) ); + } + + return [ + $header, + $payload, + $obtained_signature, + ]; + } + + /** + * Verifies the signature in token. + * + * @return void + * @throws Exception Failed signature verification. + */ + private function is_valid_signature(): void { + list( $header, $payload, $obtained_signature ) = $this->is_valid_jwt(); + $parsed_header = json_decode( $header ); + $parsed_header = wp_parse_args( + (array) $parsed_header, + [ + 'kid' => null, + 'alg' => null, + 'typ' => 'JWT', + ] + ); + + if ( ! $parsed_header['kid'] || ! $parsed_header['alg'] ) { + throw new Exception( __( 'Cannot verify the ID token signature. Please try again.', 'login-with-google' ) ); + } + + $pubkey_pem = $this->get_public_key( $parsed_header['kid'] ); + $decryption_key = openssl_pkey_get_public( $pubkey_pem ); + $data = $this->base64_encode_url( $header ) . '.' . $this->base64_encode_url( $payload ); + $calculated_signature = openssl_verify( $data, $this->base64_decode_url( $obtained_signature ), $decryption_key, self::get_supported_algorithm( $parsed_header['alg'] ) ); + + if ( 1 === (int) $calculated_signature ) { + $this->current_user = json_decode( $payload ); + + return; + } + + throw new Exception( __( 'Cannot verify the ID token signature. Please try again.', 'login-with-google' ) ); + } + + /** + * Check the validity of data. + * + * @throws Exception If user is not set. + */ + private function valid_data(): void { + if ( is_null( $this->current_user ) ) { + throw new Exception( __( 'No user present to validate', 'login-with-google' ) ); + } + + if ( $this->settings->client_id !== $this->current_user->aud ) { + throw new Exception( __( 'Invalid data found for authentication', 'login-with-google' ) ); + } + + if ( ! in_array( $this->current_user->iss, [ 'accounts.google.com', 'https://accounts.google.com' ], true ) ) { + throw new Exception( __( 'Invalid source found for authentication', 'login-with-google' ) ); + } + + if ( $this->current_user->exp < strtotime( 'now' ) ) { + throw new Exception( __( 'User data is stale! Please try again.', 'login-with-google' ) ); + } + } + + /** + * Get max age to cache the response from Cache-Control header. + * + * @param Requests_Utility_CaseInsensitiveDictionary $headers List of response headers. + * + * @return int + */ + private function get_max_age( Requests_Utility_CaseInsensitiveDictionary $headers ): int { + if ( ! $headers->offsetExists( 'cache-control' ) ) { + return 0; + } + + $cache_control = $headers->offsetGet( 'cache-control' ); + $cache_control = explode( ',', $cache_control ); + $cache_control = array_map( 'trim', $cache_control ); + $cache_control = preg_grep( '/max-age=(\d+)?/', $cache_control ); + + if ( is_array( $cache_control ) && 1 === count( $cache_control ) ) { + $max_age = array_pop( $cache_control ); + $max_age = explode( '=', $max_age ); + $max_age = $max_age[1]; + + return intval( $max_age ); + } + + return 0; + } + + /** + * Set the public key in transient. + * + * @param string $key Transient key. + * @param string $value Transient value. + * @param int $expire Transient expiration time in seconds. + * + * @return void + */ + private function set_transient( string $key, string $value, int $expire = 0 ): void { + set_transient( $key, $value, $expire ); + } + + /** + * Retrieve the transient. + * + * @param string $key Transient key. + * + * @return mixed + */ + private function get_transient( string $key ) { + return get_transient( $key ); + } +} diff --git a/template/google-login-button.php b/templates/google-login-button.php similarity index 71% rename from template/google-login-button.php rename to templates/google-login-button.php index ea775f08..30c86c73 100644 --- a/template/google-login-button.php +++ b/templates/google-login-button.php @@ -2,9 +2,8 @@ /** * Template for google login button. * - * @author Dhaval Parekh - * - * @package login-with-google + * @package RtCamp\GithubLogin + * @since 1.0.0 */ $button_text = ( ! empty( $button_text ) ) ? $button_text : __( 'Log in with Google', 'login-with-google' ); @@ -14,8 +13,7 @@ } ?> -