diff --git a/THIRD-PARTY-LICENSES.txt b/THIRD-PARTY-LICENSES.txt index 7b6372fb0b..bb77923670 100644 --- a/THIRD-PARTY-LICENSES.txt +++ b/THIRD-PARTY-LICENSES.txt @@ -1,5 +1,5 @@ -Dependency License Report for inspectit-oce SNAPSHOT +Dependency License Report for inspectit-ocelot SNAPSHOT 1. Group: antlr Name: antlr Version: 2.7.7 @@ -435,7 +435,7 @@ POM License: Apache 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt -------------------------------------------------------------------------------- -21. Group: com.google.guava Name: guava Version: 20.0 +21. Group: com.google.guava Name: guava Version: 26.0-android Manifest Project URL: https://github.com/google/guava/ @@ -445,17 +445,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -22. Group: com.google.guava Name: guava Version: 26.0-android - -Manifest Project URL: https://github.com/google/guava/ - -Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0.txt - -POM License: The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - --------------------------------------------------------------------------------- - -23. Group: com.google.j2objc Name: j2objc-annotations Version: 1.1 +22. Group: com.google.j2objc Name: j2objc-annotations Version: 1.1 POM Project URL: https://github.com/google/j2objc/ @@ -463,7 +453,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -24. Group: com.google.protobuf Name: protobuf-java Version: 3.5.1 +23. Group: com.google.protobuf Name: protobuf-java Version: 3.5.1 Manifest Project URL: https://developers.google.com/protocol-buffers/ @@ -475,7 +465,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -25. Group: com.lmax Name: disruptor Version: 3.4.2 +24. Group: com.lmax Name: disruptor Version: 3.4.2 Project URL: http://lmax-exchange.github.com/disruptor @@ -483,7 +473,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -26. Group: com.maxmind.db Name: maxmind-db Version: 1.2.2 +25. Group: com.maxmind.db Name: maxmind-db Version: 1.2.2 POM Project URL: http://dev.maxmind.com/ @@ -491,7 +481,7 @@ POM License: Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0.htm -------------------------------------------------------------------------------- -27. Group: com.maxmind.geoip2 Name: geoip2 Version: 2.12.0 +26. Group: com.maxmind.geoip2 Name: geoip2 Version: 2.12.0 POM Project URL: http://dev.maxmind.com/geoip/geoip2/web-services @@ -499,19 +489,43 @@ POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENS -------------------------------------------------------------------------------- -28. Group: com.squareup.okhttp3 Name: okhttp Version: 3.9.0 +27. Group: com.squareup.moshi Name: moshi Version: 1.5.0 + +POM License: Apache 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + +-------------------------------------------------------------------------------- + +28. Group: com.squareup.okhttp3 Name: logging-interceptor Version: 3.13.1 + +POM License: Apache 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + +-------------------------------------------------------------------------------- + +29. Group: com.squareup.okhttp3 Name: okhttp Version: 3.13.1 POM License: Apache 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt -------------------------------------------------------------------------------- -29. Group: com.squareup.okio Name: okio Version: 1.13.0 +30. Group: com.squareup.okio Name: okio Version: 1.17.2 POM License: Apache 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt -------------------------------------------------------------------------------- -30. Group: com.zaxxer Name: HikariCP Version: 3.2.0 +31. Group: com.squareup.retrofit2 Name: converter-moshi Version: 2.5.0 + +POM License: Apache 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt + +-------------------------------------------------------------------------------- + +32. Group: com.squareup.retrofit2 Name: retrofit Version: 2.5.0 + +POM License: Apache 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt + +-------------------------------------------------------------------------------- + +33. Group: com.zaxxer Name: HikariCP Version: 3.2.0 Manifest Project URL: https://github.com/brettwooldridge @@ -523,7 +537,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -31. Group: commons-codec Name: commons-codec Version: 1.11 +34. Group: commons-codec Name: commons-codec Version: 1.11 Project URL: http://commons.apache.org/proper/commons-codec/ @@ -760,13 +774,13 @@ Copyright (c) 2008 Alexander Beider & Stephen P. Morse. -------------------------------------------------------------------------------- -32. Group: commons-io Name: commons-io Version: 2.6 +35. Group: commons-io Name: commons-io Version: 2.5 Project URL: http://commons.apache.org/proper/commons-io/ -Manifest license URL: https://www.apache.org/licenses/LICENSE-2.0.txt +Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0.txt -POM License: Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt +POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt Embedded license: @@ -979,20 +993,20 @@ Embedded license: **************************************** Apache Commons IO -Copyright 2002-2017 The Apache Software Foundation +Copyright 2002-2016 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). -------------------------------------------------------------------------------- -33. Group: commons-logging Name: commons-logging Version: 1.2 +36. Group: commons-io Name: commons-io Version: 2.6 -Project URL: http://commons.apache.org/proper/commons-logging/ +Project URL: http://commons.apache.org/proper/commons-io/ -Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0.txt +Manifest license URL: https://www.apache.org/licenses/LICENSE-2.0.txt -POM License: The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt +POM License: Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt Embedded license: @@ -1201,280 +1215,683 @@ Embedded license: See the License for the specific language governing permissions and limitations under the License. + **************************************** -Apache Commons Logging -Copyright 2003-2014 The Apache Software Foundation +Apache Commons IO +Copyright 2002-2017 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). - -------------------------------------------------------------------------------- -34. Group: io.grpc Name: grpc-context Version: 1.19.0 +37. Group: commons-logging Name: commons-logging Version: 1.2 -POM Project URL: https://github.com/grpc/grpc-java +Project URL: http://commons.apache.org/proper/commons-logging/ -POM License: Apache 2.0 - https://opensource.org/licenses/Apache-2.0 +Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0.txt --------------------------------------------------------------------------------- +POM License: The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt -35. Group: io.grpc Name: grpc-core Version: 1.19.0 +Embedded license: -POM Project URL: https://github.com/grpc/grpc-java + **************************************** -POM License: Apache 2.0 - https://opensource.org/licenses/Apache-2.0 --------------------------------------------------------------------------------- + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -36. Group: io.grpc Name: grpc-netty Version: 1.19.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -POM Project URL: https://github.com/grpc/grpc-java + 1. Definitions. -POM License: Apache 2.0 - https://opensource.org/licenses/Apache-2.0 + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. --------------------------------------------------------------------------------- + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. -37. Group: io.grpc Name: grpc-protobuf Version: 1.14.0 + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. -POM Project URL: https://github.com/grpc/grpc-java + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. -POM License: Apache 2.0 - https://opensource.org/licenses/Apache-2.0 + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. --------------------------------------------------------------------------------- + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. -38. Group: io.grpc Name: grpc-protobuf-lite Version: 1.14.0 + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). -POM Project URL: https://github.com/grpc/grpc-java + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. -POM License: Apache 2.0 - https://opensource.org/licenses/Apache-2.0 + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." --------------------------------------------------------------------------------- + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. -39. Group: io.grpc Name: grpc-stub Version: 1.19.0 + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. -POM Project URL: https://github.com/grpc/grpc-java + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. -POM License: Apache 2.0 - https://opensource.org/licenses/Apache-2.0 + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: --------------------------------------------------------------------------------- + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and -40. Group: io.jaegertracing Name: jaeger-client Version: 0.33.1 + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and -POM Project URL: https://github.com/jaegertracing/jaeger-client-java + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and -POM License: Apache 2.0 License - http://www.apache.org/licenses/LICENSE-2.0 + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. --------------------------------------------------------------------------------- + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. -41. Group: io.jaegertracing Name: jaeger-core Version: 0.33.1 + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. -POM Project URL: https://github.com/jaegertracing/jaeger-client-java + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. -POM License: Apache 2.0 License - http://www.apache.org/licenses/LICENSE-2.0 + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. --------------------------------------------------------------------------------- + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. -42. Group: io.jaegertracing Name: jaeger-thrift Version: 0.33.1 + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. -POM Project URL: https://github.com/jaegertracing/jaeger-client-java + END OF TERMS AND CONDITIONS -POM License: Apache 2.0 License - http://www.apache.org/licenses/LICENSE-2.0 + APPENDIX: How to apply the Apache License to your work. --------------------------------------------------------------------------------- + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. -43. Group: io.jaegertracing Name: jaeger-tracerresolver Version: 0.33.1 + Copyright [yyyy] [name of copyright owner] -POM Project URL: https://github.com/jaegertracing/jaeger-client-java + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at -POM License: Apache 2.0 License - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 --------------------------------------------------------------------------------- + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. -44. Group: io.jsonwebtoken Name: jjwt-api Version: 0.10.5 + **************************************** + +Apache Commons Logging +Copyright 2003-2014 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). -POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 -------------------------------------------------------------------------------- -45. Group: io.jsonwebtoken Name: jjwt-impl Version: 0.10.5 +38. Group: commons-net Name: commons-net Version: 3.3 -POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 +Project URL: http://commons.apache.org/proper/commons-net/ --------------------------------------------------------------------------------- +Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0.txt -46. Group: io.jsonwebtoken Name: jjwt-jackson Version: 0.10.5 +POM License: The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt -POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 +Embedded license: --------------------------------------------------------------------------------- + **************************************** -47. Group: io.netty Name: netty-buffer Version: 4.1.32.Final -Manifest Project URL: http://netty.io/ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + 1. Definitions. --------------------------------------------------------------------------------- + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. -48. Group: io.netty Name: netty-codec Version: 4.1.32.Final + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. -Manifest Project URL: http://netty.io/ + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. -Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. -POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. --------------------------------------------------------------------------------- + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. -49. Group: io.netty Name: netty-codec-http Version: 4.1.32.Final + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). -Manifest Project URL: http://netty.io/ + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. -Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." -POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. --------------------------------------------------------------------------------- + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. -50. Group: io.netty Name: netty-codec-http2 Version: 4.1.32.Final + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. -Manifest Project URL: http://netty.io/ + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: -Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and -POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and --------------------------------------------------------------------------------- + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and -51. Group: io.netty Name: netty-codec-socks Version: 4.1.32.Final + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. -Manifest Project URL: http://netty.io/ + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. -Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. -POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. --------------------------------------------------------------------------------- + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. -52. Group: io.netty Name: netty-common Version: 4.1.32.Final + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. -Manifest Project URL: http://netty.io/ + END OF TERMS AND CONDITIONS -Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + APPENDIX: How to apply the Apache License to your work. -POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. --------------------------------------------------------------------------------- + Copyright [yyyy] [name of copyright owner] -53. Group: io.netty Name: netty-handler Version: 4.1.32.Final + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at -Manifest Project URL: http://netty.io/ + http://www.apache.org/licenses/LICENSE-2.0 -Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. -POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + **************************************** --------------------------------------------------------------------------------- +Apache Commons Net +Copyright 2001-2013 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). -54. Group: io.netty Name: netty-handler-proxy Version: 4.1.32.Final +-------------------------------------------------------------------------------- -Manifest Project URL: http://netty.io/ +39. Group: io.grpc Name: grpc-context Version: 1.19.0 -Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 +POM Project URL: https://github.com/grpc/grpc-java -POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 +POM License: Apache 2.0 - https://opensource.org/licenses/Apache-2.0 -------------------------------------------------------------------------------- -55. Group: io.netty Name: netty-resolver Version: 4.1.32.Final +40. Group: io.grpc Name: grpc-core Version: 1.19.0 -Manifest Project URL: http://netty.io/ - -Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 +POM Project URL: https://github.com/grpc/grpc-java -POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 +POM License: Apache 2.0 - https://opensource.org/licenses/Apache-2.0 -------------------------------------------------------------------------------- -56. Group: io.netty Name: netty-transport Version: 4.1.32.Final - -Manifest Project URL: http://netty.io/ +41. Group: io.grpc Name: grpc-netty Version: 1.19.0 -Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 +POM Project URL: https://github.com/grpc/grpc-java -POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 +POM License: Apache 2.0 - https://opensource.org/licenses/Apache-2.0 -------------------------------------------------------------------------------- -57. Group: io.opencensus Name: opencensus-api Version: 0.22.1 +42. Group: io.grpc Name: grpc-protobuf Version: 1.14.0 -POM Project URL: https://github.com/census-instrumentation/opencensus-java +POM Project URL: https://github.com/grpc/grpc-java -POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt +POM License: Apache 2.0 - https://opensource.org/licenses/Apache-2.0 -------------------------------------------------------------------------------- -58. Group: io.opencensus Name: opencensus-contrib-grpc-metrics Version: 0.19.2 +43. Group: io.grpc Name: grpc-protobuf-lite Version: 1.14.0 -POM Project URL: https://github.com/census-instrumentation/opencensus-java +POM Project URL: https://github.com/grpc/grpc-java -POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt +POM License: Apache 2.0 - https://opensource.org/licenses/Apache-2.0 -------------------------------------------------------------------------------- -59. Group: io.opencensus Name: opencensus-contrib-resource-util Version: 0.22.1 +44. Group: io.grpc Name: grpc-stub Version: 1.19.0 -POM Project URL: https://github.com/census-instrumentation/opencensus-java +POM Project URL: https://github.com/grpc/grpc-java -POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt +POM License: Apache 2.0 - https://opensource.org/licenses/Apache-2.0 -------------------------------------------------------------------------------- -60. Group: io.opencensus Name: opencensus-exporter-metrics-ocagent Version: 0.22.1 +45. Group: io.jaegertracing Name: jaeger-client Version: 0.33.1 -POM Project URL: https://github.com/census-instrumentation/opencensus-java +POM Project URL: https://github.com/jaegertracing/jaeger-client-java -POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt +POM License: Apache 2.0 License - http://www.apache.org/licenses/LICENSE-2.0 -------------------------------------------------------------------------------- -61. Group: io.opencensus Name: opencensus-exporter-metrics-util Version: 0.22.1 +46. Group: io.jaegertracing Name: jaeger-core Version: 0.33.1 -POM Project URL: https://github.com/census-instrumentation/opencensus-java +POM Project URL: https://github.com/jaegertracing/jaeger-client-java -POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt +POM License: Apache 2.0 License - http://www.apache.org/licenses/LICENSE-2.0 -------------------------------------------------------------------------------- -62. Group: io.opencensus Name: opencensus-exporter-stats-prometheus Version: 0.22.1 +47. Group: io.jaegertracing Name: jaeger-thrift Version: 0.33.1 -POM Project URL: https://github.com/census-instrumentation/opencensus-java +POM Project URL: https://github.com/jaegertracing/jaeger-client-java -POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt +POM License: Apache 2.0 License - http://www.apache.org/licenses/LICENSE-2.0 -------------------------------------------------------------------------------- -63. Group: io.opencensus Name: opencensus-exporter-trace-jaeger Version: 0.22.1 +48. Group: io.jaegertracing Name: jaeger-tracerresolver Version: 0.33.1 -POM Project URL: https://github.com/census-instrumentation/opencensus-java +POM Project URL: https://github.com/jaegertracing/jaeger-client-java -POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt +POM License: Apache 2.0 License - http://www.apache.org/licenses/LICENSE-2.0 -------------------------------------------------------------------------------- -64. Group: io.opencensus Name: opencensus-exporter-trace-ocagent Version: 0.22.1 +49. Group: io.jsonwebtoken Name: jjwt-api Version: 0.10.5 -POM Project URL: https://github.com/census-instrumentation/opencensus-java +POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + +-------------------------------------------------------------------------------- + +50. Group: io.jsonwebtoken Name: jjwt-impl Version: 0.10.5 + +POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + +-------------------------------------------------------------------------------- + +51. Group: io.jsonwebtoken Name: jjwt-jackson Version: 0.10.5 + +POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + +-------------------------------------------------------------------------------- + +52. Group: io.micrometer Name: micrometer-core Version: 1.1.4 + +POM Project URL: https://github.com/micrometer-metrics/micrometer + +POM License: The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + +-------------------------------------------------------------------------------- + +53. Group: io.netty Name: netty-buffer Version: 4.1.32.Final + +Manifest Project URL: http://netty.io/ + +Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + +POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + +-------------------------------------------------------------------------------- + +54. Group: io.netty Name: netty-codec Version: 4.1.32.Final + +Manifest Project URL: http://netty.io/ + +Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + +POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + +-------------------------------------------------------------------------------- + +55. Group: io.netty Name: netty-codec-http Version: 4.1.32.Final + +Manifest Project URL: http://netty.io/ + +Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + +POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + +-------------------------------------------------------------------------------- + +56. Group: io.netty Name: netty-codec-http2 Version: 4.1.32.Final + +Manifest Project URL: http://netty.io/ + +Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + +POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + +-------------------------------------------------------------------------------- + +57. Group: io.netty Name: netty-codec-socks Version: 4.1.32.Final + +Manifest Project URL: http://netty.io/ + +Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + +POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + +-------------------------------------------------------------------------------- + +58. Group: io.netty Name: netty-common Version: 4.1.32.Final + +Manifest Project URL: http://netty.io/ + +Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + +POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + +-------------------------------------------------------------------------------- + +59. Group: io.netty Name: netty-handler Version: 4.1.32.Final + +Manifest Project URL: http://netty.io/ + +Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + +POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + +-------------------------------------------------------------------------------- + +60. Group: io.netty Name: netty-handler-proxy Version: 4.1.32.Final + +Manifest Project URL: http://netty.io/ + +Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + +POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + +-------------------------------------------------------------------------------- + +61. Group: io.netty Name: netty-resolver Version: 4.1.32.Final + +Manifest Project URL: http://netty.io/ + +Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + +POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + +-------------------------------------------------------------------------------- + +62. Group: io.netty Name: netty-transport Version: 4.1.32.Final + +Manifest Project URL: http://netty.io/ + +Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0 + +POM License: Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 + +-------------------------------------------------------------------------------- + +63. Group: io.opencensus Name: opencensus-api Version: 0.22.1.ocelot.1 + +POM Project URL: https://github.com/inspectIT/opencensus-java POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt -------------------------------------------------------------------------------- -65. Group: io.opencensus Name: opencensus-exporter-trace-util Version: 0.22.1 +64. Group: io.opencensus Name: opencensus-contrib-grpc-metrics Version: 0.19.2 POM Project URL: https://github.com/census-instrumentation/opencensus-java @@ -1482,31 +1899,87 @@ POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LI -------------------------------------------------------------------------------- -66. Group: io.opencensus Name: opencensus-exporter-trace-zipkin Version: 0.22.1 +65. Group: io.opencensus Name: opencensus-contrib-resource-util Version: 0.22.1.ocelot.1 -POM Project URL: https://github.com/census-instrumentation/opencensus-java +POM Project URL: https://github.com/inspectIT/opencensus-java POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt -------------------------------------------------------------------------------- -67. Group: io.opencensus Name: opencensus-impl Version: 0.22.1 +66. Group: io.opencensus Name: opencensus-exporter-metrics-ocagent Version: 0.22.1.ocelot.1 -POM Project URL: https://github.com/census-instrumentation/opencensus-java +POM Project URL: https://github.com/inspectIT/opencensus-java POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt -------------------------------------------------------------------------------- -68. Group: io.opencensus Name: opencensus-impl-core Version: 0.22.1 +67. Group: io.opencensus Name: opencensus-exporter-metrics-util Version: 0.22.1.ocelot.1 -POM Project URL: https://github.com/census-instrumentation/opencensus-java +POM Project URL: https://github.com/inspectIT/opencensus-java + +POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + +-------------------------------------------------------------------------------- + +68. Group: io.opencensus Name: opencensus-exporter-stats-prometheus Version: 0.22.1.ocelot.1 + +POM Project URL: https://github.com/inspectIT/opencensus-java + +POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + +-------------------------------------------------------------------------------- + +69. Group: io.opencensus Name: opencensus-exporter-trace-jaeger Version: 0.22.1.ocelot.1 + +POM Project URL: https://github.com/inspectIT/opencensus-java + +POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + +-------------------------------------------------------------------------------- + +70. Group: io.opencensus Name: opencensus-exporter-trace-ocagent Version: 0.22.1.ocelot.1 + +POM Project URL: https://github.com/inspectIT/opencensus-java + +POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + +-------------------------------------------------------------------------------- + +71. Group: io.opencensus Name: opencensus-exporter-trace-util Version: 0.22.1.ocelot.1 + +POM Project URL: https://github.com/inspectIT/opencensus-java + +POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + +-------------------------------------------------------------------------------- + +72. Group: io.opencensus Name: opencensus-exporter-trace-zipkin Version: 0.22.1.ocelot.1 + +POM Project URL: https://github.com/inspectIT/opencensus-java + +POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + +-------------------------------------------------------------------------------- + +73. Group: io.opencensus Name: opencensus-impl Version: 0.22.1.ocelot.1 + +POM Project URL: https://github.com/inspectIT/opencensus-java + +POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + +-------------------------------------------------------------------------------- + +74. Group: io.opencensus Name: opencensus-impl-core Version: 0.22.1.ocelot.1 + +POM Project URL: https://github.com/inspectIT/opencensus-java POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt -------------------------------------------------------------------------------- -69. Group: io.opencensus Name: opencensus-proto Version: 0.2.0 +75. Group: io.opencensus Name: opencensus-proto Version: 0.2.0 POM Project URL: https://github.com/census-instrumentation/opencensus-proto @@ -1514,31 +1987,31 @@ POM License: The Apache License, Version 2.0 - http://www.apache.org/licenses/LI -------------------------------------------------------------------------------- -70. Group: io.opentracing Name: opentracing-api Version: 0.31.0 +76. Group: io.opentracing Name: opentracing-api Version: 0.31.0 POM License: The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt -------------------------------------------------------------------------------- -71. Group: io.opentracing Name: opentracing-noop Version: 0.31.0 +77. Group: io.opentracing Name: opentracing-noop Version: 0.31.0 POM License: The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt -------------------------------------------------------------------------------- -72. Group: io.opentracing Name: opentracing-util Version: 0.31.0 +78. Group: io.opentracing Name: opentracing-util Version: 0.31.0 POM License: The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt -------------------------------------------------------------------------------- -73. Group: io.opentracing.contrib Name: opentracing-tracerresolver Version: 0.1.5 +79. Group: io.opentracing.contrib Name: opentracing-tracerresolver Version: 0.1.5 POM License: Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt -------------------------------------------------------------------------------- -74. Group: io.prometheus Name: simpleclient Version: 0.6.0 +80. Group: io.prometheus Name: simpleclient Version: 0.6.0 Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0.txt @@ -1546,7 +2019,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -75. Group: io.prometheus Name: simpleclient_common Version: 0.6.0 +81. Group: io.prometheus Name: simpleclient_common Version: 0.6.0 Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0.txt @@ -1554,7 +2027,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -76. Group: io.prometheus Name: simpleclient_httpserver Version: 0.6.0 +82. Group: io.prometheus Name: simpleclient_httpserver Version: 0.6.0 Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0.txt @@ -1562,7 +2035,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -77. Group: io.springfox Name: springfox-core Version: 2.9.2 +83. Group: io.springfox Name: springfox-core Version: 2.9.2 POM Project URL: https://github.com/springfox/springfox @@ -1570,7 +2043,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -78. Group: io.springfox Name: springfox-schema Version: 2.9.2 +84. Group: io.springfox Name: springfox-schema Version: 2.9.2 POM Project URL: https://github.com/springfox/springfox @@ -1578,7 +2051,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -79. Group: io.springfox Name: springfox-spi Version: 2.9.2 +85. Group: io.springfox Name: springfox-spi Version: 2.9.2 POM Project URL: https://github.com/springfox/springfox @@ -1586,7 +2059,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -80. Group: io.springfox Name: springfox-spring-web Version: 2.9.2 +86. Group: io.springfox Name: springfox-spring-web Version: 2.9.2 POM Project URL: https://github.com/springfox/springfox @@ -1594,7 +2067,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -81. Group: io.springfox Name: springfox-swagger-common Version: 2.9.2 +87. Group: io.springfox Name: springfox-swagger-common Version: 2.9.2 POM Project URL: https://github.com/springfox/springfox @@ -1602,7 +2075,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -82. Group: io.springfox Name: springfox-swagger-ui Version: 2.9.2 +88. Group: io.springfox Name: springfox-swagger-ui Version: 2.9.2 POM Project URL: https://github.com/springfox/springfox @@ -1610,7 +2083,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -83. Group: io.springfox Name: springfox-swagger2 Version: 2.9.2 +89. Group: io.springfox Name: springfox-swagger2 Version: 2.9.2 POM Project URL: https://github.com/springfox/springfox @@ -1618,7 +2091,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -84. Group: io.swagger Name: swagger-annotations Version: 1.5.20 +90. Group: io.swagger Name: swagger-annotations Version: 1.5.20 Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0.html @@ -1626,7 +2099,7 @@ POM License: Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0.htm -------------------------------------------------------------------------------- -85. Group: io.swagger Name: swagger-models Version: 1.5.20 +91. Group: io.swagger Name: swagger-models Version: 1.5.20 Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0.html @@ -1634,7 +2107,7 @@ POM License: Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0.htm -------------------------------------------------------------------------------- -86. Group: io.zipkin.reporter2 Name: zipkin-reporter Version: 2.7.14 +92. Group: io.zipkin.reporter2 Name: zipkin-reporter Version: 2.7.14 Manifest Project URL: http://zipkin.io/ @@ -1644,7 +2117,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -87. Group: io.zipkin.reporter2 Name: zipkin-sender-urlconnection Version: 2.7.14 +93. Group: io.zipkin.reporter2 Name: zipkin-sender-urlconnection Version: 2.7.14 Manifest Project URL: http://zipkin.io/ @@ -1654,7 +2127,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -88. Group: io.zipkin.zipkin2 Name: zipkin Version: 2.12.0 +94. Group: io.zipkin.zipkin2 Name: zipkin Version: 2.12.0 Manifest Project URL: http://zipkin.io/ @@ -1664,7 +2137,7 @@ POM License: The Apache Software License, Version 2.0 - http://www.apache.org/li -------------------------------------------------------------------------------- -89. Group: javax.activation Name: javax.activation-api Version: 1.2.0 +95. Group: javax.activation Name: javax.activation-api Version: 1.2.0 Manifest Project URL: http://www.oracle.com @@ -2438,7 +2911,7 @@ from your version. -------------------------------------------------------------------------------- -90. Group: javax.annotation Name: javax.annotation-api Version: 1.3.2 +96. Group: javax.annotation Name: javax.annotation-api Version: 1.3.2 Manifest Project URL: https://javaee.github.io/glassfish @@ -2718,7 +3191,7 @@ As a special exception, the copyright holders of this library give you permissio -------------------------------------------------------------------------------- -91. Group: javax.persistence Name: javax.persistence-api Version: 2.2 +97. Group: javax.persistence Name: javax.persistence-api Version: 2.2 Manifest license URL: http://www.eclipse.org/legal/epl-v10.html, http://www.eclipse.org/org/documents/edl-v10.php @@ -2730,7 +3203,7 @@ POM License: Eclipse Public License v1.0 - http://www.eclipse.org/legal/epl-v10. -------------------------------------------------------------------------------- -92. Group: javax.transaction Name: javax.transaction-api Version: 1.3 +98. Group: javax.transaction Name: javax.transaction-api Version: 1.3 Manifest Project URL: https://glassfish.java.net @@ -3010,7 +3483,7 @@ As a special exception, the copyright holders of this library give you permissio -------------------------------------------------------------------------------- -93. Group: javax.validation Name: validation-api Version: 2.0.1.Final +99. Group: javax.validation Name: validation-api Version: 2.0.1.Final Manifest license URL: http://www.apache.org/licenses/LICENSE-2.0.txt @@ -3029,7 +3502,7 @@ See the license.txt file in the root directory or getAllYamlFiles(String path) throws IOException { + private List getAllYamlFiles(String path) { String cleanedPath; if (path.startsWith("/")) { cleanedPath = path.substring(1); } else { cleanedPath = path; } - if (fileManager.exists(cleanedPath)) { - if (fileManager.isDirectory(cleanedPath)) { - return fileManager.getFilesInDirectory(cleanedPath, true).stream() - .flatMap(f -> f.getAbsoluteFilePaths(cleanedPath)) + + AbstractWorkingDirectoryAccessor workingDirectory = fileManager.getWorkingDirectory(); + + if (workingDirectory.configurationFileExists(cleanedPath)) { + if (workingDirectory.configurationFileIsDirectory(cleanedPath)) { + List fileInfos = workingDirectory.listConfigurationFiles(cleanedPath); + + return fileInfos.stream() + .flatMap(file -> file.getAbsoluteFilePaths(cleanedPath)) .filter(HAS_YAML_ENDING) .sorted() .collect(Collectors.toList()); @@ -157,11 +164,11 @@ private List getAllYamlFiles(String path) throws IOException { * @param toMerge the existing structure of nested maps / lists with which the loaded yaml will be merged. * @param path the path of the yaml file to load * @return the merged structure - * @throws IOException in case an error occurs while loading the file */ - private Object loadAndMergeYaml(Object toMerge, String path) throws IOException { + private Object loadAndMergeYaml(Object toMerge, String path) { Yaml yaml = new Yaml(); - String src = fileManager.readFile(path); + String src = fileManager.getWorkingDirectory().readConfigurationFile(path).orElse(""); + try { Object loadedYaml = yaml.load(src); if (toMerge == null) { diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/agentstatus/AgentMetaInformation.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/agentstatus/AgentMetaInformation.java index 45b715bc51..9a2c93f97a 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/agentstatus/AgentMetaInformation.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/agentstatus/AgentMetaInformation.java @@ -1,13 +1,11 @@ package rocks.inspectit.ocelot.agentstatus; import com.google.common.base.Preconditions; -import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Value; import org.apache.commons.lang3.StringUtils; import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; import java.util.Map; /** diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ActionInputAutoCompleter.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ActionInputAutoCompleter.java index c2ecdac406..bccbbfb568 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ActionInputAutoCompleter.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ActionInputAutoCompleter.java @@ -87,7 +87,6 @@ private static void setupUsagePaths() { * Ignores action inputs that start with the String defined in INPUT_FILTER_ONSET. * * @param path A given path as List. Each String should act as a literal of the path. - * * @return A List of Strings resembling the declared actions that could be used with the given path. */ @Override @@ -105,7 +104,6 @@ public List getSuggestions(List path) { * INPUT_FILTER_ONSET. * * @param path A given path as List. Each String should act as a literal of the path. - * * @return A List of Strings resembling the declared actions that could be used with the given path. */ private List getActionInputs(List path) { @@ -125,7 +123,6 @@ private List getActionInputs(List path) { * ["inspectit","instrumentation","actions","action_B","input"] is returned. * * @param path A given path as List. Each String should act as a literal of the path. - * * @return A List containing String Lists. Each of these Lists resemble one path as described. */ private List> buildActionPaths(List path) { @@ -145,7 +142,6 @@ private List> buildActionPaths(List path) { * found under "inspectit.instrumentation.rules.my-rule.entry.my-entry-method.action" are returned. * * @param path A List of Strings resembling a path to a rule-input. - * * @return The actions declared in this rule. */ private List getActions(List path) { diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/autocomplete/util/ConfigurationFilesCache.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/autocomplete/util/ConfigurationFilesCache.java index bedc47d255..5b61201482 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/autocomplete/util/ConfigurationFilesCache.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/autocomplete/util/ConfigurationFilesCache.java @@ -8,14 +8,12 @@ import org.yaml.snakeyaml.Yaml; import rocks.inspectit.ocelot.config.loaders.ConfigFileLoader; import rocks.inspectit.ocelot.file.FileChangedEvent; +import rocks.inspectit.ocelot.file.FileInfo; import rocks.inspectit.ocelot.file.FileManager; import javax.annotation.PostConstruct; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -97,17 +95,12 @@ private Object parseYaml(String content) { */ @VisibleForTesting Object loadYamlFile(String path) { - String src; - try { - src = fileManager.readFile(path); - } catch (IOException e) { + Optional src = fileManager.getWorkingDirectory().readConfigurationFile(path); + + return src.map(this::parseYaml).orElseGet(() -> { log.warn("Unable to load file with path {}", path); return null; - } - if (src != null) { - return parseYaml(src); - } - return null; + }); } /** @@ -119,8 +112,10 @@ Object loadYamlFile(String path) { @VisibleForTesting List getAllPaths() { try { - return fileManager.getFilesInDirectory("", true).stream() - .flatMap(f -> f.getAbsoluteFilePaths("")) + List fileInfos = fileManager.getWorkingDirectory().listConfigurationFiles(""); + + return fileInfos.stream() + .flatMap(file -> file.getAbsoluteFilePaths("")) .filter(HAS_YAML_ENDING) .sorted() .collect(Collectors.toList()); diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/config/BeanConfiguration.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/config/BeanConfiguration.java index 1a9bf58af1..9163f27234 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/config/BeanConfiguration.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/config/BeanConfiguration.java @@ -1,15 +1,8 @@ package rocks.inspectit.ocelot.config; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.ldap.core.support.LdapContextSource; -import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; -import org.springframework.security.ldap.search.LdapUserSearch; -import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator; -import org.springframework.security.ldap.userdetails.LdapUserDetailsService; import rocks.inspectit.ocelot.config.model.InspectitServerSettings; -import rocks.inspectit.ocelot.config.model.LdapSettings; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/config/model/DefaultUserSettings.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/config/model/DefaultUserSettings.java index b3f0a08be2..46ea43df8e 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/config/model/DefaultUserSettings.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/config/model/DefaultUserSettings.java @@ -18,5 +18,5 @@ public class DefaultUserSettings { private String name; private String password; - + } diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/config/model/InspectitServerSettings.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/config/model/InspectitServerSettings.java index 00179827ec..55621af5d8 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/config/model/InspectitServerSettings.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/config/model/InspectitServerSettings.java @@ -57,5 +57,5 @@ public class InspectitServerSettings { @Valid @Builder.Default - private SecuritySettings security = SecuritySettings.builder().build(); + private SecuritySettings security = SecuritySettings.builder().build(); } diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/error/ApiError.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/error/ApiError.java index 2e8d237051..bd389ed891 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/error/ApiError.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/error/ApiError.java @@ -1,7 +1,6 @@ package rocks.inspectit.ocelot.error; import com.fasterxml.jackson.annotation.JsonFormat; -import lombok.Builder; import lombok.Data; import org.springframework.http.HttpStatus; diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/FileInfo.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/FileInfo.java index e66598c25b..b6e8bb252d 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/FileInfo.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/FileInfo.java @@ -2,14 +2,19 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; +import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; @Data @Builder +@NoArgsConstructor +@AllArgsConstructor public class FileInfo { public enum Type { @@ -28,7 +33,7 @@ public enum Type { /** * If this is a file, its absolute path is returned given the containing directory. - * Otherwise the absolute paths of all (transitively) contianed files (not directories) is returned. + * Otherwise the absolute paths of all (transitively) contained files (not directories) is returned. * * @param containingDir the path directory containing this file, can be empty if this file is at the root * @return a stream of absolute paths @@ -43,4 +48,16 @@ public Stream getAbsoluteFilePaths(String containingDir) { return Stream.of(absolutePath); } } + + /** + * Adds a child and initializes the children list in case it does not exist. + * + * @param fileInfo the child to add + */ + synchronized void addChild(FileInfo fileInfo) { + if (children == null) { + children = new ArrayList<>(); + } + children.add(fileInfo); + } } diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/FileInfoVisitor.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/FileInfoVisitor.java new file mode 100644 index 0000000000..2dda8f63af --- /dev/null +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/FileInfoVisitor.java @@ -0,0 +1,90 @@ +package rocks.inspectit.ocelot.file; + +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Collections; +import java.util.List; +import java.util.Stack; + +/** + * FileVisitor for walking a file tree and creating a {@link FileInfo} representation of it. + */ +@Slf4j +public class FileInfoVisitor implements FileVisitor { + + /** + * The directory stack. The latest directory is the current one. + */ + private Stack directoryStack = new Stack<>(); + + /** + * The {@link FileInfo} which represents the starting directory of the walk. + */ + private FileInfo rootDirectory; + + @Override + public FileVisitResult preVisitDirectory(Path directory, BasicFileAttributes attrs) { + FileInfo currentDirectory = FileInfo.builder() + .name(directory.getFileName().toString()) + .type(FileInfo.Type.DIRECTORY) + .build(); + + // add directory as child, otherwise set it as root + if (directoryStack.isEmpty()) { + rootDirectory = currentDirectory; + } else { + directoryStack.peek().addChild(currentDirectory); + } + + directoryStack.add(currentDirectory); + + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + // adding each file to the current directory + FileInfo fileInfo = FileInfo.builder() + .name(file.getFileName().toString()) + .type(FileInfo.Type.FILE) + .build(); + + directoryStack.peek().addChild(fileInfo); + + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) { + log.error("Could not visit file '{}'.", file, exc); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) { + // leaving directory, so set stack to the parent directory + directoryStack.pop(); + return FileVisitResult.CONTINUE; + } + + /** + * @return Returns the list of visited files and directories. + */ + public List getFileInfos() { + if (rootDirectory == null) { + return Collections.emptyList(); + } else { + List children = rootDirectory.getChildren(); + if (children == null) { + return Collections.emptyList(); + } else { + return children; + } + } + } +} diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/FileManager.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/FileManager.java index 45c2592263..d32e9b1121 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/FileManager.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/FileManager.java @@ -1,23 +1,15 @@ package rocks.inspectit.ocelot.file; -import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.FileUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; import rocks.inspectit.ocelot.config.model.InspectitServerSettings; +import rocks.inspectit.ocelot.file.accessor.workingdirectory.AbstractWorkingDirectoryAccessor; +import rocks.inspectit.ocelot.file.accessor.workingdirectory.WorkingDirectoryAccessor; -import javax.annotation.PostConstruct; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.nio.file.*; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.nio.file.Path; +import java.nio.file.Paths; /** * Encapsulates access to the file system storing the source config files managed by this server. @@ -26,244 +18,21 @@ @Slf4j public class FileManager { - /** - * The subfolder within the working directory which acts as - * filesRoot for the files and directories managed by this class. - */ - @VisibleForTesting - static final String FILES_SUBFOLDER = "files"; - - @VisibleForTesting - static final Charset ENCODING = StandardCharsets.UTF_8; - - @VisibleForTesting - @Autowired - InspectitServerSettings config; + private WorkingDirectoryAccessor workingDirectoryAccessor; @Autowired - private ApplicationEventPublisher eventPublisher; - - /** - * The path under which the file system accessible by this component lies. - * This is the absolute, normalized path represented by {@link InspectitServerSettings#getWorkingDirectory()} with {@link #FILES_SUBFOLDER} appended. - */ - private Path filesRoot; - - @PostConstruct - @VisibleForTesting - void init() throws IOException { - filesRoot = Paths.get(config.getWorkingDirectory()).resolve(FILES_SUBFOLDER).toAbsolutePath().normalize(); - Files.createDirectories(filesRoot); - } - - /** - * Returns all files recursively contained under the given path. - * - * @param path the path of the directory - * @param recursive if true, the entire file tree within this directory is returned. Otherwise only the direct children. - * @return the directory contents - * @throws IOException If the input was not valid or something in the filesystem went wrong - */ - public synchronized List getFilesInDirectory(String path, boolean recursive) throws IOException { - Path dir; - if (StringUtils.isEmpty(path)) { - dir = filesRoot; - } else { - assertPathWithinFilesRoot(path); - dir = filesRoot.resolve(path); - } - try (Stream files = Files.list(dir)) { - List result = new ArrayList<>(); - for (Path child : files.collect(Collectors.toList())) { - boolean isDirectory = Files.isDirectory(child); - FileInfo.FileInfoBuilder builder = FileInfo.builder() - .name(child.getFileName().toString()) - .type(isDirectory ? FileInfo.Type.DIRECTORY : FileInfo.Type.FILE); - if (isDirectory && recursive) { - builder.children(getFilesInDirectory(getRelativePath(child), true)); - } - result.add(builder.build()); - } - return result; - } - } - - /** - * @param path the path to check - * @return true if the given path denotes a directory - * @throws AccessDeniedException if access is forbidden - */ - public boolean isDirectory(String path) throws AccessDeniedException { - assertPathWithinFilesRoot(path); - return Files.isDirectory(filesRoot.resolve(path)); - } - - /** - * @param path the path to check - * @return true if the given path denotes an existing file or directory - * @throws AccessDeniedException if access is forbidden - */ - public boolean exists(String path) throws AccessDeniedException { - assertPathWithinFilesRoot(path); - return Files.exists(filesRoot.resolve(path)); - } - - /** - * Creates a new directory with the given path - * - * @param path the path of the directory to create - * @throws IOException if the directory already exists or could not be created for any reason - */ - public synchronized void createDirectory(String path) throws IOException { - assertValidSubPath(path); - Path dir = filesRoot.resolve(path); - - FileUtils.forceMkdir(dir.toFile()); - fireFileChangeEvent(); - } - - /** - * Deletes the given directory including all contents. - * - * @param path the path of the directory - * @throws IOException if the directory could not be deleted - */ - public synchronized void deleteDirectory(String path) throws IOException { - assertValidSubPath(path); - Path dir = filesRoot.resolve(path); - // throw a more meaningful exception instead of the illegal argument exception thrown by - // FileUtils.deleteDirectory - if (!Files.exists(dir) || !Files.isDirectory(dir)) { - throw new NotDirectoryException(getRelativePath(dir)); - } - FileUtils.deleteDirectory(dir.toFile()); - fireFileChangeEvent(); - } + public FileManager(InspectitServerSettings settings, ApplicationEventPublisher eventPublisher) { + Path workingDirectory = Paths.get(settings.getWorkingDirectory()).toAbsolutePath().normalize(); - /** - * Reads the given file content into a string - * - * @param path the path of the file - * @return the files content - * @throws IOException if the file could not be read - */ - public synchronized String readFile(String path) throws IOException { - assertValidSubPath(path); - Path file = filesRoot.resolve(path); - if (Files.exists(file) && !Files.isRegularFile(file)) { - throw new AccessDeniedException(path + " is a directory!"); - } - return new String(Files.readAllBytes(file), ENCODING); - } - - /** - * Creates or replaces the file under the given path with the given content. - * If required, parent directories are automatically created. - * - * @param path the path of the file - * @param content the content of the file - * @throws IOException if the file could not be written - */ - public synchronized void createOrReplaceFile(String path, String content) throws IOException { - assertValidSubPath(path); - Path file = filesRoot.resolve(path); - if (Files.exists(file) && !Files.isRegularFile(file)) { - throw new AccessDeniedException(path + " is a directory!"); - } - FileUtils.forceMkdir(file.getParent().toFile()); - Files.write(file, content.getBytes(ENCODING)); - fireFileChangeEvent(); - } - - /** - * Deletes the given file. Does not delete directories. - * - * @param path the path of the file to delete - * @throws IOException if the file could not be deleted. - */ - public synchronized void deleteFile(String path) throws IOException { - assertValidSubPath(path); - Path file = filesRoot.resolve(path); - if (Files.isRegularFile(file)) { - Files.delete(file); - fireFileChangeEvent(); - } else { - throw new AccessDeniedException(path); - } - } - - /** - * Moves and / or renames the given file or directories. - * Directories are moved including their contents. - * - * @param source the source file or directory path - * @param destination the target file or directory path - * @throws IOException if the given file or directory could not be renamed / moved - */ - public synchronized void move(String source, String destination) throws IOException { - assertValidSubPath(source); - assertValidSubPath(destination); - Path src = filesRoot.resolve(source); - Path dest = filesRoot.resolve(destination); - - FileUtils.forceMkdir(dest.getParent().toFile()); - - if (Files.isDirectory(src)) { - FileUtils.moveDirectory(src.toFile(), dest.toFile()); - } else { - FileUtils.moveFile(src.toFile(), dest.toFile()); - } - fireFileChangeEvent(); - } - - private void fireFileChangeEvent() { - eventPublisher.publishEvent(new FileChangedEvent(this)); - } - - private String getRelativePath(Path f) { - return filesRoot.relativize(f.normalize()) - .toString() - .replace(f.getFileSystem().getSeparator(), "/"); - } - - /** - * Ensures that the given path is a subpath of {@link #filesRoot} - * - * @param path the pat hto check - * @throws AccessDeniedException if the file is not a subpath of the filesRoot - */ - private void assertValidSubPath(String path) throws AccessDeniedException { - assertPathNotEmpty(path); - assertPathWithinFilesRoot(path); - } - - /** - * Ensures that the given path does not point to the filesRoot directory. - * - * @param path the path to test - * @throws AccessDeniedException thrown if the path points to the filesRoot - */ - private void assertPathNotEmpty(String path) throws AccessDeniedException { - if (StringUtils.isEmpty(path)) { - throw new AccessDeniedException("/"); - } - if (filesRoot.resolve(path).toAbsolutePath().normalize().equals(filesRoot.toAbsolutePath())) { - throw new AccessDeniedException("/"); - } + this.workingDirectoryAccessor = new WorkingDirectoryAccessor(workingDirectory, eventPublisher); } /** - * Ensures that the given path does not point to a file outside of the filesRoot directory. - * This method succeeds if the given path represents the filesRoot. + * Returns the file accessor to access the files in the working directory. * - * @param path the path to test - * @throws AccessDeniedException thrown if the path points to a file outside of the filesRoot + * @return an instance of {@link WorkingDirectoryAccessor} */ - private void assertPathWithinFilesRoot(String path) throws AccessDeniedException { - String subPath = filesRoot.resolve(path).toAbsolutePath().normalize().toString(); - String rootPath = filesRoot.toString(); - if (!subPath.startsWith(rootPath)) { - throw new AccessDeniedException(path); - } + public AbstractWorkingDirectoryAccessor getWorkingDirectory() { + return workingDirectoryAccessor; } } diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/accessor/AbstractFileAccessor.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/accessor/AbstractFileAccessor.java new file mode 100644 index 0000000000..be9cb3efe0 --- /dev/null +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/accessor/AbstractFileAccessor.java @@ -0,0 +1,130 @@ +package rocks.inspectit.ocelot.file.accessor; + +import lombok.extern.slf4j.Slf4j; +import rocks.inspectit.ocelot.file.FileInfo; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Optional; + +/** + * Abstract class providing a read access to the configuration files and agent mappings. + */ +@Slf4j +public abstract class AbstractFileAccessor { + + /** + * The default file encoding of all files. + */ + public static final Charset FILE_ENCODING = StandardCharsets.UTF_8; + + /** + * The file name of the agent mappings. + */ + public static final String AGENT_MAPPINGS_FILE_NAME = "agent_mappings.yaml"; + + /** + * The subfolder within the working directory which acts as + * filesRoot for the files and directories managed by this class. + */ + public static final String CONFIGURATION_FILES_SUBFOLDER = "files"; + + /** + * Verifies the given path and checks if it is located beneath the given base path. It has to be ensured, that + * the given path is not navigate out of the given base path, resulting in a traversal attack. A cleaned and + * sanitized path is returned of this method, which can safely be used be the concrete implementation for file + * handling. + * + * @param relativeBasePath the base path where the path must be in + * @param path the relative user path + * @return a sanitized representation of the given user path + * @throws IllegalArgumentException is thrown if the path is not valid. + */ + protected abstract String verifyPath(String relativeBasePath, String path) throws IllegalArgumentException; + + /** + * Reads and returns the file's content which is located under the given path. + * + * @param path the file to read + * @return the content of the file + */ + protected abstract Optional readFile(String path); + + /** + * Returns a list of all files and directories located under the specified path. + * + * @param path the root path to start listing + * @return a list of files and directories + */ + protected abstract List listFiles(String path); + + /** + * @return Checks whether the given path exists. + */ + protected abstract boolean exists(String path); + + /** + * @return Checks whether the given path is a directory. + */ + protected abstract boolean isDirectory(String path); + + /** + * Reads the file content of the specified configuration file. In case the file does not exist or cannot be read, + * the resulting {@link Optional} will be empty. + * + * @param file the configuration file to read + * @return the content of the specified file + */ + public Optional readConfigurationFile(String file) { + log.debug("Reading configuration file: {}", file); + String targetPath = verifyPath(CONFIGURATION_FILES_SUBFOLDER, file); + + Optional fileContent = readFile(targetPath); + + return fileContent.map(String::new); + } + + /** + * Lists all configuration files and directories which are located under the specified path + * (also in sub directories). The listing will be resolved recursively. + * + * @param path a list of the files in the specified directory + * @return a list of existing files and directories + */ + public List listConfigurationFiles(String path) { + log.debug("Listing configuration files: {}", path); + String targetPath = verifyPath(CONFIGURATION_FILES_SUBFOLDER, path); + + return listFiles(targetPath); + } + + /** + * @return Returns the content of the agent mappings file. + */ + public Optional readAgentMappings() { + log.debug("Reading agent mappings"); + + Optional fileContent = readFile(AGENT_MAPPINGS_FILE_NAME); + + return fileContent.map(bytes -> new String(bytes, FILE_ENCODING)); + } + + /** + * @return Returns whether the given path exists. + */ + public boolean configurationFileExists(String path) { + String targetPath = verifyPath(CONFIGURATION_FILES_SUBFOLDER, path); + + return exists(targetPath); + } + + /** + * @return Returns true if the given path is a directory, otherwise false. + */ + public boolean configurationFileIsDirectory(String path) { + String targetPath = verifyPath(CONFIGURATION_FILES_SUBFOLDER, path); + + return isDirectory(targetPath); + } +} diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/accessor/workingdirectory/AbstractWorkingDirectoryAccessor.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/accessor/workingdirectory/AbstractWorkingDirectoryAccessor.java new file mode 100644 index 0000000000..b8fdd403a0 --- /dev/null +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/accessor/workingdirectory/AbstractWorkingDirectoryAccessor.java @@ -0,0 +1,124 @@ +package rocks.inspectit.ocelot.file.accessor.workingdirectory; + +import lombok.extern.slf4j.Slf4j; +import rocks.inspectit.ocelot.file.accessor.AbstractFileAccessor; + +import java.io.IOException; +import java.nio.file.Paths; + +/** + * Abstract class for reading and modifying files in the server's working directory. + */ +@Slf4j +public abstract class AbstractWorkingDirectoryAccessor extends AbstractFileAccessor { + + /** + * Writes the given content to the specified file. The file will be overwritten, if it already exists. + * + * @param path the target file + * @param content the content to write + * @throws IOException in case the file can not be written + */ + protected abstract void writeFile(String path, String content) throws IOException; + + /** + * Creates the specified directory. + * + * @param path the directory to create + * @throws IOException in case the file can not be written + */ + protected abstract void createDirectory(String path) throws IOException; + + /** + * Moves the specified source path to the specified target - can be a file or directory. + * + * @param sourcePath the source path + * @param targetPath the target path + * @throws IOException in case the file or directory can not be moved + */ + protected abstract void move(String sourcePath, String targetPath) throws IOException; + + /** + * Deletes the specified path - can be a file or directory. + * + * @param path the path to delete + * @throws IOException in case the file or directory can not be deleted + */ + protected abstract void delete(String path) throws IOException; + + /** + * Creating a new configuration directory. + * + * @param directory the directory to create + * @throws IOException in case the directory can not be created + */ + public void createConfigurationDirectory(String directory) throws IOException { + log.debug("Creating configuration directory: {}", directory); + String targetPath = verifyPath(CONFIGURATION_FILES_SUBFOLDER, directory); + + if (Paths.get(directory).normalize().toString().isEmpty()) { + throw new IllegalArgumentException("Cannot create directory. It is equal to the working directory."); + } + + createDirectory(targetPath); + } + + /** + * Writes the given content to the specified configuration file. The file will be overwritten, if it already exists. + * If the file does not exist, it will be created. In case a directory exists at the target location, the operation + * will result in an exception. + * + * @param file the file to write + * @param content the content to write + * @throws IOException in case the file can not be written + */ + public void writeConfigurationFile(String file, String content) throws IOException { + log.debug("Writing configuration file: {}", file); + String targetPath = verifyPath(CONFIGURATION_FILES_SUBFOLDER, file); + + writeFile(targetPath, content); + } + + /** + * Deletes the specified configuration file or directory. + * + * @param path the configuration to delete + * @throws IOException in case the file or directory can not be deleted + */ + public void deleteConfiguration(String path) throws IOException { + log.debug("Deleting configuration: {}", path); + String targetPath = verifyPath(CONFIGURATION_FILES_SUBFOLDER, path); + + if (Paths.get(path).normalize().toString().isEmpty()) { + throw new IllegalArgumentException("Cannot delete base directory: " + path); + } + + delete(targetPath); + } + + /** + * Moves the specified source to the target location. The source can be a file or directory. + * + * @param source the source item to move + * @param target the target item + * @throws IOException in case the file or directory can not be moved + */ + public void moveConfiguration(String source, String target) throws IOException { + log.debug("Moving configuration: {} -> {}", source, target); + String sourcePath = verifyPath(CONFIGURATION_FILES_SUBFOLDER, source); + String targetPath = verifyPath(CONFIGURATION_FILES_SUBFOLDER, target); + + move(sourcePath, targetPath); + } + + /** + * Writes the given content to the agent mappings file. The current file will be overwritten. + * + * @param content the content to write + * @throws IOException in case the agent mappings can not be written + */ + public void writeAgentMappings(String content) throws IOException { + log.debug("Writing agent mappings"); + writeFile(AGENT_MAPPINGS_FILE_NAME, content); + } +} diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/accessor/workingdirectory/WorkingDirectoryAccessor.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/accessor/workingdirectory/WorkingDirectoryAccessor.java new file mode 100644 index 0000000000..9df5cfe349 --- /dev/null +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/accessor/workingdirectory/WorkingDirectoryAccessor.java @@ -0,0 +1,194 @@ +package rocks.inspectit.ocelot.file.accessor.workingdirectory; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import rocks.inspectit.ocelot.config.model.InspectitServerSettings; +import rocks.inspectit.ocelot.file.FileChangedEvent; +import rocks.inspectit.ocelot.file.FileInfo; +import rocks.inspectit.ocelot.file.FileInfoVisitor; + +import javax.annotation.PostConstruct; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.*; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * Concrete implementation to access and modify files in the server's working directory. + */ +@Slf4j +public class WorkingDirectoryAccessor extends AbstractWorkingDirectoryAccessor { + + /** + * Event publisher to trigger events when files are being modified. + */ + private ApplicationEventPublisher eventPublisher; + + /** + * The base path of the working directory. + */ + private Path workingDirectory; + + public WorkingDirectoryAccessor(Path workingDirectory, ApplicationEventPublisher eventPublisher) { + this.workingDirectory = workingDirectory; + this.eventPublisher = eventPublisher; + } + + @PostConstruct + private void init() throws IOException { + Files.createDirectories(workingDirectory); + } + + /** + * Fires a new {@link FileChangedEvent}. + */ + private void fireFileChangeEvent() { + FileChangedEvent event = new FileChangedEvent(this); + eventPublisher.publishEvent(event); + } + + /** + * Resolve the given path in relation to the current working directory. + * + * @param path the relative path + * @return {@link Path} representing the given path string + */ + private Path resolve(String path) { + if (StringUtils.isEmpty(path)) { + return workingDirectory; + } else { + return workingDirectory.resolve(path).normalize(); + } + } + + @Override + protected Optional readFile(String path) { + Path targetPath = resolve(path); + + if (!Files.exists(targetPath) || Files.isDirectory(targetPath)) { + return Optional.empty(); + } + + try { + return Optional.of(Files.readAllBytes(targetPath)); + } catch (IOException e) { + return Optional.empty(); + } + } + + @Override + protected List listFiles(String path) { + Path targetPath = resolve(path); + + if (!Files.exists(targetPath)) { + return Collections.emptyList(); + } + + try { + FileInfoVisitor fileInfoVisitor = new FileInfoVisitor(); + + Files.walkFileTree(targetPath, fileInfoVisitor); + + return fileInfoVisitor.getFileInfos(); + } catch (IOException e) { + log.error("Exception while listing files in path '{}'.", path, e); + return Collections.emptyList(); + } + } + + @Override + protected void createDirectory(String path) throws IOException { + Path targetDirectory = resolve(path); + + if (Files.exists(targetDirectory)) { + throw new FileAlreadyExistsException("Directory already exists: " + targetDirectory); + } + + Files.createDirectories(targetDirectory); + + fireFileChangeEvent(); + } + + @Override + protected void writeFile(String path, String content) throws IOException { + Path targetFile = resolve(path); + + if (Files.exists(targetFile) && Files.isDirectory(targetFile)) { + throw new IOException("Cannot write file because target is already a directory: " + targetFile); + } + + Files.createDirectories(targetFile.getParent()); + Files.write(targetFile, content.getBytes(FILE_ENCODING)); + + fireFileChangeEvent(); + } + + @Override + protected void move(String sourcePath, String targetPath) throws IOException { + Path source = resolve(sourcePath); + Path target = resolve(targetPath); + + FileUtils.forceMkdir(target.getParent().toFile()); + + if (Files.isDirectory(source)) { + FileUtils.moveDirectory(source.toFile(), target.toFile()); + } else { + FileUtils.moveFile(source.toFile(), target.toFile()); + } + + fireFileChangeEvent(); + } + + @Override + protected void delete(String path) throws IOException { + Path targetPath = resolve(path); + + if (!Files.exists(targetPath)) { + throw new FileNotFoundException("Path cannot be deleted because it does not exist: " + targetPath); + } else if (Files.isDirectory(targetPath)) { + FileUtils.deleteDirectory(targetPath.toFile()); + } else if (Files.isRegularFile(targetPath)) { + Files.delete(targetPath); + } else { + throw new AccessDeniedException("'" + targetPath + "' could not be deleted."); + } + + fireFileChangeEvent(); + } + + @Override + protected boolean exists(String path) { + Path targetPath = resolve(path); + return Files.exists(targetPath); + } + + @Override + protected boolean isDirectory(String path) { + Path targetPath = resolve(path); + return Files.isDirectory(targetPath); + } + + @Override + protected String verifyPath(String relativeBasePath, String relativePath) { + Path path = Paths.get(relativePath); + + if (path.isAbsolute()) { + throw new IllegalArgumentException("Path must be relative: " + path); + } + + Path basePath = workingDirectory.resolve(relativeBasePath); + Path resolvedPath = basePath.resolve(path).normalize(); + + if (!resolvedPath.startsWith(basePath)) { + throw new IllegalArgumentException("User path escapes the base path: " + path); + } + + return resolvedPath.toString(); + } +} diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/filters/UiForwardFilter.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/filters/UiForwardFilter.java index 90332bee11..b1204605c9 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/filters/UiForwardFilter.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/filters/UiForwardFilter.java @@ -4,7 +4,6 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.ui.Model; -import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/agent/AgentController.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/agent/AgentController.java index 9fc38624fc..bc56afcc8c 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/agent/AgentController.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/agent/AgentController.java @@ -16,7 +16,6 @@ import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.rest.AbstractBaseController; -import java.io.IOException; import java.util.Map; @@ -42,7 +41,7 @@ public class AgentController extends AbstractBaseController { */ @ApiOperation(value = "Fetch the Agent Configuration", notes = "Reads the configuration for the given agent and returns it as a yaml string") @GetMapping(value = "agent/configuration", produces = "text/plain") - public ResponseEntity fetchConfiguration(@ApiParam("The agent attributes used to select the correct mapping") @RequestParam Map attributes, @RequestHeader Map headers) throws IOException { + public ResponseEntity fetchConfiguration(@ApiParam("The agent attributes used to select the correct mapping") @RequestParam Map attributes, @RequestHeader Map headers) { log.debug("Fetching the agent configuration for agent ({})", attributes.toString()); AgentConfiguration configuration = configManager.getConfiguration(attributes); statusManager.notifyAgentConfigurationFetched(attributes, headers, configuration); diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/file/DirectoryController.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/file/DirectoryController.java index df6241d0ce..f15df8506a 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/file/DirectoryController.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/file/DirectoryController.java @@ -2,14 +2,19 @@ import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RestController; import rocks.inspectit.ocelot.file.FileInfo; import rocks.inspectit.ocelot.rest.util.RequestUtil; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; /** * Controller for managing the configurations. @@ -17,25 +22,20 @@ @RestController public class DirectoryController extends FileBaseController { - @ApiOperation(value = "List directory contents", notes = "Can be used to get a list of the contents of a given directory.") @ApiImplicitParam(name = "Path", value = "The part of the url after /directories/ define the path to the directory whose contents shall be read.") @GetMapping(value = "directories/**") - public Collection listContents(HttpServletRequest request, - - @ApiParam("If false, only direct children of this directory are returned. Otherwise the entire file tree is returned.") - @RequestParam(defaultValue = "true") boolean recursive) throws IOException { + public Collection listContents(HttpServletRequest request) { String path = RequestUtil.getRequestSubPath(request); - return fileManager.getFilesInDirectory(path, recursive); + return fileManager.getWorkingDirectory().listConfigurationFiles(path); } - @ApiOperation(value = "Create a directory", notes = "Creates a new, empty directory including its parent folders. Does nothing if the directory already exists.") @ApiImplicitParam(name = "Path", value = "The part of the url after /directories/ define the path of the directory to create.") @PutMapping(value = "directories/**") public void createNewDirectory(HttpServletRequest request) throws IOException { String path = RequestUtil.getRequestSubPath(request); - fileManager.createDirectory(path); + fileManager.getWorkingDirectory().createConfigurationDirectory(path); } @ApiOperation(value = "Delete a directory", notes = "Deletes a directory including its contents.") @@ -43,7 +43,7 @@ public void createNewDirectory(HttpServletRequest request) throws IOException { @DeleteMapping(value = "directories/**") public void deleteDirectory(HttpServletRequest request) throws IOException { String path = RequestUtil.getRequestSubPath(request); - fileManager.deleteDirectory(path); + fileManager.getWorkingDirectory().deleteConfiguration(path); } } diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/file/FileController.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/file/FileController.java index 33a0443d2b..5da9a3c179 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/file/FileController.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/file/FileController.java @@ -3,12 +3,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.annotations.*; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import rocks.inspectit.ocelot.file.FileData; import rocks.inspectit.ocelot.rest.util.RequestUtil; import javax.servlet.http.HttpServletRequest; import java.io.IOException; +import java.util.Optional; /** * Controller for managing the configurations. @@ -33,12 +35,16 @@ public void writeFile(HttpServletRequest request, @RequestBody(required = false) String content) throws IOException { String path = RequestUtil.getRequestSubPath(request); + + String fileContent; if (raw || content == null) { - fileManager.createOrReplaceFile(path, content == null ? "" : content); + fileContent = content == null ? "" : content; } else { FileData data = objectMapper.readValue(content, FileData.class); - fileManager.createOrReplaceFile(path, data.getContent()); + fileContent = data.getContent(); } + + fileManager.getWorkingDirectory().writeConfigurationFile(path, fileContent); } @ApiOperation(value = "Read a file", notes = "Returns the contents of the given file.") @@ -52,14 +58,21 @@ public void writeFile(HttpServletRequest request, @GetMapping(value = "files/**") public Object readFile(HttpServletRequest request, @ApiParam("If true, the response body is not formatted as json and is instead the plain text content of the file.") - @RequestParam(defaultValue = "false") boolean raw) throws IOException { + @RequestParam(defaultValue = "false") boolean raw) { String path = RequestUtil.getRequestSubPath(request); - String content = fileManager.readFile(path); - if (raw) { - return content; - } else { - return FileData.builder().content(content).build(); + Optional contentOptional = fileManager.getWorkingDirectory().readConfigurationFile(path); + + if (!contentOptional.isPresent()) { + return ResponseEntity.notFound(); } + + return contentOptional.map(content -> { + if (raw) { + return content; + } else { + return FileData.builder().content(content).build(); + } + }); } @ApiOperation(value = "Delete a file", notes = "Deletes the given file") @@ -67,6 +80,7 @@ public Object readFile(HttpServletRequest request, @DeleteMapping(value = "files/**") public void deleteFile(HttpServletRequest request) throws IOException { String path = RequestUtil.getRequestSubPath(request); - fileManager.deleteFile(path); + + fileManager.getWorkingDirectory().deleteConfiguration(path); } } diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/file/MoveController.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/file/MoveController.java index 1208fc6d65..543a9efb92 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/file/MoveController.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/file/MoveController.java @@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import rocks.inspectit.ocelot.file.FileMoveDescription; +import rocks.inspectit.ocelot.file.accessor.workingdirectory.AbstractWorkingDirectoryAccessor; import java.io.IOException; @@ -20,7 +21,9 @@ public class MoveController extends FileBaseController { public void moveFileOrDirectory(@RequestBody FileMoveDescription moveDescription) throws IOException { String source = removeLeadingSlash(moveDescription.getSource()); String target = removeLeadingSlash(moveDescription.getTarget()); - fileManager.move(source, target); + + AbstractWorkingDirectoryAccessor fileAccessor = fileManager.getWorkingDirectory(); + fileAccessor.moveConfiguration(source, target); } private String removeLeadingSlash(String path) { diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/mappings/AgentMappingController.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/mappings/AgentMappingController.java index 9b5bd8eff1..da8e9752e7 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/mappings/AgentMappingController.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/mappings/AgentMappingController.java @@ -13,9 +13,7 @@ import javax.validation.Valid; import java.io.IOException; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/users/AccountController.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/users/AccountController.java index 04cc10d7b5..d4175b6240 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/users/AccountController.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/rest/users/AccountController.java @@ -17,7 +17,6 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import rocks.inspectit.ocelot.config.model.InspectitServerSettings; import rocks.inspectit.ocelot.error.exceptions.NotSupportedWithLdapException; import rocks.inspectit.ocelot.rest.AbstractBaseController; import rocks.inspectit.ocelot.rest.ErrorInfo; @@ -46,9 +45,6 @@ public class AccountController extends AbstractBaseController { @Autowired private JwtTokenManager tokenManager; - @Autowired - private InspectitServerSettings settings; - @ApiOperation(value = "Create an access token", notes = "Creates a fresh access token for the user making this request." + " Instead of using User and Password based HTTP authentication, the user can then user the header 'Authorization: Bearer ' for authentication." + "The token expires after the time specified by ${inspectit.token-lifespan}, which by default is 60 minutes." + diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/security/config/SecurityConfiguration.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/security/config/SecurityConfiguration.java index a8342d0533..87474dc33c 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/security/config/SecurityConfiguration.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/security/config/SecurityConfiguration.java @@ -52,7 +52,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { InspectitServerSettings serverSettings; @Override - public void configure(WebSecurity web) throws Exception { + public void configure(WebSecurity web) { web.ignoring().antMatchers( "/v2/api-docs", "/configuration/**", diff --git a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/IntegrationTestBase.java b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/IntegrationTestBase.java index deb2eb374f..1c55bd40f8 100644 --- a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/IntegrationTestBase.java +++ b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/IntegrationTestBase.java @@ -1,24 +1,28 @@ package rocks.inspectit.ocelot; import org.apache.commons.io.FileUtils; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import rocks.inspectit.ocelot.config.model.InspectitServerSettings; import java.io.File; +import java.io.IOException; import java.nio.file.Files; -import java.nio.file.Paths; +import java.nio.file.Path; + @ExtendWith(SpringExtension.class) @TestPropertySource(properties = { - "inspectit-config-server.working-directory=temp_work_dir", "spring.datasource.url=jdbc:h2:mem:userdb;DB_CLOSE_DELAY=-1", "spring.datasource.driver-class-name=org.h2.Driver", "spring.datasource.username=sa", @@ -27,33 +31,42 @@ "spring.jpa.hibernate.ddl-auto=create", }) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(initializers = IntegrationTestBase.Initializer.class) public class IntegrationTestBase { + static class Initializer implements ApplicationContextInitializer { + @Override + public void initialize(ConfigurableApplicationContext context) { + try { + Path tempDirectory = Files.createTempDirectory("ocelot"); + FileUtils.forceDeleteOnExit(tempDirectory.toFile()); + + TestPropertyValues.of("inspectit-config-server.working-directory=" + tempDirectory.toAbsolutePath().toString()) + .applyTo(context.getEnvironment()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + @Autowired protected TestRestTemplate rest; @Autowired - protected InspectitServerSettings serverConf; + protected InspectitServerSettings settings; /** * Authenticated restTemplate; */ protected TestRestTemplate authRest; - @BeforeAll - static void setupWorkdir() throws Exception { - Files.createDirectories(Paths.get("temp_work_dir")); - } - @BeforeEach void setupAuthentication() { - authRest = rest.withBasicAuth(serverConf.getDefaultUser().getName(), serverConf.getDefaultUser().getPassword()); + authRest = rest.withBasicAuth(settings.getDefaultUser().getName(), settings.getDefaultUser().getPassword()); } - @AfterAll - static void deleteWorkDir() throws Exception { - File temp_work_dir = new File("temp_work_dir"); - FileUtils.deleteQuietly(temp_work_dir); - FileUtils.forceDeleteOnExit(temp_work_dir); + @AfterEach + void afterEach() throws IOException { + FileUtils.cleanDirectory(new File(settings.getWorkingDirectory())); } } diff --git a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/agentconfiguration/AgentConfigurationManagerTest.java b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/agentconfiguration/AgentConfigurationManagerTest.java index f391c1be88..45b87fd258 100644 --- a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/agentconfiguration/AgentConfigurationManagerTest.java +++ b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/agentconfiguration/AgentConfigurationManagerTest.java @@ -1,6 +1,7 @@ package rocks.inspectit.ocelot.agentconfiguration; import com.google.common.collect.ImmutableMap; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -9,12 +10,13 @@ import org.mockito.junit.jupiter.MockitoExtension; import rocks.inspectit.ocelot.config.model.InspectitServerSettings; import rocks.inspectit.ocelot.file.FileManager; +import rocks.inspectit.ocelot.file.accessor.workingdirectory.WorkingDirectoryAccessor; import rocks.inspectit.ocelot.mappings.AgentMappingManager; import rocks.inspectit.ocelot.mappings.model.AgentMapping; -import java.io.IOException; import java.time.Duration; import java.util.Arrays; +import java.util.Optional; import java.util.concurrent.ExecutorService; import static org.assertj.core.api.Assertions.assertThat; @@ -23,6 +25,9 @@ @ExtendWith(MockitoExtension.class) public class AgentConfigurationManagerTest { + @InjectMocks + AgentConfigurationManager configManager; + @Mock FileManager fileManager; @@ -32,8 +37,13 @@ public class AgentConfigurationManagerTest { @Mock ExecutorService executor; - @InjectMocks - AgentConfigurationManager configManager; + @Mock + WorkingDirectoryAccessor workingDirectoryAccessor; + + @BeforeEach + public void beforeEach() { + lenient().when(fileManager.getWorkingDirectory()).thenReturn(workingDirectoryAccessor); + } void init() { doAnswer(a -> { @@ -53,7 +63,7 @@ void init() { class GetConfiguration { @Test - void noMatchingMapping() throws IOException { + void noMatchingMapping() { doReturn(Arrays.asList( AgentMapping.builder() .attribute("service", "test-\\d+") @@ -68,7 +78,7 @@ void noMatchingMapping() throws IOException { } @Test - void priorityRespected() throws IOException { + void priorityRespected() { doReturn(Arrays.asList( AgentMapping.builder() .attribute("service", "test") @@ -80,10 +90,10 @@ void priorityRespected() throws IOException { .build())) .when(mappingManager).getAgentMappings(); - doReturn(true).when(fileManager).exists(any()); - doReturn(false).when(fileManager).isDirectory(any()); - doReturn("a: test").when(fileManager).readFile("test.yml"); - doReturn("a: default").when(fileManager).readFile("default.yml"); + doReturn(true).when(workingDirectoryAccessor).configurationFileExists(any()); + doReturn(false).when(workingDirectoryAccessor).configurationFileIsDirectory(any()); + doReturn(Optional.of("a: test")).when(workingDirectoryAccessor).readConfigurationFile("test.yml"); + doReturn(Optional.of("a: default")).when(workingDirectoryAccessor).readConfigurationFile("default.yml"); init(); @@ -96,7 +106,7 @@ void priorityRespected() throws IOException { @Test - void multipleAttributesChecked() throws IOException { + void multipleAttributesChecked() { doReturn(Arrays.asList( AgentMapping.builder() .attribute("service", "test-\\d+") @@ -105,9 +115,9 @@ void multipleAttributesChecked() throws IOException { .build())) .when(mappingManager).getAgentMappings(); - doReturn(true).when(fileManager).exists(any()); - doReturn(false).when(fileManager).isDirectory(any()); - doReturn("a: test").when(fileManager).readFile("test.yml"); + doReturn(true).when(workingDirectoryAccessor).configurationFileExists(any()); + doReturn(false).when(workingDirectoryAccessor).configurationFileIsDirectory(any()); + doReturn(Optional.of("a: test")).when(workingDirectoryAccessor).readConfigurationFile("test.yml"); init(); diff --git a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/agentconfiguration/AgentConfigurationReloadTaskTest.java b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/agentconfiguration/AgentConfigurationReloadTaskTest.java index d92e96b036..b5b5e1e379 100644 --- a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/agentconfiguration/AgentConfigurationReloadTaskTest.java +++ b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/agentconfiguration/AgentConfigurationReloadTaskTest.java @@ -1,6 +1,7 @@ package rocks.inspectit.ocelot.agentconfiguration; import org.apache.commons.lang3.mutable.MutableObject; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -9,11 +10,14 @@ import org.mockito.junit.jupiter.MockitoExtension; import rocks.inspectit.ocelot.file.FileInfo; import rocks.inspectit.ocelot.file.FileManager; +import rocks.inspectit.ocelot.file.accessor.workingdirectory.WorkingDirectoryAccessor; import rocks.inspectit.ocelot.mappings.model.AgentMapping; import java.io.IOException; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.function.Consumer; import java.util.stream.Stream; @@ -31,6 +35,14 @@ public class AgentConfigurationReloadTaskTest { @Mock FileManager fileManager; + @Mock + WorkingDirectoryAccessor workingDirectoryAccessor; + + @BeforeEach + public void beforeEach() { + lenient().when(fileManager.getWorkingDirectory()).thenReturn(workingDirectoryAccessor); + } + @Nested class Run { @@ -38,11 +50,11 @@ class Run { public void loadWithException() throws Exception { FileInfo fileInfo = mock(FileInfo.class); when(fileInfo.getAbsoluteFilePaths(any())).thenReturn(Stream.of("/test.yml"), Stream.of("/test.yml")); - when(fileManager.exists(anyString())).thenReturn(true); - when(fileManager.isDirectory(anyString())).thenReturn(true); - when(fileManager.getFilesInDirectory(anyString(), anyBoolean())).thenReturn(Arrays.asList(fileInfo)); + when(workingDirectoryAccessor.configurationFileExists(anyString())).thenReturn(true); + when(workingDirectoryAccessor.configurationFileIsDirectory(anyString())).thenReturn(true); + when(workingDirectoryAccessor.listConfigurationFiles(anyString())).thenReturn(Collections.singletonList(fileInfo)); // the first call will return a broken file - when(fileManager.readFile(anyString())).thenReturn("key:\tbroken", "key: valid"); + when(workingDirectoryAccessor.readConfigurationFile(anyString())).thenReturn(Optional.of("key:\tbroken"), Optional.of("key: valid")); AgentMapping mapping = AgentMapping.builder().name("test").source("/test").build(); AgentMapping mapping2 = AgentMapping.builder().name("test2").source("/test2").build(); @@ -70,10 +82,10 @@ class LoadAndMergeYaml { public void loadYaml() throws IOException { FileInfo fileInfo = mock(FileInfo.class); when(fileInfo.getAbsoluteFilePaths(any())).thenReturn(Stream.of("/test.yml")); - when(fileManager.exists("test")).thenReturn(true); - when(fileManager.isDirectory("test")).thenReturn(true); - when(fileManager.getFilesInDirectory(anyString(), anyBoolean())).thenReturn(Arrays.asList(fileInfo)); - when(fileManager.readFile("/test.yml")).thenReturn("key: value"); + when(workingDirectoryAccessor.configurationFileExists("test")).thenReturn(true); + when(workingDirectoryAccessor.configurationFileIsDirectory("test")).thenReturn(true); + when(workingDirectoryAccessor.listConfigurationFiles(anyString())).thenReturn(Collections.singletonList(fileInfo)); + when(workingDirectoryAccessor.readConfigurationFile("/test.yml")).thenReturn(Optional.of("key: value")); AgentMapping mapping = AgentMapping.builder().name("test").source("/test").build(); String string = reloadTask.loadConfigForMapping(mapping); @@ -82,13 +94,13 @@ public void loadYaml() throws IOException { } @Test - public void yamlWithTab() throws IOException { + public void yamlWithTab() { FileInfo fileInfo = mock(FileInfo.class); when(fileInfo.getAbsoluteFilePaths(any())).thenReturn(Stream.of("/test.yml")); - when(fileManager.exists("test")).thenReturn(true); - when(fileManager.isDirectory("test")).thenReturn(true); - when(fileManager.getFilesInDirectory(anyString(), anyBoolean())).thenReturn(Arrays.asList(fileInfo)); - when(fileManager.readFile("/test.yml")).thenReturn("key:\tvalue"); + when(workingDirectoryAccessor.configurationFileExists("test")).thenReturn(true); + when(workingDirectoryAccessor.configurationFileIsDirectory("test")).thenReturn(true); + when(workingDirectoryAccessor.listConfigurationFiles(anyString())).thenReturn(Collections.singletonList(fileInfo)); + when(workingDirectoryAccessor.readConfigurationFile("/test.yml")).thenReturn(Optional.of("key:\tvalue")); AgentMapping mapping = AgentMapping.builder().name("test").source("/test").build(); @@ -113,8 +125,8 @@ void noSourcesSpecified() throws IOException { @Test void nonExistingSourcesSpecified() throws IOException { - doReturn(false).when(fileManager).exists("a.yml"); - doReturn(false).when(fileManager).exists("some/folder"); + doReturn(false).when(workingDirectoryAccessor).configurationFileExists("a.yml"); + doReturn(false).when(workingDirectoryAccessor).configurationFileExists("some/folder"); String result = reloadTask.loadConfigForMapping( AgentMapping.builder() @@ -128,9 +140,9 @@ void nonExistingSourcesSpecified() throws IOException { @Test void nonYamlIgnored() throws IOException { - doReturn(true).when(fileManager).exists(any()); - doReturn(false).when(fileManager).isDirectory(any()); - doReturn("").when(fileManager).readFile(any()); + doReturn(true).when(workingDirectoryAccessor).configurationFileExists(any()); + doReturn(false).when(workingDirectoryAccessor).configurationFileIsDirectory(any()); + doReturn(Optional.of("")).when(workingDirectoryAccessor).readConfigurationFile(any()); String result = reloadTask.loadConfigForMapping( AgentMapping.builder() @@ -141,38 +153,38 @@ void nonYamlIgnored() throws IOException { .build()); assertThat(result).isEmpty(); - verify(fileManager).readFile("a.yml"); - verify(fileManager).readFile("b.YmL"); - verify(fileManager).readFile("c.yaml"); + verify(workingDirectoryAccessor).readConfigurationFile("a.yml"); + verify(workingDirectoryAccessor).readConfigurationFile("b.YmL"); + verify(workingDirectoryAccessor).readConfigurationFile("c.yaml"); - verify(fileManager, never()).readFile("d.txt"); + verify(workingDirectoryAccessor, never()).readConfigurationFile("d.txt"); } @Test void leadingSlashesInSourcesRemoved() throws IOException { - doReturn(false).when(fileManager).exists("a.yml"); + doReturn(false).when(workingDirectoryAccessor).configurationFileExists("a.yml"); - lenient().doThrow(new RuntimeException()).when(fileManager).exists(startsWith("/")); + lenient().doThrow(new RuntimeException()).when(workingDirectoryAccessor).configurationFileExists(startsWith("/")); reloadTask.loadConfigForMapping( AgentMapping.builder() .source("/a.yml") .build()); - verify(fileManager).exists(eq("a.yml")); + verify(workingDirectoryAccessor).configurationFileExists(eq("a.yml")); } @Test void priorityRespected() throws IOException { - doReturn(true).when(fileManager).exists(any()); + when(workingDirectoryAccessor.configurationFileExists(any())).thenReturn(true); - doReturn(true).when(fileManager).isDirectory("folder"); - doReturn(false).when(fileManager).isDirectory("z.yml"); + doReturn(true).when(workingDirectoryAccessor).configurationFileIsDirectory("folder"); + doReturn(false).when(workingDirectoryAccessor).configurationFileIsDirectory("z.yml"); - doReturn(Arrays.asList( + List fileInfos = Arrays.asList( FileInfo.builder() .type(FileInfo.Type.FILE) .name("b.yml") @@ -185,13 +197,13 @@ void priorityRespected() throws IOException { .type(FileInfo.Type.FILE) .name("somethingelse") .build() + ); - )).when(fileManager).getFilesInDirectory("folder", true); - - doReturn("{ val1: z}").when(fileManager).readFile("z.yml"); - doReturn("{ val1: a, val2: a}").when(fileManager).readFile("folder/a.yml"); - doReturn("{ val1: b, val2: b, val3: b}").when(fileManager).readFile("folder/b.yml"); + when(workingDirectoryAccessor.listConfigurationFiles("folder")).thenReturn(fileInfos); + doReturn(Optional.of("{ val1: z}")).when(workingDirectoryAccessor).readConfigurationFile("z.yml"); + doReturn(Optional.of("{ val1: a, val2: a}")).when(workingDirectoryAccessor).readConfigurationFile("folder/a.yml"); + doReturn(Optional.of("{ val1: b, val2: b, val3: b}")).when(workingDirectoryAccessor).readConfigurationFile("folder/b.yml"); String result = reloadTask.loadConfigForMapping( AgentMapping.builder() @@ -201,7 +213,7 @@ void priorityRespected() throws IOException { assertThat(result).isEqualTo("{val1: z, val2: a, val3: b}\n"); - verify(fileManager, never()).readFile("folder/somethingelse"); + verify(workingDirectoryAccessor, never()).readConfigurationFile("folder/somethingelse"); } } } diff --git a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/autocomplete/util/ConfigurationFilesCacheTest.java b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/autocomplete/util/ConfigurationFilesCacheTest.java index 53bc72809f..19c666a332 100644 --- a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/autocomplete/util/ConfigurationFilesCacheTest.java +++ b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/autocomplete/util/ConfigurationFilesCacheTest.java @@ -1,5 +1,6 @@ package rocks.inspectit.ocelot.autocomplete.util; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -9,18 +10,15 @@ import org.mockito.junit.jupiter.MockitoExtension; import rocks.inspectit.ocelot.file.FileInfo; import rocks.inspectit.ocelot.file.FileManager; +import rocks.inspectit.ocelot.file.accessor.workingdirectory.WorkingDirectoryAccessor; import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; +import java.util.*; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class ConfigurationFilesCacheTest { @@ -31,13 +29,21 @@ public class ConfigurationFilesCacheTest { @Mock FileManager fileManager; + @Mock + WorkingDirectoryAccessor workingDirectoryAccessor; + + @BeforeEach + public void beforeEach() { + lenient().when(fileManager.getWorkingDirectory()).thenReturn(workingDirectoryAccessor); + } + @Nested public class LoadYamlFile { @Test - public void testLoadYaml() throws IOException { + public void testLoadYaml() { String testPath = "mockPath"; String yamlContent = "i am a:\n - test\n - yaml"; - when(fileManager.readFile(any())).thenReturn(yamlContent); + when(workingDirectoryAccessor.readConfigurationFile(any())).thenReturn(Optional.of(yamlContent)); LinkedHashMap output = (LinkedHashMap) configurationFilesCache.loadYamlFile(testPath); @@ -48,7 +54,7 @@ public void testLoadYaml() throws IOException { @Test public void fileManagerReturnsNull() throws IOException { String testPath = "mockPath"; - when(fileManager.readFile(any())).thenReturn(null); + when(workingDirectoryAccessor.readConfigurationFile(any())).thenReturn(Optional.empty()); Object output = configurationFilesCache.loadYamlFile(testPath); @@ -67,7 +73,7 @@ public void getYamlPaths() throws IOException { Stream streamB = Stream.of("path/b.yaml"); when(mockFileInfo2.getAbsoluteFilePaths(any())).thenReturn(streamB); List mockInfoList = Arrays.asList(mockFileInfo, mockFileInfo2); - when(fileManager.getFilesInDirectory("", true)).thenReturn(mockInfoList); + when(workingDirectoryAccessor.listConfigurationFiles("")).thenReturn(mockInfoList); List paths = configurationFilesCache.getAllPaths(); @@ -77,12 +83,12 @@ public void getYamlPaths() throws IOException { } @Test - public void containsNonYamlFile() throws IOException { + public void containsNonYamlFile() { FileInfo mockFileInfo = Mockito.mock(FileInfo.class); Stream streamA = Stream.of("path/a.xml"); when(mockFileInfo.getAbsoluteFilePaths(any())).thenReturn(streamA); List mockInfoList = Arrays.asList(mockFileInfo); - when(fileManager.getFilesInDirectory("", true)).thenReturn(mockInfoList); + when(workingDirectoryAccessor.listConfigurationFiles("")).thenReturn(mockInfoList); List paths = configurationFilesCache.getAllPaths(); @@ -96,12 +102,12 @@ public class LoadFiles { public void testYamlLoadingMap() throws IOException { String yamlContent1 = "i am a:\n - test\n - yaml"; String yamlContent2 = "so:\n am: i"; - when(fileManager.readFile(any())).thenReturn(yamlContent1, yamlContent2); + when(workingDirectoryAccessor.readConfigurationFile(any())).thenReturn(Optional.of(yamlContent1), Optional.of(yamlContent2)); FileInfo mockFileInfo1 = mock(FileInfo.class); when(mockFileInfo1.getAbsoluteFilePaths("")).thenReturn(Stream.of("a.yaml")); FileInfo mockFileInfo2 = mock(FileInfo.class); when(mockFileInfo2.getAbsoluteFilePaths("")).thenReturn(Stream.of("b.yaml")); - when(fileManager.getFilesInDirectory("", true)).thenReturn(Arrays.asList(mockFileInfo1, mockFileInfo2)); + when(workingDirectoryAccessor.listConfigurationFiles("")).thenReturn(Arrays.asList(mockFileInfo1, mockFileInfo2)); List list = Arrays.asList("test", "yaml"); LinkedHashMap firstElement = new LinkedHashMap<>(); firstElement.put("i am a", list); diff --git a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/file/FileManagerTest.java b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/file/FileManagerTest.java deleted file mode 100644 index 9a67df4943..0000000000 --- a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/file/FileManagerTest.java +++ /dev/null @@ -1,804 +0,0 @@ -package rocks.inspectit.ocelot.file; - - -import org.apache.commons.io.FileExistsException; -import org.junit.jupiter.api.*; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.context.ApplicationEventPublisher; -import rocks.inspectit.ocelot.config.model.InspectitServerSettings; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.file.*; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.verify; - -@ExtendWith(MockitoExtension.class) -public class FileManagerTest { - - private static final Path rootWorkDir = Paths.get("temp_test_workdir"); - private static final Path fmRoot = rootWorkDir.resolve("root"); - - @InjectMocks - private FileManager fm; - - @Mock - ApplicationEventPublisher eventPublisher; - - @BeforeAll - private static void setup() throws Exception { - Files.createDirectories(rootWorkDir); - } - - @AfterAll - private static void clean() throws Exception { - deleteDirectory(rootWorkDir); - } - - @BeforeEach - private void setupFileManager() throws Exception { - InspectitServerSettings conf = new InspectitServerSettings(); - conf.setWorkingDirectory(fmRoot.toString()); - fm.config = conf; - fm.init(); - } - - @AfterEach - private void cleanFileManager() throws Exception { - deleteDirectory(fmRoot); - } - - - private static void setupTestFiles(String... paths) { - try { - for (String path : paths) { - if (!path.contains("=")) { - Files.createDirectories(fmRoot.resolve(FileManager.FILES_SUBFOLDER).resolve(path)); - } else { - String[] splitted = path.split("="); - Path file = fmRoot.resolve(FileManager.FILES_SUBFOLDER).resolve(splitted[0]); - Files.createDirectories(file.getParent()); - String content = splitted.length > 1 ? splitted[1] : ""; - Files.write(file, content.getBytes(FileManager.ENCODING)); - } - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static void deleteDirectory(Path path) throws IOException { - List files = Files.walk(path).collect(Collectors.toCollection(ArrayList::new)); - Collections.reverse(files); - for (Path f : files) { - Files.delete(f); - } - } - - @FunctionalInterface - private interface Verfification { - void verify(T t) throws Exception; - } - - - @Nested - class GetFilesInDirectory { - - @Test - void listRootFilesRecursive() throws Exception { - setupTestFiles("topA/fileA=", "topA/nested", "topB"); - Verfification checkForCall = (path) -> - assertThat(fm.getFilesInDirectory(path, true)) - .hasSize(2) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("topA"); - assertThat(f.getChildren()) - .hasSize(2) - .anySatisfy((f2) -> { - assertThat(f2.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f2.getName()).isEqualTo("nested"); - assertThat(f2.getChildren()).isEmpty(); - }) - .anySatisfy((f2) -> { - assertThat(f2.getType()).isEqualTo(FileInfo.Type.FILE); - assertThat(f2.getName()).isEqualTo("fileA"); - }); - }) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("topB"); - assertThat(f.getChildren()).isEmpty(); - }); - - checkForCall.verify(null); - checkForCall.verify(""); - checkForCall.verify("./"); - } - - @Test - void listRootFilesNonRecursive() throws Exception { - setupTestFiles("topA/fileA=", "topA/nested", "topB"); - Verfification checkForCall = (path) -> - assertThat(fm.getFilesInDirectory(path, false)) - .hasSize(2) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("topA"); - assertThat(f.getChildren()).isNull(); - }) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("topB"); - assertThat(f.getChildren()).isNull(); - }); - - checkForCall.verify(null); - checkForCall.verify(""); - checkForCall.verify("./"); - } - - @Test - void listNonRootFiles() throws Exception { - setupTestFiles("topA/fileA=", "topA/nested/sub", "topB"); - assertThat(fm.getFilesInDirectory("topA", true)) - .hasSize(2) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("nested"); - assertThat(f.getChildren()) - .hasSize(1) - .anySatisfy((f2) -> { - assertThat(f2.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f2.getName()).isEqualTo("sub"); - assertThat(f2.getChildren()).isEmpty(); - }); - }) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.FILE); - assertThat(f.getName()).isEqualTo("fileA"); - }); - } - - - @Test - void verifyFilesOutsideWorkdirNotAccessible() { - setupTestFiles("top"); - - assertThatThrownBy(() -> fm.getFilesInDirectory("../", true)) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.getFilesInDirectory("top/../../", true)) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.getFilesInDirectory("../../", true)) - .isInstanceOf(AccessDeniedException.class); - } - } - - @Nested - class CreateDirectory { - - @Test - void createDirInRootDirect() throws Exception { - setupTestFiles("topA", "topB"); - - fm.createDirectory("myDir"); - - assertThat(fm.getFilesInDirectory("", true)) - .hasSize(3) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("topA"); - }) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("topB"); - }) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("myDir"); - }); - - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - - @Test - void createDirInRootIndirect() throws Exception { - setupTestFiles("topA", "topB"); - - fm.createDirectory("topB/.././myDir"); - - assertThat(fm.getFilesInDirectory("", true)) - .hasSize(3) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("topA"); - }) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("topB"); - }) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("myDir"); - }); - - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - - @Test - void createDirInSubFolder() throws Exception { - setupTestFiles("topA", "topB"); - - fm.createDirectory("topA/subA/subB"); - - assertThat(fm.getFilesInDirectory("", true)) - .hasSize(2) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("topA"); - assertThat(f.getChildren()) - .hasSize(1) - .anySatisfy((f2) -> { - assertThat(f2.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f2.getName()).isEqualTo("subA"); - assertThat(f2.getChildren()) - .hasSize(1) - .anySatisfy((f3) -> { - assertThat(f3.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f3.getName()).isEqualTo("subB"); - assertThat(f3.getChildren()).isEmpty(); - }); - }); - }) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("topB"); - }); - - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - @Test - void createDirOnExistingDir() throws Exception { - setupTestFiles("topA/subA", "topB"); - - fm.createDirectory("topA/subA"); - - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - @Test - void createDirOnExistingFile() { - setupTestFiles("topA=content"); - - assertThatThrownBy(() -> fm.createDirectory("topA")) - .isInstanceOf(IOException.class); - } - - - @Test - void createDirBeneathExistingFile() { - setupTestFiles("topA=content"); - - assertThatThrownBy(() -> fm.createDirectory("topA/subDir")) - .isInstanceOf(IOException.class); - } - - @Test - void verifyDirOutsideWorkdirNotCreateable() { - setupTestFiles("top"); - - assertThatThrownBy(() -> fm.createDirectory(null)) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.createDirectory("")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.createDirectory("./")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.createDirectory("../mydir")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.createDirectory("top/../../mydir")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.createDirectory("../../mydir")) - .isInstanceOf(AccessDeniedException.class); - } - } - - - @Nested - class DeleteDirectory { - - @Test - void deleteDirInRootDirect() throws Exception { - setupTestFiles("topA", "topB"); - - fm.deleteDirectory("topA"); - - assertThat(fm.getFilesInDirectory("", true)) - .hasSize(1) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("topB"); - }); - - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - - @Test - void deleteDirInRootIndirect() throws Exception { - setupTestFiles("topA", "topB"); - - fm.deleteDirectory("topB/../topA"); - - assertThat(fm.getFilesInDirectory("", true)) - .hasSize(1) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("topB"); - }); - - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - - @Test - void deleteDirInSubFolderWithContents() throws Exception { - setupTestFiles("topA/subA/subB/somedir", "topA/subA/somefile=foo", "topB"); - - fm.deleteDirectory("topA/subA"); - - assertThat(fm.getFilesInDirectory("", true)) - .hasSize(2) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("topA"); - }) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("topB"); - }); - - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - @Test - void deleteNonExistingDir() { - setupTestFiles("topA/subA", "topB"); - - assertThatThrownBy(() -> fm.deleteDirectory("topC")) - .isInstanceOf(NotDirectoryException.class); - } - - @Test - void deleteFile() { - setupTestFiles("topA=i_am_a_file", "topB"); - - assertThatThrownBy(() -> fm.deleteDirectory("topA")) - .isInstanceOf(NotDirectoryException.class); - } - - @Test - void verifyDirOutsideWorkdirNotDeleteable() { - setupTestFiles("top"); - - assertThatThrownBy(() -> fm.deleteDirectory(null)) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.deleteDirectory("")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.deleteDirectory("./")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.deleteDirectory("../mydir")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.deleteDirectory("top/../../mydir")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.deleteDirectory("../../mydir")) - .isInstanceOf(AccessDeniedException.class); - } - } - - - @Nested - class ReadFile { - - @Test - void readEmptyFile() throws Exception { - setupTestFiles("fileA=", "fileB=something"); - - assertThat(fm.readFile("fileA")).isEqualTo(""); - assertThat(fm.readFile("./fileA")).isEqualTo(""); - } - - @Test - void readNonEmptyFile() throws Exception { - setupTestFiles("fileA=", "sub/fileB=something\nsomething else"); - - assertThat(fm.readFile("sub/fileB")).isEqualTo("something\nsomething else"); - assertThat(fm.readFile("./sub/../sub/fileB")).isEqualTo("something\nsomething else"); - } - - - @Test - void readDirectory() { - setupTestFiles("dirA"); - - assertThatThrownBy(() -> fm.readFile("dirA")) - .isInstanceOf(AccessDeniedException.class); - } - - @Test - void verifyFilesOutsideWorkdirNotAccessible() { - setupTestFiles("top"); - - assertThatThrownBy(() -> fm.readFile("../someFile")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.readFile("top/../../foo")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.readFile("../../bar")) - .isInstanceOf(AccessDeniedException.class); - } - } - - - @Nested - class CreateOrReplaceFile { - - @Test - void createNewFileInRootDirectory() throws Exception { - setupTestFiles("fileA=foo", "topB"); - - fm.createOrReplaceFile("myFile", "content"); - - assertThat(fm.readFile("myFile")).isEqualTo("content"); - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - @Test - void createNewFileInSubDirectory() throws Exception { - setupTestFiles("fileA=foo", "topB"); - - fm.createOrReplaceFile("topB/../topB/./sub/myFile", "content"); - - assertThat(fm.readFile("topB/sub/myFile")).isEqualTo("content"); - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - - @Test - void removeFileContent() throws Exception { - setupTestFiles("fileA=foo", "topB"); - - fm.createOrReplaceFile("fileA", ""); - - assertThat(fm.readFile("fileA")).isEqualTo(""); - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - @Test - void replaceFileContent() throws Exception { - setupTestFiles("topA/fileA=foo", "topB"); - - fm.createOrReplaceFile("topA/fileA", "bar"); - - assertThat(fm.readFile("topA/fileA")).isEqualTo("bar"); - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - @Test - void writeToDirectory() { - setupTestFiles("topA/subA", "topB"); - - assertThatThrownBy(() -> fm.createOrReplaceFile("topA/subA", "")) - .isInstanceOf(AccessDeniedException.class); - } - - @Test - void verifyFilesOutsideWorkdirNotCreatable() { - setupTestFiles("top"); - - assertThatThrownBy(() -> fm.createOrReplaceFile(null, "")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.createOrReplaceFile("", "")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.createOrReplaceFile("./", "")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.createOrReplaceFile("../mydir", "")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.createOrReplaceFile("top/../../mydir", "")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.createOrReplaceFile("../../mydir", "")) - .isInstanceOf(AccessDeniedException.class); - } - } - - - @Nested - class DeleteFile { - - @Test - void deleteFileInRootDirect() throws Exception { - setupTestFiles("topA=foo", "topB"); - - fm.deleteFile("topA"); - - assertThat(fm.getFilesInDirectory("", true)) - .noneSatisfy((f) -> - assertThat(f.getName()).isEqualTo("topA") - ); - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - - @Test - void deleteFileInRootIndirect() throws Exception { - setupTestFiles("topA=foo", "topB"); - - fm.deleteFile("topB/.././topA"); - - assertThat(fm.getFilesInDirectory("", true)) - .noneSatisfy((f) -> - assertThat(f.getName()).isEqualTo("topA") - ); - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - - @Test - void deleteEmptyFile() throws Exception { - setupTestFiles("topA/subA/myFile=", "topA/subB"); - - fm.deleteFile("topA/subA/myFile"); - - assertThat(fm.getFilesInDirectory("", true)) - .hasSize(1) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("topA"); - assertThat(f.getChildren()) - .hasSize(2) - .anySatisfy((f2) -> { - assertThat(f2.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f2.getName()).isEqualTo("subA"); - assertThat(f2.getChildren()).isEmpty(); - }) - .anySatisfy((f2) -> { - assertThat(f2.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f2.getName()).isEqualTo("subB"); - assertThat(f2.getChildren()).isEmpty(); - }); - }); - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - @Test - void deleteNonExistingFile() { - setupTestFiles("topA/subA", "topB"); - - assertThatThrownBy(() -> fm.deleteFile("topC")) - .isInstanceOf(AccessDeniedException.class); - } - - @Test - void deleteDirectory() { - setupTestFiles("topA", "topB"); - - assertThatThrownBy(() -> fm.deleteFile("topA")) - .isInstanceOf(AccessDeniedException.class); - } - - @Test - void verifyDirOutsideWorkdirNotDeletable() { - setupTestFiles("top"); - - assertThatThrownBy(() -> fm.deleteFile(null)) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.deleteFile("")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.deleteFile("./")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.deleteFile("../myfile")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.deleteFile("top/../../myfile")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.deleteFile("../../myfile")) - .isInstanceOf(AccessDeniedException.class); - } - } - - - @Nested - class Move { - - @Test - void renameFile() throws Exception { - setupTestFiles("top", "foo=foo"); - - fm.move("foo", "bar"); - - assertThat(fm.getFilesInDirectory("", true)) - .hasSize(2) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("top"); - }) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.FILE); - assertThat(f.getName()).isEqualTo("bar"); - }); - assertThat(fm.readFile("bar")).isEqualTo("foo"); - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - - @Test - void renameFolder() throws Exception { - setupTestFiles("top", "foo/sub/something=text"); - - fm.move("foo/sub", "foo/bar"); - - assertThat(fm.getFilesInDirectory("", true)) - .hasSize(2) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("top"); - assertThat(f.getChildren()).isEmpty(); - }) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("foo"); - assertThat(f.getChildren()) - .hasSize(1) - .anySatisfy((f2) -> { - assertThat(f2.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f2.getName()).isEqualTo("bar"); - assertThat(f2.getChildren()) - .hasSize(1) - .anySatisfy((f3) -> { - assertThat(f3.getType()).isEqualTo(FileInfo.Type.FILE); - assertThat(f3.getName()).isEqualTo("something"); - }); - }); - }); - assertThat(fm.readFile("foo/bar/something")).isEqualTo("text"); - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - @Test - void moveDirectoryWithContents() throws Exception { - setupTestFiles("top", "foo/sub/something=text", "foo/sub/a/b"); - - fm.move("foo/sub", "sub"); - - assertThat(fm.getFilesInDirectory("", true)) - .hasSize(3) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("top"); - }) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("foo"); - }) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("sub"); - assertThat(f.getChildren()) - .hasSize(2) - .anySatisfy((f2) -> { - assertThat(f2.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f2.getName()).isEqualTo("a"); - assertThat(f2.getChildren()) - .hasSize(1) - .anySatisfy((f3) -> { - assertThat(f3.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f3.getName()).isEqualTo("b"); - assertThat(f3.getChildren()).isEmpty(); - }); - }) - .anySatisfy((f2) -> { - assertThat(f2.getType()).isEqualTo(FileInfo.Type.FILE); - assertThat(f2.getName()).isEqualTo("something"); - }); - }); - assertThat(fm.readFile("sub/something")).isEqualTo("text"); - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - - @Test - void moveFile() throws Exception { - setupTestFiles("file="); - - fm.move("file", "a/b/file"); - - assertThat(fm.getFilesInDirectory("", true)) - .hasSize(1) - .anySatisfy((f) -> { - assertThat(f.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f.getName()).isEqualTo("a"); - assertThat(f.getChildren()) - .hasSize(1) - .anySatisfy((f2) -> { - assertThat(f2.getType()).isEqualTo(FileInfo.Type.DIRECTORY); - assertThat(f2.getName()).isEqualTo("b"); - assertThat(f2.getChildren()) - .hasSize(1) - .anySatisfy((f3) -> { - assertThat(f3.getType()).isEqualTo(FileInfo.Type.FILE); - assertThat(f3.getName()).isEqualTo("file"); - }); - }); - }); - assertThat(fm.readFile("a/b/file")).isEqualTo(""); - verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); - } - - - @Test - void moveNonExistingFile() throws Exception { - setupTestFiles("file="); - - assertThatThrownBy(() -> fm.move("someFile", "anotherFile")) - .isInstanceOf(FileNotFoundException.class); - } - - @Test - void moveOntoExistingFile() throws Exception { - setupTestFiles("file=", "someFile="); - - assertThatThrownBy(() -> fm.move("someFile", "file")) - .isInstanceOf(FileExistsException.class); - } - - @Test - void verifyDirOutsideWorkdirNotMoveable() { - setupTestFiles("top"); - - assertThatThrownBy(() -> fm.move(null, "sub")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.move("", "sub")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.move("/", "sub")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.move("./", "sub")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.move("../myfile", "sub")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.move("top/../../myfile", "sub")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.move("../../myfile", "sub")) - .isInstanceOf(AccessDeniedException.class); - } - - @Test - void verifyCannotMoveOutsideOfWorkDir() { - setupTestFiles("top"); - - assertThatThrownBy(() -> fm.move("top", null)) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.move("top", "")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.move("top", "/")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.move("top", "./")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.move("top", "../myfile")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.move("top", "top/../../myfile")) - .isInstanceOf(AccessDeniedException.class); - assertThatThrownBy(() -> fm.move("top", "../../myfile")) - .isInstanceOf(AccessDeniedException.class); - } - } -} diff --git a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/file/accessor/workingdirectory/WorkingDirectoryAccessorTest.java b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/file/accessor/workingdirectory/WorkingDirectoryAccessorTest.java new file mode 100644 index 0000000000..0e275041b4 --- /dev/null +++ b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/file/accessor/workingdirectory/WorkingDirectoryAccessorTest.java @@ -0,0 +1,551 @@ +package rocks.inspectit.ocelot.file.accessor.workingdirectory; + +import org.apache.commons.io.FileUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.context.ApplicationEventPublisher; +import rocks.inspectit.ocelot.config.model.InspectitServerSettings; +import rocks.inspectit.ocelot.file.FileChangedEvent; +import rocks.inspectit.ocelot.file.FileInfo; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +class WorkingDirectoryAccessorTest { + + private AbstractWorkingDirectoryAccessor accessor; + + private ApplicationEventPublisher eventPublisher; + + private Path tempDirectory; + + @BeforeEach + public void beforeEach() throws IOException { + tempDirectory = Files.createTempDirectory("ocelot"); + + eventPublisher = mock(ApplicationEventPublisher.class); + + accessor = new WorkingDirectoryAccessor(tempDirectory, eventPublisher); + } + + @AfterEach + public void afterEach() throws IOException { + FileUtils.deleteDirectory(tempDirectory.toFile()); + } + + private void createTestFiles(String... files) { + try { + for (String file : files) { + String path; + String content; + if (file.contains("=")) { + String[] splitted = file.split("="); + path = splitted[0]; + content = splitted.length == 2 ? splitted[1] : ""; + } else { + path = file; + content = ""; + } + + Path targetFile = tempDirectory.resolve(path); + Files.createDirectories(targetFile.getParent()); + Files.write(targetFile, content.getBytes(StandardCharsets.UTF_8)); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Nested + class ReadConfigurationFile { + + @Test + public void fileNotExisting() { + Optional result = accessor.readConfigurationFile("test.yml"); + + assertThat(result).isEmpty(); + } + + @Test + public void emptyFile() { + createTestFiles("files/test.yml"); + + Optional result = accessor.readConfigurationFile("test.yml"); + + assertThat(result).hasValue(""); + } + + @Test + public void readFile() { + createTestFiles("files/test.yml=content"); + + Optional result = accessor.readConfigurationFile("./test.yml"); + + assertThat(result).hasValue("content"); + } + + @Test + public void readNestedFile() { + createTestFiles("files/sub/test.yml=content"); + + Optional result = accessor.readConfigurationFile("sub/test.yml"); + + assertThat(result).hasValue("content"); + } + + @Test + public void validTraversal() { + createTestFiles("files/test.yml"); + + Optional result = accessor.readConfigurationFile("sub/../test.yml"); + + assertThat(result).hasValue(""); + } + + @Test + public void illegalPath() { + createTestFiles("files/test.yml"); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> accessor.readConfigurationFile("../test.yml")) + .withMessageStartingWith("User path escapes the base path:"); + } + + @Test + public void absolutePath() { + createTestFiles("files/test.yml"); + + String path; + if (System.getProperty("os.name").contains("Windows")) { + path = "c:/file"; + } else { + path = "/absolute/file"; + } + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> accessor.readConfigurationFile(path)) + .withMessageStartingWith("Path must be relative:"); + } + } + + @Nested + class ReadAgentMappings { + + @Test + public void agentMappingsDoesNotExist() { + Optional result = accessor.readAgentMappings(); + + assertThat(result).isEmpty(); + } + + @Test + public void readEmptyAgentMappings() { + createTestFiles("agent_mappings.yaml"); + + Optional result = accessor.readAgentMappings(); + + assertThat(result).hasValue(""); + } + + @Test + public void readAgentMappings() { + createTestFiles("agent_mappings.yaml=content"); + + Optional result = accessor.readAgentMappings(); + + assertThat(result).hasValue("content"); + } + + } + + @Nested + class ListFiles { + + @Test + public void listFiles() { + createTestFiles("one.yml", "files/second.yml", "files/sub/third.yml", "files/sub/deep/four.yml", "files/sub/five.yml", "files/six.yml"); + + List result = accessor.listConfigurationFiles("."); + + assertThat(result).isNotEmpty(); + + assertThat(result).hasSize(3); + assertThat(result).anySatisfy(fileInfo -> { + assertThat(fileInfo.getName()).isEqualTo("second.yml"); + assertThat(fileInfo.getType()).isEqualTo(FileInfo.Type.FILE); + assertThat(fileInfo.getChildren()).isNull(); + }); + assertThat(result).anySatisfy(fileInfo -> { + assertThat(fileInfo.getName()).isEqualTo("six.yml"); + assertThat(fileInfo.getType()).isEqualTo(FileInfo.Type.FILE); + assertThat(fileInfo.getChildren()).isNull(); + }); + assertThat(result).anySatisfy(fileInfo -> { + assertThat(fileInfo.getName()).isEqualTo("sub"); + assertThat(fileInfo.getType()).isEqualTo(FileInfo.Type.DIRECTORY); + assertThat(fileInfo.getChildren()).hasSize(3); + + List subChildren = fileInfo.getChildren(); + + assertThat(subChildren).anySatisfy(subFileInfo -> { + assertThat(subFileInfo.getName()).isEqualTo("third.yml"); + assertThat(subFileInfo.getType()).isEqualTo(FileInfo.Type.FILE); + assertThat(subFileInfo.getChildren()).isNull(); + }); + assertThat(subChildren).anySatisfy(subFileInfo -> { + assertThat(subFileInfo.getName()).isEqualTo("five.yml"); + assertThat(subFileInfo.getType()).isEqualTo(FileInfo.Type.FILE); + assertThat(subFileInfo.getChildren()).isNull(); + }); + assertThat(subChildren).anySatisfy(subFileInfo -> { + assertThat(subFileInfo.getName()).isEqualTo("deep"); + assertThat(subFileInfo.getType()).isEqualTo(FileInfo.Type.DIRECTORY); + assertThat(subFileInfo.getChildren()).hasSize(1); + + FileInfo child = subFileInfo.getChildren().get(0); + + assertThat(child.getName()).isEqualTo("four.yml"); + assertThat(child.getType()).isEqualTo(FileInfo.Type.FILE); + assertThat(child.getChildren()).isNull(); + }); + }); + } + + @Test + public void listNestedFiles() { + createTestFiles("one.yml", "files/second.yml", "files/sub/third.yml"); + + List result = accessor.listConfigurationFiles("sub"); + + assertThat(result).isNotEmpty(); + + assertThat(result).hasSize(1); + assertThat(result).anySatisfy(fileInfo -> { + assertThat(fileInfo.getName()).isEqualTo("third.yml"); + assertThat(fileInfo.getType()).isEqualTo(FileInfo.Type.FILE); + assertThat(fileInfo.getChildren()).isNull(); + }); + } + + @Test + public void directoryDoesNotExist() { + createTestFiles("one.yml", "files/second.yml"); + + List result = accessor.listConfigurationFiles("not-existing"); + + assertThat(result).isEmpty(); + } + } + + @Nested + class WriteAgentMappings { + + @Test + public void writeAgentMappings() throws IOException { + accessor.writeAgentMappings("new content"); + + Optional result = accessor.readAgentMappings(); + + assertThat(result).hasValue("new content"); + + verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); + verifyNoMoreInteractions(eventPublisher); + } + + @Test + public void overwriteAgentMappings() throws IOException { + createTestFiles("agent_mappings.yaml=old content"); + + Optional before = accessor.readAgentMappings(); + + accessor.writeAgentMappings("new content"); + + Optional after = accessor.readAgentMappings(); + + assertThat(before).hasValue("old content"); + assertThat(after).hasValue("new content"); + + verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); + verifyNoMoreInteractions(eventPublisher); + } + } + + @Nested + class DeleteConfiguration { + + @Test + public void deleteNonExistingFile() { + assertThatExceptionOfType(IOException.class) + .isThrownBy(() -> accessor.deleteConfiguration("first.yml")) + .withMessageStartingWith("Path cannot be deleted because it does not exist: "); + + verifyZeroInteractions(eventPublisher); + } + + @Test + public void deleteRoot() { + createTestFiles("files/file.yml"); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> accessor.deleteConfiguration(".")) + .withMessageStartingWith("Cannot delete base directory: ."); + + verifyZeroInteractions(eventPublisher); + } + + @Test + public void deleteRootTraversal() { + createTestFiles("files/file.yml"); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> accessor.deleteConfiguration("./dummy/..")) + .withMessageStartingWith("Cannot delete base directory: ."); + + verifyZeroInteractions(eventPublisher); + } + + @Test + public void deleteFile() throws IOException { + createTestFiles("files/first.yml"); + + List before = accessor.listConfigurationFiles(""); + + accessor.deleteConfiguration("first.yml"); + + List after = accessor.listConfigurationFiles(""); + + assertThat(before).hasSize(1); + assertThat(after).isEmpty(); + + verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); + verifyNoMoreInteractions(eventPublisher); + } + + @Test + public void deleteDirectory() throws IOException { + createTestFiles("files/sub/first.yml"); + + List before = accessor.listConfigurationFiles("sub"); + + accessor.deleteConfiguration("sub"); + + List after = accessor.listConfigurationFiles(""); + + assertThat(before).hasSize(1); + assertThat(after).isEmpty(); + + verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); + verifyNoMoreInteractions(eventPublisher); + } + } + + @Nested + class WriteConfigurationFile { + + @Test + public void writeFile() throws IOException { + List before = accessor.listConfigurationFiles(""); + + accessor.writeConfigurationFile("first.yml", "new content"); + + List after = accessor.listConfigurationFiles(""); + Optional fileContent = accessor.readConfigurationFile("first.yml"); + + assertThat(before).isEmpty(); + assertThat(after).isNotEmpty() + .anySatisfy(fileInfo -> { + assertThat(fileInfo.getName()).isEqualTo("first.yml"); + assertThat(fileInfo.getType()).isEqualTo(FileInfo.Type.FILE); + assertThat(fileInfo.getChildren()).isNull(); + }); + + assertThat(fileContent).hasValue("new content"); + + verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); + verifyNoMoreInteractions(eventPublisher); + } + + @Test + public void overwriteFile() throws IOException { + createTestFiles("files/first.yml=old content"); + + Optional before = accessor.readConfigurationFile("first.yml"); + + accessor.writeConfigurationFile("first.yml", "new content"); + + Optional after = accessor.readConfigurationFile("first.yml"); + + assertThat(before).hasValue("old content"); + assertThat(after).hasValue("new content"); + + verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); + verifyNoMoreInteractions(eventPublisher); + } + + @Test + public void fileIsDirectory() { + createTestFiles("files/sub/first.yml"); + + assertThatExceptionOfType(IOException.class) + .isThrownBy(() -> accessor.writeConfigurationFile("sub", "new content")) + .withMessageStartingWith("Cannot write file because target is already a directory: "); + + verifyZeroInteractions(eventPublisher); + } + } + + @Nested + class MoveConfiguration { + + @Test + public void moveFile() throws IOException { + createTestFiles("files/first.yml=my file"); + + Optional beforeA = accessor.readConfigurationFile("first.yml"); + Optional beforeB = accessor.readConfigurationFile("sub/moved.yml"); + + accessor.moveConfiguration("first.yml", "sub/moved.yml"); + + Optional afterA = accessor.readConfigurationFile("first.yml"); + Optional afterB = accessor.readConfigurationFile("sub/moved.yml"); + + assertThat(beforeA).hasValue("my file"); + assertThat(beforeB).isEmpty(); + assertThat(afterA).isEmpty(); + assertThat(afterB).hasValue("my file"); + + verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); + verifyNoMoreInteractions(eventPublisher); + } + + @Test + public void moveDirectory() throws IOException { + createTestFiles("files/sub_a/file.yml=my file"); + + Optional beforeA = accessor.readConfigurationFile("sub_a/file.yml"); + Optional beforeB = accessor.readConfigurationFile("sub_b/file.yml"); + + accessor.moveConfiguration("sub_a", "sub_b"); + + Optional afterA = accessor.readConfigurationFile("sub_a/file.yml"); + Optional afterB = accessor.readConfigurationFile("sub_b/file.yml"); + + assertThat(beforeA).hasValue("my file"); + assertThat(beforeB).isEmpty(); + assertThat(afterA).isEmpty(); + assertThat(afterB).hasValue("my file"); + + verify(eventPublisher).publishEvent(any(FileChangedEvent.class)); + verifyNoMoreInteractions(eventPublisher); + } + } + + @Nested + class CreateConfigurationDirectory { + + @Test + public void createDirectory() throws IOException { + accessor.createConfigurationDirectory("test"); + + Path targetDirectory = tempDirectory.resolve("files/test"); + assertThat(targetDirectory).exists(); + } + + @Test + public void alreadyExists() { + createTestFiles("files/test/file"); + + assertThatExceptionOfType(FileAlreadyExistsException.class) + .isThrownBy(() -> accessor.createConfigurationDirectory("test")) + .withMessageStartingWith("Directory already exists:"); + } + + @Test + public void fileWithSameName() { + createTestFiles("files/test"); + + assertThatExceptionOfType(FileAlreadyExistsException.class) + .isThrownBy(() -> accessor.createConfigurationDirectory("test")) + .withMessageStartingWith("Directory already exists:"); + } + + @Test + public void invalidLocation() { + createTestFiles("test/file"); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> accessor.createConfigurationDirectory("../test")) + .withMessageStartingWith("User path escapes the base path:"); + } + } + + @Nested + class ConfigurationFileExists { + + @Test + public void doesNotExist() { + boolean result = accessor.configurationFileExists("file"); + + assertThat(result).isFalse(); + } + + @Test + public void fileExist() { + createTestFiles("files/sub/file"); + + boolean result = accessor.configurationFileExists("sub/file"); + + assertThat(result).isTrue(); + } + + @Test + public void directoryExist() { + createTestFiles("files/sub/file"); + + boolean result = accessor.configurationFileExists("sub"); + + assertThat(result).isTrue(); + } + } + + @Nested + class ConfigurationFileIsDirectory { + + @Test + public void doesNotExist() { + boolean result = accessor.configurationFileIsDirectory("target"); + + assertThat(result).isFalse(); + } + + @Test + public void isDirectory() { + createTestFiles("files/target/file"); + + boolean result = accessor.configurationFileIsDirectory("target"); + + assertThat(result).isTrue(); + } + + @Test + public void isNotDirectory() { + createTestFiles("files/target/file"); + + boolean result = accessor.configurationFileIsDirectory("target/file"); + + assertThat(result).isFalse(); + } + } +} \ No newline at end of file diff --git a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/file/DirectoryControllerIntTest.java b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/file/DirectoryControllerIntTest.java new file mode 100644 index 0000000000..8c149d95ee --- /dev/null +++ b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/file/DirectoryControllerIntTest.java @@ -0,0 +1,90 @@ +package rocks.inspectit.ocelot.rest.file; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import rocks.inspectit.ocelot.IntegrationTestBase; +import rocks.inspectit.ocelot.file.FileInfo; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +class DirectoryControllerIntTest extends IntegrationTestBase { + + @Nested + class ListContents { + + @Test + public void emptyResponse() { + ResponseEntity result = authRest.getForEntity("/api/v1/directories/", FileInfo[].class); + + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(result.getBody()).isEmpty(); + } + + @Test + public void validResponse() { + // create test files + authRest.exchange("/api/v1/files/file.yml", HttpMethod.PUT, null, Void.class); + + ResponseEntity result = authRest.getForEntity("/api/v1/directories/", FileInfo[].class); + + FileInfo[] resultBody = result.getBody(); + assertThat(resultBody) + .hasSize(1) + .extracting(FileInfo::getName, FileInfo::getType, FileInfo::getChildren) + .contains(tuple("file.yml", FileInfo.Type.FILE, null)); + } + } + + @Nested + class CreateNewDirectory { + + @Test + public void noDirectorySpecified() { + ResponseEntity result = authRest.exchange("/api/v1/directories/", HttpMethod.PUT, null, Void.class); + + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + } + + @Test + public void createDirectory() { + ResponseEntity result = authRest.exchange("/api/v1/directories/new_dir", HttpMethod.PUT, null, Void.class); + + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); + + Path new_dir = Paths.get(settings.getWorkingDirectory(), "files", "new_dir"); + assertThat(new_dir).exists(); + } + } + + @Nested + class DeleteDirectory { + + @Test + public void noDirectorySpecified() { + ResponseEntity result = authRest.exchange("/api/v1/directories/", HttpMethod.DELETE, null, Void.class); + + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + } + + @Test + public void deleteDirectory() { + // create test directory with files + authRest.exchange("/api/v1/files/root/target_dir/file.yml", HttpMethod.PUT, null, Void.class); + + assertThat(Paths.get(settings.getWorkingDirectory(), "files", "root", "target_dir", "file.yml")).exists(); + + ResponseEntity result = authRest.exchange("/api/v1/directories/root/target_dir", HttpMethod.DELETE, null, Void.class); + + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(Paths.get(settings.getWorkingDirectory(), "files", "root")).exists(); + assertThat(Paths.get(settings.getWorkingDirectory(), "files", "root", "target_dir")).doesNotExist(); + } + } +} \ No newline at end of file diff --git a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/file/DirectoryControllerTest.java b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/file/DirectoryControllerTest.java new file mode 100644 index 0000000000..b9da5bc489 --- /dev/null +++ b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/file/DirectoryControllerTest.java @@ -0,0 +1,115 @@ +package rocks.inspectit.ocelot.rest.file; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.web.servlet.HandlerMapping; +import rocks.inspectit.ocelot.file.FileInfo; +import rocks.inspectit.ocelot.file.FileManager; +import rocks.inspectit.ocelot.file.accessor.workingdirectory.WorkingDirectoryAccessor; + +import javax.servlet.http.HttpServletRequest; +import javax.swing.text.html.Option; + +import java.io.IOException; +import java.util.*; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class DirectoryControllerTest { + + @Mock + private FileManager fileManager; + + @Mock + private WorkingDirectoryAccessor accessor; + + @InjectMocks + private DirectoryController controller; + + @BeforeEach + public void beforeEach() { + when(fileManager.getWorkingDirectory()).thenReturn(accessor); + } + + @Nested + class ListContents { + + @Test + public void nullResult() { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getAttribute(anyString())).thenReturn("/api/target", "/api/**"); + when(accessor.listConfigurationFiles(any())).thenReturn(Collections.emptyList()); + + Collection result = controller.listContents(request); + + verify(accessor).listConfigurationFiles("target"); + verifyNoMoreInteractions(accessor); + assertThat(result).isEmpty(); + } + + @Test + public void emptyResult() { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getAttribute(anyString())).thenReturn("/api/target", "/api/**"); + when(accessor.listConfigurationFiles("target")).thenReturn(Collections.emptyList()); + + Collection result = controller.listContents(request); + + verify(accessor).listConfigurationFiles("target"); + verifyNoMoreInteractions(accessor); + assertThat(result).isEmpty(); + } + + @Test + public void validResponse() { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getAttribute(anyString())).thenReturn("/api/target", "/api/**"); + FileInfo fileInfo = mock(FileInfo.class); + when(accessor.listConfigurationFiles("target")).thenReturn(Collections.singletonList(fileInfo)); + + Collection result = controller.listContents(request); + + verify(accessor).listConfigurationFiles("target"); + verifyNoMoreInteractions(accessor); + assertThat(result).containsExactly(fileInfo); + } + } + + @Nested + class CreateNewDirectory { + + @Test + public void successful() throws IOException { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getAttribute(anyString())).thenReturn("/api/target", "/api/**"); + + controller.createNewDirectory(request); + + verify(accessor).createConfigurationDirectory("target"); + verifyNoMoreInteractions(accessor); + } + } + + @Nested + class DeleteDirectory { + + @Test + public void successful() throws IOException { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getAttribute(anyString())).thenReturn("/api/target", "/api/**"); + + controller.deleteDirectory(request); + + verify(accessor).deleteConfiguration("target"); + verifyNoMoreInteractions(accessor); + } + } +} \ No newline at end of file diff --git a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/file/MoveControllerIntTest.java b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/file/MoveControllerIntTest.java new file mode 100644 index 0000000000..f6ebdbd357 --- /dev/null +++ b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/file/MoveControllerIntTest.java @@ -0,0 +1,66 @@ +package rocks.inspectit.ocelot.rest.file; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import rocks.inspectit.ocelot.IntegrationTestBase; +import rocks.inspectit.ocelot.file.FileMoveDescription; + +import java.nio.file.Paths; + +import static org.assertj.core.api.Assertions.assertThat; + +class MoveControllerIntTest extends IntegrationTestBase { + + @Nested + class MoveFileOrDirectory { + + @Test + public void srcNotExisting() { + HttpEntity request = new HttpEntity<>(FileMoveDescription.builder() + .source("src") + .target("trgt") + .build()); + ResponseEntity result = authRest.exchange("/api/v1/move", HttpMethod.PUT, request, Void.class); + + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + } + + @Test + public void targetExists() { + // create test files + authRest.exchange("/api/v1/files/src/file.yml", HttpMethod.PUT, null, Void.class); + authRest.exchange("/api/v1/files/trgt/file.yml", HttpMethod.PUT, null, Void.class); + + HttpEntity request = new HttpEntity<>(FileMoveDescription.builder() + .source("src") + .target("trgt") + .build()); + ResponseEntity result = authRest.exchange("/api/v1/move", HttpMethod.PUT, request, Void.class); + + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.CONFLICT); + } + + @Test + public void successfulMove() { + // create test files + authRest.exchange("/api/v1/files/src/file.yml", HttpMethod.PUT, null, Void.class); + + assertThat(Paths.get(settings.getWorkingDirectory(), "files/src/file.yml")).exists(); + assertThat(Paths.get(settings.getWorkingDirectory(), "files/trgt/file.yml")).doesNotExist(); + + HttpEntity request = new HttpEntity<>(FileMoveDescription.builder() + .source("src") + .target("trgt") + .build()); + ResponseEntity result = authRest.exchange("/api/v1/move", HttpMethod.PUT, request, Void.class); + + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(Paths.get(settings.getWorkingDirectory(), "files/src/file.yml")).doesNotExist(); + assertThat(Paths.get(settings.getWorkingDirectory(), "files/trgt/file.yml")).exists(); + } + } +} \ No newline at end of file diff --git a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/file/MoveControllerTest.java b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/file/MoveControllerTest.java index 0a672474ac..b2ea09dd96 100644 --- a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/file/MoveControllerTest.java +++ b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/file/MoveControllerTest.java @@ -8,9 +8,10 @@ import org.mockito.junit.jupiter.MockitoExtension; import rocks.inspectit.ocelot.file.FileManager; import rocks.inspectit.ocelot.file.FileMoveDescription; +import rocks.inspectit.ocelot.file.accessor.workingdirectory.AbstractWorkingDirectoryAccessor; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class MoveControllerTest { @@ -18,6 +19,9 @@ public class MoveControllerTest { @Mock FileManager fileManager; + @Mock + AbstractWorkingDirectoryAccessor fileAccessor; + @InjectMocks MoveController controller; @@ -26,20 +30,30 @@ class MoveFileOrDirectory { @Test void sourceWithLeadingSlash() throws Exception { + when(fileManager.getWorkingDirectory()).thenReturn(fileAccessor); + controller.moveFileOrDirectory(FileMoveDescription.builder() .source("/src") .target("dest") .build()); - verify(fileManager).move(eq("src"), eq("dest")); + + verify(fileManager).getWorkingDirectory(); + verify(fileAccessor).moveConfiguration(eq("src"), eq("dest")); + verifyNoMoreInteractions(fileManager, fileAccessor); } @Test void targetWithLeadingSlash() throws Exception { + when(fileManager.getWorkingDirectory()).thenReturn(fileAccessor); + controller.moveFileOrDirectory(FileMoveDescription.builder() .source("src") .target("/dest") .build()); - verify(fileManager).move(eq("src"), eq("dest")); + + verify(fileManager).getWorkingDirectory(); + verify(fileAccessor).moveConfiguration(eq("src"), eq("dest")); + verifyNoMoreInteractions(fileManager, fileAccessor); } } diff --git a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/users/AccountControllerIntTest.java b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/users/AccountControllerIntTest.java index 23ea0bc87b..b8de12d314 100644 --- a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/users/AccountControllerIntTest.java +++ b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/users/AccountControllerIntTest.java @@ -21,9 +21,6 @@ public class AccountControllerIntTest extends IntegrationTestBase { - @Autowired - InspectitServerSettings settings; - @Autowired UserService userService; diff --git a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/users/UserControllerIntTest.java b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/users/UserControllerIntTest.java index a4450ca24c..361e2a9678 100644 --- a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/users/UserControllerIntTest.java +++ b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/rest/users/UserControllerIntTest.java @@ -10,7 +10,6 @@ import org.springframework.http.ResponseEntity; import rocks.inspectit.ocelot.IntegrationTestBase; import rocks.inspectit.ocelot.config.model.InspectitServerSettings; -import rocks.inspectit.ocelot.security.userdetails.LocalUserDetailsService; import rocks.inspectit.ocelot.user.User; import rocks.inspectit.ocelot.user.UserService; @@ -20,9 +19,6 @@ public class UserControllerIntTest extends IntegrationTestBase { - @Autowired - InspectitServerSettings settings; - @Autowired UserService userService;