diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index a5e23b1..daf30a0 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"7.3.33","version":"3.4.0:v3.4.0#47177af1cfb9dab5d1cc4daf91b7179c2efe7fad","indent":"\t","lineEnding":"\n","rules":{"encoding":true,"full_opening_tag":true,"blank_line_after_namespace":true,"braces":{"position_after_anonymous_constructs":"same","position_after_control_structures":"same","position_after_functions_and_oop_constructs":"same"},"class_definition":true,"constant_case":true,"elseif":true,"function_declaration":{"closure_function_spacing":"one"},"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ignore"},"no_break_comment":true,"no_closing_tag":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":true,"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":{"elements":["property","method","const"]},"align_multiline_comment":true,"array_indentation":true,"binary_operator_spaces":{"default":"single_space"},"blank_line_after_opening_tag":true,"no_unused_imports":true},"hashes":{"appinfo\/routes.php":3044887257,"lib\/Responses\/SCIMJSONResponse.php":2876810868,"lib\/Responses\/SCIMListResponse.php":76345573,"lib\/Responses\/SCIMErrorResponse.php":1118832559,"lib\/Controller\/ASCIMGroup.php":3712028548,"lib\/Controller\/UserController.php":2528458696,"lib\/Controller\/ASCIMUser.php":430477040,"lib\/Controller\/GroupController.php":3769468563}} \ No newline at end of file +{"php":"8.3.2","version":"3.48.0","indent":"\t","lineEnding":"\n","rules":{"encoding":true,"full_opening_tag":true,"blank_line_after_namespace":true,"braces_position":true,"class_definition":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":{"closure_function_spacing":"one"},"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ignore"},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":true,"single_import_per_statement":true,"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":{"elements":["property","method","const"]},"align_multiline_comment":true,"array_indentation":true,"binary_operator_spaces":{"default":"single_space"},"blank_line_after_opening_tag":true,"curly_braces_position":{"classes_opening_brace":"same_line","functions_opening_brace":"same_line"},"no_spaces_inside_parenthesis":true,"no_unused_imports":true,"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"alpha"}},"hashes":{"appinfo\/routes.php":"a6daac024d42530db25f8a0ef4e4b8fc","lib\/Controller\/GroupController.php":"c43793525f75ed56acbcdebae283ef1d","lib\/Controller\/ServiceProviderConfigurationController.php":"23f9ec133052c11f256873834d9e9a37","lib\/Controller\/UserController.php":"ce6845e793cb2d8af28ee9c411530b37","lib\/Responses\/SCIMJSONResponse.php":"7218478fc8e0eb92ce29236825a8c17a","lib\/Responses\/SCIMListResponse.php":"bccc6e1cb2ef0be0745c89dfe9c50635","lib\/Responses\/SCIMErrorResponse.php":"4b57fdb473f7172c748ed7baeae5f425","lib\/Service\/GroupService.php":"150281b6627a71fa5880992346575210","lib\/Service\/UserService.php":"4d9ed2664130e74d688acc2f3c364ebf","lib\/AppInfo\/Application.php":"6f51c1b09b83ffea7a7bebf34fc45aa9","lib\/Exception\/AuthException.php":"74af2c0e26c689eb5718d43f6bfcf0a0","lib\/Exception\/ContentTypeException.php":"38395fd6b2287f4a8eead0584133f194","lib\/Middleware\/ContentTypeMiddleware.php":"4b947c369e511e4bd6be652827393aa5","lib\/Middleware\/AuthMiddleware.php":"dd7b9c27697ce77f6c4284f822b1a815","lib\/Middleware\/ErrorMiddleware.php":"ccb5c3a607517e9e98386a58a2ae63b5","lib\/Util\/Util.php":"75a45e4c37bcc6dfbcdcdd4387662a8d"}} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..be3f7b2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + 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 +them 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. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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. + + 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 +state 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) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/appinfo/info.xml b/appinfo/info.xml index 877b179..603a0dc 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -5,13 +5,14 @@ SCIM Service Provider Implements SCIM protocol as a Service Provider - 0.0.1 + 1.0.0-alpha.0 agpl - Pierre Ozoux + IndieHosters + Audriga SCIMServiceProvider integration - https://lab.libreho.st/libre.sh/docker/nextcloud/ + https://lab.libreho.st/libre.sh/scim/scimserviceprovider - + diff --git a/appinfo/routes.php b/appinfo/routes.php index 65f8ecd..097ad2a 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -1,46 +1,21 @@ [ ['name' => 'service_provider_configuration#resource_types', 'url' => '/ResourceTypes', 'verb' => 'GET'], ['name' => 'service_provider_configuration#schemas', 'url' => '/Schemas', 'verb' => 'GET'], ['name' => 'service_provider_configuration#service_provider_config', 'url' => '/ServiceProviderConfig', 'verb' => 'GET'], + + ['name' => 'user#index', 'url' => '/Users', 'verb' => 'GET'], + ['name' => 'user#show', 'url' => '/Users/{id}', 'verb' => 'GET'], + ['name' => 'user#create', 'url' => '/Users', 'verb' => 'POST'], + ['name' => 'user#update', 'url' => '/Users/{id}', 'verb' => 'PUT'], + ['name' => 'user#destroy', 'url' => '/Users/{id}', 'verb' => 'DELETE'], + + ['name' => 'group#index', 'url' => '/Groups', 'verb' => 'GET'], + ['name' => 'group#show', 'url' => '/Groups/{id}', 'verb' => 'GET'], + ['name' => 'group#create', 'url' => '/Groups', 'verb' => 'POST'], + ['name' => 'group#update', 'url' => '/Groups/{id}', 'verb' => 'PUT'], + ['name' => 'group#destroy', 'url' => '/Groups/{id}', 'verb' => 'DELETE'], ] ]; - -$config = require dirname(__DIR__) . '/lib/Config/config.php'; -$userAndGroupRoutes = []; - -if (isset($config['auth_type']) && !empty($config['auth_type']) && (strcmp($config['auth_type'], 'bearer') === 0)) { - $userAndGroupRoutes = [ - ['name' => 'user_bearer#index', 'url' => '/bearer/Users', 'verb' => 'GET'], - ['name' => 'user_bearer#show', 'url' => '/bearer/Users/{id}', 'verb' => 'GET'], - ['name' => 'user_bearer#create', 'url' => '/bearer/Users', 'verb' => 'POST'], - ['name' => 'user_bearer#update', 'url' => '/bearer/Users/{id}', 'verb' => 'PUT'], - ['name' => 'user_bearer#destroy', 'url' => '/bearer/Users/{id}', 'verb' => 'DELETE'], - - ['name' => 'group_bearer#index', 'url' => '/bearer/Groups', 'verb' => 'GET'], - ['name' => 'group_bearer#show', 'url' => '/bearer/Groups/{id}', 'verb' => 'GET'], - ['name' => 'group_bearer#create', 'url' => '/bearer/Groups', 'verb' => 'POST'], - ['name' => 'group_bearer#update', 'url' => '/bearer/Groups/{id}', 'verb' => 'PUT'], - ['name' => 'group_bearer#destroy', 'url' => '/bearer/Groups/{id}', 'verb' => 'DELETE'], - ]; -} else if (!isset($config['auth_type']) || empty($config['auth_type']) || (strcmp($config['auth_type'], 'basic') === 0)) { - $userAndGroupRoutes = [ - ['name' => 'user#index', 'url' => '/Users', 'verb' => 'GET'], - ['name' => 'user#show', 'url' => '/Users/{id}', 'verb' => 'GET'], - ['name' => 'user#create', 'url' => '/Users', 'verb' => 'POST'], - ['name' => 'user#update', 'url' => '/Users/{id}', 'verb' => 'PUT'], - ['name' => 'user#destroy', 'url' => '/Users/{id}', 'verb' => 'DELETE'], - - ['name' => 'group#index', 'url' => '/Groups', 'verb' => 'GET'], - ['name' => 'group#show', 'url' => '/Groups/{id}', 'verb' => 'GET'], - ['name' => 'group#create', 'url' => '/Groups', 'verb' => 'POST'], - ['name' => 'group#update', 'url' => '/Groups/{id}', 'verb' => 'PUT'], - ['name' => 'group#destroy', 'url' => '/Groups/{id}', 'verb' => 'DELETE'], - ]; -} - -$routes['routes'] = array_merge($routes['routes'], $userAndGroupRoutes); - -return $routes; diff --git a/composer.json b/composer.json index 8db0429..c450de9 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,8 @@ { "require-dev": { "vimeo/psalm": "^4.23", - "christophwurst/nextcloud": "v22.1.1", - "nextcloud/coding-standard": "^1.0" + "nextcloud/ocp": "v26.0.9", + "nextcloud/coding-standard": "^1.1" }, "config": { "allow-plugins": { diff --git a/composer.lock b/composer.lock index 48b0b7a..5523d5e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "64b7aba10119e669997cbca8af52bd38", + "content-hash": "12168ba78199e8eb5da634f9390bd1d0", "packages": [ { "name": "audriga/scim-server-php", @@ -80,26 +80,25 @@ }, { "name": "brick/math", - "version": "0.9.3", + "version": "0.11.0", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae" + "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/ca57d18f028f84f777b2168cd1911b0dee2343ae", - "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae", + "url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478", + "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478", "shasum": "" }, "require": { - "ext-json": "*", - "php": "^7.1 || ^8.0" + "php": "^8.0" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.0", - "vimeo/psalm": "4.9.2" + "phpunit/phpunit": "^9.0", + "vimeo/psalm": "5.0.0" }, "type": "library", "autoload": { @@ -124,19 +123,85 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.9.3" + "source": "https://github.com/brick/math/tree/0.11.0" }, "funding": [ { "url": "https://github.com/BenMorel", "type": "github" + } + ], + "time": "2023-01-15T23:15:59+00:00" + }, + { + "name": "carbonphp/carbon-doctrine-types", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "a31d3358a2a5d6ae947df1691d1f321418a5f3d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/a31d3358a2a5d6ae947df1691d1f321418a5f3d5", + "reference": "a31d3358a2a5d6ae947df1691d1f321418a5f3d5", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "conflict": { + "doctrine/dbal": "<4.0.0 || >=5.0.0" + }, + "require-dev": { + "doctrine/dbal": "^4.0.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "default-branch": true, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.1.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/brick/math", + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", "type": "tidelift" } ], - "time": "2021-08-15T20:50:18+00:00" + "time": "2023-12-10T15:33:53+00:00" }, { "name": "coenjacobs/mozart", @@ -202,12 +267,12 @@ "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "bf265da9f831e46e646c7a01b59ee8a33f8c0365" + "reference": "d718c589c01ce614e03ed0ab3be831e4201bfb79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/bf265da9f831e46e646c7a01b59ee8a33f8c0365", - "reference": "bf265da9f831e46e646c7a01b59ee8a33f8c0365", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/d718c589c01ce614e03ed0ab3be831e4201bfb79", + "reference": "d718c589c01ce614e03ed0ab3be831e4201bfb79", "shasum": "" }, "require": { @@ -285,7 +350,7 @@ "type": "tidelift" } ], - "time": "2023-01-15T14:40:19+00:00" + "time": "2024-01-15T18:12:41+00:00" }, { "name": "doctrine/lexer", @@ -423,30 +488,31 @@ }, { "name": "firebase/php-jwt", - "version": "v6.3.2", + "version": "v6.10.0", "source": { "type": "git", "url": "https://github.com/firebase/php-jwt.git", - "reference": "ea7dda77098b96e666c5ef382452f94841e439cd" + "reference": "a49db6f0a5033aef5143295342f1c95521b075ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/ea7dda77098b96e666c5ef382452f94841e439cd", - "reference": "ea7dda77098b96e666c5ef382452f94841e439cd", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/a49db6f0a5033aef5143295342f1c95521b075ff", + "reference": "a49db6f0a5033aef5143295342f1c95521b075ff", "shasum": "" }, "require": { - "php": "^7.1||^8.0" + "php": "^7.4||^8.0" }, "require-dev": { "guzzlehttp/guzzle": "^6.5||^7.4", - "phpspec/prophecy-phpunit": "^1.1", - "phpunit/phpunit": "^7.5||^9.5", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", "psr/cache": "^1.0||^2.0", "psr/http-client": "^1.0", "psr/http-factory": "^1.0" }, "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" }, "type": "library", @@ -479,9 +545,9 @@ ], "support": { "issues": "https://github.com/firebase/php-jwt/issues", - "source": "https://github.com/firebase/php-jwt/tree/v6.3.2" + "source": "https://github.com/firebase/php-jwt/tree/v6.10.0" }, - "time": "2022-12-19T17:10:46+00:00" + "time": "2023-12-01T16:26:39+00:00" }, { "name": "illuminate/collections", @@ -642,12 +708,12 @@ "source": { "type": "git", "url": "https://github.com/illuminate/database.git", - "reference": "606f2935697e94fc6a9e2eae430a7f279283f60c" + "reference": "f6f16450e733932b4c2c1468d8793c0a22fb45b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/database/zipball/606f2935697e94fc6a9e2eae430a7f279283f60c", - "reference": "606f2935697e94fc6a9e2eae430a7f279283f60c", + "url": "https://api.github.com/repos/illuminate/database/zipball/f6f16450e733932b4c2c1468d8793c0a22fb45b3", + "reference": "f6f16450e733932b4c2c1468d8793c0a22fb45b3", "shasum": "" }, "require": { @@ -702,7 +768,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2022-12-19T10:24:20+00:00" + "time": "2023-06-11T21:11:10+00:00" }, { "name": "illuminate/macroable", @@ -756,12 +822,12 @@ "source": { "type": "git", "url": "https://github.com/illuminate/support.git", - "reference": "7895479ee9254069462bc9aa266eff20a5d46bae" + "reference": "ad030d68770e00a395822a4c243b931be3e4d3c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/7895479ee9254069462bc9aa266eff20a5d46bae", - "reference": "7895479ee9254069462bc9aa266eff20a5d46bae", + "url": "https://api.github.com/repos/illuminate/support/zipball/ad030d68770e00a395822a4c243b931be3e4d3c7", + "reference": "ad030d68770e00a395822a4c243b931be3e4d3c7", "shasum": "" }, "require": { @@ -816,7 +882,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2022-12-19T10:24:20+00:00" + "time": "2023-06-11T21:11:10+00:00" }, { "name": "laravel/serializable-closure", @@ -824,22 +890,23 @@ "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "975c8239e2d8c9582fa5280cf8b9496aa717c173" + "reference": "d715a63f39ba076acf8be1e4a0901acdcc92228b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/975c8239e2d8c9582fa5280cf8b9496aa717c173", - "reference": "975c8239e2d8c9582fa5280cf8b9496aa717c173", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/d715a63f39ba076acf8be1e4a0901acdcc92228b", + "reference": "d715a63f39ba076acf8be1e4a0901acdcc92228b", "shasum": "" }, "require": { "php": "^7.3|^8.0" }, "require-dev": { - "nesbot/carbon": "^2.61", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0", + "nesbot/carbon": "^2.61|^3.0", "pestphp/pest": "^1.21.3", "phpstan/phpstan": "^1.8.2", - "symfony/var-dumper": "^5.4.11" + "symfony/var-dumper": "^5.4.11|^6.2.0|^7.0.0" }, "default-branch": true, "type": "library", @@ -877,7 +944,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2022-12-28T12:34:46+00:00" + "time": "2024-01-26T17:22:21+00:00" }, { "name": "league/flysystem", @@ -975,26 +1042,26 @@ }, { "name": "league/mime-type-detection", - "version": "1.11.0", + "version": "1.15.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd" + "reference": "ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd", - "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301", + "reference": "ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301", "shasum": "" }, "require": { "ext-fileinfo": "*", - "php": "^7.2 || ^8.0" + "php": "^7.4 || ^8.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.2", "phpstan/phpstan": "^0.12.68", - "phpunit/phpunit": "^8.5.8 || ^9.3" + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" }, "type": "library", "autoload": { @@ -1015,7 +1082,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.15.0" }, "funding": [ { @@ -1027,7 +1094,7 @@ "type": "tidelift" } ], - "time": "2022-04-17T13:12:02+00:00" + "time": "2024-01-28T23:22:08+00:00" }, { "name": "monolog/monolog", @@ -1035,12 +1102,12 @@ "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "1387e02612584ffa1a9e93384d2d63ba0a747e11" + "reference": "437cb3628f4cf6042cc10ae97fc2b8472e48ca1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1387e02612584ffa1a9e93384d2d63ba0a747e11", - "reference": "1387e02612584ffa1a9e93384d2d63ba0a747e11", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/437cb3628f4cf6042cc10ae97fc2b8472e48ca1f", + "reference": "437cb3628f4cf6042cc10ae97fc2b8472e48ca1f", "shasum": "" }, "require": { @@ -1129,7 +1196,7 @@ "type": "tidelift" } ], - "time": "2022-10-14T15:01:04+00:00" + "time": "2023-10-27T15:25:26+00:00" }, { "name": "nesbot/carbon", @@ -1137,24 +1204,29 @@ "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "09acf64155c16dc6f580f36569ae89344e9734a3" + "reference": "bdd61d32949b718aa3843ea5b342738242cd5966" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/09acf64155c16dc6f580f36569ae89344e9734a3", - "reference": "09acf64155c16dc6f580f36569ae89344e9734a3", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/bdd61d32949b718aa3843ea5b342738242cd5966", + "reference": "bdd61d32949b718aa3843ea5b342738242cd5966", "shasum": "" }, "require": { + "carbonphp/carbon-doctrine-types": "*", "ext-json": "*", "php": "^7.1.8 || ^8.0", + "psr/clock": "^1.0", "symfony/polyfill-mbstring": "^1.0", "symfony/polyfill-php80": "^1.16", "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" }, + "provide": { + "psr/clock-implementation": "1.0" + }, "require-dev": { - "doctrine/dbal": "^2.0 || ^3.1.4", - "doctrine/orm": "^2.7", + "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0", + "doctrine/orm": "^2.7 || ^3.0", "friendsofphp/php-cs-fixer": "^3.0", "kylekatarnls/multi-tester": "^2.0", "ondrejmirtes/better-reflection": "*", @@ -1232,7 +1304,7 @@ "type": "tidelift" } ], - "time": "2023-01-06T15:55:01+00:00" + "time": "2024-01-22T07:01:34+00:00" }, { "name": "nikic/fast-route", @@ -1286,16 +1358,16 @@ }, { "name": "php-di/invoker", - "version": "2.3.3", + "version": "2.3.4", "source": { "type": "git", "url": "https://github.com/PHP-DI/Invoker.git", - "reference": "cd6d9f267d1a3474bdddf1be1da079f01b942786" + "reference": "33234b32dafa8eb69202f950a1fc92055ed76a86" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/cd6d9f267d1a3474bdddf1be1da079f01b942786", - "reference": "cd6d9f267d1a3474bdddf1be1da079f01b942786", + "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/33234b32dafa8eb69202f950a1fc92055ed76a86", + "reference": "33234b32dafa8eb69202f950a1fc92055ed76a86", "shasum": "" }, "require": { @@ -1329,7 +1401,7 @@ ], "support": { "issues": "https://github.com/PHP-DI/Invoker/issues", - "source": "https://github.com/PHP-DI/Invoker/tree/2.3.3" + "source": "https://github.com/PHP-DI/Invoker/tree/2.3.4" }, "funding": [ { @@ -1337,7 +1409,7 @@ "type": "github" } ], - "time": "2021-12-13T09:22:56+00:00" + "time": "2023-09-08T09:24:21+00:00" }, { "name": "php-di/php-di", @@ -1457,6 +1529,54 @@ }, "time": "2020-10-12T12:39:22+00:00" }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, { "name": "psr/container", "version": "1.x-dev", @@ -1511,17 +1631,17 @@ "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", - "reference": "5a4f141ac2e5bc35e615134f127e1833158d2944" + "reference": "7037f4b0950474e9d1350e8df89b15f1842085f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/5a4f141ac2e5bc35e615134f127e1833158d2944", - "reference": "5a4f141ac2e5bc35e615134f127e1833158d2944", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/7037f4b0950474e9d1350e8df89b15f1842085f6", + "reference": "7037f4b0950474e9d1350e8df89b15f1842085f6", "shasum": "" }, "require": { "php": ">=7.0.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0 || ^2.0" }, "default-branch": true, "type": "library", @@ -1545,7 +1665,7 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "Common interfaces for PSR-7 HTTP message factories", + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", "keywords": [ "factory", "http", @@ -1557,32 +1677,31 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-factory/tree/master" + "source": "https://github.com/php-fig/http-factory" }, - "time": "2022-07-14T07:21:53+00:00" + "time": "2023-09-22T11:16:44+00:00" }, { "name": "psr/http-message", - "version": "dev-master", + "version": "1.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "efd67d1dc14a7ef4fc4e518e7dee91c271d524e4" + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/efd67d1dc14a7ef4fc4e518e7dee91c271d524e4", - "reference": "efd67d1dc14a7ef4fc4e518e7dee91c271d524e4", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.2 || ^8.0" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { @@ -1611,9 +1730,9 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/master" + "source": "https://github.com/php-fig/http-message/tree/1.1" }, - "time": "2019-08-29T13:16:46+00:00" + "time": "2023-04-04T09:50:52+00:00" }, { "name": "psr/http-server-handler", @@ -1621,17 +1740,17 @@ "source": { "type": "git", "url": "https://github.com/php-fig/http-server-handler.git", - "reference": "cada5cd1c6a9871031e07f26d0f7b08c9de19039" + "reference": "13403d43196ab3382d0a4ca28be32984282641f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/cada5cd1c6a9871031e07f26d0f7b08c9de19039", - "reference": "cada5cd1c6a9871031e07f26d0f7b08c9de19039", + "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/13403d43196ab3382d0a4ca28be32984282641f9", + "reference": "13403d43196ab3382d0a4ca28be32984282641f9", "shasum": "" }, "require": { "php": ">=7.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0 || ^2.0" }, "default-branch": true, "type": "library", @@ -1668,9 +1787,9 @@ "server" ], "support": { - "source": "https://github.com/php-fig/http-server-handler/tree/master" + "source": "https://github.com/php-fig/http-server-handler" }, - "time": "2020-09-17T16:52:43+00:00" + "time": "2023-11-16T18:17:39+00:00" }, { "name": "psr/http-server-middleware", @@ -1678,17 +1797,17 @@ "source": { "type": "git", "url": "https://github.com/php-fig/http-server-middleware.git", - "reference": "adcb26d2fb28018fea3319d0035e46e8d08eb8c8" + "reference": "459eeb7efeae55b4102a951c4ecc93a11ce58d0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/adcb26d2fb28018fea3319d0035e46e8d08eb8c8", - "reference": "adcb26d2fb28018fea3319d0035e46e8d08eb8c8", + "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/459eeb7efeae55b4102a951c4ecc93a11ce58d0f", + "reference": "459eeb7efeae55b4102a951c4ecc93a11ce58d0f", "shasum": "" }, "require": { "php": ">=7.0", - "psr/http-message": "^1.0", + "psr/http-message": "^1.0 || ^2.0", "psr/http-server-handler": "^1.0" }, "default-branch": true, @@ -1726,9 +1845,9 @@ ], "support": { "issues": "https://github.com/php-fig/http-server-middleware/issues", - "source": "https://github.com/php-fig/http-server-middleware/tree/master" + "source": "https://github.com/php-fig/http-server-middleware" }, - "time": "2020-09-17T16:41:19+00:00" + "time": "2023-09-22T11:17:21+00:00" }, { "name": "psr/log", @@ -1877,21 +1996,20 @@ }, { "name": "ramsey/collection", - "version": "1.3.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/ramsey/collection.git", - "reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4" + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/ad7475d1c9e70b190ecffc58f2d989416af339b4", - "reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4", + "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", "shasum": "" }, "require": { - "php": "^7.4 || ^8.0", - "symfony/polyfill-php81": "^1.23" + "php": "^8.1" }, "require-dev": { "captainhook/plugin-composer": "^5.3", @@ -1951,7 +2069,7 @@ ], "support": { "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/1.3.0" + "source": "https://github.com/ramsey/collection/tree/2.0.0" }, "funding": [ { @@ -1963,29 +2081,27 @@ "type": "tidelift" } ], - "time": "2022-12-27T19:12:24+00:00" + "time": "2022-12-31T21:50:55+00:00" }, { "name": "ramsey/uuid", - "version": "4.2.3", + "version": "4.x-dev", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df" + "reference": "d459bdbb08b1d893c56b1328c81fa0fdbda3ab54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df", - "reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/d459bdbb08b1d893c56b1328c81fa0fdbda3ab54", + "reference": "d459bdbb08b1d893c56b1328c81fa0fdbda3ab54", "shasum": "" }, "require": { - "brick/math": "^0.8 || ^0.9", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11", "ext-json": "*", - "php": "^7.2 || ^8.0", - "ramsey/collection": "^1.0", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-php80": "^1.14" + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" }, "replace": { "rhumsaa/uuid": "self.version" @@ -1997,34 +2113,31 @@ "doctrine/annotations": "^1.8", "ergebnis/composer-normalize": "^2.15", "mockery/mockery": "^1.3", - "moontoast/math": "^1.1", "paragonie/random-lib": "^2", "php-mock/php-mock": "^2.2", "php-mock/php-mock-mockery": "^1.3", "php-parallel-lint/php-parallel-lint": "^1.1", "phpbench/phpbench": "^1.0", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-mockery": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.1", "phpunit/phpunit": "^8.5 || ^9", - "slevomat/coding-standard": "^7.0", + "ramsey/composer-repl": "^1.4", + "slevomat/coding-standard": "^8.4", "squizlabs/php_codesniffer": "^3.5", "vimeo/psalm": "^4.9" }, "suggest": { "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", - "ext-ctype": "Enables faster processing of character classification using ctype functions.", "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." }, + "default-branch": true, "type": "library", "extra": { - "branch-alias": { - "dev-main": "4.x-dev" - }, "captainhook": { "force-install": true } @@ -2049,7 +2162,7 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.2.3" + "source": "https://github.com/ramsey/uuid/tree/4.x" }, "funding": [ { @@ -2061,7 +2174,7 @@ "type": "tidelift" } ], - "time": "2021-09-25T23:10:38+00:00" + "time": "2024-01-22T21:44:20+00:00" }, { "name": "slim/php-view", @@ -2069,22 +2182,22 @@ "source": { "type": "git", "url": "https://github.com/slimphp/PHP-View.git", - "reference": "94cf4224f9a47b9d51d87b4450e5c0c2595ad0c7" + "reference": "ed5e37e8041959ec4e46b4bf6df5b94fed49684a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/PHP-View/zipball/94cf4224f9a47b9d51d87b4450e5c0c2595ad0c7", - "reference": "94cf4224f9a47b9d51d87b4450e5c0c2595ad0c7", + "url": "https://api.github.com/repos/slimphp/PHP-View/zipball/ed5e37e8041959ec4e46b4bf6df5b94fed49684a", + "reference": "ed5e37e8041959ec4e46b4bf6df5b94fed49684a", "shasum": "" }, "require": { "php": "^7.4 || ^8.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.1" }, "require-dev": { - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^9.6", "slim/psr7": "^1.6", - "squizlabs/php_codesniffer": "^3.7" + "squizlabs/php_codesniffer": "^3.8" }, "default-branch": true, "type": "library", @@ -2117,20 +2230,20 @@ "issues": "https://github.com/slimphp/PHP-View/issues", "source": "https://github.com/slimphp/PHP-View/tree/3.x" }, - "time": "2022-12-01T16:27:12+00:00" + "time": "2024-01-01T20:52:51+00:00" }, { "name": "slim/psr7", - "version": "1.6", + "version": "1.6.x-dev", "source": { "type": "git", "url": "https://github.com/slimphp/Slim-Psr7.git", - "reference": "3471c22c1a0d26c51c78f6aeb06489d38cf46a4d" + "reference": "72d2b2bac94ab4575d369f605dbfafbe168d3163" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim-Psr7/zipball/3471c22c1a0d26c51c78f6aeb06489d38cf46a4d", - "reference": "3471c22c1a0d26c51c78f6aeb06489d38cf46a4d", + "url": "https://api.github.com/repos/slimphp/Slim-Psr7/zipball/72d2b2bac94ab4575d369f605dbfafbe168d3163", + "reference": "72d2b2bac94ab4575d369f605dbfafbe168d3163", "shasum": "" }, "require": { @@ -2149,7 +2262,7 @@ "adriansuter/php-autoload-override": "^1.3", "ext-json": "*", "http-interop/http-factory-tests": "^0.9.0", - "php-http/psr7-integration-tests": "dev-master", + "php-http/psr7-integration-tests": "1.1", "phpspec/prophecy": "^1.15", "phpspec/prophecy-phpunit": "^2.0", "phpstan/phpstan": "^1.8", @@ -2197,9 +2310,9 @@ ], "support": { "issues": "https://github.com/slimphp/Slim-Psr7/issues", - "source": "https://github.com/slimphp/Slim-Psr7/tree/1.6" + "source": "https://github.com/slimphp/Slim-Psr7/tree/1.6.x" }, - "time": "2022-11-05T18:50:24+00:00" + "time": "2023-04-17T16:02:20+00:00" }, { "name": "slim/slim", @@ -2207,12 +2320,12 @@ "source": { "type": "git", "url": "https://github.com/slimphp/Slim.git", - "reference": "a44d0a70d49ae86d1177503b7d734303d30ea9a6" + "reference": "7354f85722b89ac08763baf326a15cc11c162837" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim/zipball/a44d0a70d49ae86d1177503b7d734303d30ea9a6", - "reference": "a44d0a70d49ae86d1177503b7d734303d30ea9a6", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/7354f85722b89ac08763baf326a15cc11c162837", + "reference": "7354f85722b89ac08763baf326a15cc11c162837", "shasum": "" }, "require": { @@ -2221,7 +2334,7 @@ "php": "^7.4 || ^8.0", "psr/container": "^1.0 || ^2.0", "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", + "psr/http-message": "^1.1 || ^2.0", "psr/http-server-handler": "^1.0", "psr/http-server-middleware": "^1.0", "psr/log": "^1.1 || ^2.0 || ^3.0" @@ -2229,19 +2342,19 @@ "require-dev": { "adriansuter/php-autoload-override": "^1.4", "ext-simplexml": "*", - "guzzlehttp/psr7": "^2.4", - "httpsoft/http-message": "^1.0", - "httpsoft/http-server-request": "^1.0", - "laminas/laminas-diactoros": "^2.17", - "nyholm/psr7": "^1.5", - "nyholm/psr7-server": "^1.0", - "phpspec/prophecy": "^1.16", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^9.5", + "guzzlehttp/psr7": "^2.6", + "httpsoft/http-message": "^1.1", + "httpsoft/http-server-request": "^1.1", + "laminas/laminas-diactoros": "^2.17 || ^3", + "nyholm/psr7": "^1.8", + "nyholm/psr7-server": "^1.1", + "phpspec/prophecy": "^1.18", + "phpspec/prophecy-phpunit": "^2.1", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.6", "slim/http": "^1.3", "slim/psr7": "^1.6", - "squizlabs/php_codesniffer": "^3.7" + "squizlabs/php_codesniffer": "^3.8" }, "suggest": { "ext-simplexml": "Needed to support XML format in BodyParsingMiddleware", @@ -2315,7 +2428,7 @@ "type": "tidelift" } ], - "time": "2022-12-01T16:30:46+00:00" + "time": "2024-01-01T20:50:49+00:00" }, { "name": "symfony/console", @@ -2323,12 +2436,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "dccb8d251a9017d5994c988b034d3e18aaabf740" + "reference": "0aff1982feec6ac2a86f821957761b707c0071e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/dccb8d251a9017d5994c988b034d3e18aaabf740", - "reference": "dccb8d251a9017d5994c988b034d3e18aaabf740", + "url": "https://api.github.com/repos/symfony/console/zipball/0aff1982feec6ac2a86f821957761b707c0071e6", + "reference": "0aff1982feec6ac2a86f821957761b707c0071e6", "shasum": "" }, "require": { @@ -2393,7 +2506,7 @@ "homepage": "https://symfony.com", "keywords": [ "cli", - "command line", + "command-line", "console", "terminal" ], @@ -2414,29 +2527,30 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-01-21T09:52:19+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "2.5.x-dev", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" + "reference": "2c438b99bb2753c1628c1e6f523991edea5b03a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/2c438b99bb2753c1628c1e6f523991edea5b03a4", + "reference": "2c438b99bb2753c1628c1e6f523991edea5b03a4", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -2465,7 +2579,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/2.5" + "source": "https://github.com/symfony/deprecation-contracts/tree/main" }, "funding": [ { @@ -2481,7 +2595,7 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2024-01-02T14:07:37+00:00" }, { "name": "symfony/finder", @@ -2489,12 +2603,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "6071aebf810ad13fe8200c224f36103abb37cf1f" + "reference": "ff4bce3c33451e7ec778070e45bd23f74214cd5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/6071aebf810ad13fe8200c224f36103abb37cf1f", - "reference": "6071aebf810ad13fe8200c224f36103abb37cf1f", + "url": "https://api.github.com/repos/symfony/finder/zipball/ff4bce3c33451e7ec778070e45bd23f74214cd5d", + "reference": "ff4bce3c33451e7ec778070e45bd23f74214cd5d", "shasum": "" }, "require": { @@ -2544,20 +2658,20 @@ "type": "tidelift" } ], - "time": "2023-01-14T19:14:44+00:00" + "time": "2023-07-31T08:02:31+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "dev-main", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", "shasum": "" }, "require": { @@ -2573,7 +2687,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2611,7 +2725,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" }, "funding": [ { @@ -2627,20 +2741,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "dev-main", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + "reference": "875e90aeea2777b6f135677f618529449334a612" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", + "reference": "875e90aeea2777b6f135677f618529449334a612", "shasum": "" }, "require": { @@ -2653,7 +2767,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2693,7 +2807,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" }, "funding": [ { @@ -2709,20 +2823,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "dev-main", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", "shasum": "" }, "require": { @@ -2735,7 +2849,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2778,7 +2892,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" }, "funding": [ { @@ -2794,20 +2908,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "dev-main", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "42292d99c55abe617799667f454222c54c60e229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", "shasum": "" }, "require": { @@ -2823,7 +2937,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2862,7 +2976,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" }, "funding": [ { @@ -2878,20 +2992,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-07-28T09:04:16+00:00" }, { "name": "symfony/polyfill-php73", - "version": "dev-main", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9" + "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9", - "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fe2f306d1d9d346a7fee353d0d5012e401e984b5", + "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5", "shasum": "" }, "require": { @@ -2901,7 +3015,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2942,7 +3056,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.28.0" }, "funding": [ { @@ -2958,20 +3072,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-php80", - "version": "dev-main", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", "shasum": "" }, "require": { @@ -2981,7 +3095,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3026,7 +3140,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" }, "funding": [ { @@ -3042,117 +3156,34 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { - "name": "symfony/polyfill-php81", + "name": "symfony/service-contracts", "version": "dev-main", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "cea2eccfcd27ac3deb252bd67f78b9b8ffc4da84" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", - "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/cea2eccfcd27ac3deb252bd67f78b9b8ffc4da84", + "reference": "cea2eccfcd27ac3deb252bd67f78b9b8ffc4da84", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1", + "psr/container": "^1.1|^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" }, "default-branch": true, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" - }, - "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": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "2.5.x-dev", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "suggest": { - "symfony/service-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -3162,7 +3193,10 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3189,7 +3223,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/2.5" + "source": "https://github.com/symfony/service-contracts/tree/main" }, "funding": [ { @@ -3205,38 +3239,38 @@ "type": "tidelift" } ], - "time": "2022-05-30T19:17:29+00:00" + "time": "2024-01-02T14:07:37+00:00" }, { "name": "symfony/string", - "version": "5.4.x-dev", + "version": "6.4.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "0a01071610fd861cc160dfb7e2682ceec66064cb" + "reference": "7a14736fb179876575464e4658fce0c304e8c15b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/0a01071610fd861cc160dfb7e2682ceec66064cb", - "reference": "0a01071610fd861cc160dfb7e2682ceec66064cb", + "url": "https://api.github.com/repos/symfony/string/zipball/7a14736fb179876575464e4658fce0c304e8c15b", + "reference": "7a14736fb179876575464e4658fce0c304e8c15b", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": ">=3.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0|^6.0" + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -3275,7 +3309,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/5.4" + "source": "https://github.com/symfony/string/tree/6.4" }, "funding": [ { @@ -3291,57 +3325,55 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-01-25T09:26:29+00:00" }, { "name": "symfony/translation", - "version": "5.4.x-dev", + "version": "6.4.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "83d487b13b7fb4c0a6ad079f4e4c9b4525e1b695" + "reference": "637c51191b6b184184bbf98937702bcf554f7d04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/83d487b13b7fb4c0a6ad079f4e4c9b4525e1b695", - "reference": "83d487b13b7fb4c0a6ad079f4e4c9b4525e1b695", + "url": "https://api.github.com/repos/symfony/translation/zipball/637c51191b6b184184bbf98937702bcf554f7d04", + "reference": "637c51191b6b184184bbf98937702bcf554f7d04", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/translation-contracts": "^2.3" + "symfony/translation-contracts": "^2.5|^3.0" }, "conflict": { - "symfony/config": "<4.4", - "symfony/console": "<5.3", - "symfony/dependency-injection": "<5.0", - "symfony/http-kernel": "<5.0", - "symfony/twig-bundle": "<5.0", - "symfony/yaml": "<4.4" + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" }, "provide": { - "symfony/translation-implementation": "2.3" + "symfony/translation-implementation": "2.3|3.0" }, "require-dev": { + "nikic/php-parser": "^4.18|^5.0", "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-client-contracts": "^1.1|^2.0|^3.0", - "symfony/http-kernel": "^5.0|^6.0", - "symfony/intl": "^4.4|^5.0|^6.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/service-contracts": "^1.1.2|^2|^3", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/log-implementation": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -3372,7 +3404,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/5.4" + "source": "https://github.com/symfony/translation/tree/6.4" }, "funding": [ { @@ -3388,32 +3420,30 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:32:19+00:00" + "time": "2024-01-29T13:11:52+00:00" }, { "name": "symfony/translation-contracts", - "version": "2.5.x-dev", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "79cebc7b8c230e88868a3a85693c1e30b44f750e" + "reference": "81a962876d52adf8ca7020f49e48f6f88cd5eddd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/79cebc7b8c230e88868a3a85693c1e30b44f750e", - "reference": "79cebc7b8c230e88868a3a85693c1e30b44f750e", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/81a962876d52adf8ca7020f49e48f6f88cd5eddd", + "reference": "81a962876d52adf8ca7020f49e48f6f88cd5eddd", "shasum": "" }, "require": { - "php": ">=7.2.5" - }, - "suggest": { - "symfony/translation-implementation": "" + "php": ">=8.1" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -3423,7 +3453,10 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\Translation\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3450,7 +3483,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/2.5" + "source": "https://github.com/symfony/translation-contracts/tree/main" }, "funding": [ { @@ -3466,7 +3499,7 @@ "type": "tidelift" } ], - "time": "2022-10-27T07:55:40+00:00" + "time": "2024-01-02T14:07:37+00:00" }, { "name": "voku/portable-ascii", @@ -3710,50 +3743,6 @@ ], "time": "2022-06-21T18:19:50+00:00" }, - { - "name": "christophwurst/nextcloud", - "version": "v22.1.1", - "source": { - "type": "git", - "url": "https://github.com/ChristophWurst/nextcloud_composer.git", - "reference": "8bb086cd016128b5ef8353662fd1852db3248d1e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ChristophWurst/nextcloud_composer/zipball/8bb086cd016128b5ef8353662fd1852db3248d1e", - "reference": "8bb086cd016128b5ef8353662fd1852db3248d1e", - "shasum": "" - }, - "require": { - "php": "^7.3 || ~8.0.0", - "psr/container": "^1.0", - "psr/event-dispatcher": "^1.0", - "psr/log": "^1.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "23.0.0-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "AGPL-3.0-or-later" - ], - "authors": [ - { - "name": "Christoph Wurst", - "email": "christoph@winzerhof-wurst.at" - } - ], - "description": "Composer package containing Nextcloud's public API (classes, interfaces)", - "support": { - "issues": "https://github.com/ChristophWurst/nextcloud_composer/issues", - "source": "https://github.com/ChristophWurst/nextcloud_composer/tree/v22.1.1" - }, - "abandoned": "nextcloud/ocp", - "time": "2021-11-11T14:01:42+00:00" - }, { "name": "composer/package-versions-deprecated", "version": "dev-master", @@ -3834,12 +3823,12 @@ "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "url": "https://api.github.com/repos/composer/pcre/zipball/00104306927c7a0919b4ced2aaa6782c1e61a3c9", + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9", "shasum": "" }, "require": { @@ -3882,7 +3871,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.0" + "source": "https://github.com/composer/pcre/tree/3.1.1" }, "funding": [ { @@ -3898,7 +3887,7 @@ "type": "tidelift" } ], - "time": "2022-11-17T09:50:14+00:00" + "time": "2023-10-11T07:11:09+00:00" }, { "name": "composer/semver", @@ -3906,12 +3895,12 @@ "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "fa1ec24f0ab1efe642671ec15c51a3ab879f59bf" + "reference": "1d09200268e7d1052ded8e5da9c73c96a63d18f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/fa1ec24f0ab1efe642671ec15c51a3ab879f59bf", - "reference": "fa1ec24f0ab1efe642671ec15c51a3ab879f59bf", + "url": "https://api.github.com/repos/composer/semver/zipball/1d09200268e7d1052ded8e5da9c73c96a63d18f5", + "reference": "1d09200268e7d1052ded8e5da9c73c96a63d18f5", "shasum": "" }, "require": { @@ -3980,7 +3969,7 @@ "type": "tidelift" } ], - "time": "2023-01-13T15:47:53+00:00" + "time": "2023-08-31T12:20:31+00:00" }, { "name": "composer/xdebug-handler", @@ -4086,81 +4075,52 @@ "time": "2019-12-04T15:06:13+00:00" }, { - "name": "doctrine/annotations", - "version": "1.14.x-dev", + "name": "doctrine/deprecations", + "version": "1.1.x-dev", "source": { "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "ad785217c1e9555a7d6c6c8c9f406395a5e2882b" + "url": "https://github.com/doctrine/deprecations.git", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/ad785217c1e9555a7d6c6c8c9f406395a5e2882b", - "reference": "ad785217c1e9555a7d6c6c8c9f406395a5e2882b", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", "shasum": "" }, "require": { - "doctrine/lexer": "^1 || ^2", - "ext-tokenizer": "*", - "php": "^7.1 || ^8.0", - "psr/cache": "^1 || ^2 || ^3" + "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "~1.4.10 || ^1.8.0", + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^4.4 || ^5.4 || ^6", - "vimeo/psalm": "^4.10" + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" }, "suggest": { - "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, "default-branch": true, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "https://www.doctrine-project.org/projects/annotations.html", - "keywords": [ - "annotations", - "docblock", - "parser" - ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", "support": { - "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/1.14.2" + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" }, - "time": "2022-12-15T06:48:22+00:00" + "time": "2023-09-27T20:04:15+00:00" }, { "name": "felixfbecker/advanced-json-rpc", @@ -4264,107 +4224,18 @@ }, "time": "2022-06-19T17:15:06+00:00" }, - { - "name": "friendsofphp/php-cs-fixer", - "version": "v3.13.2", - "source": { - "type": "git", - "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "3952f08a81bd3b1b15e11c3de0b6bf037faa8496" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/3952f08a81bd3b1b15e11c3de0b6bf037faa8496", - "reference": "3952f08a81bd3b1b15e11c3de0b6bf037faa8496", - "shasum": "" - }, - "require": { - "composer/semver": "^3.2", - "composer/xdebug-handler": "^3.0.3", - "doctrine/annotations": "^1.13", - "ext-json": "*", - "ext-tokenizer": "*", - "php": "^7.4 || ^8.0", - "sebastian/diff": "^4.0", - "symfony/console": "^5.4 || ^6.0", - "symfony/event-dispatcher": "^5.4 || ^6.0", - "symfony/filesystem": "^5.4 || ^6.0", - "symfony/finder": "^5.4 || ^6.0", - "symfony/options-resolver": "^5.4 || ^6.0", - "symfony/polyfill-mbstring": "^1.23", - "symfony/polyfill-php80": "^1.25", - "symfony/polyfill-php81": "^1.25", - "symfony/process": "^5.4 || ^6.0", - "symfony/stopwatch": "^5.4 || ^6.0" - }, - "require-dev": { - "justinrainbow/json-schema": "^5.2", - "keradus/cli-executor": "^2.0", - "mikey179/vfsstream": "^1.6.10", - "php-coveralls/php-coveralls": "^2.5.2", - "php-cs-fixer/accessible-object": "^1.1", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", - "phpspec/prophecy": "^1.15", - "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.5", - "phpunitgoodpractices/polyfill": "^1.6", - "phpunitgoodpractices/traits": "^1.9.2", - "symfony/phpunit-bridge": "^6.0", - "symfony/yaml": "^5.4 || ^6.0" - }, - "suggest": { - "ext-dom": "For handling output formats in XML", - "ext-mbstring": "For handling non-UTF8 characters." - }, - "bin": [ - "php-cs-fixer" - ], - "type": "application", - "autoload": { - "psr-4": { - "PhpCsFixer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Dariusz Rumiński", - "email": "dariusz.ruminski@gmail.com" - } - ], - "description": "A tool to automatically fix PHP code style", - "support": { - "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.13.2" - }, - "funding": [ - { - "url": "https://github.com/keradus", - "type": "github" - } - ], - "time": "2023-01-02T23:53:50+00:00" - }, { "name": "netresearch/jsonmapper", - "version": "v4.1.0", + "version": "v4.4.0", "source": { "type": "git", "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f" + "reference": "18133a2d8c24e10e58e02b700308ed3a4a60c97f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/18133a2d8c24e10e58e02b700308ed3a4a60c97f", + "reference": "18133a2d8c24e10e58e02b700308ed3a4a60c97f", "shasum": "" }, "require": { @@ -4375,7 +4246,7 @@ "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0", + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", "squizlabs/php_codesniffer": "~3.5" }, "type": "library", @@ -4400,27 +4271,27 @@ "support": { "email": "cweiske@cweiske.de", "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0" + "source": "https://github.com/cweiske/jsonmapper/tree/v4.4.0" }, - "time": "2022-12-08T20:46:14+00:00" + "time": "2024-01-28T07:31:37+00:00" }, { "name": "nextcloud/coding-standard", - "version": "v1.0.0", + "version": "v1.1.1", "source": { "type": "git", "url": "https://github.com/nextcloud/coding-standard.git", - "reference": "f3d1f9375e89c605deb1734f59a9f51ecbe80578" + "reference": "55def702fb9a37a219511e1d8c6fe8e37164c1fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/f3d1f9375e89c605deb1734f59a9f51ecbe80578", - "reference": "f3d1f9375e89c605deb1734f59a9f51ecbe80578", + "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/55def702fb9a37a219511e1d8c6fe8e37164c1fb", + "reference": "55def702fb9a37a219511e1d8c6fe8e37164c1fb", "shasum": "" }, "require": { - "friendsofphp/php-cs-fixer": "^3.2", - "php": "^7.3|^8.0" + "php": "^7.3|^8.0", + "php-cs-fixer/shim": "^3.17" }, "type": "library", "autoload": { @@ -4441,9 +4312,52 @@ "description": "Nextcloud coding standards for the php cs fixer", "support": { "issues": "https://github.com/nextcloud/coding-standard/issues", - "source": "https://github.com/nextcloud/coding-standard/tree/v1.0.0" + "source": "https://github.com/nextcloud/coding-standard/tree/v1.1.1" }, - "time": "2021-11-10T08:44:10+00:00" + "time": "2023-06-01T12:05:01+00:00" + }, + { + "name": "nextcloud/ocp", + "version": "v26.0.9", + "source": { + "type": "git", + "url": "https://github.com/nextcloud-deps/ocp.git", + "reference": "43bc0a0267d97b02966e0270e00e9d51192564af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/43bc0a0267d97b02966e0270e00e9d51192564af", + "reference": "43bc0a0267d97b02966e0270e00e9d51192564af", + "shasum": "" + }, + "require": { + "php": "^7.4 || ~8.0 || ~8.1", + "psr/container": "^1.1.1", + "psr/event-dispatcher": "^1.0", + "psr/log": "^1.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "26.0.0-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "AGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Christoph Wurst", + "email": "christoph@winzerhof-wurst.at" + } + ], + "description": "Composer package containing Nextcloud's public API (classes, interfaces)", + "support": { + "issues": "https://github.com/nextcloud-deps/ocp/issues", + "source": "https://github.com/nextcloud-deps/ocp/tree/v26.0.9" + }, + "time": "2023-11-10T00:31:54+00:00" }, { "name": "nikic/php-parser", @@ -4451,12 +4365,12 @@ "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" + "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999", + "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999", "shasum": "" }, "require": { @@ -4467,7 +4381,6 @@ "ircmaxell/php-yacc": "^0.0.7", "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, - "default-branch": true, "bin": [ "bin/php-parse" ], @@ -4498,9 +4411,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" + "source": "https://github.com/nikic/PHP-Parser/tree/4.x" }, - "time": "2023-01-16T22:05:37+00:00" + "time": "2023-12-10T21:03:43+00:00" }, { "name": "openlss/lib-array2xml", @@ -4555,6 +4468,58 @@ }, "time": "2019-03-29T20:06:56+00:00" }, + { + "name": "php-cs-fixer/shim", + "version": "v3.48.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/shim.git", + "reference": "bf0c65f1b2d943306b3574d42ae806578ce9cc3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/bf0c65f1b2d943306b3574d42ae806578ce9cc3e", + "reference": "bf0c65f1b2d943306b3574d42ae806578ce9cc3e", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "replace": { + "friendsofphp/php-cs-fixer": "self.version" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer", + "php-cs-fixer.phar" + ], + "type": "application", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "support": { + "issues": "https://github.com/PHP-CS-Fixer/shim/issues", + "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.48.0" + }, + "time": "2024-01-19T21:45:09+00:00" + }, { "name": "phpdocumentor/reflection-common", "version": "dev-master", @@ -4614,19 +4579,19 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "566af9fb94c556de91562fcfcbc392f66680111b" + "reference": "0b48e261e0a7bcbf1bd118ffd08e52ea6cfd34af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/566af9fb94c556de91562fcfcbc392f66680111b", - "reference": "566af9fb94c556de91562fcfcbc392f66680111b", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/0b48e261e0a7bcbf1bd118ffd08e52ea6cfd34af", + "reference": "0b48e261e0a7bcbf1bd118ffd08e52ea6cfd34af", "shasum": "" }, "require": { "ext-filter": "*", "php": "^7.2 || ^8.0", "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "1.x-dev@dev", + "phpdocumentor/type-resolver": "^1.7", "phpstan/phpdoc-parser": "^1.7", "webmozart/assert": "^1.9.1" }, @@ -4662,7 +4627,7 @@ }, { "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" + "email": "opensource@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.", @@ -4670,7 +4635,7 @@ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" }, - "time": "2022-11-19T20:28:46+00:00" + "time": "2024-01-26T20:33:24+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -4678,15 +4643,16 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "06f36c92b434ac686de06b6563e88046943bccbe" + "reference": "bc3dc91a5e9b14aa06d1d9e90647c5c5a2cc5353" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/06f36c92b434ac686de06b6563e88046943bccbe", - "reference": "06f36c92b434ac686de06b6563e88046943bccbe", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/bc3dc91a5e9b14aa06d1d9e90647c5c5a2cc5353", + "reference": "bc3dc91a5e9b14aa06d1d9e90647c5c5a2cc5353", "shasum": "" }, "require": { + "doctrine/deprecations": "^1.0", "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.0", "phpstan/phpdoc-parser": "^1.13" @@ -4728,26 +4694,28 @@ "issues": "https://github.com/phpDocumentor/TypeResolver/issues", "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.x" }, - "time": "2022-12-16T10:25:14+00:00" + "time": "2024-01-18T19:15:27+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "1.15.3", + "version": "1.25.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "61800f71a5526081d1b5633766aa88341f1ade76" + "reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/61800f71a5526081d1b5633766aa88341f1ade76", - "reference": "61800f71a5526081d1b5633766aa88341f1ade76", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bd84b629c8de41aa2ae82c067c955e06f1b00240", + "reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^1.5", @@ -4771,58 +4739,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.15.3" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.25.0" }, - "time": "2022-12-20T20:56:55+00:00" - }, - { - "name": "psr/cache", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], - "support": { - "source": "https://github.com/php-fig/cache/tree/master" - }, - "time": "2016-08-06T20:24:11+00:00" + "time": "2024-01-04T17:06:16+00:00" }, { "name": "psr/event-dispatcher", @@ -4830,12 +4749,12 @@ "source": { "type": "git", "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "e275e2d67d53964a3f13e056886ecd769edee021" + "reference": "977ffcf551e3bfb73d90aac3e8e1583fd8d2f89a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/e275e2d67d53964a3f13e056886ecd769edee021", - "reference": "e275e2d67d53964a3f13e056886ecd769edee021", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/977ffcf551e3bfb73d90aac3e8e1583fd8d2f89a", + "reference": "977ffcf551e3bfb73d90aac3e8e1583fd8d2f89a", "shasum": "" }, "require": { @@ -4873,22 +4792,22 @@ "psr-14" ], "support": { - "source": "https://github.com/php-fig/event-dispatcher/tree/master" + "source": "https://github.com/php-fig/event-dispatcher" }, - "time": "2022-06-29T17:22:39+00:00" + "time": "2023-09-22T11:10:57+00:00" }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", "shasum": "" }, "require": { @@ -4933,7 +4852,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0" }, "funding": [ { @@ -4941,428 +4860,7 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" - }, - { - "name": "symfony/event-dispatcher", - "version": "5.4.x-dev", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "abf49cc084c087d94b4cb939c3f3672971784e0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/abf49cc084c087d94b4cb939c3f3672971784e0c", - "reference": "abf49cc084c087d94b4cb939c3f3672971784e0c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher-contracts": "^2|^3", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/dependency-injection": "<4.4" - }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/stopwatch": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/5.4" - }, - "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": "2023-01-01T08:32:19+00:00" - }, - { - "name": "symfony/event-dispatcher-contracts", - "version": "2.5.x-dev", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "f98b54df6ad059855739db6fcbc2d36995283fe1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/f98b54df6ad059855739db6fcbc2d36995283fe1", - "reference": "f98b54df6ad059855739db6fcbc2d36995283fe1", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/event-dispatcher": "^1" - }, - "suggest": { - "symfony/event-dispatcher-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/2.5" - }, - "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": "2022-01-02T09:53:40+00:00" - }, - { - "name": "symfony/filesystem", - "version": "5.4.x-dev", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "648bfaca6a494f3e22378123bcee2894045dc9d8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/648bfaca6a494f3e22378123bcee2894045dc9d8", - "reference": "648bfaca6a494f3e22378123bcee2894045dc9d8", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides basic utilities for the filesystem", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/filesystem/tree/5.4" - }, - "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": "2023-01-14T19:14:44+00:00" - }, - { - "name": "symfony/options-resolver", - "version": "5.4.x-dev", - "source": { - "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "b03c99236445492f20c61666e8f7e5d388b078e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b03c99236445492f20c61666e8f7e5d388b078e5", - "reference": "b03c99236445492f20c61666e8f7e5d388b078e5", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php73": "~1.0", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an improved replacement for the array_replace PHP function", - "homepage": "https://symfony.com", - "keywords": [ - "config", - "configuration", - "options" - ], - "support": { - "source": "https://github.com/symfony/options-resolver/tree/5.4" - }, - "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": "2023-01-01T08:32:19+00:00" - }, - { - "name": "symfony/process", - "version": "5.4.x-dev", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "c5ba874c9b636dbccf761e22ce750e88ec3f55e1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/c5ba874c9b636dbccf761e22ce750e88ec3f55e1", - "reference": "c5ba874c9b636dbccf761e22ce750e88ec3f55e1", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/5.4" - }, - "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": "2023-01-01T08:32:19+00:00" - }, - { - "name": "symfony/stopwatch", - "version": "5.4.x-dev", - "source": { - "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "bd2b066090fd6a67039371098fa25a84cb2679ec" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/bd2b066090fd6a67039371098fa25a84cb2679ec", - "reference": "bd2b066090fd6a67039371098fa25a84cb2679ec", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/service-contracts": "^1|^2|^3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a way to profile code", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/stopwatch/tree/5.4" - }, - "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": "2023-01-01T08:32:19+00:00" + "time": "2023-05-07T05:35:17+00:00" }, { "name": "vimeo/psalm", @@ -5593,5 +5091,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.6.0" } diff --git a/lib/Adapter/Groups/NextcloudGroupAdapter.php b/lib/Adapter/Groups/NextcloudGroupAdapter.php deleted file mode 100644 index dd6c0fb..0000000 --- a/lib/Adapter/Groups/NextcloudGroupAdapter.php +++ /dev/null @@ -1,110 +0,0 @@ -logger = $container->get(LoggerInterface::class); - $this->userManager = $container->get(IUserManager::class); - $this->request = $container->get(IRequest::class); - } - - /** - * Transform an NC group into a SCIM group - */ - public function getCoreGroup(?IGroup $ncGroup): ?CoreGroup - { - $this->logger->debug( - "[" . NextcloudGroupAdapter::class . "] entering getCoreGroup() method" - ); - - $baseUrl = $this->request->getServerProtocol() . "://" . $this->request->getServerHost() . "/index.php/apps/scimserviceprovider"; - - if (!isset($ncGroup)) { - $this->logger->error( - "[" . NextcloudGroupAdapter::class . "] passed NC group in getCoreGroup() method is null" - ); - - return null; - } - - $coreGroup = new CoreGroup(); - $coreGroup->setId($ncGroup->getGID()); - $coreGroup->setDisplayName($ncGroup->getDisplayName()); - - $ncGroupMembers = $ncGroup->getUsers(); - - if (isset($ncGroupMembers) && !empty($ncGroupMembers)) { - $coreGroupMembers = []; - - foreach ($ncGroupMembers as $ncGroupMember) { - $coreGroupMember = new MultiValuedAttribute(); - $coreGroupMember->setValue($ncGroupMember->getUID()); - $coreGroupMember->setRef($baseUrl . "/Users/" . $ncGroupMember->getUID()); - $coreGroupMember->setDisplay($ncGroupMember->getDisplayName()); - - $coreGroupMembers[] = $coreGroupMember; - } - - $coreGroup->setMembers($coreGroupMembers); - } - - return $coreGroup; - } - - /** - * Transform a SCIM group into an NC group - * - * Note: the second parameter is needed, since we can't instantiate an NC group - * ourselves and need to receive an instance, passed from somewhere - */ - public function getNCGroup(?CoreGroup $coreGroup, IGroup $ncGroup): ?IGroup - { - $this->logger->debug( - "[" . NextcloudGroupAdapter::class . "] entering getNCGroup() method" - ); - - if (!isset($coreGroup) || !isset($ncGroup)) { - $this->logger->error( - "[" . NextcloudGroupAdapter::class . "] passed Core Group in getNCGroup() method is null" - ); - - return null; - } - - $ncGroup->setDisplayName($coreGroup->getDisplayName()); - - if ($coreGroup->getMembers() !== null && !empty($coreGroup->getMembers())) { - foreach ($coreGroup->getMembers() as $coreGroupMember) { - // If user with this uid exists, then add it as a member of the group - if ($coreGroupMember->getValue() !== null && !empty($coreGroupMember->getValue())) { - if ($this->userManager->userExists($coreGroupMember->getValue())) { - $ncGroup->addUser($this->userManager->get($coreGroupMember->getValue())); - } - } - } - } - - return $ncGroup; - } -} diff --git a/lib/Adapter/Users/NextcloudUserAdapter.php b/lib/Adapter/Users/NextcloudUserAdapter.php deleted file mode 100644 index 52609cd..0000000 --- a/lib/Adapter/Users/NextcloudUserAdapter.php +++ /dev/null @@ -1,125 +0,0 @@ -logger = $container->get(LoggerInterface::class); - $this->config = $container->get(IConfig::class); - $this->userManager = $container->get(IUserManager::class); - $this->secureRandom = $container->get(ISecureRandom::class); - } - - /** - * Transform an NC User into a SCIM user - */ - public function getCoreUser(?IUser $ncUser): ?CoreUser - { - $this->logger->debug( - "[" . NextcloudUserAdapter::class . "] entering getCoreUser() method" - ); - - if (!isset($ncUser)) { - $this->logger->error( - "[" . NextcloudUserAdapter::class . "] passed NC user in getCoreUser() method is null" - ); - - return null; - } - - $coreUser = new CoreUser(); - $coreUser->setId($ncUser->getUID()); - - $coreUserName = new Name(); - $coreUserName->setFormatted($ncUser->getDisplayName()); - $coreUser->setName($coreUserName); - - $coreUser->setUserName($ncUser->getUID()); - $coreUser->setDisplayName($ncUser->getDisplayName()); - $coreUser->setActive($ncUser->isEnabled()); - - $ncUserExternalId = $this->config->getUserValue($ncUser->getUID(), Application::APP_ID, 'externalId', ''); - $coreUser->setExternalId($ncUserExternalId); - - if ($ncUser->getEMailAddress() !== null && !empty($ncUser->getEMailAddress())) { - $coreUserEmail = new MultiValuedAttribute(); - $coreUserEmail->setValue($ncUser->getEMailAddress()); - $coreUserEmail->setPrimary(true); - - $coreUser->setEmails(array($coreUserEmail)); - } - - return $coreUser; - } - - /** - * Transform a SCIM user into an NC User - * - * Note: we need the second parameter, since we can't instantiate an NC user in PHP - * ourselves and need to receive an instance that we can populate with data from the SCIM user - */ - public function getNCUser(?CoreUser $coreUser, IUser $ncUser): ?IUser - { - $this->logger->debug( - "[" . NextcloudUserAdapter::class . "] entering getNCUser() method" - ); - - if (!isset($coreUser) || !isset($ncUser)) { - $this->logger->error( - "[" . NextcloudUserAdapter::class . "] passed Core User in getNCUser() method is null" - ); - - return null; - } - - if ($coreUser->getDisplayName() !== null && !empty($coreUser->getDisplayName())) { - $ncUser->setDisplayName($coreUser->getDisplayName()); - } - - if ($coreUser->getActive() !== null) { - $ncUser->setEnabled($coreUser->getActive()); - } - - if ($coreUser->getExternalId() !== null && !empty($coreUser->getExternalId())) { - $this->config->setUserValue($ncUser->getUID(), Application::APP_ID, 'externalId', $coreUser->getExternalId()); - } - - if ($coreUser->getEmails() !== null && !empty($coreUser->getEmails())) { - // Here, we use the first email of the SCIM user to set as the NC user's email - // TODO: is this ok or should we rather first iterate and search for a primary email of the SCIM user - if ($coreUser->getEmails()[0] !== null && !empty($coreUser->getEmails()[0])) { - if ($coreUser->getEmails()[0]->getValue() !== null && !empty($coreUser->getEmails()[0]->getValue())) { - $ncUser->setEMailAddress($coreUser->getEmails()[0]->getValue()); - } - } - } - - return $ncUser; - } -} diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 4220a8c..4a9d090 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -2,160 +2,43 @@ namespace OCA\SCIMServiceProvider\AppInfo; -use Error; -use OCA\SCIMServiceProvider\Adapter\Groups\NextcloudGroupAdapter; -use OCA\SCIMServiceProvider\Adapter\Users\NextcloudUserAdapter; -use OCA\SCIMServiceProvider\Controller\GroupBearerController; -use OCA\SCIMServiceProvider\Controller\GroupController; -use OCA\SCIMServiceProvider\Controller\UserBearerController; -use OCA\SCIMServiceProvider\Controller\UserController; -use OCA\SCIMServiceProvider\DataAccess\Groups\NextcloudGroupDataAccess; -use OCA\SCIMServiceProvider\DataAccess\Users\NextcloudUserDataAccess; -use OCA\SCIMServiceProvider\Middleware\BearerAuthMiddleware; -use OCA\SCIMServiceProvider\Repositories\Groups\NextcloudGroupRepository; -use OCA\SCIMServiceProvider\Repositories\Users\NextcloudUserRepository; -use OCA\SCIMServiceProvider\Service\GroupService; -use OCA\SCIMServiceProvider\Service\SCIMGroup; -use OCA\SCIMServiceProvider\Service\SCIMUser; -use OCA\SCIMServiceProvider\Service\UserService; -use OCA\SCIMServiceProvider\Util\Authentication\BearerAuthenticator; +use OCA\SCIMServiceProvider\Middleware\AuthMiddleware; +use OCA\SCIMServiceProvider\Middleware\ContentTypeMiddleware; +use OCA\SCIMServiceProvider\Middleware\ErrorMiddleware; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; -use OCP\IConfig; -use OCP\IGroupManager; -use OCP\IRequest; -use OCP\IUserManager; -use OCP\Security\ISecureRandom; -use Opf\Util\Util; -use Psr\Container\ContainerInterface; /** * The main entry point of the entire application */ -class Application extends App implements IBootstrap -{ - public const APP_ID = 'scimserviceprovider'; +class Application extends App implements IBootstrap { + public const APP_ID = 'scimserviceprovider'; + public const APP_NAME = 'SCIM'; - public function __construct(array $urlParams = []) - { - parent::__construct(self::APP_ID, $urlParams); - } + public function __construct() { + parent::__construct(self::APP_ID); + } - /** - * This method is used for registering services, needed as dependencies via dependency injection (DI) - * - * Note: "service" here means simply a class that is needed as a dependency somewhere - * and needs to be injected as such via a DI container (as per PSR-11) - */ - public function register(IRegistrationContext $context): void - { - require realpath(dirname(__DIR__) . '/../vendor/autoload.php'); - $config = require dirname(__DIR__) . '/Config/config.php'; - $context->registerService('SCIMUser', function(ContainerInterface $c) { - return new SCIMUser( - $c->get(IUserManager::class), - $c->get(IConfig::class) - ); - }); + /** + * This method is used for registering services, needed as dependencies via dependency injection (DI) + * + * Note: "service" here means simply a class that is needed as a dependency somewhere + * and needs to be injected as such via a DI container (as per PSR-11) + */ + public function register(IRegistrationContext $context): void { + require_once(__DIR__ . '/../../vendor/autoload.php'); + $context->registerMiddleware(ErrorMiddleware::class); + $context->registerMiddleware(AuthMiddleware::class); + $context->registerMiddleware(ContentTypeMiddleware::class); + } - $context->registerService(UserService::class, function(ContainerInterface $c) { - return new UserService($c); - }); - - $context->registerService(GroupService::class, function(ContainerInterface $c) { - return new GroupService($c); - }); - - $context->registerService('UserRepository', function(ContainerInterface $c) { - return new NextcloudUserRepository($c); - }); - - $context->registerService('UserAdapter', function(ContainerInterface $c) { - return new NextcloudUserAdapter($c); - }); - - $context->registerService('UserDataAccess', function(ContainerInterface $c) { - return new NextcloudUserDataAccess($c); - }); - - - $context->registerService('SCIMGroup', function(ContainerInterface $c) { - return new SCIMGroup( - $c->get(IGroupManager::class) - ); - }); - - $context->registerService('GroupRepository', function(ContainerInterface $c) { - return new NextcloudGroupRepository($c); - }); - - $context->registerService('GroupAdapter', function(ContainerInterface $c) { - return new NextcloudGroupAdapter($c); - }); - - $context->registerService('GroupDataAccess', function(ContainerInterface $c) { - return new NextcloudGroupDataAccess($c); - }); - - if (isset($config['auth_type']) && !empty($config['auth_type']) && (strcmp($config['auth_type'], 'bearer') === 0)) { - // If the auth_type is set to "bearer", then use Bearer token endpoints - // For bearer tokens, we also need to register the bearer token auth middleware - $context->registerService(BearerAuthenticator::class, function(ContainerInterface $c) { - return new BearerAuthenticator($c); - }); - - $context->registerService(BearerAuthMiddleware::class, function(ContainerInterface $c) { - return new BearerAuthMiddleware($c); - }); - - $context->registerMiddleware(BearerAuthMiddleware::class); - - $context->registerService(UserBearerController::class, function (ContainerInterface $c) { - return new UserBearerController( - self::APP_ID, - $c->get(IRequest::class), - $c->get(UserService::class) - ); - }); - - $context->registerService(GroupBearerController::class, function (ContainerInterface $c) { - return new GroupBearerController( - self::APP_ID, - $c->get(IRequest::class), - $c->get(GroupService::class) - ); - }); - } else if (!isset($config['auth_type']) || empty($config['auth_type']) || (strcmp($config['auth_type'], 'basic') === 0)) { - // Otherwise, if auth_type is set to "basic" or if it's not set at all, use Basic auth - $context->registerService(UserController::class, function (ContainerInterface $c) { - return new UserController( - self::APP_ID, - $c->get(IRequest::class), - $c->get(UserService::class) - ); - }); - - $context->registerService(GroupController::class, function (ContainerInterface $c) { - return new GroupController( - self::APP_ID, - $c->get(IRequest::class), - $c->get(GroupService::class) - ); - }); - } else { - // In the case of any other auth_type value, complain with an error message - throw new Error("Unknown auth type was set in config file"); - } - } - - /** - * This method is called for starting (i.e., booting) the application - * - * Note: here the method body is empty, since we don't need to do any extra work in it - */ - public function boot(IBootContext $context): void - { - } + /** + * This method is called for starting (i.e., booting) the application + * + * Note: here the method body is empty, since we don't need to do any extra work in it + */ + public function boot(IBootContext $context): void { + } } diff --git a/lib/Config/config.php b/lib/Config/config.php deleted file mode 100644 index b058570..0000000 --- a/lib/Config/config.php +++ /dev/null @@ -1,14 +0,0 @@ - 'bearer', - - // Config values for JWTs - 'jwt' => [ - 'secret' => 'secret' - ] -]; diff --git a/lib/Controller/GroupBearerController.php b/lib/Controller/GroupBearerController.php deleted file mode 100644 index 8a42e66..0000000 --- a/lib/Controller/GroupBearerController.php +++ /dev/null @@ -1,99 +0,0 @@ -groupService = $groupService; - } - - /** - * @NoCSRFRequired - * @PublicPage - * - * @param string $filter - * @return SCIMListResponse - * returns a list of groups and their data - */ - public function index(string $filter = ''): SCIMListResponse - { - return $this->groupService->getAll($filter); - } - - /** - * @NoCSRFRequired - * @PublicPage - * - * gets group info - * - * @param string $id - * @return SCIMJSONResponse - */ - // TODO: Add filtering support here as well - public function show(string $id): SCIMJSONResponse - { - return $this->groupService->getOneById($id); - } - - /** - * @NoCSRFRequired - * @PublicPage - * - * @param string $displayName - * @param array $members - * @return SCIMJSONResponse - */ - public function create(string $displayName = '', array $members = []): SCIMJSONResponse - { - return $this->groupService->create($displayName, $members); - } - - /** - * @NoCSRFRequired - * @PublicPage - * - * @param string $id - * - * @param string $displayName - * @param array $members - * @return SCIMJSONResponse - */ - public function update(string $id, string $displayName = '', array $members = []): SCIMJSONResponse - { - return $this->groupService->update($id, $displayName, $members); - } - - /** - * @NoCSRFRequired - * @PublicPage - * - * @param string $id - * @return Response - */ - public function destroy(string $id): Response - { - return $this->groupService->destroy($id); - } -} diff --git a/lib/Controller/GroupController.php b/lib/Controller/GroupController.php index e9eccde..397e194 100644 --- a/lib/Controller/GroupController.php +++ b/lib/Controller/GroupController.php @@ -4,91 +4,107 @@ declare(strict_types=1); namespace OCA\SCIMServiceProvider\Controller; +use OCA\SCIMServiceProvider\Responses\SCIMJSONResponse; +use OCA\SCIMServiceProvider\Responses\SCIMListResponse; +use OCA\SCIMServiceProvider\Service\GroupService; +use OCA\SCIMServiceProvider\Util\Util; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http\Response; use OCP\IRequest; -use OCA\SCIMServiceProvider\Responses\SCIMListResponse; -use OCA\SCIMServiceProvider\Responses\SCIMJSONResponse; -use OCA\SCIMServiceProvider\Service\GroupService; +use Opf\Models\SCIM\Standard\Groups\CoreGroup; -class GroupController extends ApiController -{ - /** @var GroupService */ - private $groupService; +class GroupController extends ApiController { + private GroupService $groupService; - public function __construct( - string $appName, - IRequest $request, - GroupService $groupService - ) { - parent::__construct( - $appName, - $request - ); + public function __construct( + string $appName, + IRequest $request, + GroupService $groupService + ) { + parent::__construct($appName, $request); - $this->groupService = $groupService; - } + $this->groupService = $groupService; + } - /** - * @NoCSRFRequired - * - * @param string $filter - * @return SCIMListResponse - * returns a list of groups and their data - */ - public function index(string $filter = ''): SCIMListResponse - { - return $this->groupService->getAll($filter); - } + /** + * @NoCSRFRequired + * @PublicPage + * + * @return SCIMListResponse + */ + public function index(): SCIMJSONResponse { + $params = $this->request->getParams(); + $params = Util::parsePagination($params); + $scimGroups = $this->groupService->getAll($params["startIndex"], $params["count"]); + $list = []; + foreach ($scimGroups as $scimGroup) { + $list[] = $scimGroup->toSCIM(false, Util::getBaseUrl($this->request)); + } + $total = $this->groupService->countAll(); + return new SCIMListResponse($list, $params["startIndex"], $total); + } - /** - * @NoCSRFRequired - * - * gets group info - * - * @param string $id - * @return SCIMJSONResponse - */ - // TODO: Add filtering support here as well - public function show(string $id): SCIMJSONResponse - { - return $this->groupService->getOneById($id); - } + /** + * @NoCSRFRequired + * @PublicPage + * + * @param string $id + * @return SCIMJSONResponse + */ + // TODO: Add filtering support here as well + public function show(string $id): SCIMJSONResponse { + $scimGroup = $this->groupService->get($id); + return new SCIMJSONResponse($scimGroup->toSCIM(false, Util::getBaseUrl($this->request))); + } - /** - * @NoCSRFRequired - * - * @param string $displayName - * @param array $members - * @return SCIMJSONResponse - */ - public function create(string $displayName = '', array $members = []): SCIMJSONResponse - { - return $this->groupService->create($displayName, $members); - } + /** + * @NoCSRFRequired + * @PublicPage + * + * @param string $displayName + * @param array $members + * @return SCIMJSONResponse + */ + // public function create(string $displayName, array $members = []): SCIMJSONResponse + public function create(): SCIMJSONResponse { + $params = $this->request->getParams(); - /** - * @NoCSRFRequired - * - * @param string $id - * - * @param string $displayName - * @param array $members - * @return SCIMJSONResponse - */ - public function update(string $id, string $displayName = '', array $members = []): SCIMJSONResponse - { - return $this->groupService->update($id, $displayName, $members); - } + $scimGroup = new CoreGroup(); + $scimGroup->fromSCIM($params); - /** - * @NoCSRFRequired - * - * @param string $id - * @return Response - */ - public function destroy(string $id): Response - { - return $this->groupService->destroy($id); - } + $scimGroup = $this->groupService->create($scimGroup); + return new SCIMJSONResponse($scimGroup->toSCIM(false, Util::getBaseUrl($this->request)), 201); + } + + /** + * @NoCSRFRequired + * @PublicPage + * + * @param string $id + * @return SCIMJSONResponse + */ + public function update(string $id): SCIMJSONResponse { + $params = $this->request->getParams(); + + $scimGroup = new CoreGroup(); + $scimGroup->fromSCIM($params); + + $scimGroup = $this->groupService->update($id, $scimGroup); + return new SCIMJSONResponse($scimGroup->toSCIM(false, Util::getBaseUrl($this->request))); + } + + /** + * @NoCSRFRequired + * @PublicPage + * + * @param string $id + * @return Response + */ + public function destroy(string $id): Response { + $this->groupService->destroy($id); + + $response = new Response(); + $response->setStatus(204); + return $response; + } } diff --git a/lib/Controller/ServiceProviderConfigurationController.php b/lib/Controller/ServiceProviderConfigurationController.php index 7a95901..48171d1 100644 --- a/lib/Controller/ServiceProviderConfigurationController.php +++ b/lib/Controller/ServiceProviderConfigurationController.php @@ -12,50 +12,46 @@ use OCP\IRequest; use Opf\Util\Util as SCIMUtil; use Psr\Log\LoggerInterface; -class ServiceProviderConfigurationController extends ApiController -{ - /** @var LoggerInterface */ - private $logger; +class ServiceProviderConfigurationController extends ApiController { + private LoggerInterface $logger; - public function __construct(string $appName, - IRequest $request, - LoggerInterface $logger) { - parent::__construct($appName, - $request); - $this->logger = $logger; - } + public function __construct( + string $appName, + IRequest $request, + LoggerInterface $logger, + ) { + parent::__construct($appName, $request); + $this->logger = $logger; + } - /** - * @NoCSRFRequired - * @PublicPage - */ - public function resourceTypes(): SCIMListResponse - { - $baseUrl = - $this->request->getServerProtocol() . "://" - . $this->request->getServerHost() . "/" - . Util::SCIM_APP_URL_PATH; - $resourceTypes = SCIMUtil::getResourceTypes($baseUrl); - return new SCIMListResponse($resourceTypes); - } + /** + * @NoCSRFRequired + * @PublicPage + */ + public function resourceTypes(): SCIMListResponse { + $baseUrl = + $this->request->getServerProtocol() . "://" + . $this->request->getServerHost() . "/" + . Util::SCIM_APP_URL_PATH; + $resourceTypes = SCIMUtil::getResourceTypes($baseUrl); + return new SCIMListResponse($resourceTypes); + } - /** - * @NoCSRFRequired - * @PublicPage - */ - public function schemas(): SCIMListResponse - { - $schemas = SCIMUtil::getSchemas(); - return new SCIMListResponse($schemas); - } + /** + * @NoCSRFRequired + * @PublicPage + */ + public function schemas(): SCIMListResponse { + $schemas = SCIMUtil::getSchemas(); + return new SCIMListResponse($schemas); + } - /** - * @NoCSRFRequired - * @PublicPage - */ - public function serviceProviderConfig(): SCIMJSONResponse - { - $serviceProviderConfig = SCIMUtil::getServiceProviderConfig(); - return new SCIMJSONResponse($serviceProviderConfig); - } + /** + * @NoCSRFRequired + * @PublicPage + */ + public function serviceProviderConfig(): SCIMJSONResponse { + $serviceProviderConfig = SCIMUtil::getServiceProviderConfig(); + return new SCIMJSONResponse($serviceProviderConfig); + } } diff --git a/lib/Controller/UserBearerController.php b/lib/Controller/UserBearerController.php deleted file mode 100644 index bf6ea16..0000000 --- a/lib/Controller/UserBearerController.php +++ /dev/null @@ -1,121 +0,0 @@ -userService = $userService; - } - - /** - * @NoCSRFRequired - * @PublicPage - * - * @param string $filter - * @return SCIMListResponse - * returns a list of users and their data - */ - public function index(string $filter = ''): SCIMListResponse - { - return $this->userService->getAll($filter); - } - - /** - * @NoCSRFRequired - * @PublicPage - * - * gets user info - * - * @param string $id - * @return SCIMJSONResponse - */ - // TODO: Add filtering support here as well - public function show(string $id): SCIMJSONResponse - { - return $this->userService->getOneById($id); - } - - /** - * @NoCSRFRequired - * @PublicPage - * - * @param bool $active - * @param string $displayName - * @param array $emails - * @param string $externalId - * @param string $userName - * @return SCIMJSONResponse - */ - public function create( - bool $active = true, - string $displayName = '', - array $emails = [], - string $externalId = '', - string $userName = '' - ): SCIMJSONResponse - { - return $this->userService->create( - $active, - $displayName, - $emails, - $externalId, - $userName - ); - } - - /** - * @NoCSRFRequired - * @PublicPage - * - * @param string $id - * - * @param bool $active - * @param string $displayName - * @param array $emails - * @return SCIMJSONResponse - */ - public function update( - string $id, - bool $active, - string $displayName = '', - array $emails = [] - ): SCIMJSONResponse - { - return $this->userService->update($id, $active, $displayName, $emails); - } - - /** - * @NoCSRFRequired - * @PublicPage - * - * @param string $id - * @return Response - */ - public function destroy(string $id): Response - { - return $this->userService->destroy($id); - } -} diff --git a/lib/Controller/UserController.php b/lib/Controller/UserController.php index 6c8236a..8ef1e14 100644 --- a/lib/Controller/UserController.php +++ b/lib/Controller/UserController.php @@ -4,112 +4,106 @@ declare(strict_types=1); namespace OCA\SCIMServiceProvider\Controller; +use OCA\SCIMServiceProvider\Responses\SCIMJSONResponse; +use OCA\SCIMServiceProvider\Responses\SCIMListResponse; +use OCA\SCIMServiceProvider\Service\UserService; +use OCA\SCIMServiceProvider\Util\Util; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http\Response; use OCP\IRequest; -use OCA\SCIMServiceProvider\Responses\SCIMListResponse; -use OCA\SCIMServiceProvider\Responses\SCIMJSONResponse; -use OCA\SCIMServiceProvider\Service\UserService; +use Opf\Models\SCIM\Standard\Users\CoreUser; -class UserController extends ApiController -{ - /** @var UserService */ - private $userService; +class UserController extends ApiController { + private UserService $userService; - public function __construct( - string $appName, - IRequest $request, - UserService $userService - ) { - parent::__construct( - $appName, - $request - ); + public function __construct( + string $appName, + IRequest $request, + UserService $userService + ) { + parent::__construct($appName, $request); - $this->userService = $userService; - } + $this->userService = $userService; + } - /** - * @NoCSRFRequired - * - * @param string $filter - * @return SCIMListResponse - * returns a list of users and their data - */ - public function index(string $filter = ''): SCIMListResponse - { - return $this->userService->getAll($filter); - } + /** + * @NoCSRFRequired + * @PublicPage + * + * @return SCIMListResponse + */ + public function index(): SCIMJSONResponse { + $params = $this->request->getParams(); + $params = Util::parsePagination($params); + $scimUsers = $this->userService->getAll($params["startIndex"], $params["count"]); + $list = []; + foreach ($scimUsers as $scimUser) { + $list[] = $scimUser->toSCIM(false, Util::getBaseUrl($this->request)); + } + $total = $this->userService->countAll(); + return new SCIMListResponse($list, $params["startIndex"], $total); + } - /** - * @NoCSRFRequired - * - * gets user info - * - * @param string $id - * @return SCIMJSONResponse - */ - // TODO: Add filtering support here as well - public function show(string $id): SCIMJSONResponse - { - return $this->userService->getOneById($id); - } + /** + * @NoCSRFRequired + * @PublicPage + * + * @param string $id + * @return SCIMJSONResponse + */ + // TODO: Add filtering support here as well + public function show(string $id): SCIMJSONResponse { + $scimUser = $this->userService->get($id); + return new SCIMJSONResponse($scimUser->toSCIM(false, Util::getBaseUrl($this->request))); + } - /** - * @NoCSRFRequired - * - * @param bool $active - * @param string $displayName - * @param array $emails - * @param string $externalId - * @param string $userName - * @return SCIMJSONResponse - */ - public function create( - bool $active = true, - string $displayName = '', - array $emails = [], - string $externalId = '', - string $userName = '' - ): SCIMJSONResponse - { - return $this->userService->create( - $active, - $displayName, - $emails, - $externalId, - $userName - ); - } + /** + * @NoCSRFRequired + * @PublicPage + * + * @param string $displayName + * @param array $members + * @return SCIMJSONResponse + */ + public function create(): SCIMJSONResponse { + $params = $this->request->getParams(); - /** - * @NoCSRFRequired - * - * @param string $id - * - * @param bool $active - * @param string $displayName - * @param array $emails - * @return SCIMJSONResponse - */ - public function update( - string $id, - bool $active, - string $displayName = '', - array $emails = [] - ): SCIMJSONResponse - { - return $this->userService->update($id, $active, $displayName, $emails); - } + $scimUser = new CoreUser(); + $scimUser->fromSCIM($params); - /** - * @NoCSRFRequired - * - * @param string $id - * @return Response - */ - public function destroy(string $id): Response - { - return $this->userService->destroy($id); - } + $scimGroup = $this->userService->create($scimUser); + return new SCIMJSONResponse($scimGroup->toSCIM(false, Util::getBaseUrl($this->request)), 201); + } + + /** + * @NoCSRFRequired + * @PublicPage + * + * @param string $id + * @return SCIMJSONResponse + */ + public function update(string $id): SCIMJSONResponse { + $params = $this->request->getParams(); + + $scimUser = new CoreUser(); + $scimUser->fromSCIM($params); + + $scimUser = $this->userService->update($id, $scimUser); + return new SCIMJSONResponse($scimUser->toSCIM(false, Util::getBaseUrl($this->request))); + } + + /** + * @NoCSRFRequired + * @PublicPage + * + * @param string $id + * @return Response + */ + public function destroy(string $id): Response { + $this->userService->destroy($id); + + $response = new Response(); + $response->setStatus(204); + return $response; + } } diff --git a/lib/DataAccess/Groups/NextcloudGroupDataAccess.php b/lib/DataAccess/Groups/NextcloudGroupDataAccess.php deleted file mode 100644 index e60f87a..0000000 --- a/lib/DataAccess/Groups/NextcloudGroupDataAccess.php +++ /dev/null @@ -1,173 +0,0 @@ -logger = $container->get(LoggerInterface::class); - $this->userManager = $container->get(IUserManager::class); - $this->groupManager = $container->get(IGroupManager::class); - } - - /** - * Read all groups - */ - public function getAll(): ?array - { - $ncGroups = $this->groupManager->search('', null, 0); - - $this->logger->info( - "[" . NextcloudGroupDataAccess::class . "] fetched " . count($ncGroups) . " groups" - ); - - return $ncGroups; - } - - /** - * Read a single group by ID - */ - public function getOneById($id): ?IGroup - { - $ncGroup = $this->groupManager->get($id); - - if (!isset($ncGroup)) { - $this->logger->error( - "[" . NextcloudGroupDataAccess::class . "] group with ID: " . $id . " is null" - ); - } else { - $this->logger->info( - "[" . NextcloudGroupDataAccess::class . "] fetched group with ID: " . $id - ); - } - - return $ncGroup; - } - - /** - * Create a new group - */ - public function create($displayName): ?IGroup - { - // Note: the createGroup() function requires a $gid parameter - // However, looking at the NC DB, it seems that the gid of a group - // and its displayName can have the same value, hence here we pass the - // displayName parameter to createGroup() and don't need to generate - // a unique gid for a given group during creation - $createdNcGroup = $this->groupManager->createGroup($displayName); - - if (!isset($createdNcGroup)) { - $this->logger->error( - "[" . NextcloudGroupDataAccess::class . "] creation of group with displayName: " . $displayName . " failed" - ); - return null; - } - - return $createdNcGroup; - } - - /** - * Update an existing group by ID - * - * Note: here, we pass the second parameter, since it carries the data to be updated - * and we need to pass this data to the group that is to be updated - */ - public function update(string $id, IGroup $newGroupData): ?IGroup - { - $ncGroupToUpdate = $this->groupManager->get($id); - - if (!isset($ncGroupToUpdate)) { - $this->logger->error( - "[" . NextcloudGroupDataAccess::class . "] group to be updated with ID: " . $id . " doesn't exist" - ); - return null; - } - - if ($newGroupData->getDisplayName() !== null) { - $ncGroupToUpdate->setDisplayName($newGroupData->getDisplayName()); - } - - if ($newGroupData->getUsers() !== null && !empty($newGroupData->getUsers())) { - $newNcGroupMembers = []; - - foreach ($newGroupData->getUsers() as $newNcGroupMember) { - // First check if the user is an existing one and only then try to place it as a member of the group - if ($this->userManager->userExists($newNcGroupMember->getUID())) { - $ncUserToAdd = $this->userManager->get($newNcGroupMember->getUID()); - $newNcGroupMembers[] = $ncUserToAdd; - } else { - $this->logger->error( - "[" . NextcloudGroupDataAccess::class . "] user from new group data with ID: " . $id . " doesn't exist" - ); - } - } - - $currentNcGroupMembers = $ncGroupToUpdate->getUsers(); - if (isset($currentNcGroupMembers) && !empty($currentNcGroupMembers)) { - // If the group can't remove users from itself, then we abort and return null - if (!$ncGroupToUpdate->canRemoveUser()) { - return null; - } - - // Else, if we can remove users, then we remove all current users - foreach ($currentNcGroupMembers as $currentNcGroupMember) { - $ncGroupToUpdate->removeUser($currentNcGroupMember); - } - } - - // After having deleted the current members, we try to replace them with the new ones - if (!$ncGroupToUpdate->canAddUser()) { - return null; - } - - foreach ($newNcGroupMembers as $newNcGroupMember) { - $ncGroupToUpdate->addUser($newNcGroupMember); - } - } - - // Return the now updated NC group - return $this->groupManager->get($id); - } - - /** - * Delete an existing group by ID - */ - public function delete($id): bool - { - $ncGroupToDelete = $this->groupManager->get($id); - - if (!isset($ncGroupToDelete)) { - $this->logger->error( - "[" . NextcloudGroupDataAccess::class . "] group to be deleted with ID: " . $id . " doesn't exist" - ); - - return false; - } - - if ($ncGroupToDelete->delete()) { - return true; - } - - $this->logger->error( - "[" . NextcloudGroupDataAccess::class . "] couldn't delete group with ID: " . $id - ); - - return false; - } -} diff --git a/lib/DataAccess/Users/NextcloudUserDataAccess.php b/lib/DataAccess/Users/NextcloudUserDataAccess.php deleted file mode 100644 index 857919c..0000000 --- a/lib/DataAccess/Users/NextcloudUserDataAccess.php +++ /dev/null @@ -1,144 +0,0 @@ -logger = $container->get(LoggerInterface::class); - $this->secureRandom = $container->get(ISecureRandom::class); - $this->userManager = $container->get(IUserManager::class); - $this->config = $container->get(IConfig::class); - } - - /** - * Read all users - */ - public function getAll(): ?array - { - $ncUsers = $this->userManager->search('', null, 0); - - $this->logger->info( - "[" . NextcloudUserDataAccess::class . "] fetched " . count($ncUsers) . " users" - ); - - return $ncUsers; - } - - /** - * Read a single user by ID - */ - public function getOneById($id): ?IUser - { - $ncUser = $this->userManager->get($id); - - if (!isset($ncUser)) { - $this->logger->error( - "[" . NextcloudUserDataAccess::class . "] user with ID: " . $id . " is null" - ); - } else { - $this->logger->info( - "[" . NextcloudUserDataAccess::class . "] fetched user with ID: " . $id - ); - } - - return $ncUser; - } - - /** - * Create a new user - */ - public function create($username): ?IUser - { - $createdNcUser = $this->userManager->createUser($username, $this->secureRandom->generate(64)); - - if ($createdNcUser === false) { - $this->logger->error( - "[" . NextcloudUserDataAccess::class . "] creation of user with userName: " . $username . " failed" - ); - return null; - } - - return $createdNcUser; - } - - /** - * Update an existing user by ID - * - * Note: here, we pass the second parameter, since it carries the data to be updated - * and we need to pass this data to the user that is to be updated - */ - public function update(string $id, IUser $newUserData): ?IUser - { - $ncUserToUpdate = $this->userManager->get($id); - - if ($ncUserToUpdate === null) { - $this->logger->error( - "[" . NextcloudUserDataAccess::class . "] user to be updated with ID: " . $id . " doesn't exist" - ); - - return null; - } - - if ($newUserData->getDisplayName() !== null) { - $ncUserToUpdate->setDisplayName($newUserData->getDisplayName()); - } - - if ($newUserData->isEnabled() !== null && $newUserData->isEnabled()) { - $ncUserToUpdate->setEnabled($newUserData->isEnabled()); - } - - if ($newUserData->getEMailAddress() !== null && !empty($newUserData->getEMailAddress())) { - $ncUserToUpdate->setEMailAddress($newUserData->getEMailAddress()); - } - - // Return the now updated NC user - return $this->userManager->get($id); - } - - /** - * Delete an existing user by ID - */ - public function delete($id): bool - { - $ncUserToDelete = $this->userManager->get($id); - - if ($ncUserToDelete === null) { - $this->logger->error( - "[" . NextcloudUserDataAccess::class . "] user to be deleted with ID: " . $id . " doesn't exist" - ); - - return false; - } - - if ($ncUserToDelete->delete()) { - return true; - } - - $this->logger->error( - "[" . NextcloudUserDataAccess::class . "] couldn't delete user with ID: " . $id - ); - - return false; - } -} diff --git a/lib/Exception/AuthException.php b/lib/Exception/AuthException.php index ca618fc..6848c64 100644 --- a/lib/Exception/AuthException.php +++ b/lib/Exception/AuthException.php @@ -4,6 +4,8 @@ namespace OCA\SCIMServiceProvider\Exception; use Exception; -class AuthException extends Exception -{ -} \ No newline at end of file +class AuthException extends Exception { + public function __construct(string $message) { + parent::__construct($message, 401); + } +} diff --git a/lib/Exception/ContentTypeException.php b/lib/Exception/ContentTypeException.php index 5faa3cf..75c2d29 100644 --- a/lib/Exception/ContentTypeException.php +++ b/lib/Exception/ContentTypeException.php @@ -4,6 +4,5 @@ namespace OCA\SCIMServiceProvider\Exception; use Exception; -class ContentTypeException extends Exception -{ +class ContentTypeException extends Exception { } diff --git a/lib/Middleware/AuthMiddleware.php b/lib/Middleware/AuthMiddleware.php new file mode 100644 index 0000000..3d6e1e6 --- /dev/null +++ b/lib/Middleware/AuthMiddleware.php @@ -0,0 +1,110 @@ +request = $request; + $this->userSession = $userSession; + $this->userManager = $userManager; + $this->groupManager = $groupManager; + $this->config = $config; + $this->logger = $logger; + } + + public function beforeController($controller, $methodName) { + $currentRoute = $this->request->getParams()["_route"]; + $publicRoutes = [ + "scimserviceprovider.service_provider_configuration.resource_types", + "scimserviceprovider.service_provider_configuration.schemas", + "scimserviceprovider.service_provider_configuration.service_provider_config" + ]; + + // Don't require an auth header for public routes + if (in_array($currentRoute, $publicRoutes)) { + return; + } + + $authHeader = $this->request->getHeader('Authorization'); + + if (empty($authHeader)) { + throw new AuthException("No Authorization header supplied"); + } + + $authHeaderSplit = explode(' ', $authHeader); + if (count($authHeaderSplit) !== 2) { + throw new AuthException("Incorrect authorization header"); + } + + switch ($authHeaderSplit[0]) { + case 'Basic': + $user = $this->userSession->getUser(); + if ($user == null) { + throw new AuthException("Not logged-in"); + } + break; + case 'Bearer': + $user = $this->authenticateBearerToken($authHeaderSplit[1]); + break; + default: + throw new AuthException("Incorrect authorization type"); + break; + } + + // For now only allow admin users + if (!$this->groupManager->isAdmin($user->getUID())) { + throw new AuthException("Not admin"); + } + } + + private function authenticateBearerToken(string $token): ?IUser { + $e = new AuthException("Bearer token is invalid"); + $jwtPayload = []; + $jwtSecret = $this->config->getAppValue(Application::APP_ID, "jwt-secret"); + if (empty($jwtSecret)) { + $this->logger->error("jwt-secret not configued"); + throw $e; + } + try { + $jwtPayload = (array) JWT::decode($token, new Key($jwtSecret, 'HS256')); + } catch (Exception $e2) { + $this->logger->error($e2->getMessage()); + throw $e; + } + + $username = $jwtPayload['sub']; + + // If we managed to find a user with that username, then auth succeeded + $user = $this->userManager->get($username); + if ($user === null) { + $this->logger->error("User with this username doesn't exist"); + throw $e; + } + + $this->userSession->setUser($user); + return $user; + } +} diff --git a/lib/Middleware/BearerAuthMiddleware.php b/lib/Middleware/BearerAuthMiddleware.php deleted file mode 100644 index b5339f9..0000000 --- a/lib/Middleware/BearerAuthMiddleware.php +++ /dev/null @@ -1,70 +0,0 @@ -request = $container->get(IRequest::class); - $this->bearerAuthenticator = $container->get(BearerAuthenticator::class); - } - - public function beforeController($controller, $methodName) - { - $currentRoute = $this->request->getParams()["_route"]; - $publicRoutes = [ - "scimserviceprovider.service_provider_configuration.resource_types", - "scimserviceprovider.service_provider_configuration.schemas", - "scimserviceprovider.service_provider_configuration.service_provider_config" - ]; - - // Don't require an auth header for public routes - if (in_array($currentRoute, $publicRoutes)) { - return; - } - - $authHeader = $this->request->getHeader('Authorization'); - - if (empty($authHeader)) { - throw new AuthException("No Authorization header supplied"); - } - - $authHeaderSplit = explode(' ', $authHeader); - if (count($authHeaderSplit) !== 2 || strcmp($authHeaderSplit[0], "Bearer") !== 0) { - throw new AuthException("Incorrect Bearer token format"); - } - - $token = $authHeaderSplit[1]; - - // Currently the second parameter to authenticate() is an empty array - // (the second parameter is meant to carry authorization information) - if (!$this->bearerAuthenticator->authenticate($token, [])) { - throw new AuthException("Bearer token is invalid"); - } - } - - public function afterException($controller, $methodName, Exception $exception) - { - if ($exception instanceof AuthException) { - return new SCIMErrorResponse(['message' => $exception->getMessage()], 401); - } - } -} diff --git a/lib/Middleware/ContentTypeMiddleware.php b/lib/Middleware/ContentTypeMiddleware.php index 8d8a28f..b2e3503 100644 --- a/lib/Middleware/ContentTypeMiddleware.php +++ b/lib/Middleware/ContentTypeMiddleware.php @@ -4,57 +4,47 @@ declare(strict_types=1); namespace OCA\SCIMServiceProvider\Middleware; -use Exception; use OCA\SCIMServiceProvider\Exception\ContentTypeException; -use OCA\SCIMServiceProvider\Responses\SCIMErrorResponse; use OCP\AppFramework\Middleware; use OCP\IRequest; use Psr\Container\ContainerInterface; -class ContentTypeMiddleware extends Middleware -{ - /** @var IRequest */ - private $request; +class ContentTypeMiddleware extends Middleware { + /** @var IRequest */ + private $request; - public function __construct(ContainerInterface $container) - { - $this->request = $container->get(IRequest::class); - } - - public function beforeController($controller, $methodName) - { - $requestMethod = $this->request->getMethod(); + public function __construct(ContainerInterface $container) { + $this->request = $container->get(IRequest::class); + } + + public function beforeController($controller, $methodName) { + $requestMethod = $this->request->getMethod(); - // If the incoming request is POST or PUT => check the Content-Type header and the request body - if (in_array(strtolower($requestMethod), array("post", "put"))) { - $contentTypeHeader = $this->request->getHeader("Content-Type"); - if (!isset($contentTypeHeader) || empty($contentTypeHeader)) { - throw new ContentTypeException("Content-Type header not set"); - } + // If the incoming request is POST or PUT => check the Content-Type header and the request body + if (in_array(strtolower($requestMethod), array("post", "put"))) { + $contentTypeHeader = $this->request->getHeader("Content-Type"); + if (!isset($contentTypeHeader) || empty($contentTypeHeader)) { + throw new ContentTypeException("Content-Type header not set"); + } - // Accept both "application/scim+json" and "application/json" as valid headers - // See https://www.rfc-editor.org/rfc/rfc7644.html#section-3.8 - if ( - strpos($contentTypeHeader, "application/scim+json") === false - && strpos($contentTypeHeader, "application/json") === false - ) { - throw new ContentTypeException("Content-Type header is not application/scim+json or application/json"); - } + // Accept both "application/scim+json" and "application/json" as valid headers + // See https://www.rfc-editor.org/rfc/rfc7644.html#section-3.8 + if ( + strpos($contentTypeHeader, "application/scim+json") === false + && strpos($contentTypeHeader, "application/json") === false + ) { + throw new ContentTypeException("Content-Type header is not application/scim+json or application/json"); + } - // Verify that the request body is indeed valid JSON - $requestBody = $this->request->getParams(); - if (isset($requestBody) && !empty($requestBody)) { - $requestBody = array_keys($requestBody)[0]; + // Verify that the request body is indeed valid JSON + $requestBody = $this->request->getParams(); + if (isset($requestBody) && !empty($requestBody)) { + $requestBody = array_keys($requestBody)[0]; - if (json_decode($requestBody) === false) { - throw new ContentTypeException("Request body is not valid JSON"); - } - } - } - } - - public function afterException($controller, $methodName, Exception $exception) - { - return new SCIMErrorResponse(['message' => $exception->getMessage()], 400); - } -} \ No newline at end of file + if (json_decode($requestBody) === false) { + throw new ContentTypeException("Request body is not valid JSON"); + } + } + } + } +} diff --git a/lib/Middleware/ErrorMiddleware.php b/lib/Middleware/ErrorMiddleware.php new file mode 100644 index 0000000..50a52c9 --- /dev/null +++ b/lib/Middleware/ErrorMiddleware.php @@ -0,0 +1,16 @@ +dataAccess = $container->get('GroupDataAccess'); - $this->adapter = $container->get('GroupAdapter'); - $this->logger = $container->get(LoggerInterface::class); - } - - /** - * Read all groups in SCIM format - */ - public function getAll( - $filter = '', - $startIndex = 0, - $count = 0, - $attributes = [], - $excludedAttributes = [] - ): array { - $this->logger->info( - "[" . NextcloudGroupRepository::class . "] reading all groups" - ); - - // Read all NC groups - $ncGroups = $this->dataAccess->getAll(); - $scimGroups = []; - - $this->logger->info( - "[" . NextcloudGroupRepository::class . "] fetched " . count($ncGroups) . " NC groups" - ); - - foreach ($ncGroups as $ncGroup) { - $scimGroup = $this->adapter->getCoreGroup($ncGroup); - $scimGroups[] = $scimGroup; - } - - $this->logger->info( - "[" . NextcloudGroupRepository::class . "] transformed " . count($scimGroups) . " SCIM groups" - ); - - if (isset($filter) && !empty($filter)) { - $scimGroupsToFilter = []; - foreach ($scimGroups as $scimGroup) { - $scimGroupsToFilter[] = $scimGroup->toSCIM(false); - } - - $filteredScimData = FilterUtil::performFiltering($filter, $scimGroupsToFilter); - - $scimGroups = []; - foreach ($filteredScimData as $filteredScimGroup) { - $scimGroup = new CoreGroup(); - $scimGroup->fromSCIM($filteredScimGroup); - $scimGroups[] = $scimGroup; - } - - return $scimGroups; - } - - return $scimGroups; - } - - /** - * Read a single group by ID in SCIM format - */ - public function getOneById( - string $id, - $filter = '', - $startIndex = 0, - $count = 0, - $attributes = [], - $excludedAttributes = [] - ): ?CoreGroup { - $this->logger->info( - "[" . NextcloudGroupRepository::class . "] reading group with ID: " . $id - ); - - $ncGroup = $this->dataAccess->getOneById($id); - return $this->adapter->getCoreGroup($ncGroup); - } - - /** - * Create a group from SCIM data - */ - public function create($object): ?CoreGroup - { - $scimGroupToCreate = new CoreGroup(); - $scimGroupToCreate->fromSCIM($object); - - $displayName = $scimGroupToCreate->getDisplayName(); - $ncGroupCreated = $this->dataAccess->create($displayName); - - $this->logger->info( - "[" . NextcloudGroupRepository::class . "] creating group with displayName: " . $displayName - ); - - if (isset($ncGroupCreated)) { - // Set the rest of the properties of the NC group with the adapter - $ncGroupCreated = $this->adapter->getNCGroup($scimGroupToCreate, $ncGroupCreated); - return $this->adapter->getCoreGroup($ncGroupCreated); - } - - $this->logger->error( - "[" . NextcloudGroupRepository::class . "] creation of group with displayName: " . $displayName . " failed" - ); - - return null; - } - - /** - * Update a group by ID from SCIM data - */ - public function update(string $id, $object): ?CoreGroup - { - $this->logger->info( - "[" . NextcloudGroupRepository::class . "] updating group with ID: " . $id - ); - - $scimGroupToUpdate = new CoreGroup(); - $scimGroupToUpdate->fromSCIM($object); - - $ncGroup = $this->dataAccess->getOneById($id); - - if (isset($ncGroup)) { - $ncGroupToUpdate = $this->adapter->getNCGroup($scimGroupToUpdate, $ncGroup); - $ncGroupUpdated = $this->dataAccess->update($id, $ncGroupToUpdate); - - if (isset($ncGroupUpdated)) { - return $this->adapter->getCoreGroup($ncGroupUpdated); - } - } - - $this->logger->error( - "[" . NextcloudGroupRepository::class . "] update of group with ID: " . $id . " failed" - ); - - return null; - } - - /** - * Delete a group by ID - */ - public function delete(string $id): bool - { - $this->logger->info( - "[" . NextcloudGroupRepository::class . "] deleting group with ID: " . $id - ); - - return $this->dataAccess->delete($id); - } -} diff --git a/lib/Repositories/Users/NextcloudUserRepository.php b/lib/Repositories/Users/NextcloudUserRepository.php deleted file mode 100644 index 9e3b6a8..0000000 --- a/lib/Repositories/Users/NextcloudUserRepository.php +++ /dev/null @@ -1,177 +0,0 @@ -dataAccess = $container->get('UserDataAccess'); - $this->adapter = $container->get('UserAdapter'); - $this->logger = $container->get(LoggerInterface::class); - } - - /** - * Read all users in SCIM format - */ - public function getAll( - $filter = '', - $startIndex = 0, - $count = 0, - $attributes = [], - $excludedAttributes = [] - ): array { - $this->logger->info( - "[" . NextcloudUserRepository::class . "] reading all users" - ); - - // Read all NC users - $ncUsers = $this->dataAccess->getAll(); - $scimUsers = []; - - $this->logger->info( - "[" . NextcloudUserRepository::class . "] fetched " . count($ncUsers) . " NC users" - ); - - foreach ($ncUsers as $ncUser) { - $scimUser = $this->adapter->getCoreUser($ncUser); - $scimUsers[] = $scimUser; - } - - $this->logger->info( - "[" . NextcloudUserRepository::class . "] transformed " . count($scimUsers) . " SCIM users" - ); - - if (isset($filter) && !empty($filter)) { - $scimUsersToFilter = []; - foreach ($scimUsers as $scimUser) { - $scimUsersToFilter[] = $scimUser->toSCIM(false); - } - - $filteredScimData = FilterUtil::performFiltering($filter, $scimUsersToFilter); - - $scimUsers = []; - foreach ($filteredScimData as $filteredScimUser) { - $scimUser = new CoreUser(); - $scimUser->fromSCIM($filteredScimUser); - $scimUsers[] = $scimUser; - } - - return $scimUsers; - } - - return $scimUsers; - } - - /** - * Read a single user by ID in SCIM format - */ - public function getOneById( - string $id, - $filter = '', - $startIndex = 0, - $count = 0, - $attributes = [], - $excludedAttributes = [] - ): ?CoreUser { - $this->logger->info( - "[" . NextcloudUserRepository::class . "] reading user with ID: " . $id - ); - - $ncUser = $this->dataAccess->getOneById($id); - $scimUser = $this->adapter->getCoreUser($ncUser); - - if (isset($filter) && !empty($filter)) { - $scimUsersToFilter = array($scimUser->toSCIM(false)); - $filteredScimData = FilterUtil::performFiltering($filter, $scimUsersToFilter); - - if (!empty($filteredScimData)) { - $scimUser = new CoreUser(); - $scimUser->fromSCIM($filteredScimData[0]); - return $scimUser; - } - } - - return $scimUser; - } - - /** - * Create a user from SCIM data - */ - public function create($object): ?CoreUser - { - $scimUserToCreate = new CoreUser(); - $scimUserToCreate->fromSCIM($object); - - $username = $scimUserToCreate->getUserName(); - $ncUserCreated = $this->dataAccess->create($username); - - $this->logger->info( - "[" . NextcloudUserRepository::class . "] creating user with userName: " . $username - ); - - if (isset($ncUserCreated)) { - // Set the rest of the properties of the NC user via the adapter - $ncUserCreated = $this->adapter->getNCUser($scimUserToCreate, $ncUserCreated); - return $this->adapter->getCoreUser($ncUserCreated); - } - - $this->logger->error( - "[" . NextcloudUserRepository::class . "] creation of user with username: " . $username . " failed" - ); - - return null; - } - - /** - * Update a user by ID from SCIM data - */ - public function update(string $id, $object): ?CoreUser - { - $this->logger->info( - "[" . NextcloudUserRepository::class . "] updating user with ID: " . $id - ); - - $scimUserToUpdate = new CoreUser(); - $scimUserToUpdate->fromSCIM($object); - - $ncUser = $this->dataAccess->getOneById($id); - - if (isset($ncUser)) { - $ncUserToUpdate = $this->adapter->getNCUser($scimUserToUpdate, $ncUser); - $ncUserUpdated = $this->dataAccess->update($id, $ncUserToUpdate); - - if (isset($ncUserUpdated)) { - return $this->adapter->getCoreUser($ncUserUpdated); - } - } - - $this->logger->error( - "[" . NextcloudUserRepository::class . "] update of user with ID: " . $id . " failed" - ); - - return null; - } - - /** - * Delete a user by ID - */ - public function delete(string $id): bool - { - $this->logger->info( - "[" . NextcloudUserRepository::class . "] deleting user with ID: " . $id - ); - - return $this->dataAccess->delete($id); - } -} diff --git a/lib/Responses/SCIMErrorResponse.php b/lib/Responses/SCIMErrorResponse.php index ab37d42..2c5d985 100644 --- a/lib/Responses/SCIMErrorResponse.php +++ b/lib/Responses/SCIMErrorResponse.php @@ -2,39 +2,21 @@ namespace OCA\SCIMServiceProvider\Responses; -use OCP\AppFramework\Http\Response; +use Exception; /** * Class SCIMErrorResponse * */ class SCIMErrorResponse extends SCIMJSONResponse { - /** - * response data - * @var array|object - */ - protected $data; - /** - * Returns the rendered json - * @return string the rendered json - * @since 6.0.0 - * @throws \Exception If data could not get encoded - */ - public function render() { - $message = [ + public function __construct(Exception $e) { + $data = [ 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:Error'], - 'detail' => $this->data['message'], + 'detail' => $e->getMessage(), 'scimType' => '', - 'status' => $this->getStatus() + 'status' => $e->getCode(), ]; - $response = json_encode($message, JSON_UNESCAPED_SLASHES); - - if ($response === false) { - throw new Exception(sprintf('Could not json_encode due to invalid ' . - 'non UTF-8 characters in the array: %s', var_export($this->data, true))); - } - - return $response; + parent::__construct($data, $e->getCode()); } } diff --git a/lib/Responses/SCIMJSONResponse.php b/lib/Responses/SCIMJSONResponse.php index 47576f9..a07a21f 100644 --- a/lib/Responses/SCIMJSONResponse.php +++ b/lib/Responses/SCIMJSONResponse.php @@ -2,6 +2,7 @@ namespace OCA\SCIMServiceProvider\Responses; +use Exception; use OCP\AppFramework\Http\Response; /** diff --git a/lib/Responses/SCIMListResponse.php b/lib/Responses/SCIMListResponse.php index 835e6fd..248f6e9 100644 --- a/lib/Responses/SCIMListResponse.php +++ b/lib/Responses/SCIMListResponse.php @@ -2,56 +2,27 @@ namespace OCA\SCIMServiceProvider\Responses; -use OCP\AppFramework\Http\Response; - -use OCA\SCIMServiceProvider\Exceptions\SCIMException; - /** * Class SCIMListResponse * */ -class SCIMListResponse extends Response { - /** - * response data - * @var array|object - */ - protected $data; - - +class SCIMListResponse extends SCIMJSONResponse { /** * constructor of SCIMListResponse - * @param array|object $data the object or array that should be transformed - * @param int $statusCode the Http status code, defaults to 200 + * @param array $items array that should be transformed * @since 6.0.0 */ - public function __construct($data = [], $statusCode = 200) { - parent::__construct(); - - $this->data = $data; - $this->setStatus($statusCode); - $this->addHeader('Content-Type', 'application/scim+json; charset=utf-8'); - } - - /** - * Returns the rendered json - * @return string the rendered json - * @since 6.0.0 - * @throws \Exception If data could not get encoded - */ - public function render() { - $scimReponse = [ - 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:ListResponse'], - 'startIndex' => 1, // todo pagination - 'Resources' => $this->data, - 'totalResults' => sizeof($this->data) - ]; - $response = json_encode($scimReponse, JSON_UNESCAPED_SLASHES); - - if ($response === false) { - throw new SCIMException(sprintf('Could not json_encode due to invalid ' . - 'non UTF-8 characters in the array: %s', var_export($scimReponse, true))); + public function __construct(array $items = [], int $startIndex = 1, int $total = null) { + if ($total === null) { + $total = sizeof($items); } - - return $response; + $data = [ + 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:ListResponse'], + 'startIndex' => $startIndex, + 'itemsPerPage' => sizeof($items), + 'totalResults' => $total, + 'Resources' => $items, + ]; + parent::__construct($data); } } diff --git a/lib/Service/GroupService.php b/lib/Service/GroupService.php index 3840d3a..5965407 100644 --- a/lib/Service/GroupService.php +++ b/lib/Service/GroupService.php @@ -6,153 +6,196 @@ namespace OCA\SCIMServiceProvider\Service; use Exception; use OCA\SCIMServiceProvider\AppInfo\Application; -use OCA\SCIMServiceProvider\Responses\SCIMErrorResponse; -use OCA\SCIMServiceProvider\Responses\SCIMJSONResponse; -use OCA\SCIMServiceProvider\Responses\SCIMListResponse; -use OCA\SCIMServiceProvider\Util\Util; -use OCP\AppFramework\Http\Response; +use OCP\IGroup; use OCP\IGroupManager; -use OCP\IRequest; -use Psr\Container\ContainerInterface; +use OCP\IUserManager; +use Opf\Models\SCIM\Standard\Groups\CoreGroup; +use Opf\Models\SCIM\Standard\Meta; +use Opf\Models\SCIM\Standard\MultiValuedAttribute; use Psr\Log\LoggerInterface; -class GroupService -{ - /** @var LoggerInterface */ - private $logger; +class GroupService { - /** @var \OCA\SCIMServiceProvider\Repositories\Groups\NextcloudGroupRepository */ - private $repository; + private LoggerInterface $logger; + private IGroupManager $groupManager; + private IUserManager $userManager; - /** @var IGroupManager */ - private $groupManager; - /** @var IRequest */ - private $request; + public function __construct(LoggerInterface $logger, IGroupManager $groupManager, IUserManager $userManager) { + $this->logger = $logger; + $this->groupManager = $groupManager; + $this->userManager = $userManager; + } - public function __construct(ContainerInterface $container) - { - $this->logger = $container->get(LoggerInterface::class); - $this->repository = $container->get('GroupRepository'); - $this->groupManager = $container->get(IGroupManager::class); - $this->request = $container->get(IRequest::class); - } - public function getAll(string $filter = ''): SCIMListResponse - { - $this->logger->info("Reading all groups"); + private function toSCIM(IGroup $ncGroup): CoreGroup { + $scimGroup = new CoreGroup(); + $scimGroup->setId($ncGroup->getGID()); + $scimGroup->setDisplayName($ncGroup->getDisplayName()); - $baseUrl = $this->request->getServerProtocol() . "://" - . $this->request->getServerHost() . Util::SCIM_APP_URL_PATH; + $meta = new Meta(); + $meta->setResourceType("Group"); + $scimGroup->setMeta($meta); - $groups = $this->repository->getAll($filter); + return $scimGroup; + } - $scimGroups = []; - if (!empty($groups)) { - foreach ($groups as $group) { - $scimGroups[] = $group->toSCIM(false, $baseUrl); - } - } - return new SCIMListResponse($scimGroups); - } - - public function getOneById(string $id): SCIMJSONResponse - { - $this->logger->info("Reading group with ID: " . $id); + private function getSCIMMembers(IGroup $ncGroup): array { + $members = []; + foreach ($ncGroup->getUsers() as $ncGroupMember) { + $this->logger->info($ncGroupMember->getUID()); + $member = new MultiValuedAttribute(); + $member->setType("User"); + $member->setRef("Users/" . $ncGroupMember->getUID()); + $member->setValue($ncGroupMember->getUID()); + $member->setDisplay($ncGroupMember->getDisplayName()); - $baseUrl = $this->request->getServerProtocol() . "://" . $this->request->getServerHost() . Util::SCIM_APP_URL_PATH; + $members[] = $member; + } + return $members; + } - $group = $this->repository->getOneById($id); - if (!isset($group) || empty($group)) { - $this->logger->error("Group with ID " . $id . " not found"); - return new SCIMErrorResponse(['message' => 'Group not found'], 404); - } - return new SCIMJSONResponse($group->toSCIM(false, $baseUrl)); - } + private function updateGroup(IGroup $ncGroup, CoreGroup $scimGroup) { + $displayName = $scimGroup->getDisplayName(); + if (isset($displayName)) { + $ncGroup->setDisplayName($displayName); + } - public function create(string $displayName = '', array $members = []): SCIMJSONResponse - { - $id = urlencode($displayName); - // Validate name - if (empty($id)) { - $this->logger->error('Group name not supplied', ['app' => 'provisioning_api']); - return new SCIMErrorResponse(['message' => 'Invalid group name'], 400); - } - // Check if it exists - if ($this->groupManager->groupExists($id)) { - $this->logger->error("Group to be created already exists"); - return new SCIMErrorResponse(['message' => 'Group exists'], 409); - } + $scimMembers = $scimGroup->getMembers(); + if (isset($scimMembers)) { + foreach ($ncGroup->getUsers() as $ncUser) { + $found = false; + foreach ($scimMembers as $scimMember) { + if ($ncUser->getUID() === $scimMember->getValue()) { + $found = true; + break; + } + } + if (!$found) { + $this->logger->info("remove " . $ncUser->getUID() . " from " . $ncGroup->getGID()); + $ncGroup->removeUser($ncUser); + } + } + foreach ($scimMembers as $scimMember) { + $this->logger->info($scimMember->getValue()); + $this->logger->info(json_encode($scimMember->jsonSerialize())); + $user = $this->userManager->get($scimMember->getValue()); + if (!isset($user)) { + throw new Exception("User " . $scimMember->getValue() . " not found", 404); + } + if (!$ncGroup->inGroup($user)) { + $this->logger->info("add " . $user->getUID() . " from " . $ncGroup->getGID()); + $ncGroup->addUser($user); + $this->logger->info(json_encode($ncGroup->getUsers())); + } + } + } + } - try { - $this->logger->info("Creating group with displayName: " . $displayName); + public function countAll(): int { + $ncGroups = $this->groupManager->search(""); + return sizeof($ncGroups); + } - $baseUrl = $this->request->getServerProtocol() . "://" . $this->request->getServerHost() . Util::SCIM_APP_URL_PATH; + /* + * @param string $filter + * @return OCP\IGroup\IGroup[] + */ + public function getAll(int $startIndex, int $count = null): array { + $this->logger->info("Reading all groups"); - $data = [ - 'displayName' => $displayName, - 'members' => $members - ]; + if ($count === 0) { + return []; + } - $createdGroup = $this->repository->create($data); - if (isset($createdGroup) && !empty($createdGroup)) { - return new SCIMJSONResponse($createdGroup->toSCIM(false, $baseUrl), 201); - } else { - $this->logger->error("Creating group failed"); - return new SCIMErrorResponse(['message' => 'Creating group failed'], 400); - } - } catch (Exception $e) { - $this->logger->warning('Failed createGroup attempt with SCIMException exception.', ['app' => Application::APP_ID]); - throw $e; - } - } + $ncGroups = $this->groupManager->search("", $count, $startIndex - 1); - public function update(string $id, string $displayName = '', array $members = []): SCIMJSONResponse - { - $this->logger->info("Updating group with ID: " . $id); + $scimGroups = []; + foreach ($ncGroups as $ncGroup) { + $scimGroup = $this->toSCIM($ncGroup); + $members = $this->getSCIMMembers($ncGroup); + $scimGroup->setMembers($members); + $scimGroups[] = $scimGroup; + } - $baseUrl = $this->request->getServerProtocol() . "://" . $this->request->getServerHost() . Util::SCIM_APP_URL_PATH; + return $scimGroups; + } - $group = $this->repository->getOneById($id); - if (!isset($group) || empty($group)) { - $this->logger->error("Group with ID " . $id . " not found for update"); - return new SCIMErrorResponse(['message' => 'Group not found'], 404); - } + public function get(string $id): CoreGroup { + if (!$this->groupManager->groupExists($id)) { + throw new Exception("Not found", 404); + } - $data = [ - 'displayName' => $displayName, - 'members' => $members - ]; + $ncGroup = $this->groupManager->get($id); + $scimGroup = $this->toSCIM($ncGroup); - $updatedGroup = $this->repository->update($id, $data); - if (isset($updatedGroup) && !empty($updatedGroup)) { - return new SCIMJSONResponse($updatedGroup->toSCIM(false, $baseUrl)); - } else { - $this->logger->error("Updating group with ID " . $id . " failed"); - return new SCIMErrorResponse(['message' => 'Updating group failed'], 400); - } - } - public function destroy(string $id): Response - { - $this->logger->info("Deleting group with ID: " . $id); + $members = $this->getSCIMMembers($ncGroup); + $scimGroup->setMembers($members); - if ($id === 'admin') { - // Cannot delete admin group - $this->logger->error("Deleting admin group is not allowed"); - return new SCIMErrorResponse(['message' => 'Can\'t delete admin group'], 403); - } + return $scimGroup; + } - $deleteRes = $this->repository->delete($id); - if ($deleteRes) { - $response = new Response(); - $response->setStatus(204); - return $response; - } else { - $this->logger->error("Deletion of group with ID " . $id . " failed"); - return new SCIMErrorResponse(['message' => 'Couldn\'t delete group'], 503); - } - } + public function create(CoreGroup $scimGroup): CoreGroup { + // Validate name + if (empty($scimGroup->getDisplayName())) { + $this->logger->error('Group name not supplied', ['app' => 'provisioning_api']); + throw new Exception('Invalid group name', 400); + } + // Check if it exists + if ($this->groupManager->groupExists($scimGroup->getDisplayName())) { + $this->logger->error("Group to be created already exists"); + throw new Exception('Group exists', 409); + } + + try { + $this->logger->info("Creating group with displayName: " . $scimGroup->getDisplayName()); + + $ncGroup = $this->groupManager->createGroup($scimGroup->getDisplayName()); + $this->updateGroup($ncGroup, $scimGroup); + } catch (Exception $e) { + $this->logger->warning('Failed createGroup attempt with SCIMException exception.', ['app' => Application::APP_ID]); + throw $e; + } + + return $this->get($ncGroup->getGID()); + } + + public function update(string $id, CoreGroup $scimGroup): CoreGroup { + $this->logger->info("Updating group with ID: " . $id); + + if (!$this->groupManager->groupExists($id)) { + throw new Exception('Group not found', 404); + } + + $this->logger->info($scimGroup->toSCIM()); + // $this->atomic(function () use ($id, $scimGroup) { + $ncGroup = $this->groupManager->get($id); + $this->updateGroup($ncGroup, $scimGroup); + // }, $this->db); + + + $this->logger->info(json_encode($ncGroup->getUsers())); + + return $this->get($id); + } + + public function destroy(string $id): void { + $this->logger->info("Deleting group with ID: " . $id); + + if ($id === 'admin') { + // Cannot delete admin group + $this->logger->error("Deleting admin group is not allowed"); + throw new Exception('Can\'t delete admin group', 403); + } + + + if (!$this->groupManager->groupExists($id)) { + throw new Exception('Group not found', 404); + } + $ncGroup = $this->groupManager->get($id); + $ncGroup->delete(); + } } diff --git a/lib/Service/SCIMGroup.php b/lib/Service/SCIMGroup.php deleted file mode 100644 index cc897f5..0000000 --- a/lib/Service/SCIMGroup.php +++ /dev/null @@ -1,58 +0,0 @@ -groupManager = $groupManager; - } - - /** - * creates an object with all group data - * - * @param string $groupId - * @param bool $includeScopes - * @return array - * @throws Exception - */ - public function get(string $groupId): array { - $groupId = urldecode($groupId); - - // Check the group exists - $group = $this->groupManager->get($groupId); - if ($group === null) { - return []; - } - - $members = array(); - foreach ($this->groupManager->get($groupId)->getUsers() as $member) { - $members[] = [ - 'value' => $member->getUID(), - '$ref' => '/Users/' . $member->getUID(), - 'display' => $member->getDisplayName() - ]; - } - - return [ - 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:Group'], - 'id' => $groupId, - 'displayName' => $group->getDisplayName(), - 'externalId' => '1234', // todo - 'meta' => [ - 'resourceType' => 'Group', - 'location' => '/Groups/' . $groupId, - 'created' => '1970-01-01T00:00:00.000Z', - 'lastModified' => '1970-01-01T00:00:00.000Z' - ], - 'members' => $members - ]; - } -} diff --git a/lib/Service/SCIMUser.php b/lib/Service/SCIMUser.php deleted file mode 100644 index 8801247..0000000 --- a/lib/Service/SCIMUser.php +++ /dev/null @@ -1,85 +0,0 @@ -userManager = $userManager; - $this->config = $config; - } - - /** - * creates an object with all user data - * - * @param string $userId - * @param bool $includeScopes - * @return array - * @throws Exception - */ - public function get(string $userId): array { - // Check if the target user exists - $targetUserObject = $this->userManager->get($userId); - if ($targetUserObject === null) { - return []; - } - - $enabled = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'enabled', 'true') === 'true'; - $externalId = $this->config->getUserValue($targetUserObject->getUID(), Application::APP_ID, 'externalId', ''); - $email = $targetUserObject->getSystemEMailAddress(); - - - $SCIMUser = [ - 'schemas' => ["urn:ietf:params:scim:schemas:core:2.0:User"], - 'id' => $userId, - 'name' => [ - 'formatted' => $targetUserObject->getDisplayName() - ], - 'meta' => [ - 'resourceType' => 'User', - 'location' => '/Users/' . $userId, - 'created' => '1970-01-01T00:00:00.000Z', - 'lastModified' => '1970-01-01T00:00:00.000Z' - ], - 'userName' => $userId, - 'displayName' => $targetUserObject->getDisplayName(), - 'active' => $enabled - ]; - if ($externalId !== '') { - $SCIMUser['externalId'] = $externalId; - } - if ($email !== null) { - $SCIMUser['emails'] = [ // todo if no emails - [ - 'primary' => true, - 'value' => $email - ] - ]; - } - - return $SCIMUser; - } - - /** - * Sets externalId on user - * - * @param string $userId - * @param string $externalId - * @throws Exception - */ - public function setExternalId(string $userId, string $externalId) { - $this->config->setUserValue($userId, Application::APP_ID, 'externalId', $externalId); - } - -} diff --git a/lib/Service/UserService.php b/lib/Service/UserService.php index 74291c1..4039840 100644 --- a/lib/Service/UserService.php +++ b/lib/Service/UserService.php @@ -5,146 +5,148 @@ declare(strict_types=1); namespace OCA\SCIMServiceProvider\Service; use Exception; -use OCA\SCIMServiceProvider\AppInfo\Application; -use OCA\SCIMServiceProvider\Responses\SCIMErrorResponse; -use OCA\SCIMServiceProvider\Responses\SCIMJSONResponse; -use OCA\SCIMServiceProvider\Responses\SCIMListResponse; -use OCA\SCIMServiceProvider\Util\Util; -use OCP\AppFramework\Http\Response; -use OCP\IRequest; -use Psr\Container\ContainerInterface; +use OCP\IUser; +use OCP\IUserManager; +use OCP\Security\ISecureRandom; +use Opf\Models\SCIM\Standard\Meta; +use Opf\Models\SCIM\Standard\MultiValuedAttribute; +use Opf\Models\SCIM\Standard\Users\CoreUser; use Psr\Log\LoggerInterface; -class UserService -{ - /** @var LoggerInterface */ - private $logger; +class UserService { + /** @var LoggerInterface */ + private $logger; - /** @var \OCA\SCIMServiceProvider\Repositories\Users\NextcloudUserRepository */ - private $repository; + private IUserManager $userManager; - /** @var IRequest */ - private $request; + /** @var \OCP\Security\ISecureRandom */ + private $secureRandom; - public function __construct(ContainerInterface $container) - { - $this->logger = $container->get(LoggerInterface::class); - $this->repository = $container->get('UserRepository'); - $this->request = $container->get(IRequest::class); - } + public function __construct(LoggerInterface $logger, IUserManager $userManager, ISecureRandom $secureRandom) { + $this->logger = $logger; + $this->userManager = $userManager; + $this->secureRandom = $secureRandom; + } - public function getAll(string $filter = ''): SCIMListResponse - { - $this->logger->info("Reading all users"); + private function toSCIM(IUser $ncUser): CoreUser { + $scimUser = new CoreUser(); + $scimUser->setId($ncUser->getUID()); + $scimUser->setUserName($ncUser->getUID()); + $scimUser->setDisplayName($ncUser->getDisplayName()); + $scimUser->setActive($ncUser->isEnabled()); - $baseUrl = $this->request->getServerProtocol() . "://" - . $this->request->getServerHost() . Util::SCIM_APP_URL_PATH; + $emails = []; + if (!empty($ncUser->getSystemEMailAddress())) { + $email = new MultiValuedAttribute(); + $email->setValue($ncUser->getSystemEMailAddress()); + $emails[] = $email; + } + $scimUser->setEmails($emails); - $users = $this->repository->getAll($filter); + $meta = new Meta(); + $meta->setResourceType("User"); - $scimUsers = []; - if (!empty($users)) { - foreach ($users as $user) { - $scimUsers[] = $user->toSCIM(false, $baseUrl); - } - } + $scimUser->setMeta($meta); - return new SCIMListResponse($scimUsers); - } + return $scimUser; + } - public function getOneById(string $id): SCIMJSONResponse - { - $this->logger->info("Reading user with ID: " . $id); - $baseUrl = $this->request->getServerProtocol() . "://" . $this->request->getServerHost() . Util::SCIM_APP_URL_PATH; + private function updateUser(IUser $ncUser, CoreUser $scimUser) { + $displayName = $scimUser->getDisplayName(); + if (isset($displayName)) { + $ncUser->setDisplayName($displayName); + } - $user = $this->repository->getOneById($id); - if (!isset($user) || empty($user)) { - $this->logger->error("User with ID " . $id . " not found"); - return new SCIMErrorResponse(['message' => 'User not found'], 404); - } - return new SCIMJSONResponse($user->toSCIM(false, $baseUrl)); - } + $emails = $scimUser->getEmails(); + if (isset($emails)) { + if (sizeof($emails) > 0) { + $ncUser->setSystemEMailAddress($emails[0]->getValue()); + } else { + $ncUser->setSystemEMailAddress(""); + } + } - public function create( - bool $active = true, - string $displayName = '', - array $emails = [], - string $externalId = '', - string $userName = '' - ): SCIMJSONResponse - { - try { - $this->logger->info("Creating user with userName: " . $userName); + $active = $scimUser->getActive(); + if ($active !== null) { + $ncUser->setEnabled($active); + } + } - $baseUrl = $this->request->getServerProtocol() . "://" . $this->request->getServerHost() . Util::SCIM_APP_URL_PATH; + public function countAll(): int { + $count = 0; + foreach ($this->userManager->countUsers() as $key => $value) { + $count = $count + $value; + } + return $count; + } - $data = [ - 'active' => $active, - 'displayName' => $displayName, - 'emails' => $emails, - 'externalId' => $externalId, - 'userName' => $userName - ]; + public function getAll(int $startIndex, int $count = null): array { + $this->logger->info("Reading all users"); - $createdUser = $this->repository->create($data); - if (isset($createdUser) && !empty($createdUser)) { - return new SCIMJSONResponse($createdUser->toSCIM(false, $baseUrl), 201); - } else { - $this->logger->error("Creating user failed"); - return new SCIMErrorResponse(['message' => 'Creating user failed'], 400); - } - } catch (Exception $e) { - $this->logger->warning('Failed createUser attempt with SCIMException exeption.', ['app' => Application::APP_ID]); - throw $e; - } - } + if ($count === 0) { + return []; + } - public function update( - string $id, - bool $active, - string $displayName = '', - array $emails = [] - ): SCIMJSONResponse - { - $this->logger->info("Updating user with ID: " . $id); + $ncUsers = $this->userManager->search("", $count, $startIndex - 1); - $baseUrl = $this->request->getServerProtocol() . "://" . $this->request->getServerHost() . Util::SCIM_APP_URL_PATH; + $scimUsers = []; + foreach ($ncUsers as $ncUser) { + $scimUsers[] = $this->toSCIM($ncUser); + } - $user = $this->repository->getOneById($id); - if (!isset($user) || empty($user)) { - $this->logger->error("User with ID " . $id . " not found for update"); - return new SCIMErrorResponse(['message' => 'User not found'], 404); - } + return $scimUsers; + } - $data = [ - 'active' => $active, - 'displayName' => $displayName, - 'emails' => $emails - ]; - $updatedUser = $this->repository->update($id, $data); - if (isset($updatedUser) && !empty($updatedUser)) { - return new SCIMJSONResponse($updatedUser->toSCIM(false, $baseUrl)); - } else { - $this->logger->error("Updating user with ID " . $id . " failed"); - return new SCIMErrorResponse(['message' => 'Updating user failed'], 400); - } - } + public function get(string $id): CoreUser { + $this->logger->info("Reading user with ID: " . $id); - public function destroy(string $id): Response - { - $this->logger->info("Deleting user with ID: " . $id); + if (!$this->userManager->userExists($id)) { + throw new Exception("Not found", 404); + } - $deleteRes = $this->repository->delete($id); + $ncUser = $this->userManager->get($id); + return $this->toSCIM($ncUser); + } - if ($deleteRes) { - $response = new Response(); - $response->setStatus(204); - return $response; - } else { - $this->logger->error("Deletion of user with ID " . $id . " failed"); - return new SCIMErrorResponse(['message' => 'Couldn\'t delete user'], 503); - } - } + public function create(CoreUser $scimUser): CoreUser { + $this->logger->info("Creating user with userName: " . $scimUser->getUserName()); + + + if ($this->userManager->userExists($scimUser->getUserName())) { + $this->logger->error("User to be created already exists"); + throw new Exception('User exists', 409); + } + + $ncUser = $this->userManager->createUser($scimUser->getUserName(), $this->secureRandom->generate(64)); + $this->updateUser($ncUser, $scimUser); + + return $this->get($ncUser->getUID()); + } + + public function update(string $id, CoreUser $scimUser): CoreUser { + $this->logger->info("Updating user with ID: " . $id); + + if (!$this->userManager->userExists($id)) { + throw new Exception("Not found", 404); + } + + $this->logger->info($scimUser->toSCIM()); + + $ncUser = $this->userManager->get($id); + $this->updateUser($ncUser, $scimUser); + + return $this->get($id); + } + + public function destroy(string $id): void { + $this->logger->info("Deleting user with ID: " . $id); + + if (!$this->userManager->userExists($id)) { + throw new Exception('User not found', 404); + } + $ncUser = $this->userManager->get($id); + $ncUser->delete(); + } } diff --git a/lib/Util/Authentication/BearerAuthenticator.php b/lib/Util/Authentication/BearerAuthenticator.php deleted file mode 100644 index 08a81a4..0000000 --- a/lib/Util/Authentication/BearerAuthenticator.php +++ /dev/null @@ -1,56 +0,0 @@ -logger = $container->get(LoggerInterface::class); - $this->userManager = $container->get(IUserManager::class); - } - - public function authenticate(string $credentials, array $authorizationInfo): bool - { - $jwtPayload = []; - $jwtSecret = Util::getConfigFile()['jwt']['secret']; - try { - $jwtPayload = (array) JWT::decode($credentials, new Key($jwtSecret, 'HS256')); - } catch (Exception $e) { - $this->logger->error($e->getMessage()); - return false; - } - - // If the 'user' claim is missing from the JWT, then auth is considered to have failed - if (!isset($jwtPayload['user']) || empty($jwtPayload['user'])) { - $this->logger->error("No \"user\" claim found in JWT"); - return false; - } - - $username = $jwtPayload['user']; - - // If we managed to find a user with that username, then auth succeeded - $user = $this->userManager->get($username); - if ($user !== null) { - return true; - } - - $this->logger->error("User with this username doesn't exist"); - return false; - } -} diff --git a/lib/Util/Util.php b/lib/Util/Util.php index 7f869b8..93d11f2 100644 --- a/lib/Util/Util.php +++ b/lib/Util/Util.php @@ -2,15 +2,32 @@ namespace OCA\SCIMServiceProvider\Util; -class Util -{ - public const SCIM_APP_URL_PATH = "index.php/apps/scimserviceprovider"; +use OCP\IRequest; - public static function getConfigFile() - { - $configFilePath = dirname(__DIR__) . '/Config/config.php'; - $config = require($configFilePath); +class Util { + public const SCIM_APP_URL_PATH = "index.php/apps/scimserviceprovider"; - return $config; - } + public static function getBaseUrl(IRequest $request) : string { + return $request->getServerProtocol() . "://" . $request->getServerHost() . "/" . Util::SCIM_APP_URL_PATH; + } + + public static function parsePagination(array $params): array { + if (isset($params["startIndex"])) { + $startIndex = intval($params["startIndex"]); + if ($startIndex < 1) { + $startIndex = 1; + } + $params["startIndex"] = $startIndex; + } else { + $params["startIndex"] = 1; + } + if (isset($params["count"])) { + $count = intval($params["count"]); + if ($count < 0) { + $count = 0; + } + $params["count"] = $count; + } + return $params; + } }