diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 00000000..6b06b533 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,50 @@ +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: CI + +on: + push: + branches: + - master + - '[0-9]+.[0-9]+*' + pull_request: + branches: + - master + - '[0-9]+.[0-9]+*' + +jobs: + # Run build and test + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + cache: maven + - name: Build with Maven + run: mvn -B clean install -Pextjs,h2_disk,auditing + ## Only on push event, publish on geosolutions maven repo + publish: + runs-on: ubuntu-latest + if: ${{ github.event_name == 'push' && github.repository == 'geosolutions-it/geostore' }} + needs: build + concurrency: ci-${{ github.ref }} + steps: + - uses: actions/checkout@v3 + - name: Set up Maven Central Repository + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'adopt' + server-id: geosolutions + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + - name: Publish package + run: mvn clean install deploy -Ppostgres,extjs + env: + MAVEN_USERNAME: ${{ secrets.GS_MAVEN_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.GS_MAVEN_PASSWORD }} diff --git a/.github/workflows/cut-major-branch.yml b/.github/workflows/cut-major-branch.yml new file mode 100644 index 00000000..359a7e69 --- /dev/null +++ b/.github/workflows/cut-major-branch.yml @@ -0,0 +1,64 @@ +name: Cut Release Branch +on: + workflow_dispatch: + inputs: + current-version: + description: Current version (e.g. 2.1) + required: true + next-version: + description: Next version (e.g. 2.2). + required: true + main-branch: + description: main branch + default: master + pr-options: + description: Options for Pull request + default: --squash --auto --delete-branch +jobs: + cut-release: + name: Create release branch and PRs into main main branch + runs-on: ubuntu-latest + steps: + + - name: Checkout + uses: actions/checkout@v2 + with: + ref: master + - name: Create release branch and generate PR body + id: create-branch + env: + CURR: ${{ github.event.inputs.current-version }} + NEXT: ${{ github.event.inputs.next-version }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + MAIN_BRANCH: ${{ github.event.inputs.main-branch }} + PR_OPTIONS: ${{ github.event.inputs.pr-options }} + RUN_ID: ${{ github.run_id }} + + run: | + # script will go here + echo "Initializing git" + # Optional + git config user.name github-actions + git config user.email github-actions@github.com + BRANCH_NAME="${CURR}.x" + echo "creating branch is $BRANCH_NAME" + git checkout -b "$BRANCH_NAME" + git push --set-upstream origin "$BRANCH_NAME" + echo "branch created" + echo "creating bump changes" + git checkout "$MAIN_BRANCH" + mvn -q -B release:update-versions -DautoVersionSubmodules=true -DdevelopmentVersion=${NEXT}-SNAPSHOT # sets the internal verison + mvn -q -B versions:set-property -B -Dproperty=geostore-version -DnewVersion=${NEXT}-SNAPSHOT # sets geostore-version property + pr_branch_name="bump-${NEXT}-${RUN_ID}" + echo "Creating a temp PR on branch: ${pr_branch_name}" + git checkout -b "${pr_branch_name}" + find . -name 'pom.xml' | xargs git add + git commit -m "Bump version to ${NEXT}" + git push origin "${pr_branch_name}" + pr_url=$(gh pr create -B "${MAIN_BRANCH}" -H "${pr_branch_name}" --title "[github-action] Bump version to ${NEXT}" --body "This automatic pull request bumps version of ${MAIN_BRANCH} branch to ${NEXT}") + sleep 10 + gh pr merge "$pr_url" ${PR_OPTIONS} + + + + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..ce16f7a5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,97 @@ +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Version to release, e.g. 2.1.0 (make sure to execute on this on the release branch)' + required: true + base: + description: 'Base Version. e.g. 2.1' + required: true + +jobs: + fix-version: + # if: ${{ github.repository == 'geosolutions-it/geostore' && github.ref != 'master' }} + concurrency: ci-${{ github.ref }} + runs-on: ubuntu-latest + steps: + - name: Check branch + if: ${{ github.repository != 'geosolutions-it/geostore' || github.ref == 'master' }} + uses: actions/github-script@v3 + with: + script: | + core.setFailed('This workflow can not run on master branch') + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'adopt' + cache: maven + + - name: Fix versions, commit and push new tag + run: | + # fix versions to ${{ inputs.version }} + mvn versions:set -DnewVersion=${{ inputs.version }} -DgenerateBackupPoms=false # set versions + mvn versions:set-property -Dproperty=geostore-version -DnewVersion=${{ inputs.version }} # set geostore-version property + mvn versions:use-releases # check to not use SNAPSHOTs + + # script will go here + echo "Initializing git" + # Optional + git config user.name github-actions + git config user.email github-actions@github.com + + # Commit changes + find . -name 'pom.xml' | xargs git add + git commit -m "Version Release ${{ inputs.version }}" + git tag v${{ inputs.version }} # create tag + git push origin ${{ github.ref_name }} --tags + + - name: Build with Maven + run: mvn -B clean install -Pextjs,h2_disk,auditing + + - name: Set up Maven Repository + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'adopt' + server-id: geosolutions + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + + - name: Publish package + run: mvn clean install deploy -Ppostgres,extjs + env: + MAVEN_USERNAME: ${{ secrets.GS_MAVEN_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.GS_MAVEN_PASSWORD }} + - name: Restore Snapshots + run: | + # restore versions to ${{ inputs.base }}-SNAPSHOT + mvn versions:set -DnewVersion=${{ inputs.base }}-SNAPSHOT -DgenerateBackupPoms=false + mvn versions:set-property -Dproperty=geostore-version -DnewVersion=${{ inputs.base }}-SNAPSHOT # set geostore-version property + find . -name 'pom.xml' | xargs git add + git commit -m "Restore snapshots from ${{ inputs.version }} to ${{ inputs.base }}-SNAPSHOT" + git tag ${VERSION} # create tag + git push origin ${{ github.ref_name }} + echo "Snapshots version restored" + release: + runs-on: ubuntu-latest + needs: fix-version + concurrency: release-${{ github.ref }} + steps: + - name: create_release + id: create_release + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token + with: + tag_name: "v${{ inputs.version }}" + name: "${{ inputs.version }}" + generate_release_notes: true + draft: false + prerelease: false diff --git a/README.md b/README.md index d9d9023a..e0fb162d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -[![Build Status](https://travis-ci.org/geosolutions-it/geostore.svg?branch=master)](https://travis-ci.org/geosolutions-it/geostore) -[![Build Status](http://build.geo-solutions.it/jenkins/buildStatus/icon?job=GeoStore-Master)](http://build.geo-solutions.it/jenkins/job/GeoStore-Master) +![Build Status](https://github.com/geosolutions-it/geostore/actions/workflows/CI.yml/badge.svg) [![Coverage Status](https://coveralls.io/repos/github/geosolutions-it/geostore/badge.svg?branch=master)](https://coveralls.io/github/geosolutions-it/geostore?branch=master) [GeoStore](https://github.com/geosolutions-it/geostore) is an open source Java enterprise application for storing, searching and retrieving data on the fly. @@ -8,25 +7,66 @@ GeoStore performs authentication internally (auth framework shall be pluggable), A comprehensive [REST API](https://github.com/geosolutions-it/geostore/wiki/REST-API) allows an easy handling of internal resources, so that client side applications can easily query the store, while remote server side application can integrate with GeoStore using the GeoStoreClient, an utility java class that hides the REST communications complexities. -Documentation -------------- +# Documentation + For more information check the [GeoStore wiki](https://github.com/geosolutions-it/geostore/wiki/Documentation-index) . -License ------------- +## Release process + +The release procedure is essentially made of 2 steps: +- **Cut major branch** +- Effective **Release** + +The project is developed on the main (master) branch, containing the latest `-SNAPSHOT` version of the modules of the project. When a major release starts the validation process, a new *release branch* is created (see [Cut-Release](#cut-Release-branch), named `.xx`. +After this on the main (master) branch the `-SNAPSHOT` version of the modules is increased and from this point the main branch will include commits for the next major version of the project. + +When the validation process is completed and all the fixes have been applied to the *release branch*, the version of the java modules is fixed, commit is tagged with the number of the release and the Github *release* is published. See [Release](#release). + +After this on the *release brach* the `-SNAPSHOT` version of the modules is restored so that it is possible to continue applying fixes and minor improvements, creating more releases on it with the same procedure, until end of maintainance. + +Here the steps to follow for executing the 2 procedures : + +### Cut-Release branch + +1. Run the workflow [Cut Release branch](../../actions/workflows/cut-major-branch.yml) passing + - Branch Master + - current version + - next version + - main branch (keep `master`) + - other options (can be left as default) +2. Merge the PR that is generated, if not merged automatically + +### Release + +1. Run the workflow [Release](../../actions/workflows/release.yml) with the folling parameters: + - select the branch to use (e.g. `2.1.x`) + - version to release (e.g. `2.1.0`) + - base version (e.g. `2.1`) + +The release will be automatically published on GitHub. Packages will be automatically deployed on maven repository. + + +## Relevant Workflows + +- [CI](../../actions/workflows/CI.yml): Automatically does tests for pull request or commits on `master`. For commits on the main repo (e.g. when PR are merged on `master` or stable branches, the workflow publish also the artifacts on [GeoSolutions Maven Repository](https://maven.geo-solutions.it) +- **[Cut release branch](../../actions/workflows/cut-major-branch.yml)**: (`cut-major-branch.yml`): Manually triggered workflow that allows to create a stable branch named `.x` and create a pull request for updating `master` branch `-SNAPSHOT` version with the new data. +- **[Release](../../actions/workflows/release.yml)**: (`cut-major-branch.yml`): Manually triggered workflow to apply to the stable branch that fixes the maven modules versions, tags the commit, build and deploy artifacts, restores snapshot versions and publish a Github release on the tagged commit. + +# License + **GeoStore** core modules are free and Open Source software, released under the [GPL v3](http://www.gnu.org/licenses/gpl.html) license. -Professional Support ---------------------- +# Professional Support + GeoStore is being developed by [GeoSolutions](http://www.geo-solutions.it/) hence you can talk to us for professional support. Anyway the project is a real Open Source project hence you can contribute to it (see section below). -Contributing ---------------------- +# Contributing + We welcome contributions in any form: -* pull requests for new features -* pull requests for bug fixes -* pull requests for documentation -* funding for any combination of the above +- pull requests for new features +- pull requests for bug fixes +- pull requests for documentation +- funding for any combination of the above diff --git a/doc/sql/001_setup_db.sql b/doc/sql/001_setup_db.sql index ff4e8939..4e4c48d3 100644 --- a/doc/sql/001_setup_db.sql +++ b/doc/sql/001_setup_db.sql @@ -1,25 +1,23 @@ --- CREATE SCHEMA geostore -CREATE user geostore LOGIN PASSWORD 'geostore' NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE; +CREATE USER geostore LOGIN PASSWORD 'geostore' NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE; CREATE SCHEMA geostore; GRANT USAGE ON SCHEMA geostore TO geostore ; GRANT ALL ON SCHEMA geostore TO geostore ; ---GRANT SELECT ON public.spatial_ref_sys to geostore; ---GRANT SELECT,INSERT,DELETE ON public.geometry_columns to geostore; +GRANT SELECT ON public.spatial_ref_sys to geostore; +GRANT SELECT,INSERT,DELETE ON public.geometry_columns to geostore; -alter user geostore set search_path to geostore , public; +ALTER USER geostore SET search_path TO geostore , public; --- CREATE SCHEMA georepo_test -CREATE user geostore_test LOGIN PASSWORD 'geostore_test' NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE; +CREATE USER geostore_test LOGIN PASSWORD 'geostore_test' NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE; CREATE SCHEMA geostore_test; GRANT USAGE ON SCHEMA geostore_test TO geostore_test; GRANT ALL ON SCHEMA geostore_test TO geostore_test; ---GRANT SELECT ON public.spatial_ref_sys to geostore_test; ---GRANT SELECT,INSERT,DELETE ON public.geometry_columns to geostore_test; +GRANT SELECT ON public.spatial_ref_sys to geostore_test; +GRANT SELECT,INSERT,DELETE ON public.geometry_columns to geostore_test; -alter user geostore_test set search_path to geostore_test, public; +ALTER USER geostore_test SET search_path TO geostore_test, public; diff --git a/doc/sql/002_create_schema_oracle.sql b/doc/sql/002_create_schema_oracle.sql index 4ca1e94f..1da10b71 100644 --- a/doc/sql/002_create_schema_oracle.sql +++ b/doc/sql/002_create_schema_oracle.sql @@ -26,6 +26,9 @@ metadata varchar2(4000 char), name varchar2(255 char) not null, category_id number(19,0) not null, + creator varchar2(255 char), + editor varchar2(255 char), + advertised bool not null default true, primary key (id), unique (name) ); @@ -72,6 +75,14 @@ unique (name, user_id) ); + create table gs_user_group_attribute ( + id number(19,0) not null, + name varchar2(255 char) not null, + string varchar2(255 char), + userGroup_id number(19,0) not null, + primary key (id) + ); + create table gs_usergroup ( id number(19,0) not null, groupName varchar2(255 char) not null, @@ -191,6 +202,14 @@ foreign key (user_id) references gs_user; + create index idx_user_group_attr_name on gs_user_group_attribute (name); + + create index idx_user_group_attr_text on gs_user_group_attribute (string); + + create index idx_attr_user_group on gs_user_group_attribute (userGroup_id); + + alter table gs_user_group_attribute add constraint fk_ugattrib_user_group foreign key (userGroup_id) references gs_usergroup; + create index idx_usergroup_name on gs_usergroup (groupName); create sequence hibernate_sequence; diff --git a/doc/sql/002_create_schema_postgres.sql b/doc/sql/002_create_schema_postgres.sql index 6afe24c5..bf2c36ac 100644 --- a/doc/sql/002_create_schema_postgres.sql +++ b/doc/sql/002_create_schema_postgres.sql @@ -14,6 +14,8 @@ set PGOPTIONS="--search_path=geostore_test" psql -U geostore_test -d geostore -f 002_create_schema_postgres.sql */ +SET search_path TO geostore; + create table gs_attribute ( id int8 not null, attribute_date timestamp, @@ -41,6 +43,9 @@ psql -U geostore_test -d geostore -f 002_create_schema_postgres.sql metadata varchar(30000), name varchar(255) not null, category_id int8 not null, + creator varchar(255), + editor varchar(255), + advertised bool not null default true, primary key (id), unique (name) ); @@ -95,6 +100,14 @@ psql -U geostore_test -d geostore -f 002_create_schema_postgres.sql primary key (id), unique (groupName) ); + + create table gs_user_group_attribute ( + id bigint not null, + name varchar(255) not null, + string varchar(255), + userGroup_id bigint not null, + primary key (id) + ); create table gs_usergroup_members ( user_id int8 not null, @@ -129,6 +142,14 @@ psql -U geostore_test -d geostore -f 002_create_schema_postgres.sql foreign key (resource_id) references gs_resource; + create index idx_user_group_attr_name on gs_user_group_attribute (name); + + create index idx_user_group_attr_text on gs_user_group_attribute (string); + + create index idx_attr_user_group on gs_user_group_attribute (userGroup_id); + + alter table gs_user_group_attribute add constraint fk_ugattrib_user_group foreign key (userGroup_id) references gs_usergroup; + create index idx_category_type on gs_category (name); create index idx_resource_name on gs_resource (name); @@ -209,4 +230,3 @@ psql -U geostore_test -d geostore -f 002_create_schema_postgres.sql create index idx_usergroup_name on gs_usergroup (groupName); create sequence hibernate_sequence; - diff --git a/doc/sql/migration/h2/h2-migration-from-v.1.5.0-to-v2.0.0.sql b/doc/sql/migration/h2/h2-migration-from-v.1.5.0-to-v2.0.0.sql new file mode 100644 index 00000000..35cbd7b6 --- /dev/null +++ b/doc/sql/migration/h2/h2-migration-from-v.1.5.0-to-v2.0.0.sql @@ -0,0 +1,16 @@ +create table gs_user_group_attribute ( + id bigint primary key, + name varchar(255) not null, + string varchar(255), + userGroup_id bigint not null + ); + + + +create index idx_user_group_attr_name on gs_user_group_attribute (name); + +create index idx_user_group_attr_text on gs_user_group_attribute (string); + +create index idx_attr_user_group on gs_user_group_attribute (userGroup_id); + +alter table gs_user_group_attribute add constraint fk_ugattrib_user_group foreign key (userGroup_id) references gs_usergroup; diff --git a/doc/sql/migration/h2/h2-migration-from-v.2.0.0-to-v2.1.0.sql b/doc/sql/migration/h2/h2-migration-from-v.2.0.0-to-v2.1.0.sql new file mode 100644 index 00000000..c87d69ca --- /dev/null +++ b/doc/sql/migration/h2/h2-migration-from-v.2.0.0-to-v2.1.0.sql @@ -0,0 +1,11 @@ +alter table gs_resource add column creator varchar(255); +alter table gs_resource add column editor varchar(255); +alter table gs_resource add column advertised bool not null default true; + +-- Set the Resource Creator whether this is NULL +update gs_resource as gsr +set creator = subquery.name +from +(select gsu.name, gss.resource_id from geostore.gs_security as gss join geostore.gs_user as gsu on (gss.user_id = gsu.id) + where gss.user_id IS NOT NULL) as subquery +where gsr.id = subquery.resource_id and gsr.creator IS NULL; \ No newline at end of file diff --git a/doc/sql/migration/oracle/oracle-migration-from-v.1.5.0-to-v2.0.0.sql b/doc/sql/migration/oracle/oracle-migration-from-v.1.5.0-to-v2.0.0.sql new file mode 100644 index 00000000..7a40bc89 --- /dev/null +++ b/doc/sql/migration/oracle/oracle-migration-from-v.1.5.0-to-v2.0.0.sql @@ -0,0 +1,17 @@ +create table gs_user_group_attribute ( + id number(19,0) not null, + name varchar2(255 char) not null, + string varchar2(255 char), + userGroup_id number(19,0) not null, + primary key (id) + ); + + + +create index idx_user_group_attr_name on gs_user_group_attribute (name); + +create index idx_user_group_attr_text on gs_user_group_attribute (string); + +create index idx_attr_user_group on gs_user_group_attribute (userGroup_id); + +alter table gs_user_group_attribute add constraint fk_ugattrib_user_group foreign key (userGroup_id) references gs_usergroup; diff --git a/doc/sql/migration/oracle/oracle-migration-from-v.2.0.0-to-v2.1.0.sql b/doc/sql/migration/oracle/oracle-migration-from-v.2.0.0-to-v2.1.0.sql new file mode 100644 index 00000000..f1ec4c0a --- /dev/null +++ b/doc/sql/migration/oracle/oracle-migration-from-v.2.0.0-to-v2.1.0.sql @@ -0,0 +1,11 @@ +alter table gs_resource add column creator varchar2(255 char); +alter table gs_resource add column editor varchar2(255 char); +alter table gs_resource add column advertised bool not null default true; + +-- Set the Resource Creator whether this is NULL +update gs_resource as gsr +set creator = subquery.name +from +(select gsu.name, gss.resource_id from geostore.gs_security as gss join geostore.gs_user as gsu on (gss.user_id = gsu.id) + where gss.user_id IS NOT NULL) as subquery +where gsr.id = subquery.resource_id and gsr.creator IS NULL; \ No newline at end of file diff --git a/doc/sql/migration/postgresql/postgresql-migration-from-v.1.5.0-to-v2.0.0.sql b/doc/sql/migration/postgresql/postgresql-migration-from-v.1.5.0-to-v2.0.0.sql new file mode 100644 index 00000000..63c39136 --- /dev/null +++ b/doc/sql/migration/postgresql/postgresql-migration-from-v.1.5.0-to-v2.0.0.sql @@ -0,0 +1,17 @@ +create table gs_user_group_attribute ( + id bigint not null, + name varchar(255) not null, + string varchar(255), + userGroup_id bigint not null, + primary key (id) + ); + + + +create index idx_user_group_attr_name on gs_user_group_attribute (name); + +create index idx_user_group_attr_text on gs_user_group_attribute (string); + +create index idx_attr_user_group on gs_user_group_attribute (userGroup_id); + +alter table gs_user_group_attribute add constraint fk_ugattrib_user_group foreign key (userGroup_id) references gs_usergroup; diff --git a/doc/sql/migration/postgresql/postgresql-migration-from-v.2.0.0-to-v2.1.0.sql b/doc/sql/migration/postgresql/postgresql-migration-from-v.2.0.0-to-v2.1.0.sql new file mode 100644 index 00000000..a82625e4 --- /dev/null +++ b/doc/sql/migration/postgresql/postgresql-migration-from-v.2.0.0-to-v2.1.0.sql @@ -0,0 +1,10 @@ +alter table gs_resource add column if not exists creator varchar(255); +alter table gs_resource add column if not exists editor varchar(255); +alter table gs_resource add column if not exists advertised bool not null default true; +-- Set the Resource Creator whether this is NULL +update gs_resource as gsr +set creator = subquery.name +from +(select gsu.name, gss.resource_id from geostore.gs_security as gss join geostore.gs_user as gsu on (gss.user_id = gsu.id) + where gss.user_id IS NOT NULL) as subquery +where gsr.id = subquery.resource_id and gsr.creator IS NULL; diff --git a/pom.xml b/pom.xml index 034e2e39..d13c6a00 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,11 @@ - + 4.0.0 it.geosolutions geostore - 1.7-SNAPSHOT + 2.2-SNAPSHOT pom @@ -63,8 +64,8 @@ https://github.com/geosolutions-it/geostore scm:git:git@github.com:geosolutions-it/geostore.git - HEAD - + HEAD + jenkins @@ -95,10 +96,20 @@ org.apache.maven.plugins maven-compiler-plugin - 2.0.2 + 3.10.1 - 1.7 - 1.7 + utf8 + 8 + 8 + true + UTF-8 + + true + 128M + 1512M @@ -166,6 +177,59 @@ + + org.owasp + dependency-check-maven + 9.0.9 + + true + + + + + + check + + + + + + + com.coveo + fmt-maven-plugin + 2.9.1 + + false + .*\.java + false + + + + + + format + + + + + + + org.commonjava.maven.plugins + directory-maven-plugin + 0.3.1 + + + directories + + highest-basedir + + initialize + + geostore.basedir + + + + @@ -180,7 +244,6 @@ - org.apache.maven.plugins maven-javadoc-plugin @@ -192,16 +255,9 @@ - - - org.codehaus.mojo - cobertura-maven-plugin - 2.7 - - src diff --git a/qa/pmd-ruleset.xml b/qa/pmd-ruleset.xml new file mode 100644 index 00000000..88aa5335 --- /dev/null +++ b/qa/pmd-ruleset.xml @@ -0,0 +1,104 @@ + + + + + + The default ruleset used by the Maven PMD Plugin, when no other ruleset is specified. + It contains the rules of the old (pre PMD 6.0.0) rulesets java-basic, java-empty, java-imports, + java-unnecessary, java-unusedcode. + + This ruleset might be used as a starting point for an own customized ruleset [0]. + + [0] https://pmd.github.io/latest/pmd_userdocs_understanding_rulesets.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qa/spotbugs-exclude.xml b/qa/spotbugs-exclude.xml new file mode 100644 index 00000000..038bd40c --- /dev/null +++ b/qa/spotbugs-exclude.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/cli/README.md b/src/cli/README.md new file mode 100644 index 00000000..67521d40 --- /dev/null +++ b/src/cli/README.md @@ -0,0 +1,78 @@ +GeoStore Command Line utilities +======== +The GeoStore CLI module includes command line utilites to interact with GeoStore and its internal database. + +It currently includes the following utilities: + - **H2ToPgSQLExport**: allows migrating the GeoStore database from H2 to PostgreSQL. + +Building +-------- +The standard GeoStore build does not include the CLI module, but it is possible to enable specific profiles to: + + - build the CLI module alone: you can run the maven build with the dedicated *cli* profile. + +```bash +mvn install -P cli +``` + + - run the full build and include also the CLI module: you can run the maven build with the dedicated *all* profile. + +```bash +mvn install -P all +``` + +**Note**: you can also include the optional features, adding more profiles, eg. + +```bash +mvn install -P all,postgres,extjs +``` + +The final artifacts of the build will be: + + * the H2toPgSQLExport tool, an executable jar, that includes all the needed dependencies, located in: +``` +src/cli/target/H2ToPgSQLExport.jar +``` + +H2ToPgSQLExport +--------------- +This tool can be used to migrate data from a GeoStore H2 database file, to a PostgreSQL database. + +We always advice using a full fledged database (like PosgreSQL or Oracle) in production, while an H2 embedded database can be useful during development or testing. + +Sometimes migrating a development database into production is therefore needed. + +The tool produces an SQL script that can be run on the destination database to import the data to migrate from an H2 geostore database file. + +The destination database must be: + * already populated with the geostore schema and the related tables + * all the tables should be empty, no data removal will be done by the produced scripts + +### Usage +To get a migration script, run the utility with the following parameters: + +```bash +java -jar H2ToPgSQLExport.jar [-o[=]] [-p=] [-u=] H2FILE +``` + + * **H2FILE**: path to the H2 database file to migrate + * **outputPath**: path to output SQL file, if missing the output is written to the standard output + * **password**: H2 database password, if missing, the default password (geostore) is used + * **username**: H2 database username, if missing, the default username (geostore) is used + +To import the data into an empty PostgreSQL database, using the generated script, you can use the PostgreSQL psql tool as follows: + +```bash +# creates the geostore user role and schemas +psql -U -d -W -f /doc/sql/001_setup_db.sql + +# creates the geostore tables in the geostore schema +psql -U geostore -d -W -f /doc/sql/002_create_schema_postgres.sql + +# imports data exported from H2 +psql -U geostore -d -W -f +``` + +### Connect GeoStore to the new database + +Read the [DBMS configuration docs](https://github.com/geosolutions-it/geostore/wiki/Configure-the-DBMS) to learn how to connect your new PostgreSQL database to GeoStore. diff --git a/src/cli/pom.xml b/src/cli/pom.xml new file mode 100644 index 00000000..1b9badb9 --- /dev/null +++ b/src/cli/pom.xml @@ -0,0 +1,118 @@ + + + + 4.0.0 + + + it.geosolutions.geostore + geostore-root + 2.2-SNAPSHOT + + + geostore-cli + jar + GeoStore - Command Line Interface + + + + + + com.h2database + h2 + + + + + commons-beanutils + commons-beanutils + + + + jdom + jdom + + + + info.picocli + picocli + + + commons-lang + commons-lang + + + + junit + junit + test + + + + + + + + + true + org.apache.maven.plugins + maven-source-plugin + + true + + + + attach-sources + + jar + + + + + + maven-assembly-plugin + 2.2 + + H2ToPgSQLExport + false + + + it.geosolutions.geostore.cli.H2ToPgSQLExportCLI + + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + + + diff --git a/src/cli/src/main/java/it/geosolutions/geostore/cli/H2ToPgSQLExportCLI.java b/src/cli/src/main/java/it/geosolutions/geostore/cli/H2ToPgSQLExportCLI.java new file mode 100644 index 00000000..4d70fed4 --- /dev/null +++ b/src/cli/src/main/java/it/geosolutions/geostore/cli/H2ToPgSQLExportCLI.java @@ -0,0 +1,43 @@ +/* + * ==================================================================== + * + * Copyright (C) 2021 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.cli; + +import picocli.CommandLine; + +/** + * CLI tool to export a GeoStore H2 database as a script for a destination database. Usage: + * H2ToPgSQL [-o, --output=] + */ +public class H2ToPgSQLExportCLI { + + public static void main(String[] args) { + H2ToPgSQLExporter exporter = new H2ToPgSQLExporter(); + System.exit(new CommandLine(exporter).execute(args)); + } +} diff --git a/src/cli/src/main/java/it/geosolutions/geostore/cli/H2ToPgSQLExporter.java b/src/cli/src/main/java/it/geosolutions/geostore/cli/H2ToPgSQLExporter.java new file mode 100644 index 00000000..daa036ea --- /dev/null +++ b/src/cli/src/main/java/it/geosolutions/geostore/cli/H2ToPgSQLExporter.java @@ -0,0 +1,285 @@ +/* + * ==================================================================== + * + * Copyright (C) 2021 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.cli; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.apache.commons.lang.StringUtils; +import org.h2.tools.Script; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; + +/** + * Utility to export the GeoStore database from the H2 file to a PostgreSQL database. Produces an + * SQL script to insert all the data in an exising PgSQL database, with the GeoStore schema. + */ +@SuppressWarnings("PMD.SystemPrintln") +public class H2ToPgSQLExporter implements Runnable { + + enum OutputType { + STDOUT, + FILE, + INVALID + } + + static class Output { + public OutputType type; + public Optional path; + + private Output(OutputType type) { + this.type = type; + } + + private Output(OutputType type, String path) { + this.type = type; + this.path = Optional.of(path); + } + + static Output stdout() { + return new Output(OutputType.STDOUT); + } + + static Output invalid() { + return new Output(OutputType.INVALID); + } + + static Output path(String path) { + return new Output(OutputType.FILE, path); + } + } + + @Parameters( + paramLabel = "H2FILE", + description = "path to the H2 database file to migrate", + arity = "1") + String inputPath; + + @Option( + names = {"-o", "--output"}, + description = "path to output SQL file", + arity = "0..1") + String outputPath; + + @Option( + names = {"-u", "--user"}, + description = "H2 database username", + defaultValue = "geostore") + String username; + + @Option( + names = {"-p", "--password"}, + description = "H2 database password", + defaultValue = "geostore") + String password; + + List orderedTables = + Arrays.asList( + new String[] { + "GS_CATEGORY", + "GS_RESOURCE", + "GS_ATTRIBUTE", + "GS_USER", + "GS_USER_ATTRIBUTE", + "GS_USERGROUP", + "GS_USERGROUP_MEMBERS", + "GS_SECURITY", + "GS_STORED_DATA" + }); + + Pattern searchInserts = + Pattern.compile( + "INSERT INTO PUBLIC\\.([A-Z_]+)\\([^)]+\\) VALUES\\s*(.*?);(\n|\r\n)", + Pattern.CASE_INSENSITIVE | Pattern.DOTALL); + Pattern searchDecode = Pattern.compile("STRINGDECODE\\(('.*')\\)", Pattern.CASE_INSENSITIVE); + + @Override + public void run() { + try { + Optional sql = extractAndScript(); + if (!sql.isPresent()) { + return; + } + Output output = validateOutputFile(); + if (output.type == OutputType.INVALID) { + return; + } else if (output.type == OutputType.STDOUT) { + System.out.println(sql); + } else { + Files.write( + Paths.get(output.path.get()), sql.get().getBytes(StandardCharsets.UTF_8)); + } + System.out.println("Export done!"); + + } catch (IOException e) { + System.err.println("Error writing the output file: " + e.getMessage()); + } + } + + Optional extractAndScript() throws IOException { + Optional script = exportH2AsScript(); + if (!script.isPresent()) { + return Optional.empty(); + } + List sortedInserts = filterInserts(script.get()); + List normalized = + sortedInserts.stream().map(i -> normalizeInsert(i)).collect(Collectors.toList()); + int lastId = getLastUsedId(normalized); + String sql = createSql(normalized, lastId); + return Optional.of(sql); + } + + private Comparator buildTablesComparator() { + return new Comparator() { + @Override + public int compare(String t1, String t2) { + return Integer.compare(orderedTables.indexOf(t1), orderedTables.indexOf(t2)); + } + }; + } + + int getLastUsedId(List normalized) { + return normalized.stream().map(i -> getId(i)).reduce(0, (max, id) -> Math.max(max, id)); + } + + private int getId(String i) { + return Arrays.asList(i.split("\n")).stream() + .filter(s -> s.startsWith("(")) + .map(s -> Integer.parseInt(s.substring(1).split(",")[0])) + .reduce(0, (max, id) -> Math.max(max, id)); + } + + String createSql(List inserts, int lastId) { + return StringUtils.join(inserts, "\n") + + "ALTER SEQUENCE GEOSTORE.HIBERNATE_SEQUENCE RESTART WITH " + + lastId + + ";\n"; + } + + private List flat(Map> insertsByTable) { + return insertsByTable.values().stream().flatMap(List::stream).collect(Collectors.toList()); + } + + List filterInserts(String script) { + Map> insertsByTable = + new TreeMap>(buildTablesComparator()); + Matcher m = searchInserts.matcher(script); + while (m.find()) { + String tableName = m.group(1); + String insert = m.group(0); + if (insertsByTable.containsKey(tableName)) { + insertsByTable.get(tableName).add(insert); + } else { + insertsByTable.put(tableName, new ArrayList(Arrays.asList(insert))); + } + } + return flat(insertsByTable); + } + + Optional exportH2AsScript() throws IOException { + Optional h2path = validateInputFile(); + if (!h2path.isPresent()) { + System.err.println("H2 input file not found: " + inputPath); + return Optional.empty(); + } + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + try { + Script.execute( + "jdbc:h2:" + h2path.get() + ";ACCESS_MODE_DATA=r", username, password, os); + String script = new String(os.toByteArray(), StandardCharsets.UTF_8); + return Optional.of(script); + } catch (SQLException e) { + System.err.println("Error extracting data from the H2 database: " + e.getMessage()); + return Optional.empty(); + } + } + } + + String normalizeInsert(String insert) { + + String result = insert.replace("INSERT INTO PUBLIC.", "INSERT INTO GEOSTORE."); + Matcher m = searchDecode.matcher(result); + while (m.find()) { + String value = m.group(1); + result = + result.replace( + m.group(0), + value.replaceAll("\\\\\"", Matcher.quoteReplacement("\"")) + .replaceAll("\\\\\"", Matcher.quoteReplacement("\""))); + } + return result; + } + + Optional validateInputFile() { + return exists(inputPath) ? Optional.of(removeExtension(inputPath)) : Optional.empty(); + } + + private boolean exists(String path) { + return new File(addDbExtension(path)).exists(); + } + + private String addDbExtension(String path) { + if (path.toLowerCase().endsWith(".h2.db")) return path; + if (path.endsWith(".h2")) return path + ".db"; + return path + ".h2.db"; + } + + private String addScriptExtension(String path) { + if (path.toLowerCase().endsWith(".sql")) return path; + return path + ".sql"; + } + + private String removeExtension(String path) { + if (path.indexOf(".") != -1) return path.substring(0, path.indexOf(".")); + return path; + } + + public Output validateOutputFile() { + if (outputPath == null) return Output.stdout(); + File folder = new File(outputPath).getAbsoluteFile().getParentFile(); + if (folder.exists() && folder.isDirectory()) { + return Output.path(addScriptExtension(outputPath)); + } + return Output.invalid(); + } +} diff --git a/src/cli/src/test/java/it/geosolutions/geostore/cli/H2ToPgSQLExporterInputTest.java b/src/cli/src/test/java/it/geosolutions/geostore/cli/H2ToPgSQLExporterInputTest.java new file mode 100644 index 00000000..bb54dbb0 --- /dev/null +++ b/src/cli/src/test/java/it/geosolutions/geostore/cli/H2ToPgSQLExporterInputTest.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * + * Copyright (C) 2021 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.cli; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Optional; +import org.junit.Test; + +public class H2ToPgSQLExporterInputTest extends H2ToPgSQLExporterTest { + + @Test + public void validInputPath() throws IOException { + exporter.inputPath = getTestDbPath(); + + Optional path = exporter.validateInputFile(); + + assertTrue(path.isPresent()); + assertFalse(path.get().endsWith(".h2.db")); + } + + @Test + public void inputPathWithoutExtension() throws IOException { + exporter.inputPath = getTestDbPathWithoutExtension(); + + Optional path = exporter.validateInputFile(); + + assertTrue(path.isPresent()); + } + + @Test + public void invalidInputPath() { + exporter.inputPath = getInvalidDbPath(); + + Optional path = exporter.validateInputFile(); + + assertFalse(path.isPresent()); + } +} diff --git a/src/cli/src/test/java/it/geosolutions/geostore/cli/H2ToPgSQLExporterOutputTest.java b/src/cli/src/test/java/it/geosolutions/geostore/cli/H2ToPgSQLExporterOutputTest.java new file mode 100644 index 00000000..293f9f52 --- /dev/null +++ b/src/cli/src/test/java/it/geosolutions/geostore/cli/H2ToPgSQLExporterOutputTest.java @@ -0,0 +1,97 @@ +/* + * ==================================================================== + * + * Copyright (C) 2021 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.cli; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import it.geosolutions.geostore.cli.H2ToPgSQLExporter.Output; +import it.geosolutions.geostore.cli.H2ToPgSQLExporter.OutputType; +import java.io.File; +import java.io.IOException; +import org.junit.Test; + +public class H2ToPgSQLExporterOutputTest extends H2ToPgSQLExporterTest { + @Test + public void existingFolder() throws IOException { + exporter.outputPath = getValidPath(); + + Output output = exporter.validateOutputFile(); + + assertTrue(output.type == OutputType.FILE); + assertEquals(exporter.outputPath, output.path.get()); + } + + @Test + public void pathWithoutExtension() throws IOException { + exporter.outputPath = getValidPathWithoutExtension(); + + Output output = exporter.validateOutputFile(); + + assertTrue(output.type == OutputType.FILE); + assertTrue(output.path.get().toLowerCase().endsWith(".sql")); + } + + @Test + public void notExistingFolder() throws IOException { + exporter.outputPath = getInvalidPath(); + + Output output = exporter.validateOutputFile(); + + assertTrue(output.type == OutputType.INVALID); + } + + @Test + public void standardOutput() throws IOException { + exporter.outputPath = null; + + Output output = exporter.validateOutputFile(); + + assertTrue(output.type == OutputType.STDOUT); + } + + private String getValidPath() throws IOException { + File tempFile = File.createTempFile("output", ".sql"); + tempFile.deleteOnExit(); + return tempFile.getAbsolutePath(); + } + + private String getValidPathWithoutExtension() throws IOException { + File tempFile = File.createTempFile("output", ""); + tempFile.deleteOnExit(); + return tempFile.getAbsolutePath(); + } + + private String getInvalidPath() throws IOException { + File tempFolder = File.createTempFile("output_folder", ""); + File tempFile = new File(tempFolder.getAbsolutePath() + File.separator + "output.sql"); + tempFolder.delete(); + return tempFile.getAbsolutePath(); + } +} diff --git a/src/cli/src/test/java/it/geosolutions/geostore/cli/H2ToPgSQLExporterProcessTest.java b/src/cli/src/test/java/it/geosolutions/geostore/cli/H2ToPgSQLExporterProcessTest.java new file mode 100644 index 00000000..f11a0533 --- /dev/null +++ b/src/cli/src/test/java/it/geosolutions/geostore/cli/H2ToPgSQLExporterProcessTest.java @@ -0,0 +1,180 @@ +/* + * ==================================================================== + * + * Copyright (C) 2021 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.cli; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.junit.Before; +import org.junit.Test; + +public class H2ToPgSQLExporterProcessTest extends H2ToPgSQLExporterTest { + @Before + public void setUp() { + super.setUp(); + exporter.username = "geostore"; + exporter.password = "geostore"; + } + + @Test + public void emptyListIfNoInsertsInScript() throws IOException { + String script = getInvalidScript(); + + List sql = exporter.filterInserts(script); + + assertNotNull(sql); + assertEquals(0, sql.size()); + } + + @Test + public void onlyInsertsAreTakenFromScript() throws IOException { + String script = getValidScript(); + + List sql = exporter.filterInserts(script); + + assertNotNull(sql); + sql.forEach(s -> assertTrue(s.toUpperCase().startsWith("INSERT"))); + } + + @Test + public void insertsAreSortedToAvoidConstraintsErrors() throws IOException { + String script = getValidScript(); + + List sql = exporter.filterInserts(script); + + assertNotNull(sql); + IntStream.range(0, exporter.orderedTables.size()) + .forEach(idx -> assertContains(sql.get(idx), exporter.orderedTables.get(idx))); + } + + @Test + public void dataIsStoredInTheGeoStoreSchema() throws IOException { + String script = getValidScript(); + + List sql = exporter.filterInserts(script); + + sql.forEach( + s -> + assertTrue( + exporter.normalizeInsert(sql.get(0)) + .toUpperCase() + .startsWith("INSERT INTO GEOSTORE."))); + } + + @Test + public void quotesInJsonAreCorrectlyEncoded() throws IOException { + String sql = + "INSERT INTO GEOSTORE.GS_STORED_DATA(ID, STORED_DATA, RESOURCE_ID) VALUES\r\n" + + "(74, STRINGDECODE('{\\\"html\\\":\\\"

TEXT

\\\"}'), 74);\r\n"; + + String normalized = exporter.normalizeInsert(sql); + + String expected = + "INSERT INTO GEOSTORE.GS_STORED_DATA(ID, STORED_DATA, RESOURCE_ID) VALUES\r\n" + + "(74, '{\"html\":\"

TEXT

\"}', 74);\r\n"; + assertEquals(expected, normalized); + } + + @Test + public void stringDecodeIsNotNeededForPgSQL() throws IOException { + String sql = + "INSERT INTO GEOSTORE.GS_STORED_DATA(ID, STORED_DATA, RESOURCE_ID) VALUES\r\n" + + "(36, STRINGDECODE('{\\\"widgets\\\":[]}'), 36);\r\n"; + + String normalized = exporter.normalizeInsert(sql); + + String expected = + "INSERT INTO GEOSTORE.GS_STORED_DATA(ID, STORED_DATA, RESOURCE_ID) VALUES\r\n" + + "(36, '{\"widgets\":[]}', 36);\r\n"; + assertEquals(expected, normalized); + } + + @Test + public void hibernateSequenceIsSetToMaxId() throws IOException { + String script = getValidScript(); + + List sql = exporter.filterInserts(script); + int sequence = exporter.getLastUsedId(sql); + + assertEquals(58, sequence); + } + + @Test + public void hibernateSequenceIsSetToZeroForInvalidScript() throws IOException { + String script = getInvalidScript(); + + List sql = exporter.filterInserts(script); + int sequence = exporter.getLastUsedId(sql); + + assertEquals(0, sequence); + } + + @Test + public void exportedSqlIsValid() throws IOException { + exporter.inputPath = getTestDb(); + + List sql = split(exporter.extractAndScript()); + + sql.forEach(s -> assertValidSql(s)); + } + + private List split(Optional sql) { + assertTrue(sql.isPresent()); + return Arrays.asList(sql.get().split("(;\r\n)|(;\n)")).stream() + .map(s -> s.trim()) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); + } + + private void assertValidSql(String sql) { + assertTrue( + sql.toUpperCase().startsWith("INSERT INTO GEOSTORE.") + || sql.toUpperCase().startsWith("ALTER SEQUENCE ")); + } + + private void assertContains(String sql, String tableName) { + assertTrue(sql.toUpperCase().contains(tableName.toUpperCase())); + } + + private String getValidScript() throws IOException { + exporter.inputPath = getTestDb(); + return exporter.exportH2AsScript().get(); + } + + private String getInvalidScript() throws IOException { + return "CREATE TABLE TEST\nALTER TABLE TEST\n"; + } +} diff --git a/src/cli/src/test/java/it/geosolutions/geostore/cli/H2ToPgSQLExporterScriptTest.java b/src/cli/src/test/java/it/geosolutions/geostore/cli/H2ToPgSQLExporterScriptTest.java new file mode 100644 index 00000000..32cbc594 --- /dev/null +++ b/src/cli/src/test/java/it/geosolutions/geostore/cli/H2ToPgSQLExporterScriptTest.java @@ -0,0 +1,78 @@ +/* + * ==================================================================== + * + * Copyright (C) 2021 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.cli; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Optional; +import org.junit.Test; + +public class H2ToPgSQLExporterScriptTest extends H2ToPgSQLExporterTest { + @Test + public void validDatabaseConnection() throws IOException { + exporter.inputPath = getTestDb(); + exporter.username = "geostore"; + exporter.password = "geostore"; + + Optional script = exporter.exportH2AsScript(); + + assertTrue(script.isPresent()); + } + + @Test + public void invalidCredentials() throws IOException { + exporter.inputPath = getTestDb(); + exporter.username = "XXX"; + exporter.password = "YYY"; + + Optional script = exporter.exportH2AsScript(); + + assertFalse(script.isPresent()); + } + + @Test + public void invalidPath() throws IOException { + exporter.inputPath = getInvalidDbPath(); + + Optional script = exporter.exportH2AsScript(); + + assertFalse(script.isPresent()); + } + + @Test + public void invalidDatabase() throws IOException { + exporter.inputPath = getInvalidDb(); + + Optional script = exporter.exportH2AsScript(); + + assertFalse(script.isPresent()); + } +} diff --git a/src/cli/src/test/java/it/geosolutions/geostore/cli/H2ToPgSQLExporterTest.java b/src/cli/src/test/java/it/geosolutions/geostore/cli/H2ToPgSQLExporterTest.java new file mode 100644 index 00000000..543a92dd --- /dev/null +++ b/src/cli/src/test/java/it/geosolutions/geostore/cli/H2ToPgSQLExporterTest.java @@ -0,0 +1,81 @@ +/* + * ==================================================================== + * + * Copyright (C) 2021 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.cli; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import org.junit.Before; + +public abstract class H2ToPgSQLExporterTest { + protected H2ToPgSQLExporter exporter; + + @Before + public void setUp() { + exporter = new H2ToPgSQLExporter(); + exporter.username = "geostore"; + exporter.password = "geostore"; + } + + protected String getInvalidDbPath() { + return "WRONGPATH"; + } + + protected String getTestDbPath() throws IOException { + File tempFile = File.createTempFile("geostore", ".h2.db"); + tempFile.deleteOnExit(); + return tempFile.getAbsolutePath(); + } + + protected String getTestDbPathWithoutExtension() throws IOException { + String path = getTestDbPath(); + return path.substring(0, path.indexOf(".")); + } + + protected String getTestDb() throws IOException { + File tempFile = File.createTempFile("geostore", ".h2.db"); + tempFile.delete(); + Files.copy( + H2ToPgSQLExporterScriptTest.class.getResourceAsStream("geostore.h2.db"), + Paths.get(tempFile.getAbsolutePath())); + tempFile.deleteOnExit(); + return tempFile.getAbsolutePath(); + } + + protected String getInvalidDb() throws IOException { + File tempFile = File.createTempFile("geostore", ".h2.db"); + tempFile.delete(); + Files.copy( + H2ToPgSQLExporterScriptTest.class.getResourceAsStream("geostore_invalid.h2.db"), + Paths.get(tempFile.getAbsolutePath())); + tempFile.deleteOnExit(); + return tempFile.getAbsolutePath(); + } +} diff --git a/src/cli/src/test/resources/it/geosolutions/geostore/cli/geostore.h2.db b/src/cli/src/test/resources/it/geosolutions/geostore/cli/geostore.h2.db new file mode 100644 index 00000000..58d9138d Binary files /dev/null and b/src/cli/src/test/resources/it/geosolutions/geostore/cli/geostore.h2.db differ diff --git a/src/cli/src/test/resources/it/geosolutions/geostore/cli/geostore_invalid.h2.db b/src/cli/src/test/resources/it/geosolutions/geostore/cli/geostore_invalid.h2.db new file mode 100644 index 00000000..2b2c3f41 --- /dev/null +++ b/src/cli/src/test/resources/it/geosolutions/geostore/cli/geostore_invalid.h2.db @@ -0,0 +1 @@ +INVALID!!! THIS IS NOT AN H2 DATABASE!! \ No newline at end of file diff --git a/src/core/model/pom.xml b/src/core/model/pom.xml index 722d4779..6e4e2755 100644 --- a/src/core/model/pom.xml +++ b/src/core/model/pom.xml @@ -24,7 +24,7 @@ it.geosolutions.geostore geostore-core - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-model @@ -43,46 +43,7 @@ jaxb-impl - - org.hibernate - hibernate-annotations - - - - - asm - asm - - - asm - asm-attrs - - - cglib - cglib - - - - - org.hibernate - hibernate-commons-annotations - provided - - - asm - asm - - - asm - asm-attrs - - - cglib - cglib - - - - + org.hibernatespatial @@ -119,7 +80,6 @@ jdom - junit junit @@ -147,7 +107,6 @@ - diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/Attribute.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/Attribute.java index e4b44259..a1adf31e 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/Attribute.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/Attribute.java @@ -5,7 +5,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -17,7 +17,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -29,18 +29,18 @@ package it.geosolutions.geostore.core.model; import it.geosolutions.geostore.core.model.enums.DataType; - import java.io.Serializable; -import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; - import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; +import javax.persistence.ForeignKey; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; @@ -54,182 +54,151 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; -import org.hibernate.annotations.ForeignKey; -import org.hibernate.annotations.Index; - /** * Class Attribute. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author Emanuele Tajariol (etj at geo-solutions.it) */ @Entity(name = "Attribute") -@Table(name = "gs_attribute", uniqueConstraints = { @UniqueConstraint(columnNames = { "name", - "resource_id" }) }) -@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_attribute") +@Table( + name = "gs_attribute", + uniqueConstraints = {@UniqueConstraint(columnNames = {"name", "resource_id"})}, + indexes = { + @Index(name = "idx_attribute_name", columnList = "name"), + @Index(name = "idx_attribute_text", columnList = "attribute_text"), + @Index(name = "idx_attribute_number", columnList = "attribute_number"), + @Index(name = "idx_attribute_date", columnList = "attribute_date"), + @Index(name = "idx_attribute_type", columnList = "attribute_type"), + @Index(name = "idx_attribute_resource", columnList = "resource_id"), + }) +// @Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_attribute") @XmlRootElement(name = "Attribute") public class Attribute implements Serializable { /** The Constant serialVersionUID. */ private static final long serialVersionUID = -1298676702253831972L; - @Id - @GeneratedValue - private Long id; + @Id @GeneratedValue private Long id; @Column(name = "name", nullable = false, updatable = true) - @Index(name = "idx_attribute_name") private String name; @Column(name = "attribute_text", nullable = true, updatable = true) - @Index(name = "idx_attribute_text") private String textValue; @Column(name = "attribute_number", nullable = true, updatable = true) - @Index(name = "idx_attribute_number") private Double numberValue; @Column(name = "attribute_date", nullable = true, updatable = true) - @Index(name = "idx_attribute_date") @Temporal(TemporalType.TIMESTAMP) private Date dateValue; @Column(name = "attribute_type", nullable = false, updatable = false) - @Index(name = "idx_attribute_type") @Enumerated(EnumType.STRING) private DataType type; @ManyToOne(optional = false) - @Index(name = "idx_attribute_resource") - @ForeignKey(name = "fk_attribute_resource") + @JoinColumn(foreignKey = @ForeignKey(name = "fk_attribute_resource")) private Resource resource; - /** - * Only used for XML un/marshalling - */ - public final static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + /** Only used for XML un/marshaling */ + public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; - /** - * @throws Exception - */ + /** @throws Exception */ @PreUpdate @PrePersist public void onPreUpdate() throws Exception { if (textValue == null && numberValue == null && dateValue == null) { - throw new NullPointerException("Null value not allowed in attribute: " - + this.toString()); + throw new NullPointerException( + "Null value not allowed in attribute: " + this.toString()); - } else if ((this.textValue == null && (this.numberValue != null ^ this.dateValue != null)) - || (this.numberValue == null && (this.textValue != null ^ this.dateValue != null)) - || (this.dateValue == null && (this.textValue != null ^ this.numberValue != null))) { - this.type = this.textValue != null ? DataType.STRING - : (this.numberValue != null ? DataType.NUMBER : DataType.DATE); + } else if (this.textValue == null && this.numberValue != null ^ this.dateValue != null + || this.numberValue == null && this.dateValue == null) { + this.type = + this.textValue != null + ? DataType.STRING + : (this.numberValue != null ? DataType.NUMBER : DataType.DATE); } else { - throw new Exception("Only one DataType can be not-null inside the Attribute entity: " - + this.toString()); - + throw new Exception( + "Only one DataType can be not-null inside the Attribute entity: " + + this.toString()); } } - /** - * @return the id - */ + /** @return the id */ @XmlTransient public long getId() { return id; } - /** - * @param id the id to set - */ + /** @param id the id to set */ public void setId(long id) { this.id = id; } - /** - * @return the attribute - */ + /** @return the attribute */ public String getName() { return name; } - /** - * @param name the attribute to set - */ + /** @param name the attribute to set */ public void setName(String name) { this.name = name; } - /** - * @return the textValue - */ + /** @return the textValue */ @XmlTransient public String getTextValue() { return textValue; } - /** - * @param textValue the textValue to set - */ + /** @param textValue the textValue to set */ public void setTextValue(String textValue) { this.textValue = textValue; } - /** - * @return the numberValue - */ + /** @return the numberValue */ @XmlTransient public Double getNumberValue() { return numberValue; } - /** - * @param numberValue the numberValue to set - */ + /** @param numberValue the numberValue to set */ public void setNumberValue(Double numberValue) { this.numberValue = numberValue; } - /** - * @return the dateValue - */ + /** @return the dateValue */ @XmlTransient public Date getDateValue() { return dateValue; } - /** - * @param dateValue the dateValue to set - */ + /** @param dateValue the dateValue to set */ public void setDateValue(Date dateValue) { this.dateValue = dateValue; } - /** - * Only used for XML marshalling - */ + /** Only used for XML marshalling */ @Transient @XmlElement public String getValue() { switch (type) { - case DATE: - return DATE_FORMAT.format(dateValue); - case NUMBER: - return numberValue.toString(); - case STRING: - return textValue.toString(); - default: - throw new IllegalStateException("Unknown type " + type); + case DATE: + return new SimpleDateFormat(DATE_FORMAT).format(dateValue); + case NUMBER: + return numberValue.toString(); + case STRING: + return textValue.toString(); + default: + throw new IllegalStateException("Unknown type " + type); } } - /** - * Only used for XML unmarshalling - */ + /** Only used for XML unmarshalling */ protected void setValue(String text) { if (type != null) { setValue(text, type); @@ -240,32 +209,31 @@ protected void setValue(String text) { protected void setValue(String text, DataType type) { switch (type) { - case DATE: - try { - dateValue = DATE_FORMAT.parse(text); - } catch (Exception e) { - throw new IllegalArgumentException("Can't parse date [" + text + "]", e); - } - break; - case NUMBER: - try { - numberValue = Double.valueOf(text); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Can't parse double [" + text + "]", e); - } - break; - case STRING: - textValue = text; - break; - default: - throw new IllegalStateException("Unknown type " + type); + case DATE: + try { + dateValue = new SimpleDateFormat(DATE_FORMAT).parse(text); + } catch (Exception e) { + throw new IllegalArgumentException("Can't parse date [" + text + "]", e); + } + break; + case NUMBER: + try { + numberValue = Double.valueOf(text); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Can't parse double [" + text + "]", e); + } + break; + case STRING: + textValue = text; + break; + default: + throw new IllegalStateException("Unknown type " + type); } - } /** * THe XMLAttribute annotation is to make sure that type will be unmarshalled before value. - * + * * @return the type */ @XmlAttribute @@ -273,31 +241,25 @@ public DataType getType() { return type; } - /** - * @param type the type to set - */ + /** @param type the type to set */ public void setType(DataType type) { this.type = type; } - /** - * @return the resource - */ + /** @return the resource */ @XmlTransient public Resource getResource() { return resource; } - /** - * @param resource the resource to set - */ + /** @param resource the resource to set */ public void setResource(Resource resource) { this.resource = resource; } /* * (non-Javadoc) - * + * * @see java.lang.Object#toString() */ @Override @@ -307,8 +269,7 @@ public String toString() { if (id != null) { builder.append("id=").append(id); - } else - builder.append("id is null"); + } else builder.append("id is null"); if (name != null) { builder.append(", name=").append(name); @@ -340,7 +301,7 @@ public String toString() { /* * (non-Javadoc) - * + * * @see java.lang.Object#hashCode() */ @Override @@ -359,51 +320,34 @@ public int hashCode() { /* * (non-Javadoc) - * + * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; Attribute other = (Attribute) obj; if (dateValue == null) { - if (other.dateValue != null) - return false; - } else if (!dateValue.equals(other.dateValue)) - return false; + if (other.dateValue != null) return false; + } else if (!dateValue.equals(other.dateValue)) return false; if (id == null) { - if (other.id != null) - return false; - } else if (!id.equals(other.id)) - return false; + if (other.id != null) return false; + } else if (!id.equals(other.id)) return false; if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; + if (other.name != null) return false; + } else if (!name.equals(other.name)) return false; if (numberValue == null) { - if (other.numberValue != null) - return false; - } else if (!numberValue.equals(other.numberValue)) - return false; + if (other.numberValue != null) return false; + } else if (!numberValue.equals(other.numberValue)) return false; if (resource == null) { - if (other.resource != null) - return false; - } else if (!resource.equals(other.resource)) - return false; + if (other.resource != null) return false; + } else if (!resource.equals(other.resource)) return false; if (textValue == null) { - if (other.textValue != null) - return false; - } else if (!textValue.equals(other.textValue)) - return false; - if (type != other.type) - return false; + if (other.textValue != null) return false; + } else if (!textValue.equals(other.textValue)) return false; + if (type != other.type) return false; return true; } - } diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/Category.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/Category.java index a0e459cb..a50b3c83 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/Category.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/Category.java @@ -30,43 +30,39 @@ import java.io.Serializable; import java.util.List; - import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.Index; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; -import org.hibernate.annotations.Index; - /** * Class Category. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author Emanuele Tajariol (etj at geo-solutions.it) */ @Entity(name = "Category") -@Table(name = "gs_category", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }) }) -@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_category") +@Table( + name = "gs_category", + uniqueConstraints = {@UniqueConstraint(columnNames = {"name"})}, + indexes = {@Index(name = "idx_category_type", columnList = "name")}) +// @Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_category") @XmlRootElement(name = "Category") public class Category implements Serializable { private static final long serialVersionUID = -6161770040532770363L; - @Id - @GeneratedValue - private Long id; + @Id @GeneratedValue private Long id; @Column(name = "name", nullable = false, updatable = false) - @Index(name = "idx_category_type") private String name; /* @@ -81,31 +77,23 @@ public class Category implements Serializable { // @OneToMany(mappedBy = "category", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY) // private List security; - /** - * @return the id - */ + /** @return the id */ public Long getId() { return id; } - /** - * @param id the id to set - */ + /** @param id the id to set */ public void setId(Long id) { this.id = id; } - /** - * @return the resource - */ + /** @return the resource */ @XmlTransient public List getResource() { return resource; } - /** - * @param resource the resource to set - */ + /** @param resource the resource to set */ public void setResource(List resource) { this.resource = resource; } @@ -125,16 +113,12 @@ public void setResource(List resource) { // this.security = security; // } - /** - * @return the name - */ + /** @return the name */ public String getName() { return name; } - /** - * @param name the name to set - */ + /** @param name the name to set */ public void setName(String name) { this.name = name; } diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/Resource.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/Resource.java index 9688b3b4..db9d5753 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/Resource.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/Resource.java @@ -28,16 +28,17 @@ */ package it.geosolutions.geostore.core.model; +import com.sun.xml.bind.CycleRecoverable; import java.io.Serializable; import java.util.Date; import java.util.List; - import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.Index; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.OneToOne; @@ -45,55 +46,61 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.UniqueConstraint; +import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; -import com.sun.xml.bind.CycleRecoverable; - -import javax.xml.bind.annotation.XmlElementWrapper; -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; -import org.hibernate.annotations.ForeignKey; -import org.hibernate.annotations.Index; - /** * Class Resource. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author Emanuele Tajariol (etj at geo-solutions.it) */ @Entity(name = "Resource") -@Table(name = "gs_resource", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }) }) -@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_resource") +@Table( + name = "gs_resource", + uniqueConstraints = {@UniqueConstraint(columnNames = {"name"})}, + indexes = { + @Index(name = "idx_resource_name", columnList = "name"), + @Index(name = "idx_resource_description", columnList = "description"), + @Index(name = "idx_resource_creation", columnList = "creation"), + @Index(name = "idx_resource_update", columnList = "lastUpdate"), + @Index(name = "idx_resource_metadata", columnList = "metadata"), + @Index(name = "idx_resource_advertised", columnList = "advertised"), + @Index(name = "idx_resource_category", columnList = "category_id") + }) +// @Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_resource") @XmlRootElement(name = "Resource") public class Resource implements Serializable, CycleRecoverable { private static final long serialVersionUID = 4852100679788007328L; - @Id - @GeneratedValue - private Long id; + @Id @GeneratedValue private Long id; @Column(nullable = false, updatable = true) - @Index(name = "idx_resource_name") private String name; @Column(nullable = true, updatable = true, length = 10000) - @Index(name = "idx_resource_description") private String description; @Column(nullable = false, updatable = false) @Temporal(TemporalType.TIMESTAMP) - @Index(name = "idx_resource_creation") private Date creation; @Column(nullable = true, updatable = true) @Temporal(TemporalType.TIMESTAMP) - @Index(name = "idx_resource_update") private Date lastUpdate; + @Column(nullable = true, updatable = true) + private String creator; + + @Column(nullable = true, updatable = true) + private String editor; + + @Column(nullable = true, updatable = true, columnDefinition = "bool default true") + private Boolean advertised = true; + @Column(nullable = true, updatable = true, length = 30000) - @Index(name = "idx_resource_metadata") private String metadata; @OneToMany(mappedBy = "resource", cascade = CascadeType.REMOVE, fetch = FetchType.EAGER) @@ -103,8 +110,7 @@ public class Resource implements Serializable, CycleRecoverable { private StoredData data; @ManyToOne(optional = false) - @Index(name = "idx_resource_category") - @ForeignKey(name = "fk_resource_category") + // @ForeignKey(name = "fk_resource_category") private Category category; /* @@ -113,149 +119,139 @@ public class Resource implements Serializable, CycleRecoverable { @OneToMany(mappedBy = "resource", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY) private List security; - /** - * @return the id - */ + /** @return the id */ public Long getId() { return id; } - /** - * @param id the id to set - */ + /** @param id the id to set */ public void setId(Long id) { this.id = id; } - /** - * @return the name - */ + /** @return the name */ public String getName() { return name; } - /** - * @param name the name to set - */ + /** @param name the name to set */ public void setName(String name) { this.name = name; } - /** - * @return the description - */ + /** @return the description */ public String getDescription() { return description; } - /** - * @param description the description to set - */ + /** @param description the description to set */ public void setDescription(String description) { this.description = description; } - /** - * @return the creation - */ + /** @return the creation */ public Date getCreation() { return creation; } - /** - * @param creation the creation to set - */ + /** @param creation the creation to set */ public void setCreation(Date creation) { this.creation = creation; } - /** - * @return the lastUpdate - */ + /** @return the lastUpdate */ public Date getLastUpdate() { return lastUpdate; } - /** - * @param lastUpdate the lastUpdate to set - */ + /** @param lastUpdate the lastUpdate to set */ public void setLastUpdate(Date lastUpdate) { this.lastUpdate = lastUpdate; } - /** - * @return the metadata - */ + /** @return the advertised */ + public Boolean isAdvertised() { + return advertised; + } + + /** @param advertised the advertised to set */ + public void setAdvertised(Boolean advertised) { + this.advertised = advertised; + } + + /** @return the metadata */ public String getMetadata() { return metadata; } - /** - * @param metadata the metadata to set - */ + /** @param metadata the metadata to set */ public void setMetadata(String metadata) { this.metadata = metadata; } - /** - * @return the attribute - */ + /** @return the attribute */ @XmlElementWrapper(name = "Attributes") public List getAttribute() { return attribute; } - /** - * @param attribute the attribute to set - */ + /** @param attribute the attribute to set */ public void setAttribute(List attribute) { this.attribute = attribute; } - /** - * @return the data - */ + /** @return the data */ // @XmlTransient public StoredData getData() { return data; } - /** - * @param data the data to set - */ + /** @param data the data to set */ public void setData(StoredData data) { this.data = data; } - /** - * @return the category - */ + /** @return the category */ public Category getCategory() { return category; } - /** - * @param category the category to set - */ + /** @param category the category to set */ public void setCategory(Category category) { this.category = category; } - /** - * @return the security - */ + /** @return the security */ @XmlTransient public List getSecurity() { return security; } - /** - * @param security the security to set - */ + /** @param security the security to set */ public void setSecurity(List security) { this.security = security; } + /** @return the creator username */ + public String getCreator() { + return creator; + } + + /** @param creator the creator username */ + public void setCreator(String creator) { + this.creator = creator; + } + + /** @return the editor username */ + public String getEditor() { + return editor; + } + + /** @param editor the creator username */ + public void setEditor(String editor) { + this.editor = editor; + } + /* * (non-Javadoc) @see java.lang.Object#toString() */ @@ -290,17 +286,32 @@ public String toString() { if (attribute != null) { builder.append(", "); - builder.append("attribute=").append(attribute.toString()); + builder.append("attribute=").append(attribute); } if (data != null) { builder.append(", "); - builder.append("data=").append(data.toString()); + builder.append("data=").append(data); } if (category != null) { builder.append(", "); - builder.append("category=").append(category.toString()); + builder.append("category=").append(category); + } + + if (creator != null) { + builder.append(", "); + builder.append("creator=").append(creator); + } + + if (editor != null) { + builder.append(", "); + builder.append("editor=").append(editor); + } + + if (advertised != null) { + builder.append(", "); + builder.append("advertised=").append(advertised); } builder.append(']'); @@ -325,6 +336,9 @@ public int hashCode() { result = (prime * result) + ((metadata == null) ? 0 : metadata.hashCode()); result = (prime * result) + ((name == null) ? 0 : name.hashCode()); result = (prime * result) + ((security == null) ? 0 : security.hashCode()); + result = (prime * result) + ((creator == null) ? 0 : creator.hashCode()); + result = (prime * result) + ((editor == null) ? 0 : editor.hashCode()); + result = (prime * result) + ((advertised == null) ? 0 : advertised.hashCode()); return result; } @@ -394,6 +408,13 @@ public boolean equals(Object obj) { } else if (!lastUpdate.equals(other.lastUpdate)) { return false; } + if (advertised == null) { + if (other.advertised != null) { + return false; + } + } else if (!advertised.equals(other.advertised)) { + return false; + } if (metadata == null) { if (other.metadata != null) { return false; @@ -409,12 +430,20 @@ public boolean equals(Object obj) { return false; } if (security == null) { - if (other.security != null) { - return false; - } + return other.security == null; } else if (!security.equals(other.security)) { return false; } + if (creator == null) { + return other.creator == null; + } else if (!creator.equals(other.creator)) { + return false; + } + if (editor == null) { + return other.editor == null; + } else if (!editor.equals(other.editor)) { + return false; + } return true; } @@ -428,10 +457,13 @@ public Object onCycleDetected(Context arg0) { r.setCreation(this.creation); r.setDescription(this.description); r.setLastUpdate(this.lastUpdate); + r.setAdvertised(this.advertised); r.setMetadata(this.metadata); r.setName(this.name); r.setAttribute(null); r.setData(null); + r.setCreator(this.creator); + r.setEditor(this.editor); return r; } diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/SecurityRule.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/SecurityRule.java index b3dcf1f2..6445d072 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/SecurityRule.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/SecurityRule.java @@ -28,11 +28,13 @@ package it.geosolutions.geostore.core.model; import java.io.Serializable; - import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.ForeignKey; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; @@ -41,49 +43,54 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; -import org.hibernate.annotations.ForeignKey; -import org.hibernate.annotations.Index; - /** * Class Security. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ @Entity(name = "Security") -@Table(name = "gs_security", uniqueConstraints = { - @UniqueConstraint(columnNames = { "user_id", "resource_id" }), - /* @UniqueConstraint(columnNames = {"user_id", "category_id"}), */ - @UniqueConstraint(columnNames = { "resource_id", "group_id" }) /* +@Table( + name = "gs_security", + uniqueConstraints = { + @UniqueConstraint(columnNames = {"user_id", "resource_id"}), + /* @UniqueConstraint(columnNames = {"user_id", "category_id"}), */ + @UniqueConstraint(columnNames = {"resource_id", "group_id"}) /* * , - * + * * @UniqueConstraint(columnNames = {"category_id", "group_id"}) - */}) -@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_security") + */ + }, + indexes = { + @Index(name = "idx_security_resource", columnList = "resource_id"), + @Index(name = "idx_security_user", columnList = "user_id"), + @Index(name = "idx_security_group", columnList = "group_id"), + @Index(name = "idx_security_read", columnList = "canread"), + @Index(name = "idx_security_write", columnList = "canwrite"), + @Index(name = "idx_security_username", columnList = "username"), + @Index(name = "idx_security_groupname", columnList = "groupname") + }) +// @Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_security") @XmlRootElement(name = "Security") public class SecurityRule implements Serializable { private static final long serialVersionUID = -4160546863296343389L; - @Id - @GeneratedValue - private Long id; + @Id @GeneratedValue private Long id; /** - * A SecurityRule may refer either to a resource or to a Category, then neither of them are mandatory. A check to ensure they are not both null is - * done in onPreUpdate()
+ * A SecurityRule may refer either to a resource or to a Category, then neither of them are + * mandatory. A check to ensure they are not both null is done in onPreUpdate()
* TODO: it would be nice to have a DB constraint on nonnullability on them. */ @ManyToOne(optional = true) - @Index(name = "idx_security_resource") - @ForeignKey(name = "fk_security_resource") + @JoinColumn(foreignKey = @ForeignKey(name = "fk_security_resource")) private Resource resource; // /** - // * A SecurityRule may refer either to a resource or to a Category, then neither of them are mandatory. A check to ensure they - // * are not both null is done in onPreUpdate()
TODO: it would be nice to have a DB constraint on nonnullability on them. + // * A SecurityRule may refer either to a resource or to a Category, then neither of them are + // mandatory. A check to ensure they + // * are not both null is done in onPreUpdate()
TODO: it would be nice to have a DB + // constraint on nonnullability on them. // */ // @ManyToOne(optional = true) // @Index(name = "idx_security_category") @@ -91,68 +98,53 @@ public class SecurityRule implements Serializable { // private Category category; @ManyToOne(optional = true) - @Index(name = "idx_security_user") - @ForeignKey(name = "fk_security_user") + @JoinColumn(foreignKey = @ForeignKey(name = "fk_security_user")) private User user; @ManyToOne(optional = true) - @Index(name = "idx_security_group") - @ForeignKey(name = "fk_security_group") + @JoinColumn(foreignKey = @ForeignKey(name = "fk_security_group")) private UserGroup group; @Column(nullable = false, updatable = true) - @Index(name = "idx_security_read") private boolean canRead; @Column(nullable = false, updatable = true) - @Index(name = "idx_security_write") private boolean canWrite; - + @Column(nullable = true, updatable = true) - @Index(name = "idx_security_username") private String username; - + @Column(nullable = true, updatable = true) - @Index(name = "idx_security_groupname") private String groupname; - /** - * @throws Exception - */ + /** @throws Exception */ @PreUpdate @PrePersist public void onPreUpdate() throws Exception { // if ( !((this.resource != null) ^ (this.category != null)) ) { - // throw new Exception("Only one between Category and Resource can be not-null inside the Security entity"); + // throw new Exception("Only one between Category and Resource can be not-null inside the + // Security entity"); // } } - /** - * @return the id - */ + /** @return the id */ @XmlTransient public Long getId() { return id; } - /** - * @param id the id to set - */ + /** @param id the id to set */ public void setId(Long id) { this.id = id; } - /** - * @return the resource - */ + /** @return the resource */ @XmlTransient public Resource getResource() { return resource; } - /** - * @param resource the resource to set - */ + /** @param resource the resource to set */ public void setResource(Resource resource) { this.resource = resource; } @@ -172,94 +164,66 @@ public void setResource(Resource resource) { // this.category = category; // } - /** - * @return the user - */ + /** @return the user */ @XmlTransient public User getUser() { return user; } - /** - * @param user the user to set - */ + /** @param user the user to set */ public void setUser(User user) { this.user = user; } - /** - * @return the group - */ + /** @return the group */ @XmlTransient public UserGroup getGroup() { return group; } - /** - * @param group the group to set - */ + /** @param group the group to set */ public void setGroup(UserGroup group) { this.group = group; } - /** - * - * @return the username (from external authentication) - */ + /** @return the username (from external authentication) */ public String getUsername() { return username; } - /** - * - * @param username the user name to set - */ + /** @param username the user name to set */ public void setUsername(String username) { this.username = username; } - /** - * - * @return the group name (from external authentication) - */ + /** @return the group name (from external authentication) */ public String getGroupname() { return groupname; } - /** - * - * @param groupname the group name to set - */ + /** @param groupname the group name to set */ public void setGroupname(String groupname) { this.groupname = groupname; } - /** - * @return the canRead - */ + /** @return the canRead */ @XmlTransient public boolean isCanRead() { return canRead; } - /** - * @param canRead the canRead to set - */ + /** @param canRead the canRead to set */ public void setCanRead(boolean canRead) { this.canRead = canRead; } - /** - * @return the canWrite - */ + /** @return the canWrite */ @XmlTransient public boolean isCanWrite() { return canWrite; } - /** - * @param canWrite the canWrite to set - */ + /** @param canWrite the canWrite to set */ public void setCanWrite(boolean canWrite) { this.canWrite = canWrite; } diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/StoredData.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/StoredData.java index c074344b..5c011615 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/StoredData.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/StoredData.java @@ -28,61 +28,49 @@ package it.geosolutions.geostore.core.model; import java.io.Serializable; - import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.ForeignKey; import javax.persistence.Id; +import javax.persistence.JoinColumn; import javax.persistence.OneToOne; import javax.persistence.Table; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; -import org.hibernate.annotations.ForeignKey; - /** * Class StoredData. - * + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ @Entity(name = "StoreData") @Table(name = "gs_stored_data") -@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_stored_data") +// @Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_stored_data") @XmlRootElement(name = "StoredData") public class StoredData implements Serializable { private static final long serialVersionUID = -2584592064221812813L; - @Id - private Long id; + @Id private Long id; @Column(name = "stored_data", nullable = false, updatable = true, length = 10000000) private String data; @OneToOne(optional = false) - @ForeignKey(name = "fk_data_resource") + @JoinColumn(foreignKey = @ForeignKey(name = "fk_data_resource")) private Resource resource; - /** - * Instantiates a new instance. - */ - public StoredData() { - } + /** Instantiates a new instance. */ + public StoredData() {} - /** - * @return the id - */ + /** @return the id */ @XmlTransient public Long getId() { return id; } - /** - * @param id the id to set - */ + /** @param id the id to set */ public void setId(long id) { this.id = id; } @@ -95,17 +83,13 @@ public void setData(String data) { this.data = data; } - /** - * @return the resource - */ + /** @return the resource */ @XmlTransient public Resource getResource() { return resource; } - /** - * @param resource the resource to set - */ + /** @param resource the resource to set */ public void setResource(Resource resource) { this.resource = resource; } diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/User.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/User.java index f79a2f89..020ba9ba 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/User.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/User.java @@ -29,11 +29,9 @@ package it.geosolutions.geostore.core.model; import it.geosolutions.geostore.core.model.enums.Role; - import java.io.Serializable; import java.util.List; import java.util.Set; - import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; @@ -42,6 +40,7 @@ import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.Index; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; @@ -52,51 +51,60 @@ import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; - -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; -import org.hibernate.annotations.Index; import org.hibernate.annotations.Type; /** * Class User. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author Emanuele Tajariol (etj at geo-solutions.it) */ @Entity(name = "User") -@Table(name = "gs_user", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }) }) -@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_user") +@Table( + name = "gs_user", + uniqueConstraints = {@UniqueConstraint(columnNames = {"name"})}, + indexes = { + @Index(name = "idx_user_name", columnList = "name"), + @Index(name = "idx_user_password", columnList = "user_password"), + @Index(name = "idx_user_role", columnList = "user_role") + }) +// @Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_user") @XmlRootElement(name = "User") public class User implements Serializable { - /** - * The Constant serialVersionUID. - */ + /** The Constant serialVersionUID. */ private static final long serialVersionUID = -138056245004697133L; - /** - * The id. - */ - @Id - @GeneratedValue - private Long id; + /** The id. */ + @Id @GeneratedValue private Long id; @Column(nullable = false, updatable = false, length = 255) - @Index(name = "idx_user_name") private String name; @Column(name = "user_password", updatable = true) - @Index(name = "idx_user_password") private String password; @Column(name = "user_role", nullable = false, updatable = true) - @Index(name = "idx_user_role") @Enumerated(EnumType.STRING) private Role role; + public User() {}; + + public User(User user) { + this.id = user.id; + this.name = user.name; + this.password = user.password; + this.role = user.role; + this.newPassword = user.newPassword; + this.trusted = user.trusted; + this.attribute = user.attribute; + this.security = user.security; + this.groups = user.groups; + this.enabled = user.enabled; + } + /* * NOT to be saved on DB */ @@ -114,177 +122,148 @@ public class User implements Serializable { private List security; @ManyToMany(fetch = FetchType.EAGER) - @JoinTable(name = "gs_usergroup_members", - joinColumns = { @JoinColumn(name = "user_id", nullable = false, updatable = false) }, - inverseJoinColumns = { @JoinColumn(name = "group_id", nullable = false, updatable = false) }) - @Index(name = "idx_user_group") + @JoinTable( + name = "gs_usergroup_members", + joinColumns = {@JoinColumn(name = "user_id", nullable = false, updatable = false)}, + inverseJoinColumns = { + @JoinColumn(name = "group_id", nullable = false, updatable = false) + }) private Set groups; - @Type(type="yes_no") - @Column(nullable = false,updatable =true) - private boolean enabled=true; + @Type(type = "yes_no") + @Column(nullable = false, updatable = true) + private boolean enabled = true; - /** - * @return the id - */ + /** @return the id */ // @XmlTransient public Long getId() { return id; } - /** - * @param id the id to set - */ + /** @param id the id to set */ public void setId(Long id) { this.id = id; } - /** - * @return the group - */ + /** @return the group */ @XmlElementWrapper - @XmlElement(name="group") + @XmlElement(name = "group") public Set getGroups() { return groups; } - /** - * @param group the group to set - */ + /** @param group the group to set */ public void setGroups(Set groups) { this.groups = groups; } - /** - * @return true if the group with id groupId was associated with the user and was removed. - */ + /** @return true if the group with id groupId was associated with the user and was removed. */ public boolean removeGroup(long groupId) { - if(groups != null) { + if (groups != null) { for (UserGroup group : groups) { - if(groupId == group.getId()) { + if (groupId == group.getId()) { return groups.remove(group); } } } - + return false; } - /** - * @return the security - */ + /** @return the security */ @XmlTransient public List getSecurity() { return security; } - /** - * @param security the security to set - */ + /** @param security the security to set */ public void setSecurity(List security) { this.security = security; } - /** - * @return the name - */ + /** @return the name */ public String getName() { return name; } - /** - * @param name the name to set - */ + /** @param name the name to set */ public void setName(String name) { this.name = name; } - /** - * @return the password - */ + /** @return the password */ @XmlTransient public String getPassword() { return password; } /** - * DON'T USE THIS METHOD
- * You will probably break the password by using this method.
+ * DON'T USE THIS METHOD
+ * You will probably break the password by using this method.
* Please use the {@link setNewPassword()} method instead. - * + * * @param password the password to set */ public void setPassword(String password) { this.password = password; } - /** - * @return the newPassword - */ + /** @return the newPassword */ public String getNewPassword() { return newPassword; } /** - * Set the cleartext password.
- * Before being persisted, the password will be automatically encoded, and will be accessible with {@link getPassword()}. - *

- * Please note that this is NOT a persisted field - * + * Set the cleartext password.
+ * Before being persisted, the password will be automatically encoded, and will be accessible + * with {@link getPassword()}. + * + *

Please note that this is NOT a persisted field + * * @param newPassword the cleartext newPassword */ public void setNewPassword(String newPassword) { this.newPassword = newPassword; } - /** - * @return the attribute - */ + /** @return the attribute */ public List getAttribute() { return attribute; } - /** - * @param attribute the attribute to set - */ + /** @param attribute the attribute to set */ public void setAttribute(List attribute) { this.attribute = attribute; } - /** - * - * @return the enabled flag - */ + /** @return the enabled flag */ public boolean isEnabled() { - return enabled; - } + return enabled; + } /** * set enabled flag + * * @param enabled */ - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - /** - * @return the role - */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + /** @return the role */ public Role getRole() { return role; } - /** - * @param role the role to set - */ + /** @param role the role to set */ public void setRole(Role role) { this.role = role; } - + /** - * A trusted user is not validated through the database. - * Used when we want to externalize user authentication and we get the user from the external source. - * + * A trusted user is not validated through the database. Used when we want to externalize user + * authentication and we get the user from the external source. + * * @return */ @XmlTransient @@ -336,7 +315,7 @@ public int hashCode() { final int prime = 31; int result = 1; result = (prime * result) + ((attribute == null) ? 0 : attribute.hashCode()); - //result = (prime * result) + ((groups == null) ? 0 : groups.hashCode()); + // result = (prime * result) + ((groups == null) ? 0 : groups.hashCode()); result = (prime * result) + ((id == null) ? 0 : id.hashCode()); result = (prime * result) + ((name == null) ? 0 : name.hashCode()); result = (prime * result) + ((password == null) ? 0 : password.hashCode()); diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/UserAttribute.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/UserAttribute.java index a2d22ade..9b0343ec 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/UserAttribute.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/UserAttribute.java @@ -29,108 +29,89 @@ package it.geosolutions.geostore.core.model; import java.io.Serializable; - import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.ForeignKey; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; -import org.hibernate.annotations.ForeignKey; -import org.hibernate.annotations.Index; - /** * Class Attribute. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ @Entity(name = "UserAttribute") -@Table(name = "gs_user_attribute", uniqueConstraints = { @UniqueConstraint(columnNames = { "name", - "user_id" }) }) -@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_user_attribute") +@Table( + name = "gs_user_attribute", + uniqueConstraints = {@UniqueConstraint(columnNames = {"name", "user_id"})}, + indexes = { + @Index(name = "idx_user_attribute_name", columnList = "name"), + @Index(name = "idx_user_attribute_text", columnList = "string"), + @Index(name = "idx_attribute_user", columnList = "user_id") + }) +// @Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_user_attribute") @XmlRootElement(name = "UserAttribute") public class UserAttribute implements Serializable { private static final long serialVersionUID = 8215714782335367731L; - @Id - @GeneratedValue - private Long id; + @Id @GeneratedValue private Long id; @Column(name = "name", nullable = false, updatable = true) - @Index(name = "idx_user_attribute_name") private String name; @Column(name = "string", nullable = true, updatable = true) - @Index(name = "idx_user_attribute_text") private String value; @ManyToOne(optional = false) - @Index(name = "idx_attribute_user") - @ForeignKey(name = "fk_uattrib_user") + @JoinColumn(foreignKey = @ForeignKey(name = "fk_uattrib_user")) private User user; - /** - * @return the id - */ + /** @return the id */ @XmlTransient public Long getId() { return id; } - /** - * @param id the id to set - */ + /** @param id the id to set */ public void setId(Long id) { this.id = id; } - /** - * @return the name - */ + /** @return the name */ public String getName() { return name; } - /** - * @param name the name to set - */ + /** @param name the name to set */ public void setName(String name) { this.name = name; } - /** - * @return the value - */ + /** @return the value */ public String getValue() { return value; } - /** - * @param value the value to set - */ + /** @param value the value to set */ public void setValue(String value) { this.value = value; } - /** - * @return the user - */ + /** @return the user */ @XmlTransient public User getUser() { return user; } - /** - * @param user the user to set - */ + /** @param user the user to set */ public void setUser(User user) { this.user = user; } diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/UserGroup.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/UserGroup.java index 5ee12ccc..028658d8 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/UserGroup.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/UserGroup.java @@ -37,25 +37,25 @@ import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.Index; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; -import org.hibernate.annotations.Index; import org.hibernate.annotations.Type; /** * Class Group. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ @Entity(name = "UserGroup") -@Table(name = "gs_usergroup", uniqueConstraints = { @UniqueConstraint(columnNames = { "groupName" }) }) -@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_usergroup") +@Table( + name = "gs_usergroup", + uniqueConstraints = {@UniqueConstraint(columnNames = {"groupName"})}, + indexes = {@Index(name = "idx_usergroup_name", columnList = "groupName")}) +// @Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "gs_usergroup") @XmlRootElement(name = "UserGroup") public class UserGroup implements Serializable { @@ -63,29 +63,29 @@ public class UserGroup implements Serializable { private static final long serialVersionUID = 6065837305601115748L; /** The id. */ - @Id - @GeneratedValue - private Long id; + @Id @GeneratedValue private Long id; @Column(nullable = false, updatable = false, length = 255) - @Index(name = "idx_usergroup_name") private String groupName; @Column(nullable = true, updatable = true, length = 255) private String description; - + /* * Only To allow the CASCADING operation */ @OneToMany(mappedBy = "group", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY) private List security; - @Type(type="yes_no") - @Column(nullable = false,updatable =true) - private boolean enabled=true; - + @Type(type = "yes_no") + @Column(nullable = false, updatable = true) + private boolean enabled = true; + private transient List users = new ArrayList(); - + + @OneToMany(mappedBy = "userGroup", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY) + private List attributes; + @XmlTransient public List getUsers() { return users; @@ -93,90 +93,83 @@ public List getUsers() { /** * Users belonging to this UserGroup. - * + * * @param users */ public void setUsers(List users) { this.users = users; } - /** - * - * @return the enabled flag - */ + /** @return the enabled flag */ public boolean isEnabled() { return enabled; } /** * set enabled flag + * * @param enabled */ public void setEnabled(boolean enabled) { this.enabled = enabled; } - /** - * @return the id - */ - //@XmlTransient + /** @return the id */ + // @XmlTransient public Long getId() { return id; } - /** - * @param id the id to set - */ + /** @param id the id to set */ public void setId(Long id) { this.id = id; } - /** - * @return the groupName - */ + /** @return the groupName */ public String getGroupName() { return groupName; } - /** - * @param groupName the groupName to set - */ + /** @param groupName the groupName to set */ public void setGroupName(String groupName) { this.groupName = groupName; } - /** - * @return the security - */ + /** @return the security */ @XmlTransient public List getSecurity() { return security; } - /** - * @param security the security to set - */ + /** @param security the security to set */ public void setSecurity(List security) { this.security = security; } - /** - * @return the description - */ + /** @return the description */ public String getDescription() { return description; } - /** - * @param description the description to set - */ + /** @param description the description to set */ public void setDescription(String description) { this.description = description; } + /** @return the attribute */ + @XmlTransient + public List getAttributes() { + return attributes; + } + + /** @param attributes the attribute to set */ + public void setAttributes(List attributes) { + this.attributes = attributes; + } + /* * (non-Javadoc) - * + * * @see java.lang.Object#toString() */ @Override @@ -200,7 +193,7 @@ public String toString() { /* * (non-Javadoc) - * + * * @see java.lang.Object#hashCode() */ @Override @@ -210,13 +203,14 @@ public int hashCode() { result = (prime * result) + ((groupName == null) ? 0 : groupName.hashCode()); result = (prime * result) + ((id == null) ? 0 : id.hashCode()); result = (prime * result) + ((security == null) ? 0 : security.hashCode()); + result = (prime * result) + ((attributes == null) ? 0 : attributes.hashCode()); return result; } /* * (non-Javadoc) - * + * * @see java.lang.Object#equals(java.lang.Object) */ @Override @@ -254,7 +248,14 @@ public boolean equals(Object obj) { return false; } + if (attributes == null) { + if (other.attributes != null) { + return false; + } + } else if (!attributes.equals(other.attributes)) { + return false; + } + return true; } - } diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/UserGroupAttribute.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/UserGroupAttribute.java new file mode 100644 index 00000000..e902745b --- /dev/null +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/UserGroupAttribute.java @@ -0,0 +1,170 @@ +package it.geosolutions.geostore.core.model; + +import java.io.Serializable; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.ForeignKey; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; + +@Entity(name = "UserGroupAttribute") +@Table( + name = "gs_user_group_attribute", + indexes = { + @Index(name = "idx_user_group_attr_name", columnList = "name"), + @Index(name = "idx_user_group_attr_text", columnList = "string"), + @Index(name = "idx_attr_user_group", columnList = "usergroup_id") + }) +@XmlRootElement(name = "UserGroupAttribute") +public class UserGroupAttribute implements Serializable { + + @Id @GeneratedValue private Long id; + + @Column(name = "name", nullable = false, updatable = true) + private String name; + + @Column(name = "string", nullable = true, updatable = true) + private String value; + + @ManyToOne(optional = false) + @JoinColumn(foreignKey = @ForeignKey(name = "fk_ugattrib_user_group")) + private UserGroup userGroup; + + /** @return the id */ + @XmlTransient + public Long getId() { + return id; + } + + /** @param id the id to set */ + public void setId(Long id) { + this.id = id; + } + + /** @return the name */ + public String getName() { + return name; + } + + /** @param name the name to set */ + public void setName(String name) { + this.name = name; + } + + /** @return the value */ + public String getValue() { + return value; + } + + /** @param value the value to set */ + public void setValue(String value) { + this.value = value; + } + + /** @return the user */ + @XmlTransient + public UserGroup getUserGroup() { + return userGroup; + } + + /** @param userGroup the userGroup to set */ + public void setUserGroup(UserGroup userGroup) { + this.userGroup = userGroup; + } + + /* + * (non-Javadoc) @see java.lang.Object#toString() + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(getClass().getSimpleName()).append('['); + + if (id != null) { + builder.append("id=").append(id); + } + + if (name != null) { + builder.append(", "); + builder.append("name=").append(name); + } + + if (value != null) { + builder.append(", "); + builder.append("value=").append(value); + } + + builder.append(']'); + + return builder.toString(); + } + + /* + * (non-Javadoc) @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = (prime * result) + ((id == null) ? 0 : id.hashCode()); + result = (prime * result) + ((name == null) ? 0 : name.hashCode()); + result = (prime * result) + ((userGroup == null) ? 0 : userGroup.hashCode()); + result = (prime * result) + ((value == null) ? 0 : value.hashCode()); + + return result; + } + + /* + * (non-Javadoc) @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + + UserGroupAttribute other = (UserGroupAttribute) obj; + if (id == null) { + if (other.id != null) { + return false; + } + } else if (!id.equals(other.id)) { + return false; + } + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (userGroup == null) { + if (other.userGroup != null) { + return false; + } + } else if (!userGroup.equals(other.userGroup)) { + return false; + } + if (value == null) { + if (other.value != null) { + return false; + } + } else if (!value.equals(other.value)) { + return false; + } + + return true; + } +} diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/GeometryAdapter.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/GeometryAdapter.java index d79e7c25..dd202af0 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/GeometryAdapter.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/GeometryAdapter.java @@ -28,18 +28,16 @@ */ package it.geosolutions.geostore.core.model.adapter; -import javax.xml.bind.annotation.adapters.XmlAdapter; - import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKTReader; import com.vividsolutions.jts.io.WKTWriter; +import javax.xml.bind.annotation.adapters.XmlAdapter; /** * The Class GeometryAdapter. - * + * * @param the generic type - * * @author Emanuele Tajariol (etj at geo-solutions.it) */ public class GeometryAdapter extends XmlAdapter { diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/MultiPolygonAdapter.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/MultiPolygonAdapter.java index a91a5d82..028c8cec 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/MultiPolygonAdapter.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/MultiPolygonAdapter.java @@ -33,8 +33,7 @@ /** * The Class MultiPolygonAdapter. - * + * * @author Emanuele Tajariol (etj at geo-solutions.it) */ -public class MultiPolygonAdapter extends GeometryAdapter { -} +public class MultiPolygonAdapter extends GeometryAdapter {} diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/PolygonAdapter.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/PolygonAdapter.java index bcfd308e..a1562d04 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/PolygonAdapter.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/PolygonAdapter.java @@ -28,17 +28,16 @@ */ package it.geosolutions.geostore.core.model.adapter; -import javax.xml.bind.annotation.adapters.XmlAdapter; - import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKTReader; import com.vividsolutions.jts.io.WKTWriter; +import javax.xml.bind.annotation.adapters.XmlAdapter; /** * The Class PolygonAdapter. - * + * * @author Emanuele Tajariol (etj at geo-solutions.it) */ public class PolygonAdapter extends XmlAdapter { diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/XMultiPolygonAdapter.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/XMultiPolygonAdapter.java index 036357c8..16b9e7e3 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/XMultiPolygonAdapter.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/XMultiPolygonAdapter.java @@ -28,17 +28,16 @@ */ package it.geosolutions.geostore.core.model.adapter; -import javax.xml.bind.annotation.adapters.XmlAdapter; - import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKTReader; import com.vividsolutions.jts.io.WKTWriter; +import javax.xml.bind.annotation.adapters.XmlAdapter; /** * The Class XMultiPolygonAdapter. - * + * * @author Emanuele Tajariol (etj at geo-solutions.it) */ public class XMultiPolygonAdapter extends XmlAdapter { diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/package-info.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/package-info.java index b26ca32a..1514ed5f 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/package-info.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/adapter/package-info.java @@ -1,5 +1,2 @@ -/** - * Provides... - */ +/** Provides... */ package it.geosolutions.geostore.core.model.adapter; - diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/AccessType.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/AccessType.java index 0b24ca65..71845f88 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/AccessType.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/AccessType.java @@ -31,8 +31,8 @@ /** * Class AccessType - * - * The Enum AccessType. + * + *

The Enum AccessType. */ public enum AccessType { diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/DataType.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/DataType.java index dffa6617..a912594d 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/DataType.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/DataType.java @@ -30,10 +30,11 @@ /** * Enum DataType. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public enum DataType { - STRING, NUMBER, DATE; + STRING, + NUMBER, + DATE; } diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/GroupReservedNames.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/GroupReservedNames.java index d5bf1ba9..e232d9b0 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/GroupReservedNames.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/GroupReservedNames.java @@ -19,57 +19,51 @@ */ package it.geosolutions.geostore.core.model.enums; -import java.util.ArrayList; +import it.geosolutions.geostore.core.model.UserGroup; import java.util.Collection; import java.util.HashSet; -import java.util.List; import java.util.Set; -import it.geosolutions.geostore.core.model.UserGroup; - -/** - * @author DamianoG - * - */ +/** @author DamianoG */ public enum GroupReservedNames { - EVERYONE ("everyone"); - + EVERYONE("everyone"); + private final String groupNameToPersist; - - GroupReservedNames(String groupNameToPersist){ + + GroupReservedNames(String groupNameToPersist) { this.groupNameToPersist = groupNameToPersist; } - + public String groupName() { return groupNameToPersist; } - + /** - * Given a candidate groupName this method checks if the name is allowed. - * This enum holds the list of reserved names. A groupname is not allowed if it matches ignoring the case - * at least one of the reserved names. - * + * Given a candidate groupName this method checks if the name is allowed. This enum holds the + * list of reserved names. A groupname is not allowed if it matches ignoring the case at least + * one of the reserved names. + * * @param groupNameToCheck * @return */ - public static boolean isAllowedName(String groupNameToCheck){ - if(EVERYONE.groupName().equalsIgnoreCase(groupNameToCheck)){ + public static boolean isAllowedName(String groupNameToCheck) { + if (EVERYONE.groupName().equalsIgnoreCase(groupNameToCheck)) { return false; } return true; } - + /** * Utility method to remove Reserved group (for example EVERYONE) from a group list - * + * * @param groups * @return */ public static Set checkReservedGroups(Collection groups) { Set result = new HashSet(); - for(UserGroup ug : groups){ - if(GroupReservedNames.isAllowedName(ug.getGroupName())){ - result.add(ug); + for (UserGroup ug : groups) { + if (GroupReservedNames.isAllowedName(ug.getGroupName())) { + result.add(ug); } } return result; diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/Role.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/Role.java index 4bf13654..2c3ce961 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/Role.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/Role.java @@ -30,10 +30,11 @@ /** * Enum Role. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public enum Role { - ADMIN, USER, GUEST + ADMIN, + USER, + GUEST } diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/UserReservedNames.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/UserReservedNames.java index f977fc47..b2a8b293 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/UserReservedNames.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/enums/UserReservedNames.java @@ -19,34 +19,30 @@ */ package it.geosolutions.geostore.core.model.enums; -/** - * @author DamianoG - * - */ +/** @author DamianoG */ public enum UserReservedNames { - - GUEST ("guest"); - + GUEST("guest"); + private final String userNameToPersist; - - UserReservedNames(String userNameToPersist){ + + UserReservedNames(String userNameToPersist) { this.userNameToPersist = userNameToPersist; } - - public String userName(){ + + public String userName() { return userNameToPersist; } - + /** - * Given a candidate userName this method checks if the name is allowed. - * This enum holds the list of reserved names. A username is not allowed if it matches ignoring the case - * at least one of the reserved names. - * + * Given a candidate userName this method checks if the name is allowed. This enum holds the + * list of reserved names. A username is not allowed if it matches ignoring the case at least + * one of the reserved names. + * * @param groupNameToCheck * @return */ - public static boolean isAllowedName(String groupNameToCheck){ - if(GUEST.userName().equalsIgnoreCase(groupNameToCheck)){ + public static boolean isAllowedName(String groupNameToCheck) { + if (GUEST.userName().equalsIgnoreCase(groupNameToCheck)) { return false; } return true; diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/package-info.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/package-info.java index 39c4ce9b..75b5eee8 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/package-info.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/package-info.java @@ -1,5 +1,2 @@ -/** - * Provides... - */ +/** Provides... */ package it.geosolutions.geostore.core.model; - diff --git a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/util/package-info.java b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/util/package-info.java index 6581d6c8..fe99075e 100644 --- a/src/core/model/src/main/java/it/geosolutions/geostore/core/model/util/package-info.java +++ b/src/core/model/src/main/java/it/geosolutions/geostore/core/model/util/package-info.java @@ -1,5 +1,2 @@ -/** - * Provides... - */ +/** Provides... */ package it.geosolutions.geostore.core.model.util; - diff --git a/src/core/model/src/test/java/it/geosolutions/geostore/core/model/AttributeTest.java b/src/core/model/src/test/java/it/geosolutions/geostore/core/model/AttributeTest.java index 70281ebc..3655801c 100644 --- a/src/core/model/src/test/java/it/geosolutions/geostore/core/model/AttributeTest.java +++ b/src/core/model/src/test/java/it/geosolutions/geostore/core/model/AttributeTest.java @@ -1,42 +1,36 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.core.model; +import static org.junit.Assert.*; + import it.geosolutions.geostore.core.model.enums.DataType; -import java.io.StringReader; -import java.io.StringWriter; import java.util.Date; -import javax.xml.bind.JAXB; import org.junit.Test; -import static org.junit.Assert.*; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class AttributeTest { - private final static Marshaler MARSHALER = new Marshaler(Attribute.class); + private static final Marshaler MARSHALER = new Marshaler(Attribute.class); - public AttributeTest() { - } + public AttributeTest() {} @Test public void testMarshallingString() throws Exception { diff --git a/src/core/model/src/test/java/it/geosolutions/geostore/core/model/CategoryTest.java b/src/core/model/src/test/java/it/geosolutions/geostore/core/model/CategoryTest.java index d33bfdde..6f34f35a 100644 --- a/src/core/model/src/test/java/it/geosolutions/geostore/core/model/CategoryTest.java +++ b/src/core/model/src/test/java/it/geosolutions/geostore/core/model/CategoryTest.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,23 +21,14 @@ import static org.junit.Assert.assertTrue; -import java.io.StringReader; -import java.io.StringWriter; - -import javax.xml.bind.JAXB; - import org.junit.Test; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class CategoryTest { - private final static Marshaler MARSHALER = new Marshaler(Category.class); + private static final Marshaler MARSHALER = new Marshaler(Category.class); - public CategoryTest() { - } + public CategoryTest() {} @Test public void testMarshallingString() throws Exception { @@ -59,4 +50,3 @@ private void doTheTest(Category a0) { assertTrue(a0.equals(a1)); } } - diff --git a/src/core/model/src/test/java/it/geosolutions/geostore/core/model/GroupReservedNamesTest.java b/src/core/model/src/test/java/it/geosolutions/geostore/core/model/GroupReservedNamesTest.java index 98a5cc62..11cb93bb 100644 --- a/src/core/model/src/test/java/it/geosolutions/geostore/core/model/GroupReservedNamesTest.java +++ b/src/core/model/src/test/java/it/geosolutions/geostore/core/model/GroupReservedNamesTest.java @@ -1,27 +1,28 @@ /* * Copyright (C) 2021 - 2011 GeoSolutions S.A.S. http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. - * + * * You should have received a copy of the GNU General Public License along with this program. If * not, see . */ package it.geosolutions.geostore.core.model; +import static org.junit.Assert.assertEquals; + +import it.geosolutions.geostore.core.model.enums.GroupReservedNames; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.junit.Test; -import it.geosolutions.geostore.core.model.enums.GroupReservedNames; -import static org.junit.Assert.assertEquals; public class GroupReservedNamesTest { @Test @@ -33,9 +34,9 @@ public void testRemoveReserved() { UserGroup sample = new UserGroup(); sample.setGroupName("sample"); groups.add(sample); - + Set result = GroupReservedNames.checkReservedGroups(groups); - + assertEquals(1, result.size()); assertEquals("sample", result.iterator().next().getGroupName()); } diff --git a/src/core/model/src/test/java/it/geosolutions/geostore/core/model/Marshaler.java b/src/core/model/src/test/java/it/geosolutions/geostore/core/model/Marshaler.java index 50f76ed8..d3b011cf 100644 --- a/src/core/model/src/test/java/it/geosolutions/geostore/core/model/Marshaler.java +++ b/src/core/model/src/test/java/it/geosolutions/geostore/core/model/Marshaler.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2019 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -41,5 +41,4 @@ protected T unmarshal(String s) { StringReader sr = new StringReader(s); return JAXB.unmarshal(sr, _class); } - } diff --git a/src/core/model/src/test/java/it/geosolutions/geostore/core/model/SecurityRuleTest.java b/src/core/model/src/test/java/it/geosolutions/geostore/core/model/SecurityRuleTest.java index ffa5f210..8f58bc1e 100644 --- a/src/core/model/src/test/java/it/geosolutions/geostore/core/model/SecurityRuleTest.java +++ b/src/core/model/src/test/java/it/geosolutions/geostore/core/model/SecurityRuleTest.java @@ -1,37 +1,39 @@ /* * Copyright (C) 2019 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.core.model; import static org.junit.Assert.assertTrue; + import org.junit.Test; public class SecurityRuleTest { - private final static Marshaler MARSHALER = new Marshaler(SecurityRule.class); - + private static final Marshaler MARSHALER = + new Marshaler(SecurityRule.class); + @Test public void testMarshallingUsername() throws Exception { SecurityRule sr0 = new SecurityRule(); sr0.setUsername("testuser"); doTheTest(sr0); } - + @Test public void testMarshallingGroupname() throws Exception { SecurityRule sr0 = new SecurityRule(); @@ -46,5 +48,3 @@ private void doTheTest(SecurityRule a0) { assertTrue(a0.equals(a1)); } } - - diff --git a/src/core/model/src/test/java/it/geosolutions/geostore/core/model/UserGroupTest.java b/src/core/model/src/test/java/it/geosolutions/geostore/core/model/UserGroupTest.java index 861d10be..62114047 100644 --- a/src/core/model/src/test/java/it/geosolutions/geostore/core/model/UserGroupTest.java +++ b/src/core/model/src/test/java/it/geosolutions/geostore/core/model/UserGroupTest.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,32 +21,29 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; + import java.util.Arrays; import org.junit.Test; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class UserGroupTest { - private final static Marshaler MARSHALER = new Marshaler(UserGroup.class); + private static final Marshaler MARSHALER = new Marshaler(UserGroup.class); - public UserGroupTest() { - } + public UserGroupTest() {} @Test public void testMarshallingString() throws Exception { - UserGroup g0 = new UserGroup(); - g0.setGroupName("group name"); - g0.setDescription("desciption"); - g0.setEnabled(true); - - User u0 = new User(); - u0.setName("user name"); - u0.setEnabled(true); - g0.setUsers(Arrays.asList(u0)); - + UserGroup g0 = new UserGroup(); + g0.setGroupName("group name"); + g0.setDescription("desciption"); + g0.setEnabled(true); + + User u0 = new User(); + u0.setName("user name"); + u0.setEnabled(true); + g0.setUsers(Arrays.asList(u0)); + doTheTest(g0); } @@ -56,5 +53,4 @@ private void doTheTest(UserGroup g0) { assertEquals(0, ug.getUsers().size()); assertTrue(g0.equals(ug)); } - -} +} diff --git a/src/core/model/src/test/java/it/geosolutions/geostore/core/model/UserTest.java b/src/core/model/src/test/java/it/geosolutions/geostore/core/model/UserTest.java index 0fe9e7b5..4a09f03f 100644 --- a/src/core/model/src/test/java/it/geosolutions/geostore/core/model/UserTest.java +++ b/src/core/model/src/test/java/it/geosolutions/geostore/core/model/UserTest.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2019 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,13 +21,13 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; + import org.junit.Test; public class UserTest { - private final static Marshaler MARSHALER = new Marshaler(User.class); + private static final Marshaler MARSHALER = new Marshaler(User.class); - public UserTest() { - } + public UserTest() {} @Test public void testMarshallingString() throws Exception { @@ -35,7 +35,7 @@ public void testMarshallingString() throws Exception { u0.setName("user name"); u0.setEnabled(true); u0.setTrusted(true); - + doTheTest(u0); } diff --git a/src/core/persistence/pom.xml b/src/core/persistence/pom.xml index 2b80d332..9eec2a30 100644 --- a/src/core/persistence/pom.xml +++ b/src/core/persistence/pom.xml @@ -18,14 +18,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - + 4.0.0 it.geosolutions.geostore geostore-core - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-persistence @@ -41,14 +42,14 @@ it.geosolutions.geostore geostore-model - + it.geosolutions.geostore geostore-security - + commons-lang @@ -73,16 +74,8 @@ - dom4j - dom4j - - - log4j - log4j - - - org.slf4j - slf4j-log4j12 + org.apache.logging.log4j + log4j-core @@ -110,48 +103,58 @@ - org.springframework.security - spring-security-ldap - - - - - - + org.springframework.security + spring-security-ldap + + + + + + + + - javax.persistence - persistence-api - + org.hibernate.javax.persistence + hibernate-jpa-2.1-api - + + com.googlecode.genericdao - dao - + dao-hibernate + com.googlecode.genericdao search-jpa-hibernate - + - + - com.h2database - h2 - 1.3.168 - + com.h2database + h2 + - + + + org.hibernate - hibernate-annotations + hibernate-core asm @@ -169,32 +172,10 @@ org.hibernate - hibernate-commons-annotations - - - - org.hibernate - hibernate-entitymanager - - - asm - asm - - - asm - asm-attrs - - - cglib - cglib - - + hibernate-ehcache - + asm asm @@ -204,11 +185,11 @@ cglib-nodep - - + + org.aspectj aspectjrt @@ -218,13 +199,13 @@ aspectjweaver - + - + junit junit @@ -234,32 +215,31 @@ - - - - - postgres - - - - postgresql - postgresql - - - - - - oracle - - - - com.oracle - ojdbc6 - 11.2.0 - - - - + + + + + postgres + + + + org.postgresql + postgresql + + + + + + oracle + + + + com.oracle + ojdbc6 + + + + diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/AttributeDAO.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/AttributeDAO.java index 063f4e63..6f80d832 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/AttributeDAO.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/AttributeDAO.java @@ -23,10 +23,7 @@ /** * Interface AttributeDAO. Public interface to define operations on DataType - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ -public interface AttributeDAO extends RestrictedGenericDAO { - -} +public interface AttributeDAO extends RestrictedGenericDAO {} diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/CategoryDAO.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/CategoryDAO.java index 702926b4..bab16e4b 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/CategoryDAO.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/CategoryDAO.java @@ -19,16 +19,14 @@ */ package it.geosolutions.geostore.core.dao; -import java.util.List; - import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.core.model.SecurityRule; +import java.util.List; /** * Interface CategoryDAO. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public interface CategoryDAO extends RestrictedGenericDAO { @@ -40,7 +38,6 @@ public interface CategoryDAO extends RestrictedGenericDAO { List findUserSecurityRule(String userName, long categoryId); /** - * * @param userName * @param categoryId * @return diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ResourceDAO.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ResourceDAO.java index a74c8bff..99896638 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ResourceDAO.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ResourceDAO.java @@ -27,20 +27,18 @@ */ package it.geosolutions.geostore.core.dao; -import java.util.List; -import javax.persistence.NonUniqueResultException; import com.googlecode.genericdao.search.ISearch; import it.geosolutions.geostore.core.model.Attribute; import it.geosolutions.geostore.core.model.Resource; +import java.util.List; +import javax.persistence.NonUniqueResultException; /** * Interface ResourceDAO. Public interface to define operations on Resource * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ -public interface ResourceDAO extends RestrictedGenericDAO -{ +public interface ResourceDAO extends RestrictedGenericDAO { /** * @param resourceId @@ -48,14 +46,10 @@ public interface ResourceDAO extends RestrictedGenericDAO */ public List findAttributes(long resourceId); - /** - * @param search - */ + /** @param search */ public void removeResources(ISearch search); - /** - * @param resourcesIDs A list of resources Ids to search - */ + /** @param resourcesIDs A list of resources Ids to search */ public List findResources(List resourcesIds); /** diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/RestrictedGenericDAO.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/RestrictedGenericDAO.java index ad3699d5..2371832a 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/RestrictedGenericDAO.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/RestrictedGenericDAO.java @@ -24,12 +24,12 @@ import java.util.List; /** - * Public interface to define a restricted set of operation wrt to ones defined in GenericDAO. This may be useful if some constraints are implemented - * in the DAO, so that fewer point of access are allowed. - * + * Public interface to define a restricted set of operation wrt to ones defined in GenericDAO. This + * may be useful if some constraints are implemented in the DAO, so that fewer point of access are + * allowed. + * * @author Emanuele Tajariol (etj at geo-solutions.it) */ - public interface RestrictedGenericDAO { public List findAll(); diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/SecurityDAO.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/SecurityDAO.java index d3e1f80f..43f40fed 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/SecurityDAO.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/SecurityDAO.java @@ -19,23 +19,23 @@ */ package it.geosolutions.geostore.core.dao; -import java.util.List; import com.googlecode.genericdao.search.Search; import it.geosolutions.geostore.core.model.SecurityRule; import it.geosolutions.geostore.core.model.User; +import java.util.List; /** * Interface SecurityDAO. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public interface SecurityDAO extends RestrictedGenericDAO { - /** - * Add security filtering in order to filter out resources the user has not read access to - */ + /** Add security filtering in order to filter out resources the user has not read access to */ void addReadSecurityConstraints(Search searchCriteria, User user); - + + /** Add security filtering in order to filter out resources hidden the user */ + void addAdvertisedSecurityConstraints(Search searchCriteria, User user); + /** * @param userName * @param resourceId @@ -44,8 +44,7 @@ public interface SecurityDAO extends RestrictedGenericDAO { public List findUserSecurityRule(String userName, long resourceId); /** - * - * @param userName + * @param groupNames * @param resourceId * @return */ diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/StoredDataDAO.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/StoredDataDAO.java index 368190d7..0f17ecf4 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/StoredDataDAO.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/StoredDataDAO.java @@ -24,9 +24,7 @@ /** * Interface StoredDataDAO. Public interface to define operations on Rule - * + * * @author Emanuele Tajariol (etj at geo-solutions.it) */ -public interface StoredDataDAO extends RestrictedGenericDAO { - -} +public interface StoredDataDAO extends RestrictedGenericDAO {} diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/UserAttributeDAO.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/UserAttributeDAO.java index 27efbd47..498f6d84 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/UserAttributeDAO.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/UserAttributeDAO.java @@ -23,10 +23,7 @@ /** * Interface UserAttributeDAO. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ -public interface UserAttributeDAO extends RestrictedGenericDAO { - -} +public interface UserAttributeDAO extends RestrictedGenericDAO {} diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/UserDAO.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/UserDAO.java index 83a51e98..0da6a1ca 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/UserDAO.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/UserDAO.java @@ -23,10 +23,7 @@ /** * Interface UserDAO. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ -public interface UserDAO extends RestrictedGenericDAO { - -} +public interface UserDAO extends RestrictedGenericDAO {} diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/UserGroupAttributeDAO.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/UserGroupAttributeDAO.java new file mode 100644 index 00000000..7293c792 --- /dev/null +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/UserGroupAttributeDAO.java @@ -0,0 +1,5 @@ +package it.geosolutions.geostore.core.dao; + +import it.geosolutions.geostore.core.model.UserGroupAttribute; + +public interface UserGroupAttributeDAO extends RestrictedGenericDAO {} diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/UserGroupDAO.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/UserGroupDAO.java index 5da30aed..7cfdffe6 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/UserGroupDAO.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/UserGroupDAO.java @@ -23,10 +23,10 @@ /** * Interface UserGroupDAO. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public interface UserGroupDAO extends RestrictedGenericDAO { + UserGroup findByName(String name); } diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/AttributeDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/AttributeDAOImpl.java index 152bce1e..7643110f 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/AttributeDAOImpl.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/AttributeDAOImpl.java @@ -21,28 +21,27 @@ package it.geosolutions.geostore.core.dao.impl; import com.googlecode.genericdao.search.ISearch; -import java.util.List; - import it.geosolutions.geostore.core.dao.AttributeDAO; import it.geosolutions.geostore.core.model.Attribute; - -import org.apache.log4j.Logger; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.transaction.annotation.Transactional; /** * Class AttributeDAOImpl. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ @Transactional(value = "geostoreTransactionManager") public class AttributeDAOImpl extends BaseDAO implements AttributeDAO { - private static final Logger LOGGER = Logger.getLogger(AttributeDAOImpl.class); + private static final Logger LOGGER = LogManager.getLogger(AttributeDAOImpl.class); /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#persist(T[]) */ @Override @@ -56,7 +55,7 @@ public void persist(Attribute... entities) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#findAll() */ @Override @@ -66,7 +65,7 @@ public List findAll() { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#search(com.trg.search.ISearch) */ @SuppressWarnings("unchecked") @@ -77,7 +76,7 @@ public List search(ISearch search) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#merge(java.lang.Object) */ @Override @@ -87,7 +86,7 @@ public Attribute merge(Attribute entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#remove(java.lang.Object) */ @Override @@ -97,12 +96,11 @@ public boolean remove(Attribute entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#removeById(java.io.Serializable) */ @Override public boolean removeById(Long id) { return super.removeById(id); } - } diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/BaseDAO.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/BaseDAO.java index b66aee09..8be995da 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/BaseDAO.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/BaseDAO.java @@ -21,20 +21,16 @@ import com.googlecode.genericdao.dao.jpa.GenericDAOImpl; import com.googlecode.genericdao.search.jpa.JPASearchProcessor; - import java.io.Serializable; - import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; - import org.springframework.stereotype.Repository; /** - * * Class BaseDAO. - * - * The base DAO furnish a set of methods usually used - * + * + *

The base DAO furnish a set of methods usually used + * * @author Tobia Di Pisa (tobia.dipisa@geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ @@ -48,7 +44,7 @@ public class BaseDAO extends GenericDAOImpl { /** * EntityManager setting - * + * * @param entityManager the entity manager to set */ @Override @@ -59,7 +55,7 @@ public void setEntityManager(EntityManager entityManager) { /** * JPASearchProcessor setting - * + * * @param searchProcessor the search processor to set */ @Override @@ -70,7 +66,7 @@ public void setSearchProcessor(JPASearchProcessor searchProcessor) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.JPABaseDAO#em() */ @Override diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/CategoryDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/CategoryDAOImpl.java index e1fad2ff..bfa2a1ab 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/CategoryDAOImpl.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/CategoryDAOImpl.java @@ -22,30 +22,28 @@ import com.googlecode.genericdao.search.Filter; import com.googlecode.genericdao.search.ISearch; import com.googlecode.genericdao.search.Search; - -import java.util.List; - import it.geosolutions.geostore.core.dao.CategoryDAO; import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.core.model.SecurityRule; - -import org.apache.log4j.Logger; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.transaction.annotation.Transactional; /** * Class CategoryDAOImpl. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ @Transactional(value = "geostoreTransactionManager") public class CategoryDAOImpl extends BaseDAO implements CategoryDAO { - private static final Logger LOGGER = Logger.getLogger(CategoryDAOImpl.class); + private static final Logger LOGGER = LogManager.getLogger(CategoryDAOImpl.class); /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#persist(T[]) */ @Override @@ -59,7 +57,7 @@ public void persist(Category... entities) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#findAll() */ @Override @@ -69,7 +67,7 @@ public List findAll() { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#search(com.trg.search.ISearch) */ @SuppressWarnings("unchecked") @@ -80,7 +78,7 @@ public List search(ISearch search) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#merge(java.lang.Object) */ @Override @@ -90,7 +88,7 @@ public Category merge(Category entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#remove(java.lang.Object) */ @Override @@ -100,7 +98,7 @@ public boolean remove(Category entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#removeById(java.io.Serializable) */ @Override @@ -110,7 +108,7 @@ public boolean removeById(Long id) { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.core.dao.CategoryDAO#findUserSecurityRule(java.lang.String, long) */ @Override @@ -118,10 +116,12 @@ public List findUserSecurityRule(String userName, long categoryId) Search searchCriteria = new Search(Category.class); searchCriteria.addField("security"); - Filter securityFilter = Filter.some( - "security", - Filter.and(Filter.equal("category.id", categoryId), - Filter.equal("user.name", userName))); + Filter securityFilter = + Filter.some( + "security", + Filter.and( + Filter.equal("category.id", categoryId), + Filter.equal("user.name", userName))); searchCriteria.addFilter(securityFilter); return super.search(searchCriteria); @@ -135,13 +135,14 @@ public List findGroupSecurityRule(List userGroups, long ca Search searchCriteria = new Search(Category.class); searchCriteria.addField("security"); - Filter securityFilter = Filter.some( - "security", - Filter.and(Filter.equal("category.id", categoryId), - Filter.equal("user.groups.groupName", userGroups))); + Filter securityFilter = + Filter.some( + "security", + Filter.and( + Filter.equal("category.id", categoryId), + Filter.equal("user.groups.groupName", userGroups))); searchCriteria.addFilter(securityFilter); return super.search(searchCriteria); } - } diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/ExternalSecurityDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/ExternalSecurityDAOImpl.java index a72ec97c..25fe1f65 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/ExternalSecurityDAOImpl.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/ExternalSecurityDAOImpl.java @@ -19,8 +19,6 @@ */ package it.geosolutions.geostore.core.dao.impl; -import java.util.ArrayList; -import java.util.List; import com.googlecode.genericdao.search.Filter; import com.googlecode.genericdao.search.ISearch; import com.googlecode.genericdao.search.Search; @@ -28,16 +26,16 @@ import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.core.model.enums.Role; +import java.util.ArrayList; +import java.util.List; /** - * Alternative implementation of SecurityDAO that uses names for users and groups instead of - * loading data from the database. - * To be activated as an alias when external authentication is enabled: - * - * - * - * @author mauro.bartolomeoli@geo-solutions.it + * Alternative implementation of SecurityDAO that uses names for users and groups instead of loading + * data from the database. To be activated as an alias when external authentication is enabled: + * + *

* + * @author mauro.bartolomeoli@geo-solutions.it */ public class ExternalSecurityDAOImpl extends SecurityDAOImpl { @@ -56,9 +54,9 @@ public List findAll() { } /** - * Returns a new list populating User object from username and UserGroup object from groupname so - * that using external users is transparent for higher levels. - * + * Returns a new list populating User object from username and UserGroup object from groupname + * so that using external users is transparent for higher levels. + * * @param rules input rules * @return rules with populated user objects */ @@ -96,9 +94,9 @@ private List fillFromNames(List rules) { } /** - * Extracts username and groupname from DTO objets, so that security is persisted - * with the names instead of the object. - * + * Extracts username and groupname from DTO objets, so that security is persisted with the names + * instead of the object. + * * @param rules input rules * @return */ @@ -129,36 +127,28 @@ private SecurityRule[] extractNames(SecurityRule[] rules) { public List search(ISearch search) { return fillFromNames(super.search(search)); } - - /** - * Add security filtering in order to filter out resources the user has not read access to - */ + + /** Add security filtering in order to filter out resources the user has not read access to */ @Override - public void addReadSecurityConstraints(Search searchCriteria, User user) - { + public void addReadSecurityConstraints(Search searchCriteria, User user) { // no further constraints for admin user - if(user.getRole() == Role.ADMIN) { + if (user.getRole() == Role.ADMIN) { return; } Filter userFiltering = Filter.equal("username", user.getName()); - if(! user.getGroups().isEmpty()) { + if (user.getGroups() != null && !user.getGroups().isEmpty()) { List groupsName = new ArrayList<>(); for (UserGroup group : user.getGroups()) { groupsName.add(group.getGroupName()); } - - userFiltering = Filter.or( userFiltering, Filter.in("groupname", groupsName)); + + userFiltering = Filter.or(userFiltering, Filter.in("groupname", groupsName)); } - Filter securityFilter = Filter.some( - "security", - Filter.and( - Filter.equal("canRead", true), - userFiltering - ) - ); + Filter securityFilter = + Filter.some("security", Filter.and(Filter.equal("canRead", true), userFiltering)); searchCriteria.addFilter(securityFilter); } @@ -172,17 +162,18 @@ public List findUserSecurityRule(String userName, long resourceId) Search searchCriteria = new Search(SecurityRule.class); Filter securityFilter = - Filter.and(Filter.equal("resource.id", resourceId), + Filter.and( + Filter.equal("resource.id", resourceId), Filter.equal("username", userName)); searchCriteria.addFilter(securityFilter); - // now rules are not properly filtered. - // so no user rules have to be removed externally (see RESTServiceImpl > ResourceServiceImpl) - // TODO: apply same worakaround of findGroupSecurityRule or fix searchCriteria issue (when this unit is well tested). + // now rules are not properly filtered. + // so no user rules have to be removed externally (see RESTServiceImpl > + // ResourceServiceImpl) + // TODO: apply same worakaround of findGroupSecurityRule or fix searchCriteria issue (when + // this unit is well tested). return fillFromNames(super.search(searchCriteria)); } - - @Override public List findSecurityRules(long resourceId) { return fillFromNames(super.findSecurityRules(resourceId)); @@ -194,10 +185,10 @@ public List findSecurityRules(long resourceId) { @Override public List findGroupSecurityRule(List groupNames, long resourceId) { List rules = findSecurityRules(resourceId); - //WORKAROUND + // WORKAROUND List filteredRules = new ArrayList(); - for(SecurityRule sr : rules){ - if(sr.getGroupname() != null && groupNames.contains(sr.getGroupname())){ + for (SecurityRule sr : rules) { + if (sr.getGroupname() != null && groupNames.contains(sr.getGroupname())) { filteredRules.add(sr); } } diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/ResourceDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/ResourceDAOImpl.java index 90a9af04..85c3f2a4 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/ResourceDAOImpl.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/ResourceDAOImpl.java @@ -28,42 +28,34 @@ package it.geosolutions.geostore.core.dao.impl; +import com.googlecode.genericdao.search.Filter; +import com.googlecode.genericdao.search.ISearch; +import com.googlecode.genericdao.search.Search; import it.geosolutions.geostore.core.dao.ResourceDAO; import it.geosolutions.geostore.core.model.Attribute; import it.geosolutions.geostore.core.model.Resource; -import it.geosolutions.geostore.core.model.SecurityRule; -import it.geosolutions.geostore.core.model.User; -import it.geosolutions.geostore.core.model.UserGroup; - -import java.util.ArrayList; import java.util.Date; import java.util.List; - import javax.persistence.NoResultException; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.hibernate.Hibernate; import org.springframework.transaction.annotation.Transactional; -import com.googlecode.genericdao.search.Filter; -import com.googlecode.genericdao.search.ISearch; -import com.googlecode.genericdao.search.Search; -import it.geosolutions.geostore.core.model.enums.Role; - /** * Class ResourceDAOImpl. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ @Transactional(value = "geostoreTransactionManager") public class ResourceDAOImpl extends BaseDAO implements ResourceDAO { - private static final Logger LOGGER = Logger.getLogger(ResourceDAOImpl.class); + private static final Logger LOGGER = LogManager.getLogger(ResourceDAOImpl.class); /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#persist(T[]) */ @Override @@ -84,7 +76,7 @@ public void persist(Resource... entities) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#findAll() */ @Override @@ -94,7 +86,7 @@ public List findAll() { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#find(java.io.Serializable) */ @Override @@ -120,7 +112,7 @@ public List findAttributes(long resourceId) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#search(com.trg.search.ISearch) */ @SuppressWarnings("unchecked") @@ -131,7 +123,7 @@ public List search(ISearch search) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#merge(java.lang.Object) */ @Override @@ -143,7 +135,7 @@ public Resource merge(Resource entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#remove(java.lang.Object) */ @Override @@ -153,7 +145,7 @@ public boolean remove(Resource entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#remove(java.lang.Object) */ @Override @@ -164,7 +156,7 @@ public void removeResources(ISearch search) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#removeById(java.io.Serializable) */ @Override @@ -172,19 +164,18 @@ public boolean removeById(Long id) { return super.removeById(id); } - /* (non-Javadoc) * @see it.geosolutions.geostore.core.dao.ResourceDAO#findResources(java.util.List) */ @Override public List findResources(List resourcesIds) { Search search = new Search(Resource.class); - Filter filter = Filter.in("id", resourcesIds); + Filter filter = Filter.in("id", resourcesIds); search.addFilter(filter); List resourceToSet = super.search(search); - //Initialize Lazy collection - for(Resource resource : resourceToSet){ - if(!Hibernate.isInitialized(resource.getSecurity())){ + // Initialize Lazy collection + for (Resource resource : resourceToSet) { + if (!Hibernate.isInitialized(resource.getSecurity())) { Hibernate.initialize(resource.getSecurity()); } } @@ -195,8 +186,7 @@ public List findResources(List resourcesIds) { * @see it.geosolutions.geostore.core.dao.ResourceDAO#findByName(java.lang.String) */ @Override - public Resource findByName(String resourceName) - { + public Resource findByName(String resourceName) { Search searchCriteria = new Search(Resource.class); Filter filter = Filter.equal("name", resourceName); searchCriteria.addFilter(filter); @@ -206,7 +196,8 @@ public Resource findByName(String resourceName) try { foundResource = super.searchUnique(searchCriteria); } catch (NoResultException ex) { - // I ignore the exception and return null on purpose, mimicking the behavior of ResourceDAO#find(java.lang.Long) + // I ignore the exception and return null on purpose, mimicking the behavior of + // ResourceDAO#find(java.lang.Long) foundResource = null; } @@ -214,8 +205,7 @@ public Resource findByName(String resourceName) } @Override - public List findResourceNamesMatchingPattern(String pattern) - { + public List findResourceNamesMatchingPattern(String pattern) { Search searchCriteria = new Search(Resource.class); searchCriteria.addField("name"); searchCriteria.addFilterLike("name", pattern); @@ -225,5 +215,4 @@ public List findResourceNamesMatchingPattern(String pattern) return resourceNames; } - } diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/SecurityDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/SecurityDAOImpl.java index 63b3c3a1..c3d969df 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/SecurityDAOImpl.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/SecurityDAOImpl.java @@ -19,35 +19,41 @@ */ package it.geosolutions.geostore.core.dao.impl; -import java.util.ArrayList; -import java.util.List; -import org.apache.log4j.Logger; -import org.springframework.transaction.annotation.Transactional; import com.googlecode.genericdao.search.Filter; import com.googlecode.genericdao.search.ISearch; import com.googlecode.genericdao.search.Search; +import it.geosolutions.geostore.core.dao.ResourceDAO; import it.geosolutions.geostore.core.dao.SecurityDAO; import it.geosolutions.geostore.core.dao.UserGroupDAO; +import it.geosolutions.geostore.core.model.Resource; import it.geosolutions.geostore.core.model.SecurityRule; import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.core.model.enums.Role; +import java.util.ArrayList; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.transaction.annotation.Transactional; /** * Class SecurityDAOImpl. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ @Transactional(value = "geostoreTransactionManager") public class SecurityDAOImpl extends BaseDAO implements SecurityDAO { - private static final Logger LOGGER = Logger.getLogger(SecurityDAOImpl.class); + private static final Logger LOGGER = LogManager.getLogger(SecurityDAOImpl.class); private UserGroupDAO userGroupDAO; + + private ResourceDAO resourceDAO; + /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#persist(T[]) */ @Override @@ -57,10 +63,11 @@ public void persist(SecurityRule... entities) { } for (SecurityRule rule : entities) { validateGroup(rule); + validateCreatorAndEditor(rule); } super.persist(entities); } - + protected void validateGroup(SecurityRule rule) throws InternalError { if (rule.getGroup() != null) { UserGroup ug = userGroupDAO.find(rule.getGroup().getId()); @@ -71,9 +78,29 @@ protected void validateGroup(SecurityRule rule) throws InternalError { } } + private void validateCreatorAndEditor(SecurityRule rule) { + if (rule.getResource() != null && (rule.getUser() != null || rule.getUsername() != null)) { + Resource resource = rule.getResource(); + boolean updated = false; + if (resource.getCreator() == null) { + resource.setCreator( + rule.getUser() != null ? rule.getUser().getName() : rule.getUsername()); + updated = true; + } + if (rule.getUser() != null || !rule.getUsername().isEmpty()) { + resource.setEditor( + rule.getUser() != null ? rule.getUser().getName() : rule.getUsername()); + updated = true; + } + if (updated) { + resourceDAO.merge(resource); + } + } + } + /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#findAll() */ @Override @@ -83,7 +110,7 @@ public List findAll() { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#search(com.trg.search.ISearch) */ @SuppressWarnings("unchecked") @@ -94,7 +121,7 @@ public List search(ISearch search) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#merge(java.lang.Object) */ @Override @@ -104,7 +131,7 @@ public SecurityRule merge(SecurityRule entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#remove(java.lang.Object) */ @Override @@ -114,42 +141,70 @@ public boolean remove(SecurityRule entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#removeById(java.io.Serializable) */ @Override public boolean removeById(Long id) { return super.removeById(id); } - - /** - * Add security filtering in order to filter out resources the user has not read access to - */ - public void addReadSecurityConstraints(Search searchCriteria, User user) - { + + /** Add security filtering in order to filter out resources the user has not read access to */ + public void addReadSecurityConstraints(Search searchCriteria, User user) { // no further constraints for admin user - if(user.getRole() == Role.ADMIN) { + if (user.getRole() == Role.ADMIN) { return; } + // User filtering based on user and groups Filter userFiltering = Filter.equal("user.name", user.getName()); - if(! user.getGroups().isEmpty()) { + if (user.getGroups() != null && !user.getGroups().isEmpty()) { List groupsId = new ArrayList<>(); for (UserGroup group : user.getGroups()) { groupsId.add(group.getId()); } - - userFiltering = Filter.or( userFiltering, Filter.in("group.id", groupsId)); + + userFiltering = Filter.or(userFiltering, Filter.in("group.id", groupsId)); } - Filter securityFilter = Filter.some( - "security", - Filter.and( - Filter.equal("canRead", true), - userFiltering - ) - ); + Filter securityFilter = + Filter.some("security", Filter.and(Filter.equal("canRead", true), userFiltering)); + + searchCriteria.addFilter(securityFilter); + } + + /** Add security filtering in order to filter out resources hidden the user */ + public void addAdvertisedSecurityConstraints(Search searchCriteria, User user) { + // no further constraints for admin user + if (user.getRole() == Role.ADMIN) { + return; + } + + // User filtering based on user and groups + Filter userFiltering = Filter.equal("user.name", user.getName()); + + // Combine owner and advertisedFilter using OR + /** The user is the owner of the resource or the resource is advertised. */ + Filter advertisedFiltering = + Filter.or( + Filter.equal("user.name", user.getName()), + Filter.equal("resource.advertised", true)); + + if (user.getGroups() != null && !user.getGroups().isEmpty()) { + List groupsId = new ArrayList<>(); + for (UserGroup group : user.getGroups()) { + groupsId.add(group.getId()); + } + + userFiltering = + Filter.and( + advertisedFiltering, + Filter.or(userFiltering, Filter.in("group.id", groupsId))); + } + + Filter securityFilter = + Filter.some("security", Filter.and(Filter.equal("canRead", true), userFiltering)); searchCriteria.addFilter(securityFilter); } @@ -163,13 +218,16 @@ public void addReadSecurityConstraints(Search searchCriteria, User user) public List findUserSecurityRule(String userName, long resourceId) { Search searchCriteria = new Search(SecurityRule.class); - Filter securityFilter = - Filter.and(Filter.equal("resource.id", resourceId), + Filter securityFilter = + Filter.and( + Filter.equal("resource.id", resourceId), Filter.equal("user.name", userName)); searchCriteria.addFilter(securityFilter); - // now rules are not properly filtered. - // so no user rules have to be removed externally (see RESTServiceImpl > ResourceServiceImpl) - // TODO: apply same worakaround of findGroupSecurityRule or fix searchCriteria issue (when this unit is well tested). + // now rules are not properly filtered. + // so no user rules have to be removed externally (see RESTServiceImpl > + // ResourceServiceImpl) + // TODO: apply same workaround of findGroupSecurityRule or fix searchCriteria issue (when + // this unit is well tested). return super.search(searchCriteria); } @@ -187,17 +245,17 @@ public List findSecurityRules(long resourceId) { return super.search(searchCriteria); } - + /* (non-Javadoc) * @see it.geosolutions.geostore.core.dao.ResourceDAO#findGroupSecurityRule(java.lang.String, long) */ @Override public List findGroupSecurityRule(List groupNames, long resourceId) { List rules = findSecurityRules(resourceId); - //WORKAROUND + // WORKAROUND List filteredRules = new ArrayList(); - for(SecurityRule sr : rules){ - if(sr.getGroup() != null && groupNames.contains(sr.getGroup().getGroupName())){ + for (SecurityRule sr : rules) { + if (sr.getGroup() != null && groupNames.contains(sr.getGroup().getGroupName())) { filteredRules.add(sr); } } @@ -211,6 +269,12 @@ public UserGroupDAO getUserGroupDAO() { public void setUserGroupDAO(UserGroupDAO userGroupDAO) { this.userGroupDAO = userGroupDAO; } - - + + public ResourceDAO getResourceDAO() { + return resourceDAO; + } + + public void setResourceDAO(ResourceDAO resourceDAO) { + this.resourceDAO = resourceDAO; + } } diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/StoredDataDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/StoredDataDAOImpl.java index f5e97d11..34bca0e1 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/StoredDataDAOImpl.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/StoredDataDAOImpl.java @@ -19,30 +19,24 @@ */ package it.geosolutions.geostore.core.dao.impl; -import com.googlecode.genericdao.search.Filter; import com.googlecode.genericdao.search.ISearch; -import com.googlecode.genericdao.search.Search; - -import java.util.ArrayList; -import java.util.List; - import it.geosolutions.geostore.core.dao.StoredDataDAO; -import it.geosolutions.geostore.core.model.SecurityRule; import it.geosolutions.geostore.core.model.StoredData; - -import org.apache.log4j.Logger; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.transaction.annotation.Transactional; /** * Class StoredDataDAOImpl. - * + * * @author Emanuele Tajariol (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ @Transactional(value = "geostoreTransactionManager") public class StoredDataDAOImpl extends BaseDAO implements StoredDataDAO { - private static final Logger LOGGER = Logger.getLogger(StoredDataDAOImpl.class); + private static final Logger LOGGER = LogManager.getLogger(StoredDataDAOImpl.class); @Override public void persist(StoredData... entities) { diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/UserAttributeDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/UserAttributeDAOImpl.java index 22f10095..0c1efa69 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/UserAttributeDAOImpl.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/UserAttributeDAOImpl.java @@ -20,29 +20,27 @@ package it.geosolutions.geostore.core.dao.impl; import com.googlecode.genericdao.search.ISearch; - -import java.util.List; - import it.geosolutions.geostore.core.dao.UserAttributeDAO; import it.geosolutions.geostore.core.model.UserAttribute; - -import org.apache.log4j.Logger; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.transaction.annotation.Transactional; /** * Class UserAttributeDAOImpl. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ @Transactional(value = "geostoreTransactionManager") public class UserAttributeDAOImpl extends BaseDAO implements UserAttributeDAO { - private static final Logger LOGGER = Logger.getLogger(UserAttributeDAOImpl.class); + private static final Logger LOGGER = LogManager.getLogger(UserAttributeDAOImpl.class); /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#persist(T[]) */ @Override @@ -56,7 +54,7 @@ public void persist(UserAttribute... entities) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#findAll() */ @Override @@ -66,7 +64,7 @@ public List findAll() { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#search(com.trg.search.ISearch) */ @SuppressWarnings("unchecked") @@ -77,7 +75,7 @@ public List search(ISearch search) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#merge(java.lang.Object) */ @Override @@ -87,7 +85,7 @@ public UserAttribute merge(UserAttribute entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#remove(java.lang.Object) */ @Override @@ -97,12 +95,11 @@ public boolean remove(UserAttribute entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#removeById(java.io.Serializable) */ @Override public boolean removeById(Long id) { return super.removeById(id); } - } diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/UserDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/UserDAOImpl.java index 4ae7060b..0aed5e90 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/UserDAOImpl.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/UserDAOImpl.java @@ -20,34 +20,32 @@ package it.geosolutions.geostore.core.dao.impl; import com.googlecode.genericdao.search.ISearch; - -import java.util.List; -import java.util.Set; - import it.geosolutions.geostore.core.dao.UserDAO; import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserAttribute; import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.core.security.password.PwEncoder; - -import org.apache.log4j.Logger; +import java.util.List; +import java.util.Set; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.hibernate.Hibernate; import org.springframework.transaction.annotation.Transactional; /** * Class UserDAOImpl. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ @Transactional(value = "geostoreTransactionManager") public class UserDAOImpl extends BaseDAO implements UserDAO { - private static final Logger LOGGER = Logger.getLogger(UserDAOImpl.class); + private static final Logger LOGGER = LogManager.getLogger(UserDAOImpl.class); /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#persist(T[]) */ @Override @@ -69,7 +67,7 @@ public void persist(User... entities) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#findAll() */ @Override @@ -79,7 +77,7 @@ public List findAll() { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#search(com.trg.search.ISearch) */ @SuppressWarnings("unchecked") @@ -90,7 +88,7 @@ public List search(ISearch search) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#merge(java.lang.Object) */ @Override @@ -106,7 +104,7 @@ public User merge(User entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#remove(java.lang.Object) */ @Override @@ -116,7 +114,7 @@ public boolean remove(User entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#removeById(java.io.Serializable) */ @Override @@ -126,7 +124,7 @@ public boolean removeById(Long id) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#find(java.io.Serializable) */ @Override @@ -150,7 +148,7 @@ public User find(Long id) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#save(T[]) */ @Override @@ -169,5 +167,4 @@ public User[] save(User... entities) { return super.save(entities); } - } diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/UserGroupAttributeDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/UserGroupAttributeDAOImpl.java new file mode 100644 index 00000000..6b97ea48 --- /dev/null +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/UserGroupAttributeDAOImpl.java @@ -0,0 +1,81 @@ +package it.geosolutions.geostore.core.dao.impl; + +import com.googlecode.genericdao.search.ISearch; +import it.geosolutions.geostore.core.dao.UserGroupAttributeDAO; +import it.geosolutions.geostore.core.model.UserGroupAttribute; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(value = "geostoreTransactionManager") +public class UserGroupAttributeDAOImpl extends BaseDAO + implements UserGroupAttributeDAO { + + private static final Logger LOGGER = LogManager.getLogger(UserGroupAttributeDAOImpl.class); + + /* + * (non-Javadoc) + * + * @see com.trg.dao.jpa.GenericDAOImpl#persist(T[]) + */ + @Override + public void persist(UserGroupAttribute... entities) { + if (LOGGER.isDebugEnabled()) { + LOGGER.info("Inserting new entities for UserGroupAttribute ... "); + } + + super.persist(entities); + } + + /* + * (non-Javadoc) + * + * @see com.trg.dao.jpa.GenericDAOImpl#findAll() + */ + @Override + public List findAll() { + return super.findAll(); + } + + /* + * (non-Javadoc) + * + * @see com.trg.dao.jpa.GenericDAOImpl#search(com.trg.search.ISearch) + */ + @SuppressWarnings("unchecked") + @Override + public List search(ISearch search) { + return super.search(search); + } + + /* + * (non-Javadoc) + * + * @see com.trg.dao.jpa.GenericDAOImpl#merge(java.lang.Object) + */ + @Override + public UserGroupAttribute merge(UserGroupAttribute entity) { + return super.merge(entity); + } + + /* + * (non-Javadoc) + * + * @see com.trg.dao.jpa.GenericDAOImpl#remove(java.lang.Object) + */ + @Override + public boolean remove(UserGroupAttribute entity) { + return super.remove(entity); + } + + /* + * (non-Javadoc) + * + * @see com.trg.dao.jpa.GenericDAOImpl#removeById(java.io.Serializable) + */ + @Override + public boolean removeById(Long id) { + return super.removeById(id); + } +} diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/UserGroupDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/UserGroupDAOImpl.java index 59232982..0a576b94 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/UserGroupDAOImpl.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/impl/UserGroupDAOImpl.java @@ -19,30 +19,32 @@ */ package it.geosolutions.geostore.core.dao.impl; +import com.googlecode.genericdao.search.ISearch; +import com.googlecode.genericdao.search.Search; import it.geosolutions.geostore.core.dao.UserGroupDAO; +import it.geosolutions.geostore.core.model.SecurityRule; import it.geosolutions.geostore.core.model.UserGroup; - +import it.geosolutions.geostore.core.model.UserGroupAttribute; import java.util.List; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.hibernate.Hibernate; import org.springframework.transaction.annotation.Transactional; -import com.googlecode.genericdao.search.ISearch; - /** * Class UserGroupDAOImpl. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ @Transactional(value = "geostoreTransactionManager") public class UserGroupDAOImpl extends BaseDAO implements UserGroupDAO { - private static final Logger LOGGER = Logger.getLogger(UserGroupDAOImpl.class); + private static final Logger LOGGER = LogManager.getLogger(UserGroupDAOImpl.class); /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#persist(T[]) */ @Override @@ -56,27 +58,53 @@ public void persist(UserGroup... entities) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#findAll() */ @Override public List findAll() { return super.findAll(); } - + /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#find(java.io.Serializable) */ @Override public UserGroup find(Long id) { - return super.find(id); + UserGroup group = super.find(id); + if (group != null) { + initializeLazyMembers(group); + } + return group; + } + + private void initializeLazyMembers(UserGroup group) { + if (Hibernate.isInitialized(group)) { + List attributes = group.getAttributes(); + Hibernate.initialize(attributes); + List secRules = group.getSecurity(); + Hibernate.initialize(secRules); + } + } + + @Override + public UserGroup findByName(String name) { + Search searchCriteria = new Search(UserGroup.class); + searchCriteria.addFilterEqual("groupName", name); + UserGroup result = null; + List existingGroups = search(searchCriteria); + if (existingGroups.size() > 0) { + result = existingGroups.get(0); + initializeLazyMembers(result); + } + return result; } /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#search(com.trg.search.ISearch) */ @SuppressWarnings("unchecked") @@ -87,7 +115,7 @@ public List search(ISearch search) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#merge(java.lang.Object) */ @Override @@ -97,7 +125,7 @@ public UserGroup merge(UserGroup entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#remove(java.lang.Object) */ @Override @@ -107,12 +135,11 @@ public boolean remove(UserGroup entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#removeById(java.io.Serializable) */ @Override public boolean removeById(Long id) { return super.removeById(id); } - } diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/LdapBaseDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/LdapBaseDAOImpl.java index 2c4b7181..2765488d 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/LdapBaseDAOImpl.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/LdapBaseDAOImpl.java @@ -20,12 +20,12 @@ package it.geosolutions.geostore.core.dao.ldap.impl; +import com.googlecode.genericdao.search.Filter; +import com.googlecode.genericdao.search.ISearch; import java.util.ArrayList; import java.util.List; import java.util.Map; - import javax.naming.directory.DirContext; - import org.apache.commons.lang.StringUtils; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; @@ -35,17 +35,13 @@ import org.springframework.ldap.core.DirContextProcessor; import org.springframework.ldap.core.LdapTemplate; -import com.googlecode.genericdao.search.Filter; -import com.googlecode.genericdao.search.ISearch; - /** - * Class LdapBaseDAOImpl. - * Base class for LDAP (read-only) based DAOs. - * + * Class LdapBaseDAOImpl. Base class for LDAP (read-only) based DAOs. + * * @author Mauro Bartolomeoli (mauro.bartolomeoli at geo-solutions.it) */ public abstract class LdapBaseDAOImpl { - + public static final class NullDirContextProcessor implements DirContextProcessor { public void postProcess(DirContext ctx) { // Do nothing @@ -55,28 +51,28 @@ public void preProcess(DirContext ctx) { // Do nothing } } - + protected String searchBase = ""; protected String baseFilter = "cn=*"; - protected String nameAttribute = "cn"; - protected String descriptionAttribute = "description"; + protected String nameAttribute = "cn"; + protected String descriptionAttribute = "description"; protected boolean sortEnabled = false; - + protected ContextSource contextSource; protected LdapTemplate template; - + public LdapBaseDAOImpl(ContextSource contextSource) { this.contextSource = contextSource; template = new LdapTemplate(contextSource); } - + public String getSearchBase() { return searchBase; } /** * LDAP root for all searches. - * + * * @param searchBase */ public void setSearchBase(String searchBase) { @@ -89,7 +85,7 @@ public String getBaseFilter() { /** * Filter applied to all searches (eventually combined with a more specific filter). - * + * * @param filter */ public void setBaseFilter(String filter) { @@ -102,7 +98,7 @@ public String getNameAttribute() { /** * Attribute to be mapped to the GeoStore object name. - * + * * @param nameAttribute */ public void setNameAttribute(String nameAttribute) { @@ -115,17 +111,16 @@ public String getDescriptionAttribute() { /** * Attribute to be mapped to the GeoStore object description. - * + * * @param nameAttribute */ public void setDescriptionAttribute(String descriptionAttribute) { this.descriptionAttribute = descriptionAttribute; } - + /** - * Builds a proper processor for the given search. - * Implements sorting if enabled. - * + * Builds a proper processor for the given search. Implements sorting if enabled. + * * @param search * @return */ @@ -135,14 +130,14 @@ protected DirContextProcessor getProcessorForSearch(ISearch search) { } return new NullDirContextProcessor(); } - + public boolean isSortEnabled() { return sortEnabled; } /** * Enables LDAP-side sorting (to be enabled if supported by the LDAP server). - * + * * @param sortEnabled */ public void setSortEnabled(boolean sortEnabled) { @@ -150,9 +145,9 @@ public void setSortEnabled(boolean sortEnabled) { } /** - * Returns a combined filter (AND) from the given two. - * If any is empty, the other filter is returned. - * + * Returns a combined filter (AND) from the given two. If any is empty, the other filter is + * returned. + * * @param baseFilter * @param ldapFilter * @return @@ -164,12 +159,12 @@ protected String combineFilters(String baseFilter, String ldapFilter) { if ("".equals(ldapFilter)) { return baseFilter; } - return "(& ("+baseFilter+") ("+ldapFilter+"))"; + return "(& (" + baseFilter + ") (" + ldapFilter + "))"; } /** * Creates an LDAP filter for a GenericDAO search. - * + * * @param search * @return */ @@ -184,10 +179,9 @@ protected String getLdapFilter(ISearch search, Map propertyMappe return currentFilter; } - /** - * Creates an LDAP filter for a GenericDAO filter - * . + * Creates an LDAP filter for a GenericDAO filter . + * * @param filter * @return */ @@ -196,45 +190,45 @@ private String getLdapFilter(Filter filter, Map propertyMapper) Map mapper = propertyMapper; if (propertyMapper.containsKey(property)) { if (propertyMapper.get(property) instanceof String) { - property = (String)propertyMapper.get(property); + property = (String) propertyMapper.get(property); } else if (propertyMapper.get(property) instanceof Map) { - mapper = (Map)propertyMapper.get(property); + mapper = (Map) propertyMapper.get(property); } } // we support the minimum set of operators used by GeoStore user and group services - switch(filter.getOperator()) { + switch (filter.getOperator()) { case Filter.OP_EQUAL: return property + "=" + filter.getValue().toString(); case Filter.OP_SOME: - return getLdapFilter((Filter)filter.getValue(), mapper); + return getLdapFilter((Filter) filter.getValue(), mapper); case Filter.OP_ILIKE: return property + "=" + filter.getValue().toString().replaceAll("[%]", "*"); case Filter.OP_IN: - return getInLdapFilter(property, (List)filter.getValue()); - //TODO: implement all operators + return getInLdapFilter(property, (List) filter.getValue()); + // TODO: implement all operators } return ""; } - + /** - * Builds a filter for property in (values) search type. - * This is done by creating a list of property=value combined by or (|). - * + * Builds a filter for property in (values) search type. This is done by creating a list of + * property=value combined by or (|). + * * @param property * @param values * @return */ private String getInLdapFilter(String property, List values) { - List filters = new ArrayList(); - for(Object value : values) { - filters.add("(" + property + "=" + value.toString() + ")"); - } - return StringUtils.join(filters, "|"); - } + List filters = new ArrayList(); + for (Object value : values) { + filters.add("(" + property + "=" + value.toString() + ")"); + } + return StringUtils.join(filters, "|"); + } - /** + /** * Returns true if the given search has one or more filters on a nested object. - * + * * @param search * @return */ @@ -245,23 +239,24 @@ protected boolean isNested(ISearch search) { } return found; } - + /** - * Returns true if the given filter works on a nested object. - * - * @param filter - * @return - */ + * Returns true if the given filter works on a nested object. + * + * @param filter + * @return + */ private boolean isNested(Filter filter) { if (filter.getOperator() == Filter.OP_SOME || filter.getOperator() == Filter.OP_ALL) { return true; } return false; } - + /** - * If the given search has filters working on nested objects, - * replaces them with the nested filter. + * If the given search has filters working on nested objects, replaces them with the nested + * filter. + * * @param search * @return */ @@ -279,29 +274,29 @@ protected ISearch getNestedSearch(ISearch search) { search.getFilters().addAll(newFilters); return search; } - + /** * Returns the internal filter of a nested filter, null otherwise. - * + * * @param filter * @return */ protected Filter getNestedFilter(Filter filter) { if (filter.getOperator() == Filter.OP_SOME || filter.getOperator() == Filter.OP_ALL) { - return (Filter)filter.getValue(); + return (Filter) filter.getValue(); } return null; } /** * Creates an SpEL expression for a GenericDAO search. - * + * * @param search * @return */ protected Expression getSearchExpression(ISearch search) { String expression = ""; - for (Filter filter: search.getFilters()) { + for (Filter filter : search.getFilters()) { expression = combineExpressions(expression, getSearchExpression(filter)); } if ("".equals(expression)) { @@ -312,9 +307,9 @@ protected Expression getSearchExpression(ISearch search) { } /** - * Returns a combined expression (AND) from the given two. - * If any is empty, the other filter is returned. - * + * Returns a combined expression (AND) from the given two. If any is empty, the other filter is + * returned. + * * @param expression * @param searchExpression * @return @@ -326,7 +321,7 @@ protected String combineExpressions(String expression, String searchExpression) if ("".equals(searchExpression)) { return expression; } - return "("+expression+") && ("+searchExpression+")"; + return "(" + expression + ") && (" + searchExpression + ")"; } /** @@ -336,14 +331,16 @@ protected String combineExpressions(String expression, String searchExpression) * @return */ private String getSearchExpression(Filter filter) { - switch(filter.getOperator()) { + switch (filter.getOperator()) { case Filter.OP_EQUAL: - return filter.getProperty() + "=='" + filter.getValue().toString() +"'"; + return filter.getProperty() + "=='" + filter.getValue().toString() + "'"; case Filter.OP_ILIKE: - return filter.getProperty() + " matches '^" + filter.getValue().toString().replace("*", ".*") +"$'"; - //TODO: implement all operators + return filter.getProperty() + + " matches '^" + + filter.getValue().toString().replace("*", ".*") + + "$'"; + // TODO: implement all operators } return ""; } - } diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/UserDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/UserDAOImpl.java index b2b3adef..2b47f845 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/UserDAOImpl.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/UserDAOImpl.java @@ -19,6 +19,15 @@ */ package it.geosolutions.geostore.core.dao.ldap.impl; +import com.googlecode.genericdao.search.Filter; +import com.googlecode.genericdao.search.ISearch; +import com.googlecode.genericdao.search.Search; +import it.geosolutions.geostore.core.dao.UserDAO; +import it.geosolutions.geostore.core.dao.search.GeoStoreISearchWrapper; +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.UserAttribute; +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.enums.Role; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -26,68 +35,57 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; - import javax.naming.directory.SearchControls; - import org.springframework.ldap.core.ContextSource; import org.springframework.ldap.core.DirContextOperations; import org.springframework.ldap.core.DirContextProcessor; import org.springframework.ldap.core.support.AbstractContextMapper; -import com.googlecode.genericdao.search.Filter; -import com.googlecode.genericdao.search.ISearch; -import com.googlecode.genericdao.search.Search; - -import it.geosolutions.geostore.core.dao.UserDAO; -import it.geosolutions.geostore.core.model.User; -import it.geosolutions.geostore.core.model.UserAttribute; -import it.geosolutions.geostore.core.model.UserGroup; -import it.geosolutions.geostore.core.model.enums.Role; - /** - * Class UserDAOImpl. - * LDAP (read-only) implementation of UserDAO. - * Allows fetching User from an LDAP repository. - * + * Class UserDAOImpl. LDAP (read-only) implementation of UserDAO. Allows fetching User from an LDAP + * repository. + * * @author Mauro Bartolomeoli (mauro.bartolomeoli at geo-solutions.it) */ public class UserDAOImpl extends LdapBaseDAOImpl implements UserDAO { protected Map attributesMapper = new HashMap(); private Pattern memberPattern = Pattern.compile("^(.*)$"); private String adminRoleGroup = "ADMIN"; - + UserGroupDAOImpl userGroupDAO = null; - + public UserDAOImpl(ContextSource contextSource) { super(contextSource); } - + public void setUserGroupDAO(UserGroupDAOImpl userGroupDAO) { if (this.userGroupDAO == null) { this.userGroupDAO = userGroupDAO; userGroupDAO.setUserDAO(this); } } - + public String getAdminRoleGroup() { - return adminRoleGroup; - } + return adminRoleGroup; + } /** - * Case insensitive name of the group associated to the ADMIN role. - * This is used to assign ADMIN role to users belonging to a specific LDAP group. - * + * Case insensitive name of the group associated to the ADMIN role. This is used to assign ADMIN + * role to users belonging to a specific LDAP group. + * * @param adminRoleGroup ADMIN role group name (default to ADMIN) */ - public void setAdminRoleGroup(String adminRoleGroup) { - this.adminRoleGroup = adminRoleGroup; - } + public void setAdminRoleGroup(String adminRoleGroup) { + this.adminRoleGroup = adminRoleGroup; + } - /** + /** * Sets regular expression used to extract the member user name from a member LDAP attribute. - * The LDAP attribute can contain a DN, so this is useful to extract the real member name from it. - * - * e.g. ^(uid=[^,]+.*)$ extracts the uid fragment of a DN + * The LDAP attribute can contain a DN, so this is useful to extract the real member name from + * it. + * + *

e.g. ^(uid=[^,]+.*)$ extracts the uid fragment of a DN + * * @param memberPattern */ public void setMemberPattern(String memberPattern) { @@ -100,16 +98,16 @@ public Map getAttributesMapper() { /** * Mapping of LDAP attribute names to geostore attribute names. - * + * * @param attributesMapper */ public void setAttributesMapper(Map attributesMapper) { this.attributesMapper = attributesMapper; } - + /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#persist(T[]) */ @Override @@ -122,7 +120,7 @@ public void persist(User... entities) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#findAll() */ @Override @@ -132,7 +130,7 @@ public List findAll() { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#search(com.trg.search.ISearch) */ @SuppressWarnings("unchecked") @@ -141,23 +139,26 @@ public List search(ISearch search) { if (isNested(search)) { // users belonging to a group List users = new ArrayList(); - for(UserGroup group : userGroupDAO.search(getNestedSearch(search))) { + for (UserGroup group : userGroupDAO.search(getNestedSearch(search))) { users.addAll(group.getUsers()); } return users; } else { - return ldapSearch(combineFilters(baseFilter, getLdapFilter(search, getPropertyMapper())), getProcessorForSearch(search)); + return ldapSearch( + combineFilters(baseFilter, getLdapFilter(search, getPropertyMapper())), + getProcessorForSearch(search)); } } /** * Maps user properties to LDAP properties. + * * @return */ private Map getPropertyMapper() { Map mapper = new HashMap<>(); mapper.put("name", nameAttribute); - for(String ldap : attributesMapper.keySet()) { + for (String ldap : attributesMapper.keySet()) { mapper.put(attributesMapper.get(ldap), ldap); } // sub-mapper for groups properties @@ -170,24 +171,30 @@ private Map getPropertyMapper() { protected List ldapSearch(String filter, DirContextProcessor processor) { SearchControls controls = new SearchControls(); controls.setSearchScope(SearchControls.SUBTREE_SCOPE); - return template.search(searchBase, filter, controls, new AbstractContextMapper() { - int counter = 1; - @Override - protected User doMapFromContext(DirContextOperations ctx) { - User user = new User(); - user.setId((long)counter++); // TODO: optionally map an attribute to the id - user.setEnabled(true); - user.setName(ctx.getStringAttribute(nameAttribute)); - user.setAttribute(fetchAttributes(ctx)); - assignGroupsAndRole(ctx, user); - return user; - } - }, processor); + return template.search( + searchBase, + filter, + controls, + new AbstractContextMapper() { + int counter = 1; + + @Override + protected User doMapFromContext(DirContextOperations ctx) { + User user = new User(); + user.setId((long) counter++); // TODO: optionally map an attribute to the id + user.setEnabled(true); + user.setName(ctx.getStringAttribute(nameAttribute)); + user.setAttribute(fetchAttributes(ctx)); + assignGroupsAndRole(ctx, user); + return user; + } + }, + processor); } - + /** * Gets all the attributes defined in AttributeMapper. - * + * * @param ctx * @return */ @@ -203,13 +210,13 @@ private List fetchAttributes(DirContextOperations ctx) { } return attributes; } - + /** - * If UserGroupDAO is defined, fetches all the groups - * using a membership filter (member=) on groups. - * - * Assigns the ADMIN role to users belonging to the adminRoleGroup group. - * + * If UserGroupDAO is defined, fetches all the groups using a membership filter + * (member=) on groups. + * + *

Assigns the ADMIN role to users belonging to the adminRoleGroup group. + * * @param ctx * @param user */ @@ -219,31 +226,32 @@ private void assignGroupsAndRole(DirContextOperations ctx, User user) { user.setRole(Role.USER); if (userGroupDAO != null) { Search searchCriteria = new Search(UserGroup.class); - searchCriteria.addFilterSome("user", - new Filter("name", ctx.getNameInNamespace(), Filter.OP_EQUAL)); - for (UserGroup ug : userGroupDAO.search(searchCriteria)) { + searchCriteria.addFilterSome( + "user", new Filter("name", ctx.getNameInNamespace(), Filter.OP_EQUAL)); + GeoStoreISearchWrapper searchWrapper = + new GeoStoreISearchWrapper(searchCriteria, this.getClass()); + for (UserGroup ug : userGroupDAO.search(searchWrapper)) { if (isAdminGroup(ug)) { user.setRole(Role.ADMIN); } user.getGroups().add(ug); } - } } - + /** * Returns true if the given group is the adminRoleGroup group. - * + * * @param ug * @return */ private boolean isAdminGroup(UserGroup ug) { - return ug.getGroupName().equalsIgnoreCase(adminRoleGroup); - } + return ug.getGroupName().equalsIgnoreCase(adminRoleGroup); + } /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#merge(java.lang.Object) */ @Override @@ -252,12 +260,12 @@ public User merge(User entity) { // some authentication providers try to persist stuff for synchronization // purposes and they don't know DAOs can be readonly // TODO: make readonly behaviour explicit - return entity; + return entity; } /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#remove(java.lang.Object) */ @Override @@ -271,7 +279,7 @@ public boolean remove(User entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#removeById(java.io.Serializable) */ @Override @@ -285,7 +293,7 @@ public boolean removeById(Long id) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#find(java.io.Serializable) */ @Override @@ -297,7 +305,7 @@ public User find(Long id) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#save(T[]) */ @Override @@ -313,7 +321,7 @@ public int count(ISearch search) { /** * Create a User object from a group member name. - * + * * @param member * @return */ @@ -327,7 +335,7 @@ public User createMemberUser(String member) { /** * Extracts a User name from a member name, using the memberPattern regexp. - * + * * @param member * @return */ @@ -338,5 +346,4 @@ private String getMemberName(String member) { } return member; } - } diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/UserGroupDAOImpl.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/UserGroupDAOImpl.java index 75f1232e..c7f1f343 100644 --- a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/UserGroupDAOImpl.java +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/ldap/impl/UserGroupDAOImpl.java @@ -19,6 +19,14 @@ */ package it.geosolutions.geostore.core.dao.ldap.impl; +import com.googlecode.genericdao.search.Filter; +import com.googlecode.genericdao.search.ISearch; +import com.googlecode.genericdao.search.Search; +import it.geosolutions.geostore.core.dao.UserDAO; +import it.geosolutions.geostore.core.dao.UserGroupDAO; +import it.geosolutions.geostore.core.dao.search.GeoStoreISearchWrapper; +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.enums.GroupReservedNames; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -29,46 +37,36 @@ import org.springframework.ldap.core.DirContextProcessor; import org.springframework.ldap.core.support.AbstractContextMapper; -import com.googlecode.genericdao.search.Filter; -import com.googlecode.genericdao.search.ISearch; -import it.geosolutions.geostore.core.dao.UserGroupDAO; -import it.geosolutions.geostore.core.model.UserGroup; -import it.geosolutions.geostore.core.model.enums.GroupReservedNames; - /** - * Class UserGroupDAOImpl. - * LDAP (read-only) implementation of UserGroupDAO. - * Allows fetching UserGroup from an LDAP repository. - * + * Class UserGroupDAOImpl. LDAP (read-only) implementation of UserGroupDAO. Allows fetching + * UserGroup from an LDAP repository. + * * @author Mauro Bartolomeoli (mauro.bartolomeoli at geo-solutions.it) */ -public class UserGroupDAOImpl extends LdapBaseDAOImpl implements UserGroupDAO { - +public class UserGroupDAOImpl extends LdapBaseDAOImpl implements UserGroupDAO { private boolean addEveryOneGroup = false; private String memberAttribute = "member"; private UserDAOImpl userDAO = null; - + public UserGroupDAOImpl(ContextSource contextSource) { super(contextSource); } - + public String getMemberAttribute() { return memberAttribute; } /** - * LDAP Attribute containing the list of members of a group. - * A multi-valued attribute. Each value should identify a user, either through its DN or simple name. - * + * LDAP Attribute containing the list of members of a group. A multi-valued attribute. Each + * value should identify a user, either through its DN or simple name. + * * @param memberAttribute */ public void setMemberAttribute(String memberAttribute) { this.memberAttribute = memberAttribute; } - - public void setUserDAO(UserDAOImpl userDAO) { if (this.userDAO == null) { this.userDAO = userDAO; @@ -82,7 +80,7 @@ public boolean isAddEveryOneGroup() { /** * Add everyOne group to search results. - * + * * @param addEveryOneGroup */ public void setAddEveryOneGroup(boolean addEveryOneGroup) { @@ -91,7 +89,7 @@ public void setAddEveryOneGroup(boolean addEveryOneGroup) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#persist(T[]) */ @Override @@ -101,20 +99,17 @@ public void persist(UserGroup... entities) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#findAll() */ @Override public List findAll() { - return addEveryOne( - ldapSearch(baseFilter, new NullDirContextProcessor()), - null - ); + return addEveryOne(ldapSearch(baseFilter, new NullDirContextProcessor(), null), null); } - + /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#find(java.io.Serializable) */ @Override @@ -124,9 +119,21 @@ public UserGroup find(Long id) { return null; } + @Override + public UserGroup findByName(String name) { + Search searchCriteria = new Search(UserGroup.class); + searchCriteria.addFilterEqual(nameAttribute, name); + UserGroup result = null; + List existingGroups = search(searchCriteria); + if (existingGroups.size() > 0) { + result = existingGroups.get(0); + } + return result; + } + /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#search(com.trg.search.ISearch) */ @SuppressWarnings("unchecked") @@ -141,13 +148,14 @@ public List search(ISearch search) { filter = getLdapFilter(search, getPropertyMapper()); } return addEveryOne( - ldapSearch(combineFilters(baseFilter, filter), getProcessorForSearch(search)), - search - ); + ldapSearch( + combineFilters(baseFilter, filter), getProcessorForSearch(search), search), + search); } - + /** * Maps group properties to LDAP properties. + * * @return */ public Map getPropertyMapper() { @@ -157,41 +165,52 @@ public Map getPropertyMapper() { return mapper; } - protected List ldapSearch(String filter, DirContextProcessor processor) { + protected List ldapSearch( + String filter, DirContextProcessor processor, final ISearch search) { SearchControls controls = new SearchControls(); controls.setSearchScope(SearchControls.SUBTREE_SCOPE); - return template.search(searchBase, filter, controls, new AbstractContextMapper() { - int counter = 1; - @Override - protected UserGroup doMapFromContext(DirContextOperations ctx) { - UserGroup group = new UserGroup(); - group.setId((long)counter++); // TODO: optionally map an attribute to the id - group.setEnabled(true); - group.setGroupName(ctx.getStringAttribute(nameAttribute)); - group.setDescription(ctx.getStringAttribute(descriptionAttribute)); - // if we bind users to groups through member attribute on groups, we fill the users list here - if (!"".equals(memberAttribute) && userDAO != null && ctx.getStringAttributes(memberAttribute) != null) { - for(String member : ctx.getStringAttributes(memberAttribute)) { - group.getUsers().add(userDAO.createMemberUser(member)); + return template.search( + searchBase, + filter, + controls, + new AbstractContextMapper() { + int counter = 1; + + @Override + protected UserGroup doMapFromContext(DirContextOperations ctx) { + UserGroup group = new UserGroup(); + group.setId( + (long) counter++); // TODO: optionally map an attribute to the id + group.setEnabled(true); + group.setGroupName(ctx.getStringAttribute(nameAttribute)); + group.setDescription(ctx.getStringAttribute(descriptionAttribute)); + // if we bind users to groups through member attribute on groups, we fill + // the users list here + if (!"".equals(memberAttribute) && userDAO != null && loadUsers(search)) { + String[] memberAttrs = ctx.getStringAttributes(memberAttribute); + if (memberAttrs != null && memberAttrs.length > 0) { + for (String member : memberAttrs) { + group.getUsers().add(userDAO.createMemberUser(member)); + } + } + } + return group; } - } - return group; - } - - }, processor); + }, + processor); } /** * Add the everyOne group to the LDAP returned list. - * + * * @param groups - * @param filters + * @param search * @return */ private List addEveryOne(List groups, ISearch search) { UserGroup everyoneGroup = new UserGroup(); everyoneGroup.setGroupName(GroupReservedNames.EVERYONE.groupName()); - everyoneGroup.setId((long)(groups.size() + 1)); + everyoneGroup.setId((long) (groups.size() + 1)); everyoneGroup.setEnabled(true); if (search == null || matchFilters(everyoneGroup, search)) { boolean everyoneFound = false; @@ -209,9 +228,9 @@ private List addEveryOne(List groups, ISearch search) { /** * Returns true if the group matches the given filters. - * + * * @param group - * @param filters + * @param search * @return */ protected boolean matchFilters(UserGroup group, ISearch search) { @@ -221,7 +240,7 @@ protected boolean matchFilters(UserGroup group, ISearch search) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#merge(java.lang.Object) */ @Override @@ -231,7 +250,7 @@ public UserGroup merge(UserGroup entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#remove(java.lang.Object) */ @Override @@ -241,7 +260,7 @@ public boolean remove(UserGroup entity) { /* * (non-Javadoc) - * + * * @see com.trg.dao.jpa.GenericDAOImpl#removeById(java.io.Serializable) */ @Override @@ -260,4 +279,12 @@ public int count(ISearch search) { return search(search).size(); } + protected boolean loadUsers(ISearch search) { + if (search instanceof GeoStoreISearchWrapper) { + GeoStoreISearchWrapper wrapper = (GeoStoreISearchWrapper) search; + Class clazz = wrapper.getCallerContext(); + if (clazz != null && UserDAO.class.isAssignableFrom(clazz)) return false; + } + return true; + } } diff --git a/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/search/GeoStoreISearchWrapper.java b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/search/GeoStoreISearchWrapper.java new file mode 100644 index 00000000..ab9c5cef --- /dev/null +++ b/src/core/persistence/src/main/java/it/geosolutions/geostore/core/dao/search/GeoStoreISearchWrapper.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2007 - 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package it.geosolutions.geostore.core.dao.search; + +import com.googlecode.genericdao.search.Field; +import com.googlecode.genericdao.search.Filter; +import com.googlecode.genericdao.search.ISearch; +import com.googlecode.genericdao.search.Sort; +import java.util.List; + +/** A wrapper for an ISearch. It is meant to provide the callerContext as a Class. */ +public class GeoStoreISearchWrapper implements ISearch { + + private ISearch delegate; + + private Class callerContext; + + public GeoStoreISearchWrapper(ISearch delegate, Class callerContext) { + this.delegate = delegate; + this.callerContext = callerContext; + } + + @Override + public int getFirstResult() { + return delegate.getFirstResult(); + } + + @Override + public int getMaxResults() { + return delegate.getMaxResults(); + } + + @Override + public int getPage() { + return delegate.getPage(); + } + + @Override + public Class getSearchClass() { + return delegate.getSearchClass(); + } + + @Override + public List getFilters() { + return delegate.getFilters(); + } + + @Override + public boolean isDisjunction() { + return delegate.isDisjunction(); + } + + @Override + public List getSorts() { + return delegate.getSorts(); + } + + @Override + public List getFields() { + return delegate.getFields(); + } + + @Override + public boolean isDistinct() { + return delegate.isDistinct(); + } + + @Override + public List getFetches() { + return delegate.getFetches(); + } + + @Override + public int getResultMode() { + return delegate.getResultMode(); + } + + /** @return the caller context if present, null otherwise. */ + public Class getCallerContext() { + return callerContext; + } +} diff --git a/src/core/persistence/src/main/resources/META-INF/geostore-persistence.xml b/src/core/persistence/src/main/resources/META-INF/geostore-persistence.xml index 6b4b6299..0d030037 100644 --- a/src/core/persistence/src/main/resources/META-INF/geostore-persistence.xml +++ b/src/core/persistence/src/main/resources/META-INF/geostore-persistence.xml @@ -33,5 +33,6 @@ it.geosolutions.geostore.core.model.UserAttribute it.geosolutions.geostore.core.model.User it.geosolutions.geostore.core.model.UserGroup + it.geosolutions.geostore.core.model.UserGroupAttribute diff --git a/src/core/persistence/src/main/resources/applicationContext-geostoreDatasource.xml b/src/core/persistence/src/main/resources/applicationContext-geostoreDatasource.xml index ca9dad71..f23249a5 100644 --- a/src/core/persistence/src/main/resources/applicationContext-geostoreDatasource.xml +++ b/src/core/persistence/src/main/resources/applicationContext-geostoreDatasource.xml @@ -74,6 +74,7 @@ + diff --git a/src/core/persistence/src/main/resources/applicationContext.xml b/src/core/persistence/src/main/resources/applicationContext.xml index 394e0330..0384f6ae 100644 --- a/src/core/persistence/src/main/resources/applicationContext.xml +++ b/src/core/persistence/src/main/resources/applicationContext.xml @@ -65,6 +65,12 @@ + + + + + + diff --git a/src/core/persistence/src/main/resources/geostore-ehcache.xml b/src/core/persistence/src/main/resources/geostore-ehcache.xml index a9d1543f..1aced7f3 100644 --- a/src/core/persistence/src/main/resources/geostore-ehcache.xml +++ b/src/core/persistence/src/main/resources/geostore-ehcache.xml @@ -3,7 +3,7 @@ - @@ -60,6 +60,6 @@ name="org.hibernate.cache.UpdateTimestampsCache" maxElementsInMemory="5000" eternal="true" - overflowToDisk="true"/> + overflowToDisk="true"/>--> \ No newline at end of file diff --git a/src/core/persistence/src/main/resources/securityapplicationContext.xml b/src/core/persistence/src/main/resources/securityapplicationContext.xml index 394e0330..d033b475 100644 --- a/src/core/persistence/src/main/resources/securityapplicationContext.xml +++ b/src/core/persistence/src/main/resources/securityapplicationContext.xml @@ -61,6 +61,10 @@ + + + + diff --git a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/AttributeDAOTest.java b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/AttributeDAOTest.java index 3e2caf03..19ecbd67 100644 --- a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/AttributeDAOTest.java +++ b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/AttributeDAOTest.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2012 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -22,32 +22,28 @@ import com.googlecode.genericdao.search.Filter; import com.googlecode.genericdao.search.Search; - import it.geosolutions.geostore.core.model.Attribute; import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.core.model.Resource; import it.geosolutions.geostore.core.model.StoredData; import it.geosolutions.geostore.core.model.enums.DataType; - import java.util.Date; import java.util.List; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.Test; /** * Class AttributeDAOTest - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ public class AttributeDAOTest extends BaseDAOTest { - final private static Logger LOGGER = Logger.getLogger(AttributeDAOTest.class); + private static final Logger LOGGER = LogManager.getLogger(AttributeDAOTest.class); - /** - * @throws Exception - */ + /** @throws Exception */ @Test public void testPersistAttribute() throws Exception { @@ -184,12 +180,9 @@ public void testPersistAttribute() throws Exception { attributeDAO.removeById(id); assertNull("Attribute not deleted", attributeDAO.find(id)); } - } - /** - * @throws Exception - */ + /** @throws Exception */ @Test public void testSearchAttribute() throws Exception { @@ -326,8 +319,8 @@ public void testSearchAttribute() throws Exception { // { Search criteria = new Search(Attribute.class); - criteria.addFilterOr(Filter.notEqual("type", DataType.NUMBER), - Filter.equal("type", DataType.DATE)); + criteria.addFilterOr( + Filter.notEqual("type", DataType.NUMBER), Filter.equal("type", DataType.DATE)); List attrList = attributeDAO.search(criteria); @@ -349,5 +342,4 @@ public void testSearchAttribute() throws Exception { assertEquals(0, storedDataDAO.count(null)); } } - } diff --git a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/BaseDAOTest.java b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/BaseDAOTest.java index 6c50cd6d..a637bfc9 100644 --- a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/BaseDAOTest.java +++ b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/BaseDAOTest.java @@ -20,11 +20,6 @@ package it.geosolutions.geostore.core.dao; -import java.util.List; -import org.apache.log4j.Logger; -import org.junit.Test; -import org.springframework.context.support.ClassPathXmlApplicationContext; -import it.geosolutions.geostore.core.dao.impl.ExternalSecurityDAOImpl; import it.geosolutions.geostore.core.model.Attribute; import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.core.model.Resource; @@ -32,11 +27,16 @@ import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserAttribute; import it.geosolutions.geostore.core.model.UserGroup; +import java.util.List; import junit.framework.TestCase; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Class BaseDAOTest - * + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ @@ -53,7 +53,7 @@ public abstract class BaseDAOTest extends TestCase { protected static CategoryDAO categoryDAO; protected static SecurityDAO securityDAO; - + protected static SecurityDAO externalSecurityDAO; protected static UserAttributeDAO userAttributeDAO; @@ -65,12 +65,12 @@ public abstract class BaseDAOTest extends TestCase { protected static ClassPathXmlApplicationContext ctx = null; public BaseDAOTest() { - LOGGER = Logger.getLogger(getClass()); + LOGGER = LogManager.getLogger(getClass()); synchronized (BaseDAOTest.class) { if (ctx == null) { - String[] paths = { "applicationContext.xml" - // ,"applicationContext-test.xml" + String[] paths = {"applicationContext.xml" + // ,"applicationContext-test.xml" }; ctx = new ClassPathXmlApplicationContext(paths); @@ -159,8 +159,8 @@ private void removeAllUserAttribute() { assertTrue("UserAttribute not removed", ret); } - assertEquals("UserAttribute have not been properly deleted", 0, - userAttributeDAO.count(null)); + assertEquals( + "UserAttribute have not been properly deleted", 0, userAttributeDAO.count(null)); } protected void removeAllStoredData() { @@ -195,5 +195,4 @@ private void removeAllAttribute() { assertEquals("DataType have not been properly deleted", 0, attributeDAO.count(null)); } - } diff --git a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/CategoryDAOTest.java b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/CategoryDAOTest.java index a2cac2cd..82333495 100644 --- a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/CategoryDAOTest.java +++ b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/CategoryDAOTest.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,25 +21,21 @@ import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.core.model.Resource; - import java.util.Date; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.Test; /** * Class CategoryDAOTest. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class CategoryDAOTest extends BaseDAOTest { - final private static Logger LOGGER = Logger.getLogger(CategoryDAOTest.class); + private static final Logger LOGGER = LogManager.getLogger(CategoryDAOTest.class); - /** - * @throws Exception - */ + /** @throws Exception */ @Test public void testPersistCategory() throws Exception { @@ -114,7 +110,5 @@ public void testPersistCategory() throws Exception { assertNull("Resource not deleted", resourceDAO.find(resourceId)); // assertNull("SecurityRule not deleted", securityDAO.find(securityId)); } - } - } diff --git a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ExternalSecurityDAOTest.java b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ExternalSecurityDAOTest.java index 84bd8e3f..891f4ee6 100644 --- a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ExternalSecurityDAOTest.java +++ b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ExternalSecurityDAOTest.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -26,23 +26,20 @@ import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.core.model.enums.Role; import java.util.Date; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.Test; /** * Class SecurityDAOTest. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class ExternalSecurityDAOTest extends BaseDAOTest { - final private static Logger LOGGER = Logger.getLogger(ExternalSecurityDAOTest.class); + private static final Logger LOGGER = LogManager.getLogger(ExternalSecurityDAOTest.class); - /** - * @throws Exception - */ + /** @throws Exception */ @Test public void testPersistSecurity() throws Exception { @@ -121,15 +118,14 @@ public void testPersistSecurity() throws Exception { externalSecurityDAO.removeById(securityId); assertNull("Security not deleted", securityDAO.find(categoryId)); } - } - + @Test public void testPersistSecurityUsingNames() throws Exception { final String NAME = "NAME"; - final String USERNAME= "USER"; - final String GROUPNAME= "GROUP"; + final String USERNAME = "USER"; + final String GROUPNAME = "GROUP"; if (LOGGER.isDebugEnabled()) { LOGGER.debug("Persisting Security"); @@ -153,7 +149,7 @@ public void testPersistSecurityUsingNames() throws Exception { assertEquals(1, categoryDAO.count(null)); assertEquals(1, categoryDAO.findAll().size()); - + User user = new User(); user.setName(USERNAME); user.setRole(Role.USER); @@ -161,7 +157,7 @@ public void testPersistSecurityUsingNames() throws Exception { userId = user.getId(); assertEquals(1, userDAO.count(null)); assertEquals(1, userDAO.findAll().size()); - + UserGroup group = new UserGroup(); group.setGroupName(GROUPNAME); userGroupDAO.persist(group); @@ -179,7 +175,6 @@ public void testPersistSecurityUsingNames() throws Exception { assertEquals(1, resourceDAO.count(null)); assertEquals(1, resourceDAO.findAll().size()); - } // @@ -201,7 +196,7 @@ public void testPersistSecurityUsingNames() throws Exception { assertNotNull(rule.getUsername()); externalSecurityDAO.removeById(securityId); } - + // // PERSIST WITH USER // @@ -224,7 +219,7 @@ public void testPersistSecurityUsingNames() throws Exception { assertNotNull(rule.getUsername()); externalSecurityDAO.removeById(securityId); } - + // // PERSIST WITH GROUPNAME // @@ -244,7 +239,7 @@ public void testPersistSecurityUsingNames() throws Exception { assertNotNull(rule.getGroupname()); externalSecurityDAO.removeById(securityId); } - + // // PERSIST WITH GROUP // @@ -291,7 +286,5 @@ public void testPersistSecurityUsingNames() throws Exception { externalSecurityDAO.removeById(securityId); assertNull("Security not deleted", externalSecurityDAO.find(categoryId)); } - } - } diff --git a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ResourceDAOTest.java b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ResourceDAOTest.java index 5c68e548..e51f7434 100644 --- a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ResourceDAOTest.java +++ b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ResourceDAOTest.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -24,16 +24,13 @@ import it.geosolutions.geostore.core.model.Resource; import it.geosolutions.geostore.core.model.SecurityRule; import it.geosolutions.geostore.core.model.StoredData; - import java.util.Date; - import org.junit.Test; /** * Class ResourceDAOTest - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class ResourceDAOTest extends BaseDAOTest { @@ -63,6 +60,8 @@ public void testPersistResource() throws Exception { resource.setName(NAME1); resource.setCreation(new Date()); resource.setCategory(category); + resource.setCreator("USER1"); + resource.setEditor("USER2"); resourceDAO.persist(resource); resourceId = resource.getId(); @@ -103,7 +102,11 @@ public void testPersistResource() throws Exception { assertNotNull("Can't retrieve resource", loaded); assertEquals(NAME1, loaded.getName()); + assertEquals("USER1", loaded.getCreator()); + assertEquals("USER2", loaded.getEditor()); loaded.setName(NAME2); + loaded.setCreator("USER1Updated"); + loaded.setEditor("USER2Updated"); resourceDAO.merge(loaded); } @@ -111,6 +114,8 @@ public void testPersistResource() throws Exception { Resource loaded = resourceDAO.find(resourceId); assertNotNull("Can't retrieve resource", loaded); assertEquals(NAME2, loaded.getName()); + assertEquals("USER1Updated", loaded.getCreator()); + assertEquals("USER2Updated", loaded.getEditor()); } // @@ -131,7 +136,5 @@ public void testPersistResource() throws Exception { assertNull("SecurityRule not deleted", securityDAO.find(securityId)); assertNull("StoredData not deleted", storedDataDAO.find(dataId)); } - } - } diff --git a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/SecurityDAOTest.java b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/SecurityDAOTest.java index 1a4ec0ed..a4918c99 100644 --- a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/SecurityDAOTest.java +++ b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/SecurityDAOTest.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -26,26 +26,22 @@ import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.core.model.enums.Role; import java.util.Date; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.Test; /** * Class SecurityDAOTest. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class SecurityDAOTest extends BaseDAOTest { - final private static Logger LOGGER = Logger.getLogger(SecurityDAOTest.class); + private static final Logger LOGGER = LogManager.getLogger(SecurityDAOTest.class); - /** - * @throws Exception - */ + /** @throws Exception */ @Test public void testPersistSecurity() throws Exception { - final String NAME = "NAME"; if (LOGGER.isDebugEnabled()) { @@ -80,6 +76,11 @@ public void testPersistSecurity() throws Exception { assertEquals(1, resourceDAO.count(null)); assertEquals(1, resourceDAO.findAll().size()); + Resource loaded = resourceDAO.find(resourceId); + assertNotNull(loaded); + assertNull(loaded.getCreator()); + assertNull(loaded.getEditor()); + // SecurityRule security = new SecurityRule(); // security.setCanRead(true); // security.setCanWrite(true); @@ -104,16 +105,21 @@ public void testPersistSecurity() throws Exception { // PERSIST // { + Resource loaded = resourceDAO.find(resourceId); SecurityRule security = new SecurityRule(); security.setCanRead(true); security.setCanWrite(true); - security.setResource(resourceDAO.find(resourceId)); + security.setResource(loaded); securityDAO.persist(security); securityId = security.getId(); assertEquals(1, securityDAO.count(null)); assertEquals(1, securityDAO.findAll().size()); + + loaded = resourceDAO.find(resourceId); + assertNull(loaded.getCreator()); + assertNull(loaded.getEditor()); } // @@ -140,15 +146,13 @@ public void testPersistSecurity() throws Exception { securityDAO.removeById(securityId); assertNull("Security not deleted", securityDAO.find(categoryId)); } - } - + @Test public void testPersistSecurityUsingNames() throws Exception { - final String NAME = "NAME"; - final String USERNAME= "USER"; - final String GROUPNAME= "GROUP"; + final String USERNAME = "USER"; + final String GROUPNAME = "GROUP"; if (LOGGER.isDebugEnabled()) { LOGGER.debug("Persisting Security"); @@ -172,7 +176,7 @@ public void testPersistSecurityUsingNames() throws Exception { assertEquals(1, categoryDAO.count(null)); assertEquals(1, categoryDAO.findAll().size()); - + User user = new User(); user.setName(USERNAME); user.setRole(Role.USER); @@ -180,7 +184,7 @@ public void testPersistSecurityUsingNames() throws Exception { userId = user.getId(); assertEquals(1, userDAO.count(null)); assertEquals(1, userDAO.findAll().size()); - + UserGroup group = new UserGroup(); group.setGroupName(GROUPNAME); userGroupDAO.persist(group); @@ -199,6 +203,9 @@ public void testPersistSecurityUsingNames() throws Exception { assertEquals(1, resourceDAO.count(null)); assertEquals(1, resourceDAO.findAll().size()); + Resource loaded = resourceDAO.find(resourceId); + assertNull(loaded.getCreator()); + assertNull(loaded.getEditor()); } // @@ -218,18 +225,30 @@ public void testPersistSecurityUsingNames() throws Exception { SecurityRule rule = securityDAO.find(securityId); assertNotNull(rule); assertNotNull(rule.getUsername()); + + Resource loaded = resourceDAO.find(resourceId); + assertNotNull(loaded.getCreator()); + assertNotNull(loaded.getEditor()); + assertEquals("testuser", loaded.getCreator()); + assertEquals("testuser", loaded.getEditor()); + securityDAO.removeById(securityId); } - + // // PERSIST WITH USER // { + Resource loaded = resourceDAO.find(resourceId); + loaded.setEditor(null); + resourceDAO.merge(loaded); + SecurityRule security = new SecurityRule(); security.setCanRead(true); security.setCanWrite(true); security.setResource(resourceDAO.find(resourceId)); User testUser = new User(); + testUser.setName("TheTestUser"); testUser.setId(userId); security.setUser(testUser); securityDAO.persist(security); @@ -240,9 +259,16 @@ public void testPersistSecurityUsingNames() throws Exception { SecurityRule rule = securityDAO.find(securityId); assertNotNull(rule); assertNotNull(rule.getUser()); + + loaded = resourceDAO.find(resourceId); + assertNotNull(loaded.getCreator()); + assertNotNull(loaded.getEditor()); + assertEquals("testuser", loaded.getCreator()); + assertEquals(testUser.getName(), loaded.getEditor()); + securityDAO.removeById(securityId); } - + // // PERSIST WITH GROUPNAME // @@ -262,7 +288,7 @@ public void testPersistSecurityUsingNames() throws Exception { assertNotNull(rule.getGroupname()); securityDAO.removeById(securityId); } - + // // PERSIST WITH GROUP // @@ -308,7 +334,5 @@ public void testPersistSecurityUsingNames() throws Exception { securityDAO.removeById(securityId); assertNull("Security not deleted", securityDAO.find(categoryId)); } - } - } diff --git a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/StoredDataDAOTest.java b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/StoredDataDAOTest.java index bfa52f00..2601ec1b 100644 --- a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/StoredDataDAOTest.java +++ b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/StoredDataDAOTest.java @@ -23,14 +23,12 @@ import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.core.model.Resource; import it.geosolutions.geostore.core.model.StoredData; - import java.util.Date; - import org.junit.Test; /** * Class StoredDataDAOTest - * + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ @@ -141,8 +139,7 @@ public void testBigData() { assertNotNull("Can't retrieve data", loaded); assertEquals(CAPACITY, loaded.getData().length()); } - } - //TODO missing tests for stored data security methods + // TODO missing tests for stored data security methods -} \ No newline at end of file +} diff --git a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/UserAttributeDAOTest.java b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/UserAttributeDAOTest.java index c8b2c81e..3b52e52a 100644 --- a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/UserAttributeDAOTest.java +++ b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/UserAttributeDAOTest.java @@ -1,48 +1,44 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.core.dao; -import java.util.HashSet; -import java.util.Set; - import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserAttribute; import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.core.model.enums.Role; - -import org.apache.log4j.Logger; +import java.util.HashSet; +import java.util.Set; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.Test; /** * Class RoleDAOTest. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class UserAttributeDAOTest extends BaseDAOTest { - final private static Logger LOGGER = Logger.getLogger(UserAttributeDAOTest.class); + private static final Logger LOGGER = LogManager.getLogger(UserAttributeDAOTest.class); - /** - * @throws Exception - */ + /** @throws Exception */ @Test public void testPersistRole() throws Exception { @@ -124,7 +120,5 @@ public void testPersistRole() throws Exception { userAttributeDAO.removeById(attributeId); assertNull("Role not deleted", userAttributeDAO.find(attributeId)); } - } - } diff --git a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/UserDAOTest.java b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/UserDAOTest.java index 47da9e77..51665a95 100644 --- a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/UserDAOTest.java +++ b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/UserDAOTest.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -26,27 +26,23 @@ import it.geosolutions.geostore.core.model.UserAttribute; import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.core.model.enums.Role; - import java.util.Date; import java.util.HashSet; import java.util.Set; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.Test; /** * Class UserDAOTest. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class UserDAOTest extends BaseDAOTest { - final private static Logger LOGGER = Logger.getLogger(UserDAOTest.class); + private static final Logger LOGGER = LogManager.getLogger(UserDAOTest.class); - /** - * @throws Exception - */ + /** @throws Exception */ @Test public void testPersistUser() throws Exception { @@ -133,7 +129,7 @@ public void testPersistUser() throws Exception { assertEquals(1, securityDAO.count(null)); assertEquals(1, securityDAO.findAll().size()); - + SecurityRule security2 = new SecurityRule(); security2.setCanRead(true); security2.setCanWrite(true); @@ -145,7 +141,6 @@ public void testPersistUser() throws Exception { assertEquals(2, securityDAO.count(null)); assertEquals(2, securityDAO.findAll().size()); - } // @@ -162,10 +157,10 @@ public void testPersistUser() throws Exception { // Cascading // assertNull("SecurityRule not deleted", securityDAO.find(securityId1)); - assertNotNull("Group SecurityRule deleted while deleting user...", securityDAO.find(securityId2)); + assertNotNull( + "Group SecurityRule deleted while deleting user...", + securityDAO.find(securityId2)); assertNull("UserAttribute not deleted", userAttributeDAO.find(attributeId)); } - } - } diff --git a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/UserGroupDAOTest.java b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/UserGroupDAOTest.java index da09c83e..fb7a8b90 100644 --- a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/UserGroupDAOTest.java +++ b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/UserGroupDAOTest.java @@ -1,56 +1,50 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.core.dao; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.persistence.PersistenceException; - -import org.apache.log4j.Logger; -import org.junit.Test; - import com.googlecode.genericdao.search.Filter; import com.googlecode.genericdao.search.Search; - import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.core.model.SecurityRule; import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.core.model.enums.Role; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.persistence.PersistenceException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.Test; /** * Class UserGroupDAOTest. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author DamianoG - * */ public class UserGroupDAOTest extends BaseDAOTest { - final private static Logger LOGGER = Logger.getLogger(UserGroupDAOTest.class); + private static final Logger LOGGER = LogManager.getLogger(UserGroupDAOTest.class); - /** - * @throws Exception - */ + /** @throws Exception */ @Test public void testPersistUserGroup() throws Exception { @@ -81,27 +75,28 @@ public void testPersistUserGroup() throws Exception { g1.setGroupName("GROUP1"); UserGroup g2 = new UserGroup(); g2.setGroupName("GROUP2"); - - try{ + + try { UserGroup g3 = new UserGroup(); - g3.setGroupName("GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3"); + g3.setGroupName( + "GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3GROUP3"); userGroupDAO.persist(g3); - }catch(PersistenceException e){ - assertTrue(true); + } catch (PersistenceException e) { + assertTrue(true); } - + groups.add(g1); groups.add(g2); userGroupDAO.persist(g1); userGroupDAO.persist(g2); - + groupId2 = g2.getId(); - + assertEquals(3, userGroupDAO.count(null)); assertEquals(3, userGroupDAO.findAll().size()); - //Create User1, associate to him group1 and group2 and set an example security rule + // Create User1, associate to him group1 and group2 and set an example security rule User user1 = new User(); user1.setGroups(groups); user1.setName("USER1_NAME"); @@ -110,14 +105,14 @@ public void testPersistUserGroup() throws Exception { userDAO.persist(user1); userId1 = user1.getId(); - + assertEquals(1, userDAO.findAll().size()); - assertEquals(1, userDAO.count(null)); + assertEquals(1, userDAO.count(null)); SecurityRule security = new SecurityRule(); security.setCanRead(true); security.setCanWrite(true); - //Why set both user and group? just for test? The Application shouldn't allows that... + // Why set both user and group? just for test? The Application shouldn't allows that... security.setGroup(g1); security.setUser(user1); @@ -126,8 +121,8 @@ public void testPersistUserGroup() throws Exception { assertEquals(1, securityDAO.count(null)); assertEquals(1, securityDAO.findAll().size()); - - //Create User2, associate to him group2 and set an example security rule + + // Create User2, associate to him group2 and set an example security rule user2 = new User(); user2.setGroups(groups); user2.setName("USER2_NAME"); @@ -135,15 +130,15 @@ public void testPersistUserGroup() throws Exception { user2.setRole(Role.USER); userDAO.persist(user2); - + assertEquals(2, userDAO.findAll().size()); assertEquals(2, userDAO.count(null)); - SecurityRule security2 = new SecurityRule(); security2.setCanRead(true); security2.setCanWrite(true); - //Set a security rule just for the group (the GeoStore application logic creates another rule also for the owner user) + // Set a security rule just for the group (the GeoStore application logic creates + // another rule also for the owner user) security2.setGroup(g2); securityDAO.persist(security2); @@ -151,15 +146,16 @@ public void testPersistUserGroup() throws Exception { assertEquals(2, securityDAO.count(null)); assertEquals(2, securityDAO.findAll().size()); - } // // LOAD, REMOVE, CASCADING // { - // USER REMOVAL, the cascading on security rules relation will remove also the related rules. - // The user is also the owner of the many2many relations with entity GROUP so the entry on that relation table is automatically deleted. + // USER REMOVAL, the cascading on security rules relation will remove also the related + // rules. + // The user is also the owner of the many2many relations with entity GROUP so the entry + // on that relation table is automatically deleted. User user1 = userDAO.find(userId1); userDAO.remove(user1); assertEquals(1, securityDAO.count(null)); @@ -169,9 +165,12 @@ public void testPersistUserGroup() throws Exception { assertEquals(3, userGroupDAO.findAll().size()); assertEquals(3, userGroupDAO.count(null)); assertNull("SecurityRule not deleted", securityDAO.find(securityId1)); - assertNotNull("Group SecurityRule deleted... that's a mistake!", securityDAO.find(securityId2)); - - // GROUP REMOVAL, being the entity USER the owner of the relation we should remove manually the entries in the relationship table + assertNotNull( + "Group SecurityRule deleted... that's a mistake!", + securityDAO.find(securityId2)); + + // GROUP REMOVAL, being the entity USER the owner of the relation we should remove + // manually the entries in the relationship table // removing the group association to all user UserGroup group2 = userGroupDAO.find(groupId2); assertNotNull(group2); @@ -184,7 +183,7 @@ public void testPersistUserGroup() throws Exception { assertEquals(1, users.size()); assertEquals(2, users.get(0).getGroups().size()); - for(User u : users){ + for (User u : users) { LOGGER.info("Removing group " + group2 + " from user " + u); assertTrue(u.removeGroup(groupId2)); userDAO.merge(u); @@ -195,7 +194,7 @@ public void testPersistUserGroup() throws Exception { searchByGroup.addFilterSome("groups", Filter.equal("id", groupId2)); users = userDAO.search(searchByGroup); - for(User u : users){ + for (User u : users) { LOGGER.error("Found user " + u); } } @@ -207,7 +206,5 @@ public void testPersistUserGroup() throws Exception { assertNull("Group SecurityRule not deleted", securityDAO.find(securityId2)); userDAO.remove(user2); } - } - } diff --git a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ldap/BaseDAOTest.java b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ldap/BaseDAOTest.java index 39daa422..3a80ce5b 100644 --- a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ldap/BaseDAOTest.java +++ b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ldap/BaseDAOTest.java @@ -1,24 +1,26 @@ /* * Copyright (C) 2021 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.core.dao.ldap; +import it.geosolutions.geostore.core.ldap.IterableNamingEnumeration; +import it.geosolutions.geostore.core.ldap.MockDirContextOperations; import java.util.Arrays; import java.util.Collections; import javax.naming.NamingEnumeration; @@ -28,125 +30,146 @@ import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import org.springframework.ldap.core.DirContextAdapter; -import it.geosolutions.geostore.core.ldap.IterableNamingEnumeration; -import it.geosolutions.geostore.core.ldap.MockDirContextOperations; public abstract class BaseDAOTest { protected DirContext buildContextForUsers() { return new DirContextAdapter() { @Override - public NamingEnumeration search(String name, String filter, SearchControls cons) - throws NamingException { + public NamingEnumeration search( + String name, String filter, SearchControls cons) throws NamingException { if ("ou=users".equals(name)) { - if("cn=*".equals(filter)) { - SearchResult sr1 = new SearchResult("cn=*", null, new MockDirContextOperations() { + if ("cn=*".equals(filter)) { + SearchResult sr1 = + new SearchResult( + "cn=*", + null, + new MockDirContextOperations() { - @Override - public String getNameInNamespace() { - return "cn=username,ou=users"; - } + @Override + public String getNameInNamespace() { + return "cn=username,ou=users"; + } - @Override - public String getStringAttribute(String name) { - if ("cn".equals(name)) { - return "username"; - } - return ""; - } - - }, new BasicAttributes()); - SearchResult sr2 = new SearchResult("cn=*", null, new MockDirContextOperations() { + @Override + public String getStringAttribute(String name) { + if ("cn".equals(name)) { + return "username"; + } + return ""; + } + }, + new BasicAttributes()); + SearchResult sr2 = + new SearchResult( + "cn=*", + null, + new MockDirContextOperations() { - @Override - public String getNameInNamespace() { - return "cn=username2,ou=users"; - } + @Override + public String getNameInNamespace() { + return "cn=username2,ou=users"; + } - @Override - public String getStringAttribute(String name) { - if ("cn".equals(name)) { - return "username2"; - } - return ""; - } - - }, new BasicAttributes()); + @Override + public String getStringAttribute(String name) { + if ("cn".equals(name)) { + return "username2"; + } + return ""; + } + }, + new BasicAttributes()); return new IterableNamingEnumeration(Arrays.asList(sr1, sr2)); } else if ("(& (cn=*) (cn=username))".equals(filter)) { - SearchResult sr = new SearchResult("cn=*", null, new MockDirContextOperations() { + SearchResult sr = + new SearchResult( + "cn=*", + null, + new MockDirContextOperations() { - @Override - public String getNameInNamespace() { - return "cn=username,ou=users"; - } + @Override + public String getNameInNamespace() { + return "cn=username,ou=users"; + } - @Override - public String getStringAttribute(String name) { - if ("cn".equals(name)) { - return "username"; - } - return ""; - } - - }, new BasicAttributes()); + @Override + public String getStringAttribute(String name) { + if ("cn".equals(name)) { + return "username"; + } + return ""; + } + }, + new BasicAttributes()); return new IterableNamingEnumeration(Collections.singletonList(sr)); } else if ("(& (cn=*) (cn=username2))".equals(filter)) { - SearchResult sr = new SearchResult("cn=*", null, new MockDirContextOperations() { + SearchResult sr = + new SearchResult( + "cn=*", + null, + new MockDirContextOperations() { - @Override - public String getNameInNamespace() { - return "cn=username2,ou=users"; - } + @Override + public String getNameInNamespace() { + return "cn=username2,ou=users"; + } - @Override - public String getStringAttribute(String name) { - if ("cn".equals(name)) { - return "username2"; - } - return ""; - } - - }, new BasicAttributes()); + @Override + public String getStringAttribute(String name) { + if ("cn".equals(name)) { + return "username2"; + } + return ""; + } + }, + new BasicAttributes()); return new IterableNamingEnumeration(Collections.singletonList(sr)); - } + } } return new IterableNamingEnumeration(Collections.EMPTY_LIST); } }; } - + protected DirContext buildContextForGroupsMembership(final String memberString) { return new DirContextAdapter() { @Override - public NamingEnumeration search(String name, String filter, SearchControls cons) - throws NamingException { + public NamingEnumeration search( + String name, String filter, SearchControls cons) throws NamingException { if ("ou=groups".equals(name)) { if ("(& (cn=*) (cn=group))".equals(filter)) { - SearchResult sr = new SearchResult("cn=*", null, new MockDirContextOperations() { + SearchResult sr = + new SearchResult( + "cn=*", + null, + new MockDirContextOperations() { - @Override - public String getNameInNamespace() { - return "cn=group,ou=groups"; - } + @Override + public String getNameInNamespace() { + return "cn=group,ou=groups"; + } - @Override - public String getStringAttribute(String name) { - if ("cn".equals(name)) { - return "group"; - } - return ""; - } + @Override + public String getStringAttribute(String name) { + if ("cn".equals(name)) { + return "group"; + } + return ""; + } - @Override - public String[] getStringAttributes(String name) { - if ("member".equals(name)) { - return new String[] {memberString == null ? "username" : memberString}; - } - return new String[] {}; - } - - - }, new BasicAttributes()); + @Override + public String[] getStringAttributes(String name) { + if ("member".equals(name)) { + return new String[] { + memberString == null + ? "username" + : memberString + }; + } + return new String[] {}; + } + }, + new BasicAttributes()); return new IterableNamingEnumeration(Collections.singletonList(sr)); } } @@ -154,125 +177,163 @@ public String[] getStringAttributes(String name) { } }; } - + protected DirContext buildContextForGroups() { return new DirContextAdapter() { @Override - public NamingEnumeration search(String name, String filter, SearchControls cons) - throws NamingException { + public NamingEnumeration search( + String name, String filter, SearchControls cons) throws NamingException { if ("ou=groups".equals(name)) { if ("cn=*".equals(filter)) { - SearchResult sr1 = new SearchResult("cn=*", null, new MockDirContextOperations() { + SearchResult sr1 = + new SearchResult( + "cn=*", + null, + new MockDirContextOperations() { - @Override - public String getNameInNamespace() { - return "cn=group,ou=groups"; - } + @Override + public String getNameInNamespace() { + return "cn=group,ou=groups"; + } - @Override - public String getStringAttribute(String name) { - if ("cn".equals(name)) { - return "group"; - } - return ""; - } + @Override + public String getStringAttribute(String name) { + if ("cn".equals(name)) { + return "group"; + } + return ""; + } + }, + new BasicAttributes()); + SearchResult sr2 = + new SearchResult( + "cn=*", + null, + new MockDirContextOperations() { - }, new BasicAttributes()); - SearchResult sr2 = new SearchResult("cn=*", null, new MockDirContextOperations() { + @Override + public String getNameInNamespace() { + return "cn=group2,ou=groups"; + } - @Override - public String getNameInNamespace() { - return "cn=group2,ou=groups"; - } - - @Override - public String getStringAttribute(String name) { - if ("cn".equals(name)) { - return "group2"; - } - return ""; - } - - }, new BasicAttributes()); + @Override + public String getStringAttribute(String name) { + if ("cn".equals(name)) { + return "group2"; + } + return ""; + } + }, + new BasicAttributes()); return new IterableNamingEnumeration(Arrays.asList(sr1, sr2)); } else if ("(& (cn=*) (cn=group))".equals(filter)) { - SearchResult sr = new SearchResult("cn=*", null, new MockDirContextOperations() { + SearchResult sr = + new SearchResult( + "cn=*", + null, + new MockDirContextOperations() { - @Override - public String getNameInNamespace() { - return "cn=group,ou=groups"; - } + @Override + public String getNameInNamespace() { + return "cn=group,ou=groups"; + } - @Override - public String getStringAttribute(String name) { - if ("cn".equals(name)) { - return "group"; - } - return ""; - } - - }, new BasicAttributes()); + @Override + public String getStringAttribute(String name) { + if ("cn".equals(name)) { + return "group"; + } + return ""; + } + + @Override + public String[] getStringAttributes(String name) { + if ("member".equals(name)) { + return new String[] {"username"}; + } + return super.getStringAttributes(name); + } + }, + new BasicAttributes()); return new IterableNamingEnumeration(Collections.singletonList(sr)); - } else if("(& (cn=*) (member=cn=username,ou=users))".contentEquals(filter)) { - SearchResult sr1 = new SearchResult("cn=*", null, new MockDirContextOperations() { + } else if ("(& (cn=*) (member=cn=username,ou=users))".contentEquals(filter)) { + SearchResult sr1 = + new SearchResult( + "cn=*", + null, + new MockDirContextOperations() { + + @Override + public String getNameInNamespace() { + return "cn=group,ou=groups"; + } - @Override - public String getNameInNamespace() { - return "cn=group,ou=groups"; - } + @Override + public String getStringAttribute(String name) { + if ("cn".equals(name)) { + return "group"; + } + return ""; + } - @Override - public String getStringAttribute(String name) { - if ("cn".equals(name)) { - return "group"; - } - return ""; - } - - }, new BasicAttributes()); - SearchResult sr2 = new SearchResult("cn=*", null, new MockDirContextOperations() { + @Override + public String[] getStringAttributes(String name) { + if ("member".equals(name)) { + return new String[] {"username"}; + } + return super.getStringAttributes(name); + } + }, + new BasicAttributes()); + SearchResult sr2 = + new SearchResult( + "cn=*", + null, + new MockDirContextOperations() { - @Override - public String getNameInNamespace() { - return "cn=admin,ou=groups"; - } + @Override + public String getNameInNamespace() { + return "cn=admin,ou=groups"; + } - @Override - public String getStringAttribute(String name) { - if ("cn".equals(name)) { - return "admin"; - } - return ""; - } - - }, new BasicAttributes()); + @Override + public String getStringAttribute(String name) { + if ("cn".equals(name)) { + return "admin"; + } + return ""; + } + }, + new BasicAttributes()); return new IterableNamingEnumeration( Arrays.asList(new SearchResult[] {sr1, sr2})); - } else if("(& (cn=*) (member=cn=username2,ou=users))".contentEquals(filter)) { - SearchResult sr = new SearchResult("cn=*", null, new MockDirContextOperations() { + } else if ("(& (cn=*) (member=cn=username2,ou=users))".contentEquals(filter)) { + SearchResult sr = + new SearchResult( + "cn=*", + null, + new MockDirContextOperations() { - @Override - public String getNameInNamespace() { - return "cn=group,ou=groups"; - } + @Override + public String getNameInNamespace() { + return "cn=group,ou=groups"; + } - @Override - public String getStringAttribute(String name) { - if ("cn".equals(name)) { - return "group"; - } - return ""; - } - - }, new BasicAttributes()); - - return new IterableNamingEnumeration( - Collections.singletonList(sr)); + @Override + public String getStringAttribute(String name) { + if ("cn".equals(name)) { + return "group"; + } + return ""; + } + }, + new BasicAttributes()); + + return new IterableNamingEnumeration(Collections.singletonList(sr)); } } return new IterableNamingEnumeration(Collections.EMPTY_LIST); } }; } - } diff --git a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ldap/UserDAOTest.java b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ldap/UserDAOTest.java index 105c5c61..90185524 100644 --- a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ldap/UserDAOTest.java +++ b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ldap/UserDAOTest.java @@ -1,51 +1,41 @@ /* * Copyright (C) 2019 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.core.dao.ldap; -import static org.junit.Assert.assertEquals; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.BasicAttributes; -import javax.naming.directory.DirContext; -import javax.naming.directory.SearchControls; -import javax.naming.directory.SearchResult; -import org.junit.Test; -import org.springframework.ldap.core.DirContextAdapter; +import static org.junit.Assert.*; + import com.googlecode.genericdao.search.Filter; import com.googlecode.genericdao.search.Search; import it.geosolutions.geostore.core.dao.ldap.impl.UserDAOImpl; import it.geosolutions.geostore.core.dao.ldap.impl.UserGroupDAOImpl; -import it.geosolutions.geostore.core.ldap.IterableNamingEnumeration; import it.geosolutions.geostore.core.ldap.MockContextSource; -import it.geosolutions.geostore.core.ldap.MockDirContextOperations; import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.core.model.enums.Role; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Test; public class UserDAOTest extends BaseDAOTest { - - + @Test public void testFindAll() { UserDAOImpl userDAO = new UserDAOImpl(new MockContextSource(buildContextForUsers())); @@ -55,9 +45,9 @@ public void testFindAll() { User user = users.get(0); assertEquals("username", user.getName()); } - + @Test - public void testSearchByname() { + public void testSearchByName() { UserDAOImpl userDAO = new UserDAOImpl(new MockContextSource(buildContextForUsers())); userDAO.setSearchBase("ou=users"); Search search = new Search(User.class); @@ -66,48 +56,50 @@ public void testSearchByname() { User user = users.get(0); assertEquals("username", user.getName()); } - + @Test public void testGroupsAreFetched() { UserDAOImpl userDAO = new UserDAOImpl(new MockContextSource(buildContextForUsers())); userDAO.setSearchBase("ou=users"); - UserGroupDAOImpl userGroupDAO = new UserGroupDAOImpl(new MockContextSource(buildContextForGroups())); + UserGroupDAOImpl userGroupDAO = + new UserGroupDAOImpl(new MockContextSource(buildContextForGroups())); userGroupDAO.setSearchBase("ou=groups"); userDAO.setUserGroupDAO(userGroupDAO); - + Search search = new Search(User.class); List users = userDAO.search(search.addFilter(Filter.equal("name", "username"))); - + User user = users.get(0); assertEquals(2, user.getGroups().size()); } - + @Test public void testRolesAreAssigned() { UserDAOImpl userDAO = new UserDAOImpl(new MockContextSource(buildContextForUsers())); userDAO.setSearchBase("ou=users"); userDAO.setAdminRoleGroup("admin"); - UserGroupDAOImpl userGroupDAO = new UserGroupDAOImpl(new MockContextSource(buildContextForGroups())); + UserGroupDAOImpl userGroupDAO = + new UserGroupDAOImpl(new MockContextSource(buildContextForGroups())); userGroupDAO.setSearchBase("ou=groups"); userDAO.setUserGroupDAO(userGroupDAO); - + Search search = new Search(User.class); List users = userDAO.search(search.addFilter(Filter.equal("name", "username"))); - + User user = users.get(0); assertEquals(Role.ADMIN, user.getRole()); - + search = new Search(User.class); users = userDAO.search(search.addFilter(Filter.equal("name", "username2"))); - + user = users.get(0); assertEquals(Role.USER, user.getRole()); } - + @Test public void testAttributesMapper() { UserDAOImpl userDAO = new UserDAOImpl(new MockContextSource(buildContextForUsers())); - Map mapper = new HashMap(); + Map mapper = new HashMap<>(); mapper.put("mail", "email"); userDAO.setAttributesMapper(mapper); userDAO.setSearchBase("ou=users"); @@ -117,25 +109,32 @@ public void testAttributesMapper() { assertEquals(1, user.getAttribute().size()); assertEquals("email", user.getAttribute().get(0).getName()); } - + @Test public void testSearchByGroup() { - UserGroupDAOImpl userGroupDAO = new UserGroupDAOImpl(new MockContextSource(buildContextForGroupsMembership(null))); + UserGroupDAOImpl userGroupDAO = + new UserGroupDAOImpl(new MockContextSource(buildContextForGroupsMembership(null))); userGroupDAO.setSearchBase("ou=groups"); UserDAOImpl userDAO = new UserDAOImpl(new MockContextSource(buildContextForUsers())); userDAO.setUserGroupDAO(userGroupDAO); userDAO.setSearchBase("ou=users"); userGroupDAO.setUserDAO(userDAO); Search search = new Search(User.class); - List users = userDAO.search(search.addFilter(Filter.some("groups", Filter.equal("groupName", "group")))); + List users = + userDAO.search( + search.addFilter( + Filter.some("groups", Filter.equal("groupName", "group")))); assertEquals(1, users.size()); User user = users.get(0); assertEquals("username", user.getName()); } - + @Test public void testMemberPattern() { - UserGroupDAOImpl userGroupDAO = new UserGroupDAOImpl(new MockContextSource(buildContextForGroupsMembership("uid=username,ou=users"))); + UserGroupDAOImpl userGroupDAO = + new UserGroupDAOImpl( + new MockContextSource( + buildContextForGroupsMembership("uid=username,ou=users"))); userGroupDAO.setSearchBase("ou=groups"); UserDAOImpl userDAO = new UserDAOImpl(new MockContextSource(buildContextForUsers())); userDAO.setUserGroupDAO(userGroupDAO); @@ -143,9 +142,40 @@ public void testMemberPattern() { userDAO.setMemberPattern("^uid=([^,]+).*$"); userGroupDAO.setUserDAO(userDAO); Search search = new Search(User.class); - List users = userDAO.search(search.addFilter(Filter.some("groups", Filter.equal("groupName", "group")))); + List users = + userDAO.search( + search.addFilter( + Filter.some("groups", Filter.equal("groupName", "group")))); assertEquals(1, users.size()); User user = users.get(0); assertEquals("username", user.getName()); } + + @Test + public void testGroupsAreFetchedWithoutNestedUsers() { + UserDAOImpl userDAO = new UserDAOImpl(new MockContextSource(buildContextForUsers())); + userDAO.setSearchBase("ou=users"); + UserGroupDAOImpl userGroupDAO = + new UserGroupDAOImpl(new MockContextSource(buildContextForGroups())); + userGroupDAO.setSearchBase("ou=groups"); + userDAO.setUserGroupDAO(userGroupDAO); + + Search search = new Search(User.class); + List users = userDAO.search(search.addFilter(Filter.equal("name", "username"))); + + User user = users.get(0); + assertEquals(2, user.getGroups().size()); + assertTrue(user.getGroups().stream().anyMatch(g -> g.getGroupName().equals("group"))); + // nested users not available when requesting them through UserDAO + for (UserGroup group : user.getGroups()) { + assertTrue(group.getUsers().isEmpty()); + } + + // but yes, the group with groupName group actually jas user assigned to it. + Search search1 = new Search(); + Filter filter = Filter.equal("groupName", "group"); + search1.addFilter(filter); + List groups = userGroupDAO.search(search1); + assertFalse(groups.get(0).getUsers().isEmpty()); + } } diff --git a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ldap/UserGroupDAOTest.java b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ldap/UserGroupDAOTest.java index 81d6f111..28d9a1b1 100644 --- a/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ldap/UserGroupDAOTest.java +++ b/src/core/persistence/src/test/java/it/geosolutions/geostore/core/dao/ldap/UserGroupDAOTest.java @@ -1,72 +1,65 @@ /* * Copyright (C) 2019 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.core.dao.ldap; import static org.junit.Assert.assertEquals; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.BasicAttributes; -import javax.naming.directory.DirContext; -import javax.naming.directory.SearchControls; -import javax.naming.directory.SearchResult; -import org.junit.Test; -import org.springframework.ldap.core.DirContextAdapter; + import com.googlecode.genericdao.search.Filter; import com.googlecode.genericdao.search.Search; import it.geosolutions.geostore.core.dao.ldap.impl.UserGroupDAOImpl; -import it.geosolutions.geostore.core.ldap.IterableNamingEnumeration; import it.geosolutions.geostore.core.ldap.MockContextSource; -import it.geosolutions.geostore.core.ldap.MockDirContextOperations; import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserGroup; +import java.util.List; +import org.junit.Test; public class UserGroupDAOTest extends BaseDAOTest { - - + @Test public void testFindAll() { - UserGroupDAOImpl userGroupDAO = new UserGroupDAOImpl(new MockContextSource(buildContextForGroups())); + UserGroupDAOImpl userGroupDAO = + new UserGroupDAOImpl(new MockContextSource(buildContextForGroups())); userGroupDAO.setSearchBase("ou=groups"); List groups = userGroupDAO.findAll(); assertEquals(2, groups.size()); UserGroup group = groups.get(0); assertEquals("group", group.getGroupName()); } - + @Test public void testSearchByname() { - UserGroupDAOImpl userGroupDAO = new UserGroupDAOImpl(new MockContextSource(buildContextForGroups())); + UserGroupDAOImpl userGroupDAO = + new UserGroupDAOImpl(new MockContextSource(buildContextForGroups())); userGroupDAO.setSearchBase("ou=groups"); Search search = new Search(User.class); - List groups = userGroupDAO.search(search.addFilter(Filter.equal("groupName", "group"))); + List groups = + userGroupDAO.search(search.addFilter(Filter.equal("groupName", "group"))); assertEquals(1, groups.size()); UserGroup group = groups.get(0); assertEquals("group", group.getGroupName()); } - + @Test public void testAddEveryOne() { - UserGroupDAOImpl userGroupDAO = new UserGroupDAOImpl(new MockContextSource(buildContextForGroups())); + UserGroupDAOImpl userGroupDAO = + new UserGroupDAOImpl(new MockContextSource(buildContextForGroups())); userGroupDAO.setSearchBase("ou=groups"); userGroupDAO.setAddEveryOneGroup(true); List groups = userGroupDAO.findAll(); diff --git a/src/core/persistence/src/test/resources/hibernate.cfg.xml b/src/core/persistence/src/test/resources/hibernate.cfg.xml index f7672e36..c7d25931 100644 --- a/src/core/persistence/src/test/resources/hibernate.cfg.xml +++ b/src/core/persistence/src/test/resources/hibernate.cfg.xml @@ -16,5 +16,6 @@ + diff --git a/src/core/persistence/src/test/resources/log4j.properties b/src/core/persistence/src/test/resources/log4j.properties deleted file mode 100644 index da8d30f0..00000000 --- a/src/core/persistence/src/test/resources/log4j.properties +++ /dev/null @@ -1,8 +0,0 @@ -log4j.rootLogger=INFO, consoleAppender - -log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender -log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout -log4j.appender.consoleAppender.layout.ConversionPattern=%p %d{yyyy-MM-dd HH:mm:ss.SSS} %c::%M - %m%n - -log4j.logger.org.hibernate=INFO -log4j.logger.com.trg=INFO diff --git a/src/core/persistence/src/test/resources/log4j2.properties b/src/core/persistence/src/test/resources/log4j2.properties new file mode 100644 index 00000000..7b955859 --- /dev/null +++ b/src/core/persistence/src/test/resources/log4j2.properties @@ -0,0 +1,15 @@ +rootLogger.level = INFO +appenders= console + + +appender.console.type = Console +appender.console.name = LogToConsole +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = %p %d{yyyy-MM-dd HH:mm:ss.SSS} %c::%M:%L - %m%n +rootLogger.appenderRef.stdout.ref = LogToConsole +rootLogger.appenderRef.console.ref = LogToConsole + +logger.hibernate1.name=org.hibernate +logger.hibernate1.level=INFO +logger.trg1.name=com.trg +logger.trg1.level=INFO \ No newline at end of file diff --git a/src/core/persistence/src/test/resources/oracle/geostore-datasource-ovr.properties b/src/core/persistence/src/test/resources/oracle/geostore-datasource-ovr.properties index af9fab30..30970cdb 100644 --- a/src/core/persistence/src/test/resources/oracle/geostore-datasource-ovr.properties +++ b/src/core/persistence/src/test/resources/oracle/geostore-datasource-ovr.properties @@ -3,6 +3,23 @@ geostoreDataSource.driverClassName=oracle.jdbc.OracleDriver geostoreDataSource.url=jdbc:oracle:thin:@localhost:1521/ORCL geostoreDataSource.username=geostore geostoreDataSource.password=geostore + +### Fine Tuning of Connection Pool ### + +#geostoreDataSource.initialSize=0 +#geostoreDataSource.maxIdle=8 +#geostoreDataSource.minIdle=0 +#geostoreDataSource.poolPreparedStatements=false +#geostoreDataSource.maxOpenPreparedStatements=-1 +#geostoreDataSource.validationQuery=SELECT 1 SFOM DUAL +#geostoreDataSource.testWhileIdle=true +#geostoreDataSource.testOnBorrow=true +#geostoreDataSource.timeBetweenEvictionRunsMillis=30000 +#geostoreDataSource.numTestsPerEvictionRun=5 +#geostoreDataSource.minEvictableIdleTimeMillis=900000 + +## Vendor Adapter ## + geostoreVendorAdapter.databasePlatform=org.hibernate.dialect.Oracle10gDialect geostoreEntityManagerFactory.jpaPropertyMap[hibernate.hbm2ddl.auto]= geostoreEntityManagerFactory.jpaPropertyMap[hibernate.default_schema]=GEOSTORE diff --git a/src/core/persistence/src/test/resources/oracle/hibernate.cfg.xml b/src/core/persistence/src/test/resources/oracle/hibernate.cfg.xml index 5a973939..1c7cfc0e 100644 --- a/src/core/persistence/src/test/resources/oracle/hibernate.cfg.xml +++ b/src/core/persistence/src/test/resources/oracle/hibernate.cfg.xml @@ -16,5 +16,6 @@ + diff --git a/src/core/persistence/src/test/resources/postgres/geostore-datasource-ovr.properties b/src/core/persistence/src/test/resources/postgres/geostore-datasource-ovr.properties index 1fdfe7ea..98e73013 100644 --- a/src/core/persistence/src/test/resources/postgres/geostore-datasource-ovr.properties +++ b/src/core/persistence/src/test/resources/postgres/geostore-datasource-ovr.properties @@ -3,6 +3,23 @@ geostoreDataSource.driverClassName=org.postgresql.Driver geostoreDataSource.url=jdbc:postgresql://localhost:5432/geostore geostoreDataSource.username=geostore_test geostoreDataSource.password=geostore_test + +### Fine Tuning of Connection Pool ### + +#geostoreDataSource.initialSize=0 +#geostoreDataSource.maxIdle=8 +#geostoreDataSource.minIdle=0 +#geostoreDataSource.poolPreparedStatements=false +#geostoreDataSource.maxOpenPreparedStatements=-1 +#geostoreDataSource.validationQuery=SELECT 1 +#geostoreDataSource.testWhileIdle=true +#geostoreDataSource.testOnBorrow=true +#geostoreDataSource.timeBetweenEvictionRunsMillis=30000 +#geostoreDataSource.numTestsPerEvictionRun=5 +#geostoreDataSource.minEvictableIdleTimeMillis=900000 + +## Vendor Adapter ## + geostoreVendorAdapter.databasePlatform=org.hibernate.dialect.PostgreSQLDialect geostoreEntityManagerFactory.jpaPropertyMap[hibernate.hbm2ddl.auto]=validate geostoreEntityManagerFactory.jpaPropertyMap[hibernate.default_schema]=geostore_test diff --git a/src/core/persistence/src/test/resources/postgres/hibernate.cfg.xml b/src/core/persistence/src/test/resources/postgres/hibernate.cfg.xml index 62ca34ae..98546aab 100644 --- a/src/core/persistence/src/test/resources/postgres/hibernate.cfg.xml +++ b/src/core/persistence/src/test/resources/postgres/hibernate.cfg.xml @@ -16,5 +16,6 @@ + diff --git a/src/core/pom.xml b/src/core/pom.xml index cb87d419..753ff8c1 100644 --- a/src/core/pom.xml +++ b/src/core/pom.xml @@ -25,7 +25,7 @@ it.geosolutions.geostore geostore-root - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-core diff --git a/src/core/security/pom.xml b/src/core/security/pom.xml index c8e9a3cd..8320c607 100644 --- a/src/core/security/pom.xml +++ b/src/core/security/pom.xml @@ -18,13 +18,14 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - + 4.0.0 - + it.geosolutions.geostore geostore-core - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-security @@ -34,7 +35,7 @@ - + commons-lang @@ -55,25 +56,17 @@ commons-dbcp - commons-io - commons-io - + commons-io + commons-io + - dom4j - dom4j - - - log4j - log4j - - - org.slf4j - slf4j-log4j12 + org.apache.logging.log4j + log4j-core @@ -90,28 +83,32 @@ spring-context - - - + + + - - org.springframework.security - spring-security-core - - - org.springframework.security - spring-security-crypto - - - org.jasypt - jasypt - - - org.springframework.security - spring-security-ldap - - - + + org.springframework.security + spring-security-core + + + org.springframework.security + spring-security-crypto + + + org.jasypt + jasypt-acegisecurity + + + org.acegisecurity + acegi-security-tiger + + + org.springframework.security + spring-security-ldap + + + commons-beanutils commons-beanutils @@ -121,25 +118,24 @@ jdom jdom - + junit junit test - - - net.sf.json-lib - json-lib - 2.4 - jdk15 - - - - it.geosolutions.geostore - geostore-model - - + + + net.sf.json-lib + json-lib + + + + + it.geosolutions.geostore + geostore-model + + diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/ldap/IterableNamingEnumeration.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/ldap/IterableNamingEnumeration.java index 20a0d398..4eddfbae 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/ldap/IterableNamingEnumeration.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/ldap/IterableNamingEnumeration.java @@ -1,7 +1,6 @@ package it.geosolutions.geostore.core.ldap; import java.util.Iterator; - import javax.naming.NamingEnumeration; import javax.naming.NamingException; @@ -23,8 +22,7 @@ public boolean hasMore() { } @Override - public void close() throws NamingException { - } + public void close() throws NamingException {} @Override public boolean hasMoreElements() { diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/ldap/MockContextSource.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/ldap/MockContextSource.java index 80de910d..22d708c9 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/ldap/MockContextSource.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/ldap/MockContextSource.java @@ -7,9 +7,11 @@ public class MockContextSource implements ContextSource { DirContext ctx; + public MockContextSource(DirContext ctx) { this.ctx = ctx; } + @Override public DirContext getContext(String arg0, String arg1) throws NamingException { return ctx; @@ -24,5 +26,4 @@ public DirContext getReadOnlyContext() throws NamingException { public DirContext getReadWriteContext() throws NamingException { return ctx; } - } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/ldap/MockDirContextOperations.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/ldap/MockDirContextOperations.java index 7c277b31..1161d155 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/ldap/MockDirContextOperations.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/ldap/MockDirContextOperations.java @@ -4,7 +4,6 @@ import java.util.Hashtable; import java.util.Map; import java.util.SortedSet; - import javax.naming.Binding; import javax.naming.Context; import javax.naming.Name; @@ -17,13 +16,12 @@ import javax.naming.directory.ModificationItem; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; - import org.springframework.ldap.core.DirContextOperations; -public class MockDirContextOperations implements DirContextOperations{ +public class MockDirContextOperations implements DirContextOperations { + + private Map ldapAttributes = new HashMap(); - private Map ldapAttributes = new HashMap(); - public Map getLdapAttributes() { return ldapAttributes; } @@ -31,13 +29,13 @@ public Map getLdapAttributes() { @Override public void bind(Name name, Object obj, Attributes attrs) throws NamingException { // TODO Auto-generated method stub - + } @Override public void bind(String name, Object obj, Attributes attrs) throws NamingException { // TODO Auto-generated method stub - + } @Override @@ -103,37 +101,37 @@ public DirContext getSchemaClassDefinition(String name) throws NamingException { @Override public void modifyAttributes(Name name, ModificationItem[] mods) throws NamingException { // TODO Auto-generated method stub - + } @Override public void modifyAttributes(String name, ModificationItem[] mods) throws NamingException { // TODO Auto-generated method stub - + } @Override public void modifyAttributes(Name name, int mod_op, Attributes attrs) throws NamingException { // TODO Auto-generated method stub - + } @Override public void modifyAttributes(String name, int mod_op, Attributes attrs) throws NamingException { // TODO Auto-generated method stub - + } @Override public void rebind(Name name, Object obj, Attributes attrs) throws NamingException { // TODO Auto-generated method stub - + } @Override public void rebind(String name, Object obj, Attributes attrs) throws NamingException { // TODO Auto-generated method stub - + } @Override @@ -151,15 +149,17 @@ public NamingEnumeration search(String name, Attributes matchingAt } @Override - public NamingEnumeration search(Name name, Attributes matchingAttributes, - String[] attributesToReturn) throws NamingException { + public NamingEnumeration search( + Name name, Attributes matchingAttributes, String[] attributesToReturn) + throws NamingException { // TODO Auto-generated method stub return null; } @Override - public NamingEnumeration search(String name, Attributes matchingAttributes, - String[] attributesToReturn) throws NamingException { + public NamingEnumeration search( + String name, Attributes matchingAttributes, String[] attributesToReturn) + throws NamingException { // TODO Auto-generated method stub return null; } @@ -179,15 +179,17 @@ public NamingEnumeration search(String name, String filter, Search } @Override - public NamingEnumeration search(Name name, String filterExpr, - Object[] filterArgs, SearchControls cons) throws NamingException { + public NamingEnumeration search( + Name name, String filterExpr, Object[] filterArgs, SearchControls cons) + throws NamingException { // TODO Auto-generated method stub return null; } @Override - public NamingEnumeration search(String name, String filterExpr, - Object[] filterArgs, SearchControls cons) throws NamingException { + public NamingEnumeration search( + String name, String filterExpr, Object[] filterArgs, SearchControls cons) + throws NamingException { // TODO Auto-generated method stub return null; } @@ -201,19 +203,19 @@ public Object addToEnvironment(String propName, Object propVal) throws NamingExc @Override public void bind(Name name, Object obj) throws NamingException { // TODO Auto-generated method stub - + } @Override public void bind(String name, Object obj) throws NamingException { // TODO Auto-generated method stub - + } @Override public void close() throws NamingException { // TODO Auto-generated method stub - + } @Override @@ -243,13 +245,13 @@ public Context createSubcontext(String name) throws NamingException { @Override public void destroySubcontext(Name name) throws NamingException { // TODO Auto-generated method stub - + } @Override public void destroySubcontext(String name) throws NamingException { // TODO Auto-generated method stub - + } @Override @@ -321,13 +323,13 @@ public Object lookupLink(String name) throws NamingException { @Override public void rebind(Name name, Object obj) throws NamingException { // TODO Auto-generated method stub - + } @Override public void rebind(String name, Object obj) throws NamingException { // TODO Auto-generated method stub - + } @Override @@ -339,25 +341,25 @@ public Object removeFromEnvironment(String propName) throws NamingException { @Override public void rename(Name oldName, Name newName) throws NamingException { // TODO Auto-generated method stub - + } @Override public void rename(String oldName, String newName) throws NamingException { // TODO Auto-generated method stub - + } @Override public void unbind(Name name) throws NamingException { // TODO Auto-generated method stub - + } @Override public void unbind(String name) throws NamingException { // TODO Auto-generated method stub - + } @Override @@ -392,43 +394,43 @@ public Object getObjectAttribute(String name) { @Override public void setAttributeValue(String name, Object value) { // TODO Auto-generated method stub - + } @Override public void setAttributeValues(String name, Object[] values) { // TODO Auto-generated method stub - + } @Override public void setAttributeValues(String name, Object[] values, boolean orderMatters) { // TODO Auto-generated method stub - + } @Override public void addAttributeValue(String name, Object value) { // TODO Auto-generated method stub - + } @Override public void addAttributeValue(String name, Object value, boolean addIfDuplicateExists) { // TODO Auto-generated method stub - + } @Override public void removeAttributeValue(String name, Object value) { // TODO Auto-generated method stub - + } @Override public void update() { // TODO Auto-generated method stub - + } @Override @@ -458,7 +460,7 @@ public Name getDn() { @Override public void setDn(Name dn) { // TODO Auto-generated method stub - + } @Override @@ -484,4 +486,9 @@ public Attributes getAttributes() { return null; } + @Override + public boolean attributeExists(String arg0) { + // TODO Auto-generated method stub + return false; + } } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/ExpressionUserMapper.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/ExpressionUserMapper.java index 1cccd65c..6b0224f9 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/ExpressionUserMapper.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/ExpressionUserMapper.java @@ -29,54 +29,47 @@ import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserAttribute; - import java.util.ArrayList; import java.util.List; import java.util.Map; - import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; /** - * Implementation of UserMapper that maps attributes from a generic object - * to GeoStore User attributes. Mappings are expressed using SpEL expressions. - * - * Inherited classes should add their propertyAccessor implementations to the evaluationContext. - * - * @author Mauro Bartolomeoli + * Implementation of UserMapper that maps attributes from a generic object to GeoStore User + * attributes. Mappings are expressed using SpEL expressions. * + *

Inherited classes should add their propertyAccessor implementations to the evaluationContext. + * + * @author Mauro Bartolomeoli */ public abstract class ExpressionUserMapper implements UserMapper { Map attributeMappings; - + public static SpelExpressionParser parser = new SpelExpressionParser(); - + protected StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); - - /** - * - * @param attributeMappings maps attribute names to SpEL expressions (using a UserDetailsWithAttributes - * source) + * @param attributeMappings maps attribute names to SpEL expressions (using a + * UserDetailsWithAttributes source) */ public ExpressionUserMapper(Map attributeMappings) { this.attributeMappings = attributeMappings; } - + @Override /** * If details is a UserDetailsWithAttributes object, its attributes are mapped to User - * attributes. - * Each mapping is an SpEL expression using the UserDetailsWithAttributes as a source. - * + * attributes. Each mapping is an SpEL expression using the UserDetailsWithAttributes as a + * source. */ public void mapUser(Object details, User user) { List attributes = new ArrayList(); details = preProcessDetails(details); - for(String attributeName : attributeMappings.keySet()) { - + for (String attributeName : attributeMappings.keySet()) { + Expression exp = parser.parseExpression(attributeMappings.get(attributeName)); UserAttribute userAttribute = new UserAttribute(); userAttribute.setName(attributeName); @@ -85,11 +78,9 @@ public void mapUser(Object details, User user) { attributes.add(userAttribute); } user.setAttribute(attributes); - } protected Object preProcessDetails(Object details) { return details; } - } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/GrantedAuthoritiesMapper.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/GrantedAuthoritiesMapper.java index 7a56162b..763d52ec 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/GrantedAuthoritiesMapper.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/GrantedAuthoritiesMapper.java @@ -28,18 +28,16 @@ package it.geosolutions.geostore.core.security; import java.util.Collection; - import org.springframework.security.core.GrantedAuthority; /** - * Allows mapping authorities fetched from a Populator, following specific rules. - * Each implementation will implement a different set of mapping rules. - * - * @author Mauro Bartolomeoli + * Allows mapping authorities fetched from a Populator, following specific rules. Each + * implementation will implement a different set of mapping rules. * + * @author Mauro Bartolomeoli */ public interface GrantedAuthoritiesMapper { - + Collection mapAuthorities( Collection authorities); } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/JSONExpressionUserMapper.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/JSONExpressionUserMapper.java index 73ea9135..3b3fb95a 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/JSONExpressionUserMapper.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/JSONExpressionUserMapper.java @@ -28,72 +28,64 @@ package it.geosolutions.geostore.core.security; import java.util.Map; - +import net.sf.json.JSONObject; import org.springframework.expression.AccessException; import org.springframework.expression.EvaluationContext; import org.springframework.expression.PropertyAccessor; import org.springframework.expression.TypedValue; -import net.sf.json.JSONObject; - - /** * Maps user attributes for a JSON object. - * - * @author Geo * + * @author Geo */ public class JSONExpressionUserMapper extends ExpressionUserMapper { - public JSONExpressionUserMapper(Map attributeMappings) { super(attributeMappings); - // property accessor for JSONObject attributes (read only) - evaluationContext.addPropertyAccessor(new PropertyAccessor() { + // property accessor for JSONObject attributes (read-only) + evaluationContext.addPropertyAccessor( + new PropertyAccessor() { - @Override - public void write(EvaluationContext ctx, Object target, String name, Object value) - throws AccessException { + @Override + public void write( + EvaluationContext ctx, Object target, String name, Object value) + throws AccessException {} - } + @Override + public TypedValue read(EvaluationContext ctx, Object target, String name) + throws AccessException { + if (target instanceof JSONObject) { + JSONObject details = (JSONObject) target; + return new TypedValue(details.get(name)); + } + return null; + } - @Override - public TypedValue read(EvaluationContext ctx, Object target, String name) - throws AccessException { - if (target instanceof JSONObject) { - JSONObject details = (JSONObject) target; - return new TypedValue(details.get(name)); - } - return null; - } + @Override + public Class[] getSpecificTargetClasses() { + return new Class[] {JSONObject.class}; + } - @Override - public Class[] getSpecificTargetClasses() { - return new Class[] { JSONObject.class }; - } + @Override + public boolean canWrite(EvaluationContext ctx, Object target, String name) + throws AccessException { + return false; + } - @Override - public boolean canWrite(EvaluationContext ctx, Object target, String name) - throws AccessException { - return false; - } - - @Override - public boolean canRead(EvaluationContext ctx, Object target, String name) - throws AccessException { - return target instanceof JSONObject; - } - }); + @Override + public boolean canRead(EvaluationContext ctx, Object target, String name) + throws AccessException { + return target instanceof JSONObject; + } + }); } @Override protected Object preProcessDetails(Object details) { - if(details instanceof String) { - details = JSONObject.fromObject((String)details); + if (details instanceof String) { + details = JSONObject.fromObject(details); } return super.preProcessDetails(details); } - - - } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/MapExpressionUserMapper.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/MapExpressionUserMapper.java index 9acbe9f0..4e1e4cf5 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/MapExpressionUserMapper.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/MapExpressionUserMapper.java @@ -28,60 +28,55 @@ package it.geosolutions.geostore.core.security; import java.util.Map; - import org.springframework.expression.AccessException; import org.springframework.expression.EvaluationContext; import org.springframework.expression.PropertyAccessor; import org.springframework.expression.TypedValue; - /** * Maps user attributes for a JSON object. - * - * @author Geo * + * @author Geo */ public class MapExpressionUserMapper extends ExpressionUserMapper { - public MapExpressionUserMapper(Map attributeMappings) { super(attributeMappings); // property accessor for JSONObject attributes (read only) - evaluationContext.addPropertyAccessor(new PropertyAccessor() { - - @Override - public void write(EvaluationContext ctx, Object target, String name, Object value) - throws AccessException { + evaluationContext.addPropertyAccessor( + new PropertyAccessor() { - } + @Override + public void write( + EvaluationContext ctx, Object target, String name, Object value) + throws AccessException {} - @Override - public TypedValue read(EvaluationContext ctx, Object target, String name) - throws AccessException { - if (target instanceof Map) { - Map map = (Map) target; - return new TypedValue(map.get(name)); - } - return null; - } + @Override + public TypedValue read(EvaluationContext ctx, Object target, String name) + throws AccessException { + if (target instanceof Map) { + Map map = (Map) target; + return new TypedValue(map.get(name)); + } + return null; + } - @Override - public Class[] getSpecificTargetClasses() { - return new Class[] { Map.class }; - } + @Override + public Class[] getSpecificTargetClasses() { + return new Class[] {Map.class}; + } - @Override - public boolean canWrite(EvaluationContext ctx, Object target, String name) - throws AccessException { - return false; - } + @Override + public boolean canWrite(EvaluationContext ctx, Object target, String name) + throws AccessException { + return false; + } - @Override - public boolean canRead(EvaluationContext ctx, Object target, String name) - throws AccessException { - return target instanceof Map; - } - }); + @Override + public boolean canRead(EvaluationContext ctx, Object target, String name) + throws AccessException { + return target instanceof Map; + } + }); } - } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/SimpleGrantedAuthoritiesMapper.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/SimpleGrantedAuthoritiesMapper.java index ebdd38df..3edd24de 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/SimpleGrantedAuthoritiesMapper.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/SimpleGrantedAuthoritiesMapper.java @@ -32,20 +32,25 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.GrantedAuthorityImpl; +import org.springframework.security.core.authority.SimpleGrantedAuthority; /** - * Map based implementation of GrantedAuthoritiesMapper. - * * - * @author Mauro Bartolomeoli + * Map based implementation of GrantedAuthoritiesMapper. * * + * @author Mauro Bartolomeoli */ public class SimpleGrantedAuthoritiesMapper implements GrantedAuthoritiesMapper { - private Map mappings = new HashMap(); - + private Map mappings = new HashMap<>(); + + /** Do not consider authorities that do not exist in the mapping */ + private boolean dropUnmapped = false; + + private static final Log logger = LogFactory.getLog(SimpleGrantedAuthoritiesMapper.class); + public SimpleGrantedAuthoritiesMapper(Map mappings) { super(); this.mappings = mappings; @@ -54,18 +59,35 @@ public SimpleGrantedAuthoritiesMapper(Map mappings) { @Override public Collection mapAuthorities( Collection authorities) { - if(mappings.isEmpty()) { + if (mappings.isEmpty()) { return authorities; } - List result = new ArrayList(); - for(GrantedAuthority authority : authorities) { - if(mappings.containsKey(authority.getAuthority())) { - result.add(new GrantedAuthorityImpl(mappings.get(authority.getAuthority()))); + + List result = new ArrayList<>(); + + for (GrantedAuthority authority : authorities) { + String src = authority.getAuthority(); + if (mappings.containsKey(src)) { + String dst = mappings.get(authority.getAuthority()); + result.add(new SimpleGrantedAuthority(dst)); + if (logger.isDebugEnabled()) { + logger.debug("Mapping authority: " + src + " --> " + dst); + } + } else if (dropUnmapped) { + if (logger.isDebugEnabled()) { + logger.debug("Dropping unmapped authority: " + src); + } } else { result.add(authority); + if (logger.isDebugEnabled()) { + logger.debug("Adding unmapped authority: " + src); + } } } return result; } + public void setDropUnmapped(boolean dropUnmapped) { + this.dropUnmapped = dropUnmapped; + } } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/UserDetailsExpressionUserMapper.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/UserDetailsExpressionUserMapper.java index d9b8d91a..d31087ef 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/UserDetailsExpressionUserMapper.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/UserDetailsExpressionUserMapper.java @@ -28,60 +28,57 @@ package it.geosolutions.geostore.core.security; import java.util.Map; - import org.springframework.expression.AccessException; import org.springframework.expression.EvaluationContext; import org.springframework.expression.PropertyAccessor; import org.springframework.expression.TypedValue; /** - * Implementation of UserMapper that maps attributes from a UserDetailsWithAttributes - * object to GeoStore User attributes. Mappings are expressed using SpEL expressions. - * - * @author Mauro Bartolomeoli + * Implementation of UserMapper that maps attributes from a UserDetailsWithAttributes object to + * GeoStore User attributes. Mappings are expressed using SpEL expressions. * + * @author Mauro Bartolomeoli */ public class UserDetailsExpressionUserMapper extends ExpressionUserMapper { public UserDetailsExpressionUserMapper(Map attributeMappings) { super(attributeMappings); - - // property accessor for UserDetailsWithAttributes attributes (read only) - evaluationContext.addPropertyAccessor(new PropertyAccessor() { - @Override - public void write(EvaluationContext ctx, Object target, String name, Object value) - throws AccessException { + // property accessor for UserDetailsWithAttributes attributes (read only) + evaluationContext.addPropertyAccessor( + new PropertyAccessor() { - } + @Override + public void write( + EvaluationContext ctx, Object target, String name, Object value) + throws AccessException {} - @Override - public TypedValue read(EvaluationContext ctx, Object target, String name) - throws AccessException { - if (target instanceof UserDetailsWithAttributes) { - UserDetailsWithAttributes details = (UserDetailsWithAttributes) target; - return new TypedValue(details.getAttribute(name)); - } - return null; - } + @Override + public TypedValue read(EvaluationContext ctx, Object target, String name) + throws AccessException { + if (target instanceof UserDetailsWithAttributes) { + UserDetailsWithAttributes details = (UserDetailsWithAttributes) target; + return new TypedValue(details.getAttribute(name)); + } + return null; + } - @Override - public Class[] getSpecificTargetClasses() { - return new Class[] { UserDetailsWithAttributes.class }; - } + @Override + public Class[] getSpecificTargetClasses() { + return new Class[] {UserDetailsWithAttributes.class}; + } - @Override - public boolean canWrite(EvaluationContext ctx, Object target, String name) - throws AccessException { - return false; - } + @Override + public boolean canWrite(EvaluationContext ctx, Object target, String name) + throws AccessException { + return false; + } - @Override - public boolean canRead(EvaluationContext ctx, Object target, String name) - throws AccessException { - return target instanceof UserDetailsWithAttributes; - } - }); + @Override + public boolean canRead(EvaluationContext ctx, Object target, String name) + throws AccessException { + return target instanceof UserDetailsWithAttributes; + } + }); } - } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/UserDetailsWithAttributes.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/UserDetailsWithAttributes.java index 001cd2b6..b373e82c 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/UserDetailsWithAttributes.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/UserDetailsWithAttributes.java @@ -31,10 +31,9 @@ /** * Extends UserDetails with the ability to store generic attributes. - * - * @author Mauro Bartolomeoli * + * @author Mauro Bartolomeoli */ -public interface UserDetailsWithAttributes extends UserDetails{ +public interface UserDetailsWithAttributes extends UserDetails { public Object getAttribute(String name); } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/UserMapper.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/UserMapper.java index b719e74c..85b486de 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/UserMapper.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/UserMapper.java @@ -30,14 +30,12 @@ import it.geosolutions.geostore.core.model.User; /** - * Interface used to map a user detail object (in any format) to a GeoStore User - * object. Allows for custom attributes / groups / roles mapping. - * - * @author Mauro Bartolomeoli + * Interface used to map a user detail object (in any format) to a GeoStore User object. Allows for + * custom attributes / groups / roles mapping. * + * @author Mauro Bartolomeoli */ public interface UserMapper { void mapUser(Object details, User user); - } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/ldap/CustomAttributesLdapUserDetailsMapper.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/ldap/CustomAttributesLdapUserDetailsMapper.java index 5e6d84ac..bc421531 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/ldap/CustomAttributesLdapUserDetailsMapper.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/ldap/CustomAttributesLdapUserDetailsMapper.java @@ -29,7 +29,6 @@ import java.util.Collection; import java.util.Map; - import org.springframework.ldap.core.DirContextOperations; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; @@ -38,29 +37,31 @@ /** * Extends LdapUserDetailsMapper with the ability to map LDAP attributes to UserDetails attributes. - * + * * @author Mauro Bartolomeoli */ public class CustomAttributesLdapUserDetailsMapper extends LdapUserDetailsMapper { Map attributeMappings; - + public CustomAttributesLdapUserDetailsMapper(Map attributeMappings) { super(); this.attributeMappings = attributeMappings; } - - @Override - public UserDetails mapUserFromContext(DirContextOperations ctx, String username, - Collection authorities) { - LdapUserDetails details = (LdapUserDetails)super.mapUserFromContext(ctx, username, authorities); - LdapUserDetailsWithAttributes detailsWithAttributes = new LdapUserDetailsWithAttributes(details); - for(String attributeName : attributeMappings.keySet()) { - detailsWithAttributes.setAttribute(attributeName, ctx.getStringAttribute(attributeMappings.get(attributeName))); + public UserDetails mapUserFromContext( + DirContextOperations ctx, + String username, + Collection authorities) { + LdapUserDetails details = + (LdapUserDetails) super.mapUserFromContext(ctx, username, authorities); + LdapUserDetailsWithAttributes detailsWithAttributes = + new LdapUserDetailsWithAttributes(details); + for (String attributeName : attributeMappings.keySet()) { + detailsWithAttributes.setAttribute( + attributeName, ctx.getStringAttribute(attributeMappings.get(attributeName))); } return detailsWithAttributes; } - } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/ldap/LdapUserDetailsWithAttributes.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/ldap/LdapUserDetailsWithAttributes.java index 09f4d7c2..fd50929a 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/ldap/LdapUserDetailsWithAttributes.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/ldap/LdapUserDetailsWithAttributes.java @@ -27,30 +27,23 @@ */ package it.geosolutions.geostore.core.security.ldap; - - - - import it.geosolutions.geostore.core.security.UserDetailsWithAttributes; - import java.util.Collection; import java.util.HashMap; import java.util.Map; - import org.springframework.security.core.GrantedAuthority; import org.springframework.security.ldap.userdetails.LdapUserDetails; /** * Extends LdapUserDetails with the ability to store attributes coming from Ldap. - * - * @author Mauro Bartolomeoli * + * @author Mauro Bartolomeoli */ public class LdapUserDetailsWithAttributes implements LdapUserDetails, UserDetailsWithAttributes { private LdapUserDetails delegate; - + private Map attributes = new HashMap(); - + public LdapUserDetailsWithAttributes(LdapUserDetails delegate) { super(); this.delegate = delegate; @@ -59,12 +52,12 @@ public LdapUserDetailsWithAttributes(LdapUserDetails delegate) { public void setAttribute(String name, Object value) { this.attributes.put(name, value); } - + public Object getAttribute(String name) { return attributes.get(name); } - - public Collection getAuthorities() { + + public Collection getAuthorities() { return delegate.getAuthorities(); } @@ -107,6 +100,9 @@ public int hashCode() { public String toString() { return delegate.toString(); } - - + + @Override + public void eraseCredentials() { + delegate.eraseCredentials(); + } } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/AbstractGeoStorePasswordEncoder.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/AbstractGeoStorePasswordEncoder.java index efdfbd2e..9e782349 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/AbstractGeoStorePasswordEncoder.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/AbstractGeoStorePasswordEncoder.java @@ -19,19 +19,17 @@ */ package it.geosolutions.geostore.core.security.password; +import org.acegisecurity.providers.encoding.PasswordEncoder; import org.springframework.dao.DataAccessException; -import org.springframework.security.authentication.encoding.PasswordEncoder; /** - * Abstract base implementation, delegating the encoding - * to third party encoders implementing {@link PasswordEncoder} - * - * @author Lorenzo Natali + * Abstract base implementation, delegating the encoding to third party encoders implementing {@link + * PasswordEncoder} * + * @author Lorenzo Natali */ public abstract class AbstractGeoStorePasswordEncoder implements GeoStorePasswordEncoder { - protected volatile PasswordEncoder stringEncoder; protected volatile CharArrayPasswordEncoder charEncoder; @@ -41,7 +39,6 @@ public abstract class AbstractGeoStorePasswordEncoder implements GeoStorePasswor private boolean reversible = true; private String prefix; - public String getName() { return name; } @@ -50,7 +47,6 @@ public void setBeanName(String beanName) { this.name = beanName; } - protected PasswordEncoder getStringEncoder() { if (stringEncoder == null) { synchronized (this) { @@ -62,9 +58,7 @@ protected PasswordEncoder getStringEncoder() { return stringEncoder; } - /** - * Creates the encoder instance used when source is a string. - */ + /** Creates the encoder instance used when source is a string. */ protected abstract PasswordEncoder createStringEncoder(); protected CharArrayPasswordEncoder getCharEncoder() { @@ -78,26 +72,31 @@ protected CharArrayPasswordEncoder getCharEncoder() { return charEncoder; } - /** - * Creates the encoder instance used when source is a char array. - */ + /** Creates the encoder instance used when source is a char array. */ protected abstract CharArrayPasswordEncoder createCharEncoder(); - /** - * @return the concrete {@link PasswordEncoder} object - */ + /** @return the concrete {@link PasswordEncoder} object */ protected final PasswordEncoder getActualEncoder() { return null; } - - @Override + public String encodePassword(String rawPass, Object salt) throws DataAccessException { return doEncodePassword(getStringEncoder().encodePassword(rawPass, salt)); } @Override - public String encodePassword(char[] rawPass, Object salt) - throws DataAccessException { + public String encode(CharSequence rawPass) { + return doEncodePassword(getStringEncoder().encodePassword(rawPass.toString(), "salt")); + } + + @Override + public boolean matches(CharSequence encPass, String rawPass) { + if (encPass == null) return false; + return getStringEncoder().isPasswordValid(stripPrefix(encPass.toString()), rawPass, "salt"); + } + + @Override + public String encodePassword(char[] rawPass, Object salt) throws DataAccessException { return doEncodePassword(getCharEncoder().encodePassword(rawPass, salt)); } @@ -119,16 +118,15 @@ StringBuffer initPasswordBuffer() { return buff; } - @Override public boolean isPasswordValid(String encPass, String rawPass, Object salt) throws DataAccessException { - if (encPass==null) return false; + if (encPass == null) return false; return getStringEncoder().isPasswordValid(stripPrefix(encPass), rawPass, salt); } @Override public boolean isPasswordValid(String encPass, char[] rawPass, Object salt) { - if (encPass==null) return false; + if (encPass == null) return false; return getCharEncoder().isPasswordValid(stripPrefix(encPass), rawPass, salt); } @@ -137,7 +135,7 @@ String stripPrefix(String encPass) { } protected String removePrefix(String encPass) { - return encPass.replaceFirst(getPrefix()+GeoStorePasswordEncoder.PREFIX_DELIMTER, ""); + return encPass.replaceFirst(getPrefix() + GeoStorePasswordEncoder.PREFIX_DELIMTER, ""); } @Override @@ -148,18 +146,16 @@ protected String removePrefix(String encPass) { * @return true if this encoder has encoded encPass */ public boolean isResponsibleForEncoding(String encPass) { - if (encPass==null) return false; - return encPass.startsWith(getPrefix()+GeoStorePasswordEncoder.PREFIX_DELIMTER); + if (encPass == null) return false; + return encPass.startsWith(getPrefix() + GeoStorePasswordEncoder.PREFIX_DELIMTER); } - - + public String decode(String encPass) throws UnsupportedOperationException { throw new UnsupportedOperationException("decoding passwords not supported"); } @Override - public char[] decodeToCharArray(String encPass) - throws UnsupportedOperationException { + public char[] decodeToCharArray(String encPass) throws UnsupportedOperationException { throw new UnsupportedOperationException("decoding passwords not supported"); } @@ -167,7 +163,6 @@ public String getPrefix() { return prefix; } - public void setPrefix(String prefix) { this.prefix = prefix; } @@ -176,7 +171,6 @@ public boolean isAvailableWithoutStrongCryptogaphy() { return availableWithoutStrongCryptogaphy; } - public void setAvailableWithoutStrongCryptogaphy(boolean availableWithoutStrongCryptogaphy) { this.availableWithoutStrongCryptogaphy = availableWithoutStrongCryptogaphy; } @@ -189,9 +183,7 @@ public void setReversible(boolean reversible) { this.reversible = reversible; } - /** - * Interface for password encoding when source password is specified as char array. - */ + /** Interface for password encoding when source password is specified as char array. */ protected static interface CharArrayPasswordEncoder { String encodePassword(char[] rawPass, Object salt); @@ -199,4 +191,3 @@ protected static interface CharArrayPasswordEncoder { boolean isPasswordValid(String encPass, char[] rawPass, Object salt); } } - diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStoreAESEncoder.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStoreAESEncoder.java index 1809a7af..8b5fe72b 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStoreAESEncoder.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStoreAESEncoder.java @@ -2,85 +2,84 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; - import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.SecretKeySpec; - +import org.acegisecurity.providers.encoding.PasswordEncoder; import org.apache.commons.codec.binary.Base64; import org.springframework.dao.DataAccessException; -import org.springframework.security.authentication.encoding.PasswordEncoder; + /** * This class wraps the old password encoding and decoding system - * @author Lorenzo Natali (lorenzo.natali at geo-solutions.it) * + * @author Lorenzo Natali (lorenzo.natali at geo-solutions.it) */ -public class GeoStoreAESEncoder extends AbstractGeoStorePasswordEncoder { - private static final byte[] DEFALULT_KEY = "installation dependant key needed".substring(0, 16).getBytes(); - private byte[] key = GeoStoreAESEncoder.DEFALULT_KEY; +public class GeoStoreAESEncoder extends AbstractGeoStorePasswordEncoder { + private static final byte[] DEFALULT_KEY = + "installation dependant key needed".substring(0, 16).getBytes(); + private byte[] key = GeoStoreAESEncoder.DEFALULT_KEY; - public void setKey(String key) { - this.key = key.substring(0, 16).getBytes(); - } + public void setKey(String key) { + this.key = key.substring(0, 16).getBytes(); + } - @Override - protected PasswordEncoder createStringEncoder() { - // TODO Auto-generated method stub - return null; - } + @Override + protected PasswordEncoder createStringEncoder() { + // TODO Auto-generated method stub + return null; + } - @Override - protected CharArrayPasswordEncoder createCharEncoder() { - // TODO Auto-generated method stub - return null; - } + @Override + protected CharArrayPasswordEncoder createCharEncoder() { + // TODO Auto-generated method stub + return null; + } - @Override - public PasswordEncodingType getEncodingType() { - return PasswordEncodingType.GEOSTORE; - } + @Override + public PasswordEncodingType getEncodingType() { + return PasswordEncodingType.GEOSTORE; + } - @Override - public boolean isPasswordValid(String encPass, String rawPass, Object salt) - throws DataAccessException { - if (encPass==null) return false; - return rawPass.equals(decode(encPass)); - } - - @Override - public String encodePassword(char[] rawPass, Object salt) - throws DataAccessException { - try { - SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); - Cipher cipher = Cipher.getInstance("AES"); - cipher.init(Cipher.ENCRYPT_MODE, keySpec); - byte[] b = new byte[rawPass.length]; - for (int i = 0; i < b.length; i++) { - b[i] = (byte) rawPass[i]; - } + @Override + public boolean isPasswordValid(String encPass, String rawPass, Object salt) + throws DataAccessException { + if (encPass == null) return false; + return rawPass.equals(decode(encPass)); + } - byte[] input = b; - byte[] encrypted = cipher.doFinal(input); - byte[] output = Base64.encodeBase64(encrypted); - return new String(output); - } catch (NoSuchAlgorithmException ex) { - throw new RuntimeException("Error while encoding", ex); - } catch (NoSuchPaddingException ex) { - throw new RuntimeException("Error while encoding", ex); - } catch (IllegalBlockSizeException ex) { - throw new RuntimeException("Error while encoding", ex); - } catch (BadPaddingException ex) { - throw new RuntimeException("Error while encoding", ex); - } catch (InvalidKeyException ex) { - throw new RuntimeException("Error while encoding", ex); - } - } + @Override + public String encodePassword(char[] rawPass, Object salt) throws DataAccessException { + try { + SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.ENCRYPT_MODE, keySpec); + byte[] b = new byte[rawPass.length]; + for (int i = 0; i < b.length; i++) { + b[i] = (byte) rawPass[i]; + } - @Override - public String decode(String encPass) throws UnsupportedOperationException { - try { + byte[] input = b; + byte[] encrypted = cipher.doFinal(input); + byte[] output = Base64.encodeBase64(encrypted); + return new String(output); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException("Error while encoding", ex); + } catch (NoSuchPaddingException ex) { + throw new RuntimeException("Error while encoding", ex); + } catch (IllegalBlockSizeException ex) { + throw new RuntimeException("Error while encoding", ex); + } catch (BadPaddingException ex) { + throw new RuntimeException("Error while encoding", ex); + } catch (InvalidKeyException ex) { + throw new RuntimeException("Error while encoding", ex); + } + } + + @Override + public String decode(String encPass) throws UnsupportedOperationException { + try { SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, keySpec); @@ -100,20 +99,21 @@ public String decode(String encPass) throws UnsupportedOperationException { } catch (InvalidKeyException ex) { throw new RuntimeException("Error while encoding", ex); } - - } - - @Override - public boolean isResponsibleForEncoding(String encPass) { - if(encPass == null ) return false; - - String[] split = encPass.split(GeoStorePasswordEncoder.PREFIX_DELIMTER); - if(split.length == 0) return true; - //TODO get these strings in a better way - String prefix = split[0]; - - return !(prefix.equals("crypt1") || prefix.equals("crypt2") || prefix.equals("digest1") || prefix.equals("empty") || prefix.equals("plain")); - - } + } + + @Override + public boolean isResponsibleForEncoding(String encPass) { + if (encPass == null) return false; + + String[] split = encPass.split(GeoStorePasswordEncoder.PREFIX_DELIMTER); + if (split.length == 0) return true; + // TODO get these strings in a better way + String prefix = split[0]; + return !(prefix.equals("crypt1") + || prefix.equals("crypt2") + || prefix.equals("digest1") + || prefix.equals("empty") + || prefix.equals("plain")); + } } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStoreDigestPasswordEncoder.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStoreDigestPasswordEncoder.java index e036eef1..dee0e41a 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStoreDigestPasswordEncoder.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStoreDigestPasswordEncoder.java @@ -1,77 +1,76 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.core.security.password; +import static it.geosolutions.geostore.core.security.password.SecurityUtils.toBytes; + import org.apache.commons.codec.binary.Base64; import org.jasypt.digest.StandardByteDigester; -import org.jasypt.spring.security3.PasswordEncoder; +import org.jasypt.spring.security.PasswordEncoder; import org.jasypt.util.password.StrongPasswordEncryptor; -import static it.geosolutions.geostore.core.security.password.SecurityUtils.toBytes; /** - * This Encoder provide encription and check of password using a digest - * @author Lorenzo Natali (lorenzo.natali at geo-solutions.it) + * This Encoder provides encryption and check of password using a digest * + * @author Lorenzo Natali (lorenzo.natali at geo-solutions.it) */ -public class GeoStoreDigestPasswordEncoder extends AbstractGeoStorePasswordEncoder{ - +public class GeoStoreDigestPasswordEncoder extends AbstractGeoStorePasswordEncoder { + + /** The digest is not reversible */ + public GeoStoreDigestPasswordEncoder() { + setReversible(false); + } + + @Override + protected PasswordEncoder createStringEncoder() { + PasswordEncoder encoder = new PasswordEncoder(); + encoder.setPasswordEncryptor(new StrongPasswordEncryptor()); + return encoder; + } + + @Override + protected CharArrayPasswordEncoder createCharEncoder() { + return new CharArrayPasswordEncoder() { + StandardByteDigester digester = new StandardByteDigester(); - /** - * The digest is not reversible - */ - public GeoStoreDigestPasswordEncoder() { - setReversible(false); - } + { + digester.setAlgorithm("SHA-256"); + digester.setIterations(100000); + digester.setSaltSizeBytes(16); + digester.initialize(); + } - @Override - protected PasswordEncoder createStringEncoder() { - PasswordEncoder encoder = new PasswordEncoder(); - encoder.setPasswordEncryptor(new StrongPasswordEncryptor()); - return encoder; - } + @Override + public String encodePassword(char[] rawPass, Object salt) { + return new String(Base64.encodeBase64(digester.digest(toBytes(rawPass)))); + } - @Override - protected CharArrayPasswordEncoder createCharEncoder() { - return new CharArrayPasswordEncoder() { - StandardByteDigester digester = new StandardByteDigester(); - { - digester.setAlgorithm("SHA-256"); - digester.setIterations(100000); - digester.setSaltSizeBytes(16); - digester.initialize(); - } - - @Override - public String encodePassword(char[] rawPass, Object salt) { - return new String(Base64.encodeBase64(digester.digest(toBytes(rawPass)))); - } - @Override - public boolean isPasswordValid(String encPass, char[] rawPass, Object salt) { - return digester.matches(toBytes(rawPass), Base64.decodeBase64(encPass.getBytes())); - } - }; - } + @Override + public boolean isPasswordValid(String encPass, char[] rawPass, Object salt) { + return digester.matches(toBytes(rawPass), Base64.decodeBase64(encPass.getBytes())); + } + }; + } - @Override - public PasswordEncodingType getEncodingType() { - return PasswordEncodingType.DIGEST; - } - + @Override + public PasswordEncodingType getEncodingType() { + return PasswordEncodingType.DIGEST; + } } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStorePBEPasswordEncoder.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStorePBEPasswordEncoder.java index 8c854b77..8540adf8 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStorePBEPasswordEncoder.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStorePBEPasswordEncoder.java @@ -26,177 +26,174 @@ import java.io.IOException; import java.util.Arrays; - +import java.util.Base64; +import org.acegisecurity.providers.encoding.PasswordEncoder; +import org.jasypt.acegisecurity.PBEPasswordEncoder; import org.jasypt.encryption.pbe.StandardPBEByteEncryptor; import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; -import org.jasypt.spring.security3.PBEPasswordEncoder; -import org.springframework.security.authentication.encoding.PasswordEncoder; -import org.springframework.security.crypto.codec.Base64; + /** * Password Encoder using symmetric encryption - * - * The salt parameter is not used, this implementation computes a random salt as - * default. - * - * {@link #isPasswordValid(String, String, Object)} - * {@link #encodePassword(String, Object)} - * + * + *

The salt parameter is not used, this implementation computes a random salt as default. + * + *

{@link #isPasswordValid(String, String, Object)} {@link #encodePassword(String, Object)} + * * @author Lorenzo Natali - * */ public class GeoStorePBEPasswordEncoder extends AbstractGeoStorePasswordEncoder { - StandardPBEStringEncryptor stringEncrypter; - StandardPBEByteEncryptor byteEncrypter; - - private String providerName, algorithm; - private String keyAliasInKeyStore = KeyStoreProviderImpl.CONFIGPASSWORDKEY; - - private KeyStoreProvider keystoreProvider; - - public KeyStoreProvider getKeystoreProvider() { - return keystoreProvider; - } - - public void setKeystoreProvider(KeyStoreProvider keystoreProvider) { - this.keystoreProvider = keystoreProvider; - } - - public void setKeyAliasInKeyStore(String keyAliasInKeyStore) { - this.keyAliasInKeyStore = keyAliasInKeyStore; - } - - public String getProviderName() { - return providerName; - } - - public void setProviderName(String providerName) { - this.providerName = providerName; - } - - public String getAlgorithm() { - return algorithm; - } - - public void setAlgorithm(String algorithm) { - this.algorithm = algorithm; - } - - public String getKeyAliasInKeyStore() { - return keyAliasInKeyStore; - } - - @Override - protected PasswordEncoder createStringEncoder() { - byte[] password = lookupPasswordFromKeyStore(); - - char[] chars = toChars(password); - try { - stringEncrypter = new StandardPBEStringEncryptor(); - stringEncrypter.setPasswordCharArray(chars); - - if (getProviderName() != null && !getProviderName().isEmpty()) { - stringEncrypter.setProviderName(getProviderName()); - } - stringEncrypter.setAlgorithm(getAlgorithm()); - - PBEPasswordEncoder encoder = new PBEPasswordEncoder(); - encoder.setPbeStringEncryptor(stringEncrypter); - - return encoder; - } finally { - scramble(password); - scramble(chars); - } - } - - @Override - protected CharArrayPasswordEncoder createCharEncoder() { - byte[] password = lookupPasswordFromKeyStore(); - char[] chars = toChars(password); - - byteEncrypter = new StandardPBEByteEncryptor(); - byteEncrypter.setPasswordCharArray(chars); - - if (getProviderName() != null && !getProviderName().isEmpty()) { - byteEncrypter.setProviderName(getProviderName()); - } - byteEncrypter.setAlgorithm(getAlgorithm()); - - return new CharArrayPasswordEncoder() { - @Override - public boolean isPasswordValid(String encPass, char[] rawPass, - Object salt) { - byte[] decoded = Base64.decode(encPass.getBytes()); - byte[] decrypted = byteEncrypter.decrypt(decoded); - - char[] chars = toChars(decrypted); - try { - return Arrays.equals(chars, rawPass); - } finally { - scramble(decrypted); - scramble(chars); - } - } - - @Override - public String encodePassword(char[] rawPass, Object salt) { - byte[] bytes = toBytes(rawPass); - try { - return new String(Base64.encode(byteEncrypter - .encrypt(bytes))); - } finally { - scramble(bytes); - } - } - }; - } - - byte[] lookupPasswordFromKeyStore() { - try { - if (!keystoreProvider.containsAlias(getKeyAliasInKeyStore())) { - throw new RuntimeException("Keystore: " - + keystoreProvider.getFile() + " does not" - + " contain alias: " + getKeyAliasInKeyStore()); - } - return keystoreProvider.getSecretKey(getKeyAliasInKeyStore()) - .getEncoded(); - } catch (IOException e) { - throw new RuntimeException("Cannot find alias: " - + getKeyAliasInKeyStore() + " in " - + keystoreProvider.getFile().getAbsolutePath()); - } - } - - @Override - public PasswordEncodingType getEncodingType() { - return PasswordEncodingType.ENCRYPT; - } - - public String decode(String encPass) throws UnsupportedOperationException { - if (stringEncrypter == null) { - // not initialized - getStringEncoder(); - } - - return stringEncrypter.decrypt(removePrefix(encPass)); - } - - @Override - public char[] decodeToCharArray(String encPass) - throws UnsupportedOperationException { - if (byteEncrypter == null) { - // not initialized - getCharEncoder(); - } - - byte[] decoded = Base64.decode(removePrefix(encPass).getBytes()); - byte[] bytes = byteEncrypter.decrypt(decoded); - try { - return toChars(bytes); - } finally { - scramble(bytes); - } - } - + StandardPBEStringEncryptor stringEncrypter; + StandardPBEByteEncryptor byteEncrypter; + + private String providerName, algorithm; + private String keyAliasInKeyStore = KeyStoreProviderImpl.CONFIGPASSWORDKEY; + + private KeyStoreProvider keystoreProvider; + + public KeyStoreProvider getKeystoreProvider() { + return keystoreProvider; + } + + public void setKeystoreProvider(KeyStoreProvider keystoreProvider) { + this.keystoreProvider = keystoreProvider; + } + + public void setKeyAliasInKeyStore(String keyAliasInKeyStore) { + this.keyAliasInKeyStore = keyAliasInKeyStore; + } + + public String getProviderName() { + return providerName; + } + + public void setProviderName(String providerName) { + this.providerName = providerName; + } + + public String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public String getKeyAliasInKeyStore() { + return keyAliasInKeyStore; + } + + @Override + protected PasswordEncoder createStringEncoder() { + byte[] password = lookupPasswordFromKeyStore(); + + char[] chars = toChars(password); + try { + stringEncrypter = new StandardPBEStringEncryptor(); + stringEncrypter.setPasswordCharArray(chars); + + if (getProviderName() != null && !getProviderName().isEmpty()) { + stringEncrypter.setProviderName(getProviderName()); + } + stringEncrypter.setAlgorithm(getAlgorithm()); + + PBEPasswordEncoder encoder = new PBEPasswordEncoder(); + encoder.setPbeStringEncryptor(stringEncrypter); + + return encoder; + } finally { + scramble(password); + scramble(chars); + } + } + + @Override + protected CharArrayPasswordEncoder createCharEncoder() { + byte[] password = lookupPasswordFromKeyStore(); + char[] chars = toChars(password); + + byteEncrypter = new StandardPBEByteEncryptor(); + byteEncrypter.setPasswordCharArray(chars); + + if (getProviderName() != null && !getProviderName().isEmpty()) { + byteEncrypter.setProviderName(getProviderName()); + } + byteEncrypter.setAlgorithm(getAlgorithm()); + + return new CharArrayPasswordEncoder() { + @Override + public boolean isPasswordValid(String encPass, char[] rawPass, Object salt) { + byte[] decoded = Base64.getDecoder().decode(encPass.getBytes()); + byte[] decrypted = byteEncrypter.decrypt(decoded); + + char[] chars = toChars(decrypted); + try { + return Arrays.equals(chars, rawPass); + } finally { + scramble(decrypted); + scramble(chars); + } + } + + @Override + public String encodePassword(char[] rawPass, Object salt) { + byte[] bytes = toBytes(rawPass); + try { + return new String(Base64.getEncoder().encode(byteEncrypter.encrypt(bytes))); + } finally { + scramble(bytes); + } + } + }; + } + + byte[] lookupPasswordFromKeyStore() { + try { + if (!keystoreProvider.containsAlias(getKeyAliasInKeyStore())) { + throw new RuntimeException( + "Keystore: " + + keystoreProvider.getFile() + + " does not" + + " contain alias: " + + getKeyAliasInKeyStore()); + } + return keystoreProvider.getSecretKey(getKeyAliasInKeyStore()).getEncoded(); + } catch (IOException e) { + throw new RuntimeException( + "Cannot find alias: " + + getKeyAliasInKeyStore() + + " in " + + keystoreProvider.getFile().getAbsolutePath()); + } + } + + @Override + public PasswordEncodingType getEncodingType() { + return PasswordEncodingType.ENCRYPT; + } + + public String decode(String encPass) throws UnsupportedOperationException { + if (stringEncrypter == null) { + // not initialized + getStringEncoder(); + } + + return stringEncrypter.decrypt(removePrefix(encPass)); + } + + @Override + public char[] decodeToCharArray(String encPass) throws UnsupportedOperationException { + if (byteEncrypter == null) { + // not initialized + getCharEncoder(); + } + + byte[] decoded = Base64.getDecoder().decode(removePrefix(encPass).getBytes()); + byte[] bytes = byteEncrypter.decrypt(decoded); + try { + return toChars(bytes); + } finally { + scramble(bytes); + } + } } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStorePasswordEncoder.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStorePasswordEncoder.java index a4a34d9b..bd6824a7 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStorePasswordEncoder.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/GeoStorePasswordEncoder.java @@ -1,93 +1,86 @@ -package it.geosolutions.geostore.core.security.password; - -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.security.authentication.encoding.PasswordEncoder; - - - -/** - * Password encoders have to implement this interface to be used in GeoStore - * @author Lorenzo Natali (lorenzo.natali at geo-solutions.it) - * - */ -public interface GeoStorePasswordEncoder extends PasswordEncoder, BeanNameAware { - - public final static String PREFIX_DELIMTER = ":"; - - /** - * The name of the password encoder. - */ - String getName(); - - /** - * @param encPass - * @return true if this encoder has encoded encPass - */ - boolean isResponsibleForEncoding(String encPass); - - /** - * Decodes an encoded password. Only supported for - * {@link PasswordEncodingType#ENCRYPT} and - * {@link PasswordEncodingType#PLAIN} encoders, ie those that return - * true from {@link #isReversible()}. - * - * @param encPass - * The encoded password. - * @throws UnsupportedOperationException - */ - String decode(String encPass) throws UnsupportedOperationException; - - /** - * Decodes an encoded password to a char array. - * - * @see #decode(String) - */ - char[] decodeToCharArray(String encPass) - throws UnsupportedOperationException; - - /** - * Encodes a raw password from a char array. - * - * @see #encodePassword(String, Object) - */ - String encodePassword(char[] password, Object salt); - - /** - * Validates a specified "raw" password (as char array) against an encoded - * password. - * - * @see {@link #isPasswordValid(String, String, Object) - */ - boolean isPasswordValid(String encPass, char[] rawPass, Object salt); - - /** - * @return a prefix which is stored with the password. This prefix must be - * unique within all {@link GeoStorePasswordEncoder} - * implementations. - * - * Reserved: - * - * plain digest1 crypt1 - * - * A plain text password is stored as - * - * plain:password - */ - String getPrefix(); - - /** - * Is this encoder available without installing the unrestricted policy - * files of the java cryptographic extension - * - * @return - */ - boolean isAvailableWithoutStrongCryptogaphy(); - - /** - * Flag indicating if the encoder can decode an encrypted password back into - * its original plain text form. - */ - boolean isReversible(); - - PasswordEncodingType getEncodingType(); -} +package it.geosolutions.geostore.core.security.password; + +import org.springframework.beans.factory.BeanNameAware; +import org.springframework.security.crypto.password.PasswordEncoder; + +/** + * Password encoders have to implement this interface to be used in GeoStore + * + * @author Lorenzo Natali (lorenzo.natali at geo-solutions.it) + */ +public interface GeoStorePasswordEncoder extends PasswordEncoder, BeanNameAware { + + public static final String PREFIX_DELIMTER = ":"; + + /** The name of the password encoder. */ + String getName(); + + /** + * @param encPass + * @return true if this encoder has encoded encPass + */ + boolean isResponsibleForEncoding(String encPass); + + /** + * Decodes an encoded password. Only supported for {@link PasswordEncodingType#ENCRYPT} and + * {@link PasswordEncodingType#PLAIN} encoders, ie those that return true from + * {@link #isReversible()}. + * + * @param encPass The encoded password. + * @throws UnsupportedOperationException + */ + String decode(String encPass) throws UnsupportedOperationException; + + /** + * Decodes an encoded password to a char array. + * + * @see #decode(String) + */ + char[] decodeToCharArray(String encPass) throws UnsupportedOperationException; + + /** + * Encodes a raw password from a char array. + * + * @see #encodePassword(String, Object) + */ + String encodePassword(char[] password, Object salt); + + String encodePassword(String password, Object salt); + + /** + * Validates a specified "raw" password (as char array) against an encoded + * password. + * + * @see {@link #isPasswordValid(String, String, Object) + * + */ + boolean isPasswordValid(String encPass, char[] rawPass, Object salt); + + boolean isPasswordValid(String encPass, String rawPass, Object salt); + + /** + * @return a prefix which is stored with the password. This prefix must be unique within all + * {@link GeoStorePasswordEncoder} implementations. + *

Reserved: + *

plain digest1 crypt1 + *

A plain text password is stored as + *

plain:password + */ + String getPrefix(); + + /** + * Is this encoder available without installing the unrestricted policy files of the java + * cryptographic extension + * + * @return + */ + boolean isAvailableWithoutStrongCryptogaphy(); + + /** + * Flag indicating if the encoder can decode an encrypted password back into its original plain + * text form. + */ + boolean isReversible(); + + PasswordEncodingType getEncodingType(); +} diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/KeyStoreProvider.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/KeyStoreProvider.java index 918196a2..4b3ae7ae 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/KeyStoreProvider.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/KeyStoreProvider.java @@ -12,37 +12,29 @@ import java.security.PrivateKey; import java.security.PublicKey; import java.util.Enumeration; - import javax.crypto.SecretKey; - /** - * This interface have to be implemented by object that - * retrieve and serve objects. - * The interface provides utilities to access, insert and delete keys. - * @author Admin + * This interface have to be implemented by object that retrieve and serve objects. The + * interface provides utilities to access, insert and delete keys. * + * @author Admin */ public interface KeyStoreProvider { - - /** - * @return the default key store {@link File} object - */ + /** @return the default key store {@link File} object */ File getFile(); /** * Forces a reload of the key store - * + * * @throws IOException */ void reloadKeyStore() throws IOException; /** - * Gets the {@link Key} object for this alias - * null if the alias does not - * exist - * + * Gets the {@link Key} object for this alias null if the alias does not exist + * * @param alias * @return * @throws IOException @@ -50,27 +42,24 @@ public interface KeyStoreProvider { Key getKey(String alias) throws IOException; /** - * Gets the key for encrypting passwords stored - * in configuration files, may be null - * + * Gets the key for encrypting passwords stored in configuration files, may be null + * * @return * @throws IOException */ byte[] getConfigPasswordKey() throws IOException; /** - * Checks if a such a key is available - * without presenting the key itself - * + * Checks if a such a key is available without presenting the key itself + * * @return * @throws IOException */ boolean hasConfigPasswordKey() throws IOException; - /** * Test it the key store contains a alias - * + * * @param alias * @return * @throws IOException @@ -78,11 +67,11 @@ public interface KeyStoreProvider { boolean containsAlias(String alias) throws IOException; /** - * Returns the key for a {@link GeoServerUserGroupService} - * service Name. Needed if the service uses symmetric password - * encryption - * - * may be null + * Returns the key for a {@link GeoServerUserGroupService} service Name. Needed if the service + * uses symmetric password encryption + * + *

may be null + * * @param serviceName * @return * @throws IOException @@ -90,18 +79,17 @@ public interface KeyStoreProvider { byte[] getUserGroupKey(String serviceName) throws IOException; /** - * Checks if a such a key is available - * without presenting the key itself - * + * Checks if a such a key is available without presenting the key itself + * * @return * @throws IOException */ boolean hasUserGroupKey(String serviceName) throws IOException; /** - * Gets the {@link SecretKey} object for this alias - * null if the alias does not + * Gets the {@link SecretKey} object for this alias null if the alias does not * exist + * * @param name * @return * @throws IOException if the key exists but has the wrong type @@ -109,9 +97,9 @@ public interface KeyStoreProvider { SecretKey getSecretKey(String name) throws IOException; /** - * Gets the {@link SecretKey} object for this alias - * null if the alias does not + * Gets the {@link SecretKey} object for this alias null if the alias does not * exist + * * @param name * @return * @throws IOException if the key exists but has the wrong type @@ -119,9 +107,9 @@ public interface KeyStoreProvider { PublicKey getPublicKey(String name) throws IOException; /** - * Gets the {@link PrivateKey} object for this alias - * null if the alias does not + * Gets the {@link PrivateKey} object for this alias null if the alias does not * exist + * * @param name * @return * @throws IOException if the key exists but has the wrong type @@ -129,16 +117,15 @@ public interface KeyStoreProvider { PrivateKey getPrivateKey(String name) throws IOException; /** - * * @param serviceName for a {@link GeoServerUserGroupService} - * @return the following String - * {@link #USERGROUP_PREFIX}+serviceName+{@value #USERGROUP_POSTFIX} + * @return the following String {@link #USERGROUP_PREFIX}+serviceName+{@value + * #USERGROUP_POSTFIX} */ String aliasForGroupService(String serviceName); /** * Tests if the password is the key store password - * + * * @param password * @return * @throws IOException @@ -147,7 +134,7 @@ public interface KeyStoreProvider { /** * Adds/replaces a {@link SecretKey} with its alias - * + * * @param alias * @param key * @throws Exception @@ -155,7 +142,8 @@ public interface KeyStoreProvider { void setSecretKey(String alias, char[] key) throws IOException; /** - * Sets a secret for the name of a {@link GeoServerUserGroupService} + * Sets a secret for the name of a {@link GeoServerUserGroupService} + * * @param serviceName * @param password * @throws IOException @@ -164,7 +152,7 @@ public interface KeyStoreProvider { /** * Remove a key belonging to the alias - * + * * @param alias * @throws IOException */ @@ -172,51 +160,44 @@ public interface KeyStoreProvider { /** * Stores the key store to {@link #ks} - * + * * @throws IOException */ void storeKeyStore() throws IOException; /** - * Prepares a master password change. The new password is used to encrypt - * the {@link KeyStore} and each {@link Entry}; - * - * The new password is assumed to be already validated by the {@link PasswordValidator} named + * Prepares a master password change. The new password is used to encrypt the {@link KeyStore} + * and each {@link Entry}; + * + *

The new password is assumed to be already validated by the {@link PasswordValidator} named * {@link PasswordValidator#MASTERPASSWORD_NAME} - * - * A new key store named {@link #PREPARED_FILE_NAME} is created. All keys - * a re-encrypted with the new password and stored in the new key store. - * - * + * + *

A new key store named {@link #PREPARED_FILE_NAME} is created. All keys a re-encrypted with + * the new password and stored in the new key store. + * * @param oldPassword * @param newPassword * @throws IOException */ - void prepareForMasterPasswordChange(char[] oldPassword, char[] newPassword) - throws IOException; + void prepareForMasterPasswordChange(char[] oldPassword, char[] newPassword) throws IOException; - /** - * Aborts the master password change by removing - * the file named {@link #PREPARED_FILE_NAME} - * - */ + /** Aborts the master password change by removing the file named {@link #PREPARED_FILE_NAME} */ void abortMasterPasswordChange(); /** * if {@link #DEFAULT_FILE_NAME} and {@link #PREPARED_FILE_NAME} exist, * this method checks if {@link #PREPARED_FILE_NAME} can be used * with new {@link MasterPasswordProvider.#getMasterPassword() method. - * + * * YES: replace the old keystore with the new one - * + * * NO: Do nothing, log the problem and use the old configuration * A reason may be that the new master password is not properly injected - * at startup - * + * at startup + * * @throws IOException */ void commitMasterPasswordChange() throws IOException; public Enumeration aliases(); - -} \ No newline at end of file +} diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/KeyStoreProviderImpl.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/KeyStoreProviderImpl.java index ed095c99..88be14c5 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/KeyStoreProviderImpl.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/KeyStoreProviderImpl.java @@ -1,5 +1,6 @@ package it.geosolutions.geostore.core.security.password; +import static it.geosolutions.geostore.core.security.password.SecurityUtils.toBytes; import java.io.File; import java.io.FileInputStream; @@ -15,128 +16,121 @@ import java.util.Enumeration; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; - - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.BeanNameAware; -import static it.geosolutions.geostore.core.security.password.SecurityUtils.toBytes; - /** * Class for GeoStore specific key management - * - * requires a password form configuration - * - * The type of the keystore is JCEKS and can be used/modified - * with java tools like "keytool" from the command line. - * * - * - * @author Lorenzo Natali * + *

requires a password form configuration + * + *

The type of the keystore is JCEKS and can be used/modified with java tools like "keytool" from + * the command line. * + * + * @author Lorenzo Natali */ -public class KeyStoreProviderImpl implements BeanNameAware, KeyStoreProvider{ - private static final Logger LOGGER = Logger.getLogger(KeyStoreProviderImpl.class); - - public final static String DEFAULT_BEAN_NAME="DefaultKeyStoreProvider"; - public final static String DEFAULT_FILE_NAME="geostore.jceks"; - public final static String PREPARED_FILE_NAME="geostore.jceks.new"; - - public final static String CONFIGPASSWORDKEY = "ug:geostore:key"; - - public final static String USERGROUP_PREFIX = "ug:"; - public final static String USERGROUP_POSTFIX = ":key"; - +public class KeyStoreProviderImpl implements BeanNameAware, KeyStoreProvider { + private static final Logger LOGGER = LogManager.getLogger(KeyStoreProviderImpl.class); + + public static final String DEFAULT_BEAN_NAME = "DefaultKeyStoreProvider"; + public static final String DEFAULT_FILE_NAME = "geostore.jceks"; + public static final String PREPARED_FILE_NAME = "geostore.jceks.new"; + + public static final String CONFIGPASSWORDKEY = "ug:geostore:key"; + + public static final String USERGROUP_PREFIX = "ug:"; + public static final String USERGROUP_POSTFIX = ":key"; + private String keyStoreFilePath = null; protected String name; protected File keyStoreFile; protected KeyStore ks; - private char[] masterPassword; - private String keyName; + private char[] masterPassword; + private String keyName; + + private MasterPasswordProvider masterPasswordProvider; - private MasterPasswordProvider masterPasswordProvider; public MasterPasswordProvider getMasterPasswordProvider() { - return masterPasswordProvider; - } + return masterPasswordProvider; + } - public void setMasterPasswordProvider( - MasterPasswordProvider masterPasswordProvider) { - this.masterPasswordProvider = masterPasswordProvider; - } + public void setMasterPasswordProvider(MasterPasswordProvider masterPasswordProvider) { + this.masterPasswordProvider = masterPasswordProvider; + } - public String getKeyName() { - return keyName; - } + public String getKeyName() { + return keyName; + } - public void setKeyName(String keyName) { - this.keyName = keyName; - } + public void setKeyName(String keyName) { + this.keyName = keyName; + } + public void setMasterPassword(char[] masterPassword) { + this.masterPassword = masterPassword; + } - public void setMasterPassword(char[] masterPassword) { - this.masterPassword = masterPassword; - } + public static final String KEYSTORETYPE = "JCEKS"; - public final static String KEYSTORETYPE = "JCEKS"; - - public KeyStoreProviderImpl() { - } + public KeyStoreProviderImpl() {} @Override public void setBeanName(String name) { this.name = name; } - /* (non-Javadoc) * @see org.geoserver.security.password.KeystoreProvider#getKeyStoreProvderFile() */ @Override public File getFile() { - //retrieve the file on first access + // retrieve the file on first access if (keyStoreFile == null) { - //if the keyStoreFilePath is configured create it - if(getKeyStoreFilePath()!=null){ - keyStoreFile = new File(getKeyStoreFilePath()); - if(keyStoreFile!=null){ - if(!keyStoreFile.exists()){ - if(keyStoreFile.isDirectory()){ - keyStoreFile = new File(getKeyStoreFilePath()+ "geostore.jceks"); - } - LOGGER.warn("the keyStore file doesn't exist. confiure a new one"); - } - } - //if the file doesn't exist create a new one - //TODO add a key - //otherwise get the default one - }else{ - URL defaultKeyStrore = KeyStoreProviderImpl.class.getClassLoader().getResource("geostore.jceks"); - try { - if(defaultKeyStrore!= null){ - keyStoreFile = new File(defaultKeyStrore.toURI()); - } - } catch (URISyntaxException e) { - LOGGER.error("UNABLE TO GET THE DEFAULT KEY STORE"); - } + // if the keyStoreFilePath is configured create it + if (getKeyStoreFilePath() != null) { + keyStoreFile = new File(getKeyStoreFilePath()); + if (keyStoreFile != null) { + if (!keyStoreFile.exists()) { + if (keyStoreFile.isDirectory()) { + keyStoreFile = new File(getKeyStoreFilePath() + "geostore.jceks"); + } + LOGGER.warn("the keyStore file doesn't exist. confiure a new one"); + } + } + // if the file doesn't exist create a new one + // TODO add a key + // otherwise get the default one + } else { + URL defaultKeyStrore = + KeyStoreProviderImpl.class.getClassLoader().getResource("geostore.jceks"); + try { + if (defaultKeyStrore != null) { + keyStoreFile = new File(defaultKeyStrore.toURI()); + } + } catch (URISyntaxException e) { + LOGGER.error("UNABLE TO GET THE DEFAULT KEY STORE"); + } } } - + return keyStoreFile; } public String getKeyStoreFilePath() { - return keyStoreFilePath; - } - - public void setKeyStoreFilePath(String keyStoreFilePath ){ - this.keyStoreFilePath = keyStoreFilePath; + return keyStoreFilePath; } - /* (non-Javadoc) + public void setKeyStoreFilePath(String keyStoreFilePath) { + this.keyStoreFilePath = keyStoreFilePath; + } + + /* (non-Javadoc) * @see org.geoserver.security.password.KeystoreProvider#reloadKeyStore() */ @Override - public void reloadKeyStore() throws IOException{ - ks=null; + public void reloadKeyStore() throws IOException { + ks = null; assertActivatedKeyStore(); } @@ -144,60 +138,58 @@ public void reloadKeyStore() throws IOException{ * @see org.geoserver.security.password.KeystoreProvider#getKey(java.lang.String) */ @Override - public Key getKey(String alias) throws IOException{ + public Key getKey(String alias) throws IOException { assertActivatedKeyStore(); try { char[] passwd = getMasterPassword(); try { return ks.getKey(alias, passwd); - } - finally { - disposePassword(passwd); + } finally { + disposePassword(passwd); } } catch (Exception e) { throw new IOException(e); } } - + private char[] getMasterPassword() { - //TODO cifrate this password somehow - if( masterPassword !=null){ - return masterPassword; - }else{ - if(masterPasswordProvider != null){ - try { - masterPassword = masterPasswordProvider.doGetMasterPassword(); - } catch (Exception e) { - LOGGER.error("unable to read the master password\n:" + e.getStackTrace()); - } - - } - } - return masterPassword; - } - public Enumeration aliases(){ - if(ks!=null) - try { - return ks.aliases(); - } catch (KeyStoreException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - return null; - } - return null; - - } - - /* (non-Javadoc) + // TODO cifrate this password somehow + if (masterPassword != null) { + return masterPassword; + } else { + if (masterPasswordProvider != null) { + try { + masterPassword = masterPasswordProvider.doGetMasterPassword(); + } catch (Exception e) { + LOGGER.error("unable to read the master password\n:" + e.getStackTrace()); + } + } + } + return masterPassword; + } + + public Enumeration aliases() { + if (ks != null) + try { + return ks.aliases(); + } catch (KeyStoreException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + return null; + } + + /* (non-Javadoc) * @see org.geoserver.security.password.KeystoreProvider#getConfigPasswordKey() */ @Override - public byte[] getConfigPasswordKey() throws IOException{ + public byte[] getConfigPasswordKey() throws IOException { SecretKey key = getSecretKey(CONFIGPASSWORDKEY); - if (key==null) return null; + if (key == null) return null; return key.getEncoded(); } - + /* (non-Javadoc) * @see org.geoserver.security.password.KeystoreProvider#hasConfigPasswordKey() */ @@ -205,13 +197,12 @@ public byte[] getConfigPasswordKey() throws IOException{ public boolean hasConfigPasswordKey() throws IOException { return containsAlias(CONFIGPASSWORDKEY); } - - + /* (non-Javadoc) * @see org.geoserver.security.password.KeystoreProvider#containsAlias(java.lang.String) */ @Override - public boolean containsAlias(String alias) throws IOException{ + public boolean containsAlias(String alias) throws IOException { assertActivatedKeyStore(); try { return ks.containsAlias(alias); @@ -223,44 +214,41 @@ public boolean containsAlias(String alias) throws IOException{ * @see org.geoserver.security.password.KeystoreProvider#getUserGRoupKey(java.lang.String) */ @Override - public byte[] getUserGroupKey(String serviceName) throws IOException{ + public byte[] getUserGroupKey(String serviceName) throws IOException { SecretKey key = getSecretKey(aliasForGroupService(serviceName)); - if (key==null) return null; + if (key == null) return null; return key.getEncoded(); - } - + /* (non-Javadoc) * @see org.geoserver.security.password.KeystoreProvider#hasUserGRoupKey(java.lang.String) */ @Override public boolean hasUserGroupKey(String serviceName) throws IOException { return containsAlias(aliasForGroupService(serviceName)); - } - /* (non-Javadoc) * @see org.geoserver.security.password.KeystoreProvider#getSecretKey(java.lang.String) */ @Override - public SecretKey getSecretKey(String name) throws IOException{ + public SecretKey getSecretKey(String name) throws IOException { Key key = getKey(name); - if (key==null) return null; + if (key == null) return null; if ((key instanceof SecretKey) == false) - throw new IOException("Invalid key type for: "+name); + throw new IOException("Invalid key type for: " + name); return (SecretKey) key; } - + /* (non-Javadoc) * @see org.geoserver.security.password.KeystoreProvider#getPublicKey(java.lang.String) */ @Override - public PublicKey getPublicKey(String name) throws IOException{ + public PublicKey getPublicKey(String name) throws IOException { Key key = getKey(name); - if (key==null) return null; + if (key == null) return null; if ((key instanceof PublicKey) == false) - throw new IOException("Invalid key type for: "+name); + throw new IOException("Invalid key type for: " + name); return (PublicKey) key; } @@ -268,11 +256,11 @@ public PublicKey getPublicKey(String name) throws IOException{ * @see org.geoserver.security.password.KeystoreProvider#getPrivateKey(java.lang.String) */ @Override - public PrivateKey getPrivateKey(String name) throws IOException{ + public PrivateKey getPrivateKey(String name) throws IOException { Key key = getKey(name); - if (key==null) return null; + if (key == null) return null; if ((key instanceof PrivateKey) == false) - throw new IOException("Invalid key type for: "+name); + throw new IOException("Invalid key type for: " + name); return (PrivateKey) key; } @@ -284,97 +272,90 @@ public String aliasForGroupService(String serviceName) { StringBuffer buff = new StringBuffer(USERGROUP_PREFIX); buff.append(serviceName); buff.append(USERGROUP_POSTFIX); - return buff.toString(); + return buff.toString(); } - + /** - * Opens or creates a {@link KeyStore} using the file - * {@link #DEFAULT_FILE_NAME} - * - * Throws an exception for an invalid master key - * - * @throws IOException - */ + * Opens or creates a {@link KeyStore} using the file {@link #DEFAULT_FILE_NAME} + * + *

Throws an exception for an invalid master key + * + * @throws IOException + */ protected void assertActivatedKeyStore() throws IOException { - if (ks != null) - return; - + if (ks != null) return; + char[] passwd = getMasterPassword(); try { ks = KeyStore.getInstance(KEYSTORETYPE); - if (getFile().exists()==false) { // create an empy one + if (getFile().exists() == false) { // create an empy one ks.load(null, passwd); addInitialKeys(); - FileOutputStream fos = new FileOutputStream(getFile()); - ks.store(fos, passwd); - fos.close(); + try (FileOutputStream fos = new FileOutputStream(getFile())) { + ks.store(fos, passwd); + } } else { - FileInputStream fis = - new FileInputStream(getFile()); - ks.load(fis, passwd); - fis.close(); + try (FileInputStream fis = new FileInputStream(getFile())) { + ks.load(fis, passwd); + } } } catch (Exception ex) { if (ex instanceof IOException) // avoid useless wrapping - throw (IOException) ex; - throw new IOException (ex); - } - finally { + throw (IOException) ex; + throw new IOException(ex); + } finally { disposePassword(passwd); } } - + private void disposePassword(char[] passwd) { - // TODO implement it when security improved. - - } + // TODO implement it when security improved. + + } - /* (non-Javadoc) + /* (non-Javadoc) * @see org.geoserver.security.password.KeystoreProvider#isKeystorePassword(java.lang.String) */ @Override - public boolean isKeyStorePassword(char[] password) throws IOException{ - if (password==null) return false; + public boolean isKeyStorePassword(char[] password) throws IOException { + if (password == null) return false; assertActivatedKeyStore(); - - KeyStore testStore=null; + + KeyStore testStore = null; try { testStore = KeyStore.getInstance(KEYSTORETYPE); } catch (KeyStoreException e1) { // should not happen, see assertActivatedKeyStore throw new RuntimeException(e1); } - FileInputStream fis = - new FileInputStream(getFile()); - try { - testStore.load(fis, password); - } catch (IOException e2) { - // indicates invalid password - return false; - } catch (Exception e) { - // should not happen, see assertActivatedKeyStore - throw new RuntimeException(e); - } - fis.close(); + try (FileInputStream fis = new FileInputStream(getFile())) { + try { + testStore.load(fis, password); + } catch (IOException e2) { + // indicates invalid password + return false; + } catch (Exception e) { + // should not happen, see assertActivatedKeyStore + throw new RuntimeException(e); + } + } return true; } - + /* (non-Javadoc) * @see org.geoserver.security.password.KeystoreProvider#setSecretKey(java.lang.String, java.lang.String) */ @Override public void setSecretKey(String alias, char[] key) throws IOException { assertActivatedKeyStore(); - SecretKey mySecretKey=new SecretKeySpec(toBytes(key),"PBE"); - KeyStore.SecretKeyEntry skEntry = - new KeyStore.SecretKeyEntry(mySecretKey); + SecretKey mySecretKey = new SecretKeySpec(toBytes(key), "PBE"); + KeyStore.SecretKeyEntry skEntry = new KeyStore.SecretKeyEntry(mySecretKey); char[] passwd = getMasterPassword(); try { ks.setEntry(alias, skEntry, new KeyStore.PasswordProtection(passwd)); } catch (KeyStoreException e) { throw new IOException(e); - } - finally { + } finally { disposePassword(passwd); } } @@ -383,16 +364,16 @@ public void setSecretKey(String alias, char[] key) throws IOException { * @see org.geoserver.security.password.KeystoreProvider#setUserGroupKey(java.lang.String, java.lang.String) */ @Override - public void setUserGroupKey(String serviceName,char[] password) throws IOException{ + public void setUserGroupKey(String serviceName, char[] password) throws IOException { String alias = aliasForGroupService(serviceName); setSecretKey(alias, password); } - + /* (non-Javadoc) * @see org.geoserver.security.password.KeystoreProvider#removeKey(java.lang.String) */ @Override - public void removeKey(String alias ) throws IOException { + public void removeKey(String alias) throws IOException { assertActivatedKeyStore(); try { ks.deleteEntry(alias); @@ -401,98 +382,91 @@ public void removeKey(String alias ) throws IOException { } } - /* (non-Javadoc) * @see org.geoserver.security.password.KeystoreProvider#storeKeyStore() */ @Override - public void storeKeyStore() throws IOException{ + public void storeKeyStore() throws IOException { // store away the keystore assertActivatedKeyStore(); - FileOutputStream fos = new FileOutputStream(getFile()); - - char[] passwd = getMasterPassword(); - try { - ks.store(fos, passwd); - } catch (Exception e) { - throw new IOException(e); - } - finally { - disposePassword(passwd); + try (FileOutputStream fos = new FileOutputStream(getFile())) { + char[] passwd = getMasterPassword(); + try { + ks.store(fos, passwd); + } catch (Exception e) { + throw new IOException(e); + } finally { + disposePassword(passwd); + } } - fos.close(); } - + /** - * Creates initial key entries - * auto generated keys - * {@link #CONFIGPASSWORDKEY} - * + * Creates initial key entries auto generated keys {@link #CONFIGPASSWORDKEY} + * * @throws IOException */ protected void addInitialKeys() throws IOException { - RandomPasswordProvider randPasswdProvider = - getRandomPassworddProvider(); - + RandomPasswordProvider randPasswdProvider = getRandomPassworddProvider(); + char[] configKey = randPasswdProvider.getRandomPasswordWithDefaultLength(); - setSecretKey( CONFIGPASSWORDKEY, configKey); + setSecretKey(CONFIGPASSWORDKEY, configKey); } - + private RandomPasswordProvider getRandomPassworddProvider() { - return new RandomPasswordProvider(); - } + return new RandomPasswordProvider(); + } - /* (non-Javadoc) + /* (non-Javadoc) * @see org.geoserver.security.password.KeystoreProvider#prepareForMasterPasswordChange(java.lang.String, java.lang.String) */ @Override - public void prepareForMasterPasswordChange(char[] oldPassword, char[] newPassword) throws IOException{ + public void prepareForMasterPasswordChange(char[] oldPassword, char[] newPassword) + throws IOException { - File dir = getFile().getParentFile(); - File newKSFile = new File(dir,PREPARED_FILE_NAME); - if (newKSFile.exists()) - newKSFile.delete(); - + File newKSFile = new File(dir, PREPARED_FILE_NAME); + if (newKSFile.exists()) newKSFile.delete(); + try { - KeyStore oldKS=KeyStore.getInstance(KEYSTORETYPE); - FileInputStream fin = new FileInputStream(getFile()); - oldKS.load(fin, oldPassword); - fin.close(); - + KeyStore oldKS = KeyStore.getInstance(KEYSTORETYPE); + try (FileInputStream fin = new FileInputStream(getFile())) { + oldKS.load(fin, oldPassword); + } + KeyStore newKS = KeyStore.getInstance(KEYSTORETYPE); newKS.load(null, newPassword); - KeyStore.PasswordProtection protectionparam = + KeyStore.PasswordProtection protectionparam = new KeyStore.PasswordProtection(newPassword); Enumeration enumeration = oldKS.aliases(); while (enumeration.hasMoreElements()) { - String alias =enumeration.nextElement(); + String alias = enumeration.nextElement(); Key key = oldKS.getKey(alias, oldPassword); - KeyStore.Entry entry =null; - if (key instanceof SecretKey) - entry = new KeyStore.SecretKeyEntry((SecretKey)key); - if (key instanceof PrivateKey) - entry = new KeyStore.PrivateKeyEntry((PrivateKey)key, - oldKS.getCertificateChain(alias)); - if (key instanceof PublicKey) - entry = new KeyStore.TrustedCertificateEntry(oldKS.getCertificate(alias)); + KeyStore.Entry entry = null; + if (key instanceof SecretKey) entry = new KeyStore.SecretKeyEntry((SecretKey) key); + if (key instanceof PrivateKey) + entry = + new KeyStore.PrivateKeyEntry( + (PrivateKey) key, oldKS.getCertificateChain(alias)); + if (key instanceof PublicKey) + entry = new KeyStore.TrustedCertificateEntry(oldKS.getCertificate(alias)); if (entry == null) - LOGGER.warn("Unknown key in store, alias: "+alias+ - " class: "+ key.getClass().getName()); - else - newKS.setEntry(alias, entry, protectionparam); - } - - FileOutputStream fos = new FileOutputStream(newKSFile); - newKS.store(fos, newPassword); - fos.close(); + LOGGER.warn( + "Unknown key in store, alias: " + + alias + + " class: " + + key.getClass().getName()); + else newKS.setEntry(alias, entry, protectionparam); + } + try (FileOutputStream fos = new FileOutputStream(newKSFile)) { + newKS.store(fos, newPassword); + } } catch (Exception ex) { throw new IOException(ex); } - } /* (non-Javadoc) @@ -501,76 +475,72 @@ public void prepareForMasterPasswordChange(char[] oldPassword, char[] newPasswor @Override public void abortMasterPasswordChange() { File dir = getFile().getParentFile(); - File newKSFile = new File(dir,PREPARED_FILE_NAME); + File newKSFile = new File(dir, PREPARED_FILE_NAME); if (newKSFile.exists()) { - //newKSFile.delete(); + try { + newKSFile.delete(); + } catch (Exception e) { + LOGGER.error("UNABLE TO DELETE THE MASTERPWD FILE"); + } } - } - - + /* (non-Javadoc) * @see org.geoserver.security.password.KeystoreProvider#commitMasterPasswordChange() */ @Override public void commitMasterPasswordChange() throws IOException { File dir = getFile().getParentFile(); - File newKSFile = new File(dir,PREPARED_FILE_NAME); - File oldKSFile = new File(dir,DEFAULT_FILE_NAME); - - if (newKSFile.exists()==false) - return; //nothing to do - - if (oldKSFile.exists()==false) - return; //not initialized - + File newKSFile = new File(dir, PREPARED_FILE_NAME); + File oldKSFile = new File(dir, DEFAULT_FILE_NAME); + + if (newKSFile.exists() == false) return; // nothing to do + + if (oldKSFile.exists() == false) return; // not initialized + // Try to open with new password - FileInputStream fin = new FileInputStream(newKSFile); - char[] passwd = getMasterPassword(); - - try { - KeyStore newKS = KeyStore.getInstance(KEYSTORETYPE); - newKS.load(fin, passwd); - - // to be sure, decrypt all keys - Enumeration enumeration = newKS.aliases(); - while (enumeration.hasMoreElements()) { - newKS.getKey(enumeration.nextElement(), passwd); - } - fin.close(); - fin=null; - if (oldKSFile.delete()==false) { - LOGGER.error("cannot delete " +getFile().getCanonicalPath()); - return; - } - - if (newKSFile.renameTo(oldKSFile)==false) { - String msg = "cannot rename "+ newKSFile.getCanonicalPath(); - msg += "to " + oldKSFile.getCanonicalPath(); - msg += "Try to rename manually and restart"; - LOGGER.error(msg); - return; - } - reloadKeyStore(); - LOGGER.info("Successfully changed master password"); - } catch (IOException e) { - String msg = "Error creating new keystore: " + newKSFile.getCanonicalPath(); - LOGGER.warn( msg, e); - throw e; - } catch (Exception ex) { - throw new RuntimeException(ex); - } - finally { - disposePassword(passwd); - if (fin != null) { - try{ - fin.close(); - } - catch (IOException ex) { - // give up + try (FileInputStream fin = new FileInputStream(newKSFile)) { + char[] passwd = getMasterPassword(); + + try { + KeyStore newKS = KeyStore.getInstance(KEYSTORETYPE); + newKS.load(fin, passwd); + + // to be sure, decrypt all keys + Enumeration enumeration = newKS.aliases(); + while (enumeration.hasMoreElements()) { + newKS.getKey(enumeration.nextElement(), passwd); + } + if (oldKSFile.delete() == false) { + LOGGER.error("cannot delete " + getFile().getCanonicalPath()); + return; + } + + if (newKSFile.renameTo(oldKSFile) == false) { + String msg = "cannot rename " + newKSFile.getCanonicalPath(); + msg += "to " + oldKSFile.getCanonicalPath(); + msg += "Try to rename manually and restart"; + LOGGER.error(msg); + return; + } + reloadKeyStore(); + LOGGER.info("Successfully changed master password"); + } catch (IOException e) { + String msg = "Error creating new keystore: " + newKSFile.getCanonicalPath(); + LOGGER.warn(msg, e); + throw e; + } catch (Exception ex) { + throw new RuntimeException(ex); + } finally { + disposePassword(passwd); + if (fin != null) { + try { + fin.close(); + } catch (IOException ex) { + // give up + } } } } - } } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/MasterPasswordProvider.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/MasterPasswordProvider.java index a673e26a..8794a213 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/MasterPasswordProvider.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/MasterPasswordProvider.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,20 +21,19 @@ public interface MasterPasswordProvider { - /** - * Provides the master password - * @return the Master Password - * @throws Exception - */ - public char[] doGetMasterPassword() throws Exception; - - /** - * Set the master password - * @param passwd the password to set - * @throws Exception - */ - public void doSetMasterPassword(char[] passwd) throws Exception; - + /** + * Provides the master password + * + * @return the Master Password + * @throws Exception + */ + public char[] doGetMasterPassword() throws Exception; - + /** + * Set the master password + * + * @param passwd the password to set + * @throws Exception + */ + public void doSetMasterPassword(char[] passwd) throws Exception; } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/PasswordEncodingType.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/PasswordEncodingType.java index 733f0e16..04b9a2c4 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/PasswordEncodingType.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/PasswordEncodingType.java @@ -21,17 +21,22 @@ /** * Enumeration for password encoding type. + * *

+ * *

    - *
  • {@link #EMPTY} - empty, only for null or empty ("") passwords
  • - *
  • {@link #PLAIN} - plain text
  • - *
  • {@link #ENCRYPT} - symmetric encryption
  • - *
  • {@link #DIGEST} - password hashing (recommended)
  • - *
  • {@link #GEOSTORE} - old geostore system
  • - *

    - * @author Lorenzo Natali + *
  • {@link #EMPTY} - empty, only for null or empty ("") passwords + *
  • {@link #PLAIN} - plain text + *
  • {@link #ENCRYPT} - symmetric encryption + *
  • {@link #DIGEST} - password hashing (recommended) + *
  • {@link #GEOSTORE} - old geostore system * + * @author Lorenzo Natali */ public enum PasswordEncodingType { - EMPTY,PLAIN,ENCRYPT,DIGEST,GEOSTORE; -} \ No newline at end of file + EMPTY, + PLAIN, + ENCRYPT, + DIGEST, + GEOSTORE; +} diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/PwEncoder.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/PwEncoder.java index 96ca4c70..65445dd8 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/PwEncoder.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/PwEncoder.java @@ -19,31 +19,32 @@ */ package it.geosolutions.geostore.core.security.password; - /** - * * @author ETj * @author Lorenzo Natali */ public class PwEncoder { - private static GeoStorePasswordEncoder encoder = new it.geosolutions.geostore.core.security.password.GeoStoreDigestPasswordEncoder(); + private static GeoStorePasswordEncoder encoder = + new it.geosolutions.geostore.core.security.password.GeoStoreDigestPasswordEncoder(); + public static String encode(String msg) { - return encoder.encodePassword(msg.toCharArray(), null); + return encoder.encodePassword(msg.toCharArray(), null); } public static String decode(String msg) { return encoder.decode(msg); } - - public static boolean isPasswordValid(String encPass,String rawPass){ - return encoder.isPasswordValid(encPass, rawPass, null); + + public static boolean isPasswordValid(String encPass, String rawPass) { + return encoder.isPasswordValid(encPass, rawPass, null); } - - public static void setEncoder(GeoStorePasswordEncoder e){ - encoder=e; + + public static void setEncoder(GeoStorePasswordEncoder e) { + encoder = e; } - public static GeoStorePasswordEncoder getEncoder(){ - return encoder; + + public static GeoStorePasswordEncoder getEncoder() { + return encoder; } } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/RandomPasswordProvider.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/RandomPasswordProvider.java index b9341694..5a2619ef 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/RandomPasswordProvider.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/RandomPasswordProvider.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -31,74 +31,63 @@ /** * This Class for generating random passwords using {@link SecureRandom}. - *

    - * The password alphabet is {@link #PRINTABLE_ALPHABET}. Since the alphabet is - * not really big, the length of the password is important. - * This class is the same available in GeoServer. - *

    - * + * + *

    The password alphabet is {@link #PRINTABLE_ALPHABET}. Since the alphabet is not really big, + * the length of the password is important. This class is the same available in GeoServer. + * * @author Lorenzo Natali (lorenzo.natali at geo-solutions.it) */ public class RandomPasswordProvider { - /** alphabet */ - public static final char[] PRINTABLE_ALPHABET = { '!', '\"', '#', '$', '%', - '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', - '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '?', '@', 'A', - 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', - 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', - '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', - 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', }; + /** alphabet */ + public static final char[] PRINTABLE_ALPHABET = { + '!', '\"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', + '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', + '}', '~', + }; - /** - * The default password length assures a key strength of 2 ^ 261 - * {@link #PRINTABLE_ALPHABET} has 92 characters ln (92 ^ 40 ) / ln (2) = - * 260.942478242 - */ - public static int DefaultPasswordLength = 40; + /** + * The default password length assures a key strength of 2 ^ 261 {@link #PRINTABLE_ALPHABET} has + * 92 characters ln (92 ^ 40 ) / ln (2) = 260.942478242 + */ + public static int DefaultPasswordLength = 40; - /** - * Creates a random password of the specified length, if length <=0, return - * null - */ - public char[] getRandomPassword(int length) { - if (length <= 0) - return null; - char[] buff = new char[length]; - getRandomPassword(buff); - return buff; - } + /** + * Creates a random password of the specified length, if length <=0, return null + */ + public char[] getRandomPassword(int length) { + if (length <= 0) return null; + char[] buff = new char[length]; + getRandomPassword(buff); + return buff; + } - public char[] getRandomPasswordWithDefaultLength() { - char[] buff = new char[DefaultPasswordLength]; - getRandomPassword(buff); - return buff; - } + public char[] getRandomPasswordWithDefaultLength() { + char[] buff = new char[DefaultPasswordLength]; + getRandomPassword(buff); + return buff; + } - /** - * Creates a random password filling the specified character array. - */ - public void getRandomPassword(char[] buff) { - SecureRandom random = new SecureRandom(); - for (int i = 0; i < buff.length; i++) { - int index = random.nextInt() % PRINTABLE_ALPHABET.length; - if (index < 0) - index += PRINTABLE_ALPHABET.length; - buff[i] = PRINTABLE_ALPHABET[index]; - } - } + /** Creates a random password filling the specified character array. */ + public void getRandomPassword(char[] buff) { + SecureRandom random = new SecureRandom(); + for (int i = 0; i < buff.length; i++) { + int index = random.nextInt() % PRINTABLE_ALPHABET.length; + if (index < 0) index += PRINTABLE_ALPHABET.length; + buff[i] = PRINTABLE_ALPHABET[index]; + } + } - /** - * Creates a random password filling the specified byte array. - */ - public void getRandomPassword(byte[] buff) { - SecureRandom random = new SecureRandom(); - for (int i = 0; i < buff.length; i++) { - int index = random.nextInt() % PRINTABLE_ALPHABET.length; - if (index < 0) - index += PRINTABLE_ALPHABET.length; - buff[i] = (byte) PRINTABLE_ALPHABET[index]; - } - } + /** Creates a random password filling the specified byte array. */ + public void getRandomPassword(byte[] buff) { + SecureRandom random = new SecureRandom(); + for (int i = 0; i < buff.length; i++) { + int index = random.nextInt() % PRINTABLE_ALPHABET.length; + if (index < 0) index += PRINTABLE_ALPHABET.length; + buff[i] = (byte) PRINTABLE_ALPHABET[index]; + } + } } diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/SecurityUtils.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/SecurityUtils.java index 06624010..527cb3f3 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/SecurityUtils.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/SecurityUtils.java @@ -4,156 +4,157 @@ */ package it.geosolutions.geostore.core.security.password; +import it.geosolutions.geostore.core.model.User; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; +import java.security.Principal; import java.util.Arrays; - - +import org.springframework.security.core.userdetails.UserDetails; /** - * Common security utility methods. - * - * @author mcr + * Common security utility methods. * + * @author mcr */ public class SecurityUtils { /** - * Spring Secruity 3.x drops the common base security exception - * class SpringSecurityException, now the test is based on the package - * name - * + * Spring Secruity 3.x drops the common base security exception class SpringSecurityException, + * now the test is based on the package name + * * @param t, the exception to check * @return true if the exception is caused by Spring Security */ - public static boolean isSecurityException(Throwable t) { - return t != null && t.getClass().getPackage().getName() - .startsWith("org.springframework.security"); + public static boolean isSecurityException(Throwable t) { + return t != null + && t.getClass().getPackage().getName().startsWith("org.springframework.security"); } - + /** * Converts a char array to a byte array. - *

    - * This method is unsafe since the charset is not specified, one of - * {@link #toBytes(char[], String)} or {@link #toBytes(char[], Charset)} should be used - * instead. When not specified {@link Charset#defaultCharset()} is used. - *

    + * + *

    This method is unsafe since the charset is not specified, one of {@link #toBytes(char[], + * String)} or {@link #toBytes(char[], Charset)} should be used instead. When not specified + * {@link Charset#defaultCharset()} is used. */ public static byte[] toBytes(char[] ch) { return toBytes(ch, Charset.defaultCharset()); } - /** - * Converts a char array to a byte array. - */ + /** Converts a char array to a byte array. */ public static byte[] toBytes(char[] ch, String charset) { return toBytes(ch, Charset.forName(charset)); } - /** - * Converts a char array to a byte array. - */ - public static byte[] toBytes(char[] ch, Charset charset) { + /** Converts a char array to a byte array. */ + public static byte[] toBytes(char[] ch, Charset charset) { ByteBuffer buff = charset.encode(CharBuffer.wrap(ch)); byte[] tmp = new byte[buff.limit()]; buff.get(tmp); - return tmp; + return tmp; } /** * Converts byte array to char array. - *

    - * This method is unsafe since the charset is not specified, one of - * {@link #toChars(byte[], String)} or {@link #toChars(byte[], Charset)} should be used - * instead. When not specified {@link Charset#defaultCharset()} is used. - *

    + * + *

    This method is unsafe since the charset is not specified, one of {@link #toChars(byte[], + * String)} or {@link #toChars(byte[], Charset)} should be used instead. When not specified + * {@link Charset#defaultCharset()} is used. */ public static char[] toChars(byte[] b) { return toChars(b, Charset.defaultCharset()); } - /** - * Converts byte array to char array. - */ + /** Converts byte array to char array. */ public static char[] toChars(byte[] b, String charset) { return toChars(b, Charset.forName(charset)); } - /** - * Converts byte array to char array. - */ + /** Converts byte array to char array. */ public static char[] toChars(byte[] b, Charset charset) { - CharBuffer buff = charset.decode(ByteBuffer.wrap(b)); - char [] tmp = new char[buff.limit()]; + CharBuffer buff = charset.decode(ByteBuffer.wrap(b)); + char[] tmp = new char[buff.limit()]; buff.get(tmp); return tmp; } - /** - * Trims null characters off the end of the specified character array. - */ -// public static char[] trimNullChars(char[] ch) { -// int i = ch.length-1; -// while(i > -1 && ch[i] == 0) { -// i--; -// } -// return i < ch.length-1 ? Arrays.copyOf(ch, i+1) : ch; -// } + /** Trims null characters off the end of the specified character array. */ + // public static char[] trimNullChars(char[] ch) { + // int i = ch.length-1; + // while(i > -1 && ch[i] == 0) { + // i--; + // } + // return i < ch.length-1 ? Arrays.copyOf(ch, i+1) : ch; + // } /** - * Scrambles a char array overwriting all characters with random characters, used for - * scrambling plain text passwords after usage to avoid keeping them around in memory. + * Scrambles a char array overwriting all characters with random characters, used for scrambling + * plain text passwords after usage to avoid keeping them around in memory. */ public static void scramble(char[] ch) { - if (ch==null) return; + if (ch == null) return; RandomPasswordProvider rpp = new RandomPasswordProvider(); rpp.getRandomPassword(ch); } /** - * Scrambles a byte array overwriting all characters with random characters, used for - * scrambling plain text passwords after usage to avoid keeping them around in memory. + * Scrambles a byte array overwriting all characters with random characters, used for scrambling + * plain text passwords after usage to avoid keeping them around in memory. */ public static void scramble(byte[] ch) { - if (ch==null) return; + if (ch == null) return; RandomPasswordProvider rpp = new RandomPasswordProvider(); rpp.getRandomPassword(ch); } - - - /** - * Creates the inverse permutation array. - */ + + /** Creates the inverse permutation array. */ public static int[] createInverse(int[] perm) { int[] inverse = new int[perm.length]; - for (int i = 0; i < perm.length;i++) { - inverse[perm[i]]=i; + for (int i = 0; i < perm.length; i++) { + inverse[perm[i]] = i; } return inverse; } /** - * Applies a permutation. - * + * Applies a permutation. + * * @param base source array * @param times number of repetitions - * @param perm the permutation + * @param perm the permutation */ public static char[] permute(char[] base, int times, int[] perm) { - + char[][] working = new char[2][base.length]; - + System.arraycopy(base, 0, working[0], 0, base.length); - for (int j=0; j< times;j++) { + for (int j = 0; j < times; j++) { int source = j % 2; - int target = (j+1) % 2; - for (int i = 0; i< working[source].length;i++) - working[target][perm[i]]=working[source][i]; + int target = (j + 1) % 2; + for (int i = 0; i < working[source].length; i++) + working[target][perm[i]] = working[source][i]; } - char [] result = working[1].clone(); + char[] result = working[1].clone(); Arrays.fill(working[0], '0'); Arrays.fill(working[1], '0'); return result; } -} \ No newline at end of file + + public static String getUsername(Object principal) { + String username = null; + if (principal != null) { + if (principal instanceof UserDetails) { + username = ((UserDetails) principal).getUsername(); + } else if (principal instanceof Principal) { + username = ((Principal) principal).getName(); + } else if (principal instanceof User) { + username = ((User) principal).getName(); + } else { + username = principal.toString(); + } + } + + return username; + } +} diff --git a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/URLMasterPasswordProvider.java b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/URLMasterPasswordProvider.java index 4279b29d..8c852aeb 100644 --- a/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/URLMasterPasswordProvider.java +++ b/src/core/security/src/main/java/it/geosolutions/geostore/core/security/password/URLMasterPasswordProvider.java @@ -1,24 +1,28 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.core.security.password; +import static it.geosolutions.geostore.core.security.password.SecurityUtils.scramble; +import static it.geosolutions.geostore.core.security.password.SecurityUtils.toBytes; +import static it.geosolutions.geostore.core.security.password.SecurityUtils.toChars; + import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -28,63 +32,65 @@ import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; - -import static it.geosolutions.geostore.core.security.password.SecurityUtils.scramble; -import static it.geosolutions.geostore.core.security.password.SecurityUtils.toBytes; -import static it.geosolutions.geostore.core.security.password.SecurityUtils.toChars; - import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; import org.jasypt.encryption.pbe.StandardPBEByteEncryptor; /** * Master password provider that retrieves and optionally stores the master password from a url. - * + * * @author Lorenzo Natali (lorenzo.natali at geo-solutions.it) */ public final class URLMasterPasswordProvider implements MasterPasswordProvider { - private URL URL; - private String configDirPath ="."; - public void setEncrypting(boolean encrypting) { - this.encrypting = encrypting; - } + private URL URL; + private String configDirPath = "."; + + public void setEncrypting(boolean encrypting) { + this.encrypting = encrypting; + } + + private boolean encrypting = true; - private boolean encrypting = true; - - - static final char[] BASE = new char[]{ 'U','n','6','d','I','l','X','T','Q','c','L',')','$','#','q','J', - 'U','l','X','Q','U','!','n','n','p','%','U','r','5','U','u','3','5','H','`','x','P','F','r','X' }; - static final int[] PERM = new int[] - {32,19,30,11,34,26,3,21,9,37,38,13,23,2,18,4,20,1,29,17,0,31,14,36,12,24,15,35,16,39,25,5,10,8,7,6,33,27,28,22 }; + static final char[] BASE = + new char[] { + 'U', 'n', '6', 'd', 'I', 'l', 'X', 'T', 'Q', 'c', 'L', ')', '$', '#', 'q', 'J', 'U', + 'l', 'X', 'Q', 'U', '!', 'n', 'n', 'p', '%', 'U', 'r', '5', 'U', 'u', '3', '5', 'H', + '`', 'x', 'P', 'F', 'r', 'X' + }; + static final int[] PERM = + new int[] { + 32, 19, 30, 11, 34, 26, 3, 21, 9, 37, 38, 13, 23, 2, 18, 4, 20, 1, 29, 17, 0, 31, + 14, 36, 12, 24, 15, 35, 16, 39, 25, 5, 10, 8, 7, 6, 33, 27, 28, 22 + }; /** * Encode the password + * * @param passwd * @return */ byte[] encode(char[] passwd) { - + if (!isEncrypting()) { return toBytes(passwd); } - //encrypt the password + // encrypt the password StandardPBEByteEncryptor encryptor = new StandardPBEByteEncryptor(); char[] key = key(); try { encryptor.setPasswordCharArray(key); return Base64.encodeBase64(encryptor.encrypt(toBytes(passwd))); - } - - finally { + } finally { scramble(key); } } /** * Decode the password + * * @param passwd * @return */ @@ -93,41 +99,36 @@ byte[] decode(byte[] passwd) { return passwd; } - //decrypt the password + // decrypt the password StandardPBEByteEncryptor encryptor = new StandardPBEByteEncryptor(); char[] key = key(); try { encryptor.setPasswordCharArray(key); return encryptor.decrypt(Base64.decodeBase64(passwd)); - } - finally { + } finally { scramble(key); } - } - + /** * Generate the key for permutation + * * @return */ - char[] key() { - //generate the key + char[] key() { + // generate the key return SecurityUtils.permute(BASE, 32, PERM); } - - /** - * Gets the Master Password - * - */ + + /** Gets the Master Password */ @Override - public char[] doGetMasterPassword() throws Exception { + public char[] doGetMasterPassword() throws Exception { try { InputStream in = input(getURL(), getConfigDir()); try { - + return toChars(decode(IOUtils.toByteArray(in))); - } - finally { + } finally { in.close(); } } catch (IOException e) { @@ -135,15 +136,12 @@ public char[] doGetMasterPassword() throws Exception { } } - - - @Override - public void doSetMasterPassword(char[] passwd) throws Exception { + @Override + public void doSetMasterPassword(char[] passwd) throws Exception { OutputStream out = output(getURL(), getConfigDir()); try { out.write(encode(passwd)); - } - finally { + } finally { out.close(); } } @@ -152,31 +150,29 @@ File getConfigDir() throws IOException { return new File(configDirPath); } - - /** * Writes the master password in the file + * * @param url * @param configDir * @return * @throws IOException */ static OutputStream output(URL url, File configDir) throws IOException { - //check for file URL + // check for file URL if ("file".equalsIgnoreCase(url.getProtocol())) { File f; try { - f = new File(url.toURI()); - } catch(URISyntaxException e) { - f = new File(url.getPath()); - } + f = new File(url.toURI()); + } catch (URISyntaxException e) { + f = new File(url.getPath()); + } if (!f.isAbsolute()) { - //make relative to config dir + // make relative to config dir f = new File(configDir, f.getPath()); } return new FileOutputStream(f); - } - else { + } else { URLConnection cx = url.openConnection(); cx.setDoOutput(true); return cx.getOutputStream(); @@ -184,72 +180,69 @@ static OutputStream output(URL url, File configDir) throws IOException { } static InputStream input(URL url, File configDir) throws IOException { - //check for a file url - if(url == null){ - //default master password - url = URLMasterPasswordProvider.class.getClassLoader().getResource("passwd"); - } + // check for a file url + if (url == null) { + // default master password + url = URLMasterPasswordProvider.class.getClassLoader().getResource("passwd"); + } if ("file".equalsIgnoreCase(url.getProtocol())) { File f; try { - f = new File(url.toURI()); - } catch(URISyntaxException e) { - f = new File(url.getPath()); - } - - //check if the file is relative + f = new File(url.toURI()); + } catch (URISyntaxException e) { + f = new File(url.getPath()); + } + + // check if the file is relative if (!f.isAbsolute()) { - //make it relative to the config directory for this password provider + // make it relative to the config directory for this password provider f = new File(configDir, f.getPath()); } return new FileInputStream(f); - } - else { + } else { return url.openStream(); } } - /** - * - * @return the config dir path - */ - public String getConfigDirPath() { - return configDirPath; - } - - /** - * Set the config dir path - * @param path - */ - public void setConfigDirPath(String path){ - this.configDirPath = path; + /** @return the config dir path */ + public String getConfigDirPath() { + return configDirPath; + } + + /** + * Set the config dir path + * + * @param path + */ + public void setConfigDirPath(String path) { + this.configDirPath = path; + } + + /** + * Set the URL of the master password file + * + * @return + */ + public URL getURL() { + // TODO Auto-generated method stub + return URL; } - - /** - * Set the URL of the master password file - * @return - */ - public URL getURL() { - // TODO Auto-generated method stub - return URL; - } - - /** - * Set the URL of the master password file - * @param url url of the master password file - */ + + /** + * Set the URL of the master password file + * + * @param url url of the master password file + */ public void setURL(URL url) { - // TODO Auto-generated method stub - this.URL=url; - } + // TODO Auto-generated method stub + this.URL = url; + } /** * is encrypting + * * @return */ private boolean isEncrypting() { - return encrypting; - } - - - -} \ No newline at end of file + return encrypting; + } +} diff --git a/src/core/security/src/test/java/it/geosolutions/geostore/core/security/ExpressionUserMapperTest.java b/src/core/security/src/test/java/it/geosolutions/geostore/core/security/ExpressionUserMapperTest.java index ebeb3f3d..65236844 100644 --- a/src/core/security/src/test/java/it/geosolutions/geostore/core/security/ExpressionUserMapperTest.java +++ b/src/core/security/src/test/java/it/geosolutions/geostore/core/security/ExpressionUserMapperTest.java @@ -28,51 +28,50 @@ package it.geosolutions.geostore.core.security; import static org.junit.Assert.assertEquals; -import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.User; import java.util.HashMap; import java.util.Map; - import org.junit.Before; import org.junit.Test; public class ExpressionUserMapperTest { ExpressionUserMapper mapper; private Map attributeMappings; - + String SAMPLE_JSON = "{\"user_id\":\"123\",\"email\":\"myemail@email.com\"}"; - + @Before public void setUp() { - attributeMappings = new HashMap(); + attributeMappings = new HashMap(); } - + @Test public void testUserDetailsMapping() { - MockUserDetailsWithAttributes detailsWithAttributes = new MockUserDetailsWithAttributes(); + MockUserDetailsWithAttributes detailsWithAttributes = new MockUserDetailsWithAttributes(); mapper = new UserDetailsExpressionUserMapper(attributeMappings); User user = new User(); detailsWithAttributes.getAttributes().put("sample", "mock"); attributeMappings.put("transformed", "sample"); mapper.mapUser(detailsWithAttributes, user); - + assertEquals(1, user.getAttribute().size()); assertEquals("transformed", user.getAttribute().get(0).getName()); assertEquals("mock", user.getAttribute().get(0).getValue()); } - + @Test public void testJsonsMapping() { mapper = new JSONExpressionUserMapper(attributeMappings); User user = new User(); attributeMappings.put("transformed", "email"); mapper.mapUser(SAMPLE_JSON, user); - + assertEquals(1, user.getAttribute().size()); assertEquals("transformed", user.getAttribute().get(0).getName()); assertEquals("myemail@email.com", user.getAttribute().get(0).getValue()); } - + @Test public void testMapMapping() { mapper = new MapExpressionUserMapper(attributeMappings); @@ -81,7 +80,7 @@ public void testMapMapping() { Map attributes = new HashMap(); attributes.put("my_email", "myemail@email.com"); mapper.mapUser(attributes, user); - + assertEquals(1, user.getAttribute().size()); assertEquals("transformed", user.getAttribute().get(0).getName()); assertEquals("myemail@email.com", user.getAttribute().get(0).getValue()); diff --git a/src/core/security/src/test/java/it/geosolutions/geostore/core/security/MockUserDetailsWithAttributes.java b/src/core/security/src/test/java/it/geosolutions/geostore/core/security/MockUserDetailsWithAttributes.java index 0f92f074..77929fe8 100644 --- a/src/core/security/src/test/java/it/geosolutions/geostore/core/security/MockUserDetailsWithAttributes.java +++ b/src/core/security/src/test/java/it/geosolutions/geostore/core/security/MockUserDetailsWithAttributes.java @@ -3,13 +3,12 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; - import org.springframework.security.core.GrantedAuthority; public class MockUserDetailsWithAttributes implements UserDetailsWithAttributes { Map attributes = new HashMap(); - + public Map getAttributes() { return attributes; } @@ -60,5 +59,4 @@ public boolean isEnabled() { public Object getAttribute(String name) { return attributes.get(name); } - } diff --git a/src/core/security/src/test/java/it/geosolutions/geostore/core/security/SimpleGrantedAuthoritiesMapperTest.java b/src/core/security/src/test/java/it/geosolutions/geostore/core/security/SimpleGrantedAuthoritiesMapperTest.java index 40ecc32f..c07dc063 100644 --- a/src/core/security/src/test/java/it/geosolutions/geostore/core/security/SimpleGrantedAuthoritiesMapperTest.java +++ b/src/core/security/src/test/java/it/geosolutions/geostore/core/security/SimpleGrantedAuthoritiesMapperTest.java @@ -7,32 +7,41 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - import org.junit.Before; import org.junit.Test; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.GrantedAuthorityImpl; +import org.springframework.security.core.authority.SimpleGrantedAuthority; public class SimpleGrantedAuthoritiesMapperTest { - - SimpleGrantedAuthoritiesMapper mapper; - private Map roleMappings; - List authorities; - - @Before - public void setUp() { - roleMappings = new HashMap(); - mapper = new SimpleGrantedAuthoritiesMapper(roleMappings); - authorities = new ArrayList(); - } - - @Test - public void testMapping() { - roleMappings.put("A", "B"); - authorities.add(new GrantedAuthorityImpl("A")); - Collection mapped = mapper.mapAuthorities(authorities); - assertEquals(1, mapped.size()); - assertEquals("B", mapped.iterator().next().getAuthority()); - } + SimpleGrantedAuthoritiesMapper mapper; + private Map roleMappings; + List authorities; + + @Before + public void setUp() { + roleMappings = new HashMap(); + mapper = new SimpleGrantedAuthoritiesMapper(roleMappings); + authorities = new ArrayList(); + } + + @Test + public void testMapping() { + roleMappings.put("A", "B"); + authorities.add(new SimpleGrantedAuthority("A")); + Collection mapped = mapper.mapAuthorities(authorities); + assertEquals(1, mapped.size()); + assertEquals("B", mapped.iterator().next().getAuthority()); + } + + @Test + public void testDropUnmappedAuthiorities() { + mapper.setDropUnmapped(true); + roleMappings.put("A", "B"); + authorities.add(new SimpleGrantedAuthority("A")); + authorities.add(new SimpleGrantedAuthority("C")); + Collection mapped = mapper.mapAuthorities(authorities); + assertEquals(1, mapped.size()); + assertEquals("B", mapped.iterator().next().getAuthority()); + } } diff --git a/src/core/security/src/test/java/it/geosolutions/geostore/core/security/ldap/CustomAttributesLdapUserDetailsMapperTest.java b/src/core/security/src/test/java/it/geosolutions/geostore/core/security/ldap/CustomAttributesLdapUserDetailsMapperTest.java index 1d0db1fe..1ddb2068 100644 --- a/src/core/security/src/test/java/it/geosolutions/geostore/core/security/ldap/CustomAttributesLdapUserDetailsMapperTest.java +++ b/src/core/security/src/test/java/it/geosolutions/geostore/core/security/ldap/CustomAttributesLdapUserDetailsMapperTest.java @@ -29,39 +29,37 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; + import it.geosolutions.geostore.core.ldap.MockDirContextOperations; import it.geosolutions.geostore.core.security.UserDetailsWithAttributes; -import it.geosolutions.geostore.core.security.ldap.CustomAttributesLdapUserDetailsMapper; - import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; - import org.junit.Before; import org.junit.Test; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; public class CustomAttributesLdapUserDetailsMapperTest { - + private static final String SAMPLE_USERNAME = "username"; - + private CustomAttributesLdapUserDetailsMapper mapper; private Map attributeMappings; private Collection authorities; - + MockDirContextOperations ctx; - + @Before public void setUp() { attributeMappings = new HashMap(); authorities = Collections.EMPTY_LIST; - mapper = new CustomAttributesLdapUserDetailsMapper(attributeMappings ); + mapper = new CustomAttributesLdapUserDetailsMapper(attributeMappings); ctx = new MockDirContextOperations(); } - + @Test public void testMappings() { ctx.getLdapAttributes().put("cn", "mock"); diff --git a/src/core/security/src/test/java/it/geosolutions/geostore/core/security/password/EncodingTest.java b/src/core/security/src/test/java/it/geosolutions/geostore/core/security/password/EncodingTest.java index ca3aac64..c06dc9bf 100644 --- a/src/core/security/src/test/java/it/geosolutions/geostore/core/security/password/EncodingTest.java +++ b/src/core/security/src/test/java/it/geosolutions/geostore/core/security/password/EncodingTest.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2014 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -22,126 +22,116 @@ import java.io.File; import java.net.URL; import javax.crypto.SecretKey; - import junit.framework.TestCase; - import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * This Test unit test the various functionalities of the encoders. + * * @author Lorenzo Natali (lorenzo.natali at geo-solutions.it) */ public class EncodingTest extends TestCase { - private static final String TEST_KEYSTORE_FILE_NAME = "geostore.jceks"; - private ClassPathXmlApplicationContext context; - private char[] passwd; - - @Override - protected void setUp() throws Exception { - super.setUp(); - String[] paths = { "classpath*:encoders-context-test.xml" }; - context = new ClassPathXmlApplicationContext(paths); - URL url = this.getClass().getResource("/geostore.jceks"); - File f = new File(url.toURI()); - - //plain text master password - URL passFile = this.getClass().getResource("/passwd-test"); - URLMasterPasswordProvider pp = new URLMasterPasswordProvider(); - pp.setEncrypting(false); - pp.setURL(passFile); - passwd = pp.doGetMasterPassword(); - if(f.exists()){ - KeyStoreProviderImpl ksp = (KeyStoreProviderImpl)context.getBean("keyStoreProvider"); - ksp.setKeyStoreFilePath(f.getAbsolutePath()); - ksp.setMasterPassword(passwd); - - } - - } - - @Test - public void testPbe() throws Exception { - GeoStorePBEPasswordEncoder pbePasswordEncoder = (GeoStorePBEPasswordEncoder)context.getBean("pbePasswordEncoder"); - KeyStoreProviderImpl p = (KeyStoreProviderImpl)context.getBean("keyStoreProvider"); - assertTrue(p.isKeyStorePassword(passwd)); - performPbeTest( pbePasswordEncoder); - - //crypt master password - URLMasterPasswordProvider pp = new URLMasterPasswordProvider(); - pp.setEncrypting(true); - URL pwenc = this.getClass().getResource("/passwd-test-enc"); - pp.setURL(pwenc); - performPbeTest( pbePasswordEncoder); - - - } - - private void performPbeTest(GeoStorePBEPasswordEncoder pbePasswordEncoder ) { - String testPassword = "testpassword"; - assertEquals(testPassword,pbePasswordEncoder.decode("crypt1:XPTERjaoupiG27xO5w/PdmrlcVDWOPVo")); - String encoded = pbePasswordEncoder.encodePassword("testpassword", null); - assertTrue(pbePasswordEncoder.isResponsibleForEncoding(encoded)); - assertEquals(testPassword,pbePasswordEncoder.decode(encoded)); - pbePasswordEncoder.isPasswordValid(encoded,testPassword , null); - - - - - - } + private static final String TEST_KEYSTORE_FILE_NAME = "geostore.jceks"; + private ClassPathXmlApplicationContext context; + private char[] passwd; + + @Override + protected void setUp() throws Exception { + super.setUp(); + String[] paths = {"classpath*:encoders-context-test.xml"}; + context = new ClassPathXmlApplicationContext(paths); + URL url = this.getClass().getResource("/geostore.jceks"); + File f = new File(url.toURI()); + + // plain text master password + URL passFile = this.getClass().getResource("/passwd-test"); + URLMasterPasswordProvider pp = new URLMasterPasswordProvider(); + pp.setEncrypting(false); + pp.setURL(passFile); + passwd = pp.doGetMasterPassword(); + if (f.exists()) { + KeyStoreProviderImpl ksp = (KeyStoreProviderImpl) context.getBean("keyStoreProvider"); + ksp.setKeyStoreFilePath(f.getAbsolutePath()); + ksp.setMasterPassword(passwd); + } + } + + @Test + public void testPbe() throws Exception { + GeoStorePBEPasswordEncoder pbePasswordEncoder = + (GeoStorePBEPasswordEncoder) context.getBean("pbePasswordEncoder"); + KeyStoreProviderImpl p = (KeyStoreProviderImpl) context.getBean("keyStoreProvider"); + assertTrue(p.isKeyStorePassword(passwd)); + performPbeTest(pbePasswordEncoder); + + // crypt master password + URLMasterPasswordProvider pp = new URLMasterPasswordProvider(); + pp.setEncrypting(true); + URL pwenc = this.getClass().getResource("/passwd-test-enc"); + pp.setURL(pwenc); + performPbeTest(pbePasswordEncoder); + } + + private void performPbeTest(GeoStorePBEPasswordEncoder pbePasswordEncoder) { + String testPassword = "testpassword"; + assertEquals( + testPassword, pbePasswordEncoder.decode("crypt1:XPTERjaoupiG27xO5w/PdmrlcVDWOPVo")); + String encoded = pbePasswordEncoder.encodePassword("testpassword", null); + assertTrue(pbePasswordEncoder.isResponsibleForEncoding(encoded)); + assertEquals(testPassword, pbePasswordEncoder.decode(encoded)); + pbePasswordEncoder.isPasswordValid(encoded, testPassword, null); + } + + @Test + public void testDigest() throws Exception { + GeoStoreDigestPasswordEncoder pe = + (GeoStoreDigestPasswordEncoder) context.getBean("digestPasswordEncoder"); + String rawPass = "testPassword"; + String encPass = pe.encodePassword(rawPass, null); + assertTrue(pe.isResponsibleForEncoding(encPass)); + assertTrue(pe.isPasswordValid(encPass, rawPass, null)); + } - @Test - public void testDigest() throws Exception{ - GeoStoreDigestPasswordEncoder pe = (GeoStoreDigestPasswordEncoder) context.getBean("digestPasswordEncoder"); - String rawPass = "testPassword"; - String encPass = pe.encodePassword(rawPass, null); - assertTrue(pe.isResponsibleForEncoding(encPass)); - assertTrue(pe.isPasswordValid(encPass, rawPass, null)); - - } - - @Test - public void testCreateKeyStore() throws Exception{ - File f = new File(EncodingTest.TEST_KEYSTORE_FILE_NAME); - if (f.exists()){ - System.out.println("delete previous keystore"); - f.delete(); - } - char[] passwd = {'t','e','s','t','p','w'}; - char[] passwd2 = {'g','e','o','s','t','o','r','e'}; - String keyName = "ug:geostore:key"; - String keyName2= "keyName2"; - KeyStoreProviderImpl ksp = new KeyStoreProviderImpl(); - ksp.setKeyName(keyName); - ksp.setKeyStoreFilePath("testStore"); - ksp.setMasterPassword(passwd); - ksp.setSecretKey(keyName, "testkey".toCharArray()); - ksp.setSecretKey(keyName2, "testkey2".toCharArray()); - SecretKey k= ksp.getSecretKey(keyName); - - assertTrue(ksp.containsAlias(keyName)); - assertTrue(ksp.containsAlias(keyName)); - ksp.removeKey(keyName2); - assertFalse(ksp.containsAlias(keyName2)); - ksp = new KeyStoreProviderImpl(); - ksp.setKeyName(keyName); - ksp.setMasterPassword(passwd2); - ksp.setKeyStoreFilePath(EncodingTest.TEST_KEYSTORE_FILE_NAME); - ksp.setSecretKey(keyName, new RandomPasswordProvider().getRandomPasswordWithDefaultLength()); - System.out.print(ksp.keyStoreFile.getAbsolutePath()); - - } - - @Override - protected void tearDown() throws Exception { - //delete test key store - File f = new File(EncodingTest.TEST_KEYSTORE_FILE_NAME); - if (f.exists()){ - f.delete(); - } - super.tearDown(); - } + @Test + public void testCreateKeyStore() throws Exception { + File f = new File(EncodingTest.TEST_KEYSTORE_FILE_NAME); + if (f.exists()) { + System.out.println("delete previous keystore"); + f.delete(); + } + char[] passwd = {'t', 'e', 's', 't', 'p', 'w'}; + char[] passwd2 = {'g', 'e', 'o', 's', 't', 'o', 'r', 'e'}; + String keyName = "ug:geostore:key"; + String keyName2 = "keyName2"; + KeyStoreProviderImpl ksp = new KeyStoreProviderImpl(); + ksp.setKeyName(keyName); + ksp.setKeyStoreFilePath("testStore"); + ksp.setMasterPassword(passwd); + ksp.setSecretKey(keyName, "testkey".toCharArray()); + ksp.setSecretKey(keyName2, "testkey2".toCharArray()); + SecretKey k = ksp.getSecretKey(keyName); + assertTrue(ksp.containsAlias(keyName)); + assertTrue(ksp.containsAlias(keyName)); + ksp.removeKey(keyName2); + assertFalse(ksp.containsAlias(keyName2)); + ksp = new KeyStoreProviderImpl(); + ksp.setKeyName(keyName); + ksp.setMasterPassword(passwd2); + ksp.setKeyStoreFilePath(EncodingTest.TEST_KEYSTORE_FILE_NAME); + ksp.setSecretKey( + keyName, new RandomPasswordProvider().getRandomPasswordWithDefaultLength()); + System.out.print(ksp.keyStoreFile.getAbsolutePath()); + } + @Override + protected void tearDown() throws Exception { + // delete test key store + File f = new File(EncodingTest.TEST_KEYSTORE_FILE_NAME); + if (f.exists()) { + f.delete(); + } + super.tearDown(); + } } diff --git a/src/core/security/src/test/java/it/geosolutions/geostore/core/security/password/MasterPasswordProviderTest.java b/src/core/security/src/test/java/it/geosolutions/geostore/core/security/password/MasterPasswordProviderTest.java index eceea2b3..bfda4bdf 100644 --- a/src/core/security/src/test/java/it/geosolutions/geostore/core/security/password/MasterPasswordProviderTest.java +++ b/src/core/security/src/test/java/it/geosolutions/geostore/core/security/password/MasterPasswordProviderTest.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2014 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -24,39 +24,35 @@ import static org.junit.Assert.assertTrue; import org.junit.Test; + /** * Test the Random Permutation - * @author Lorenzo Natali * + * @author Lorenzo Natali */ public class MasterPasswordProviderTest { - - @Test - public void testCodec(){ - URLMasterPasswordProvider mp = new URLMasterPasswordProvider(); - String testString="thisIsMyPassword"; - char[] test = testString.toCharArray(); - byte[] encPass = mp.encode(test); - //System.out.println(toChars(encPass)); - byte[] dec = mp.decode(encPass); - mp.setEncrypting(true); - assertTrue(testString.equals(new String(toChars(dec)))); - - String geostore = "kfn8fAS8YMHgLxR8i3VBhzenrp3lnLLT"; - String geoserver1 = "F8fX7L2gs8H5SVD2q7HoC3IKo/0QAbEx"; - String geoserver2 = "5Cjk79u2Ya9RVfhn72xBLpVXjiGGL4+R"; - - System.out.println(new String(toChars(mp.decode(geostore.getBytes())))); - assertEquals("geostore",new String(toChars(mp.decode(geostore.getBytes())))); - assertEquals("geoserver1",new String(toChars(mp.decode(geoserver1.getBytes())))); - assertEquals("geoserver2",new String(toChars(mp.decode(geoserver2.getBytes())))); - + @Test + public void testCodec() { + URLMasterPasswordProvider mp = new URLMasterPasswordProvider(); + String testString = "thisIsMyPassword"; + char[] test = testString.toCharArray(); + byte[] encPass = mp.encode(test); + // System.out.println(toChars(encPass)); + byte[] dec = mp.decode(encPass); + mp.setEncrypting(true); + assertTrue(testString.equals(new String(toChars(dec)))); + + String geostore = "kfn8fAS8YMHgLxR8i3VBhzenrp3lnLLT"; + String geoserver1 = "F8fX7L2gs8H5SVD2q7HoC3IKo/0QAbEx"; + String geoserver2 = "5Cjk79u2Ya9RVfhn72xBLpVXjiGGL4+R"; + + System.out.println(new String(toChars(mp.decode(geostore.getBytes())))); + assertEquals("geostore", new String(toChars(mp.decode(geostore.getBytes())))); + assertEquals("geoserver1", new String(toChars(mp.decode(geoserver1.getBytes())))); + assertEquals("geoserver2", new String(toChars(mp.decode(geoserver2.getBytes())))); } - - @Test - public void testURL(){ - - } - + + @Test + public void testURL() {} } diff --git a/src/core/security/src/test/java/it/geosolutions/geostore/core/security/password/PwEncoderTest.java b/src/core/security/src/test/java/it/geosolutions/geostore/core/security/password/PwEncoderTest.java index a454af29..277a85a3 100644 --- a/src/core/security/src/test/java/it/geosolutions/geostore/core/security/password/PwEncoderTest.java +++ b/src/core/security/src/test/java/it/geosolutions/geostore/core/security/password/PwEncoderTest.java @@ -1,26 +1,25 @@ /* * Copyright (C) 2007 - 2014 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.core.security.password; -import it.geosolutions.geostore.core.security.password.PwEncoder; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -30,42 +29,39 @@ /** * Test for password encoder + * * @author ETj * @author Lorenzo Natali */ public class PwEncoderTest { - public PwEncoderTest() { - } + public PwEncoderTest() {} @BeforeClass - public static void setUpClass() throws Exception { - } + public static void setUpClass() throws Exception {} @AfterClass - public static void tearDownClass() throws Exception { - } + public static void tearDownClass() throws Exception {} @Test public void testEncode() { testString("test"); testString("topolino"); testString(""); - } /** * Test encode and decode string + * * @param test */ public void testString(String test) { String enc = PwEncoder.encode(test); System.out.println("ENC --> " + enc); - if( PwEncoder.getEncoder().isReversible() ){ - String dec = PwEncoder.decode(enc); - System.out.println("DEC --> " + dec); + if (PwEncoder.getEncoder().isReversible()) { + String dec = PwEncoder.decode(enc); + System.out.println("DEC --> " + dec); assertEquals(test, dec); } assertTrue(PwEncoder.isPasswordValid(enc, test)); } - -} \ No newline at end of file +} diff --git a/src/core/services-api/pom.xml b/src/core/services-api/pom.xml index 526990b4..27ab9514 100644 --- a/src/core/services-api/pom.xml +++ b/src/core/services-api/pom.xml @@ -25,7 +25,7 @@ it.geosolutions.geostore geostore-core - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-services-api @@ -49,13 +49,17 @@ hibernate-core org.hibernate + + org.hibernate + hibernate-ehcache + hibernate org.hibernate hibernate-commons-annotations - org.hibernate + org.hibernate.common hibernate-spatial @@ -92,12 +96,8 @@ - dom4j - dom4j - - - log4j - log4j + org.apache.logging.log4j + log4j-core @@ -127,7 +127,7 @@ javax.servlet - servlet-api + javax.servlet-api provided @@ -138,28 +138,6 @@ org.springframework.security spring-security-core - - - - - - - - diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/CategoryService.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/CategoryService.java index 3d8fa699..c9815410 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/CategoryService.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/CategoryService.java @@ -5,7 +5,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -17,7 +17,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -31,16 +31,14 @@ import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; - import java.util.List; /** * Interafce CategoryService. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ -public interface CategoryService extends SecurityService{ +public interface CategoryService extends SecurityService { /** * @param category @@ -89,5 +87,4 @@ public interface CategoryService extends SecurityService{ * @return long */ long getCount(String nameLike); - } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/ResourceService.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/ResourceService.java index 09a10ae1..bfae93fb 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/ResourceService.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/ResourceService.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -39,16 +39,15 @@ import it.geosolutions.geostore.services.exception.DuplicatedResourceNameServiceEx; import it.geosolutions.geostore.services.exception.InternalErrorServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; - import java.util.List; /** * Interafce ResourceService. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ -public interface ResourceService extends SecurityService{ +public interface ResourceService extends SecurityService { // ========================================================================== // Basic operations @@ -59,15 +58,16 @@ public interface ResourceService extends SecurityService{ * @return long * @throws BadRequestServiceEx * @throws NotFoundServiceEx - * @throws DuplicatedResourceNameServiceEx + * @throws DuplicatedResourceNameServiceEx */ - long insert(Resource resource) throws BadRequestServiceEx, NotFoundServiceEx, DuplicatedResourceNameServiceEx; + long insert(Resource resource) + throws BadRequestServiceEx, NotFoundServiceEx, DuplicatedResourceNameServiceEx; /** * @param resource * @return long * @throws NotFoundServiceEx - * @throws DuplicatedResourceNameServiceEx + * @throws DuplicatedResourceNameServiceEx */ long update(Resource resource) throws NotFoundServiceEx, DuplicatedResourceNameServiceEx; @@ -114,7 +114,6 @@ List getAll(Integer page, Integer entries, User authUser) /** * @param nameLike * @return long - * * @deprecated count should be done on a per-user basis */ @Deprecated @@ -125,7 +124,6 @@ List getAll(Integer page, Integer entries, User authUser) * @return long * @throws InternalErrorServiceEx * @throws BadRequestServiceEx - * * @deprecated count should be done on a per-user basis */ @Deprecated @@ -179,8 +177,9 @@ List getResources(SearchFilter filter, User authUser) * @throws BadRequestServiceEx * @throws InternalErrorServiceEx */ - List getResources(SearchFilter filter, Integer page, Integer entries, - User authUser) throws BadRequestServiceEx, InternalErrorServiceEx; + List getResources( + SearchFilter filter, Integer page, Integer entries, User authUser) + throws BadRequestServiceEx, InternalErrorServiceEx; /** * @param filter @@ -192,8 +191,13 @@ List getResources(SearchFilter filter, Integer page, Integer entr * @throws BadRequestServiceEx * @throws InternalErrorServiceEx */ - List getResources(SearchFilter filter, Integer page, Integer entries, - boolean includeAttributes, boolean includeData, User authUser) + List getResources( + SearchFilter filter, + Integer page, + Integer entries, + boolean includeAttributes, + boolean includeData, + User authUser) throws BadRequestServiceEx, InternalErrorServiceEx; /** @@ -205,37 +209,35 @@ List getResources(SearchFilter filter, Integer page, Integer entries, public List getResourcesFull(SearchFilter filter, User authUser) throws BadRequestServiceEx, InternalErrorServiceEx; - /** * Returns the list of security rules for the resource. - * - * @param resources + * + * @param id * @return */ public List getSecurityRules(long id) - throws BadRequestServiceEx, InternalErrorServiceEx; - + throws BadRequestServiceEx, InternalErrorServiceEx; + /** * Replaces the list of security rules for the given resource. - * + * * @param id * @param rules * @throws BadRequestServiceEx * @throws InternalErrorServiceEx - * @throws NotFoundServiceEx + * @throws NotFoundServiceEx */ public void updateSecurityRules(long id, List rules) - throws BadRequestServiceEx, InternalErrorServiceEx, NotFoundServiceEx; - - + throws BadRequestServiceEx, InternalErrorServiceEx, NotFoundServiceEx; + /** * Get filter count by filter and user + * * @param filter * @param user * @return resources' count that the user has access - * @throws InternalErrorServiceEx + * @throws InternalErrorServiceEx * @throws BadRequestServiceEx - * * @deprecated rename into count() */ @Deprecated @@ -243,19 +245,17 @@ long getCountByFilterAndUser(SearchFilter filter, User user) throws BadRequestServiceEx, InternalErrorServiceEx; /** - * Get filter count by namerLike and user + * Get filter count by nameLike and user + * * @param nameLike * @param user * @return resources' count that the user has access - * @throws BadRequestServiceEx + * @throws BadRequestServiceEx * @deprecated rename into count() */ @Deprecated - long getCountByFilterAndUser(String nameLike, User user) - throws BadRequestServiceEx; + long getCountByFilterAndUser(String nameLike, User user) throws BadRequestServiceEx; long insertAttribute(long id, String name, String value, DataType type) - throws InternalErrorServiceEx; - - + throws InternalErrorServiceEx; } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/SecurityService.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/SecurityService.java index 3c417632..c7edc17d 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/SecurityService.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/SecurityService.java @@ -20,31 +20,27 @@ package it.geosolutions.geostore.services; import it.geosolutions.geostore.core.model.SecurityRule; - import java.util.List; /** * @author DamianoG - * - * This Interface defines operations to retrieve the security rules based on users and groups + *

    This Interface defines operations to retrieve the security rules based on users and groups */ public interface SecurityService { - + /** - * * @param userName - * @param entityId the Id of the entity (f.e. Resource, Category, StoredData...) that the underlying implementation will be responsible for retrieve security rules + * @param entityId the Id of the entity (f.e. Resource, Category, StoredData...) that the + * underlying implementation will be responsible for retrieve security rules * @return */ List getUserSecurityRule(String userName, long entityId); - + /** - * * @param groupName - * @param entityId entityId the Id of the entity (f.e. Resource, Category, StoredData...) that the underlying implementation will be responsible for retrieve security rules + * @param entityId entityId the Id of the entity (f.e. Resource, Category, StoredData...) that + * the underlying implementation will be responsible for retrieve security rules * @return */ List getGroupSecurityRule(List groupNames, long entityId); - - } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/StoredDataService.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/StoredDataService.java index 466a301c..30cda88a 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/StoredDataService.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/StoredDataService.java @@ -21,16 +21,15 @@ import it.geosolutions.geostore.core.model.StoredData; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; - import java.util.List; /** * Interafce StoredDataService. Operations on {@link StoredData StoredData}s. - * + * * @author Emanuele Tajariol (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ -public interface StoredDataService extends SecurityService{ +public interface StoredDataService extends SecurityService { /** * @param id @@ -53,14 +52,9 @@ public interface StoredDataService extends SecurityService{ */ StoredData get(long id) throws NotFoundServiceEx; - /** - * @return List - */ + /** @return List */ List getAll(); - /** - * @return List - */ + /** @return List */ List getAllFull(); - } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/UserGroupService.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/UserGroupService.java index 49b16b33..8e69d5f4 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/UserGroupService.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/UserGroupService.java @@ -21,52 +21,45 @@ import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.UserGroupAttribute; import it.geosolutions.geostore.services.dto.ShortResource; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; - +import java.util.Collection; import java.util.List; -/** - * @author DamianoG - * - */ +/** @author DamianoG */ public interface UserGroupService { /** - * * @param userGroup * @return * @throws BadRequestServiceEx */ long insert(UserGroup userGroup) throws BadRequestServiceEx; - + /** - * * @param id * @throws NotFoundServiceEx - * @throws BadRequestServiceEx + * @throws BadRequestServiceEx */ boolean delete(long id) throws NotFoundServiceEx, BadRequestServiceEx; - + /** - * * @param userId * @param groupId * @throws NotFoundServiceEx */ void assignUserGroup(long userId, long groupId) throws NotFoundServiceEx; - + /** - * * @param userId * @param groupId * @throws NotFoundServiceEx */ void deassignUserGroup(long userId, long groupId) throws NotFoundServiceEx; - + /** - * * @param page * @param entries * @return @@ -81,49 +74,61 @@ public interface UserGroupService { * @param page the requested page number * @param entries max entries for page. * @param nameLike a sub-string to search in group name - * @param all if true adds to result the 'everyone' group if it matches the searching criteria + * @param all if true adds to result the 'everyone' group if it matches the + * searching criteria * @return a list of groups that match searching criteria with pagination. - * * @throws BadRequestServiceEx */ - List getAllAllowed(User user, Integer page, Integer entries, String nameLike, boolean all) throws BadRequestServiceEx; + List getAllAllowed( + User user, Integer page, Integer entries, String nameLike, boolean all) + throws BadRequestServiceEx; UserGroup get(long id) throws BadRequestServiceEx; /** - * * @param groupId * @param resourcesToSet * @param canRead * @param canWrite * @return - * @throws BadRequestServiceEx + * @throws BadRequestServiceEx * @throws BadRequestWebEx * @throws NotFoundWebEx */ - List updateSecurityRules(Long groupId, List resourcesToSet, boolean canRead, boolean canWrite) throws NotFoundServiceEx, BadRequestServiceEx; - + List updateSecurityRules( + Long groupId, List resourcesToSet, boolean canRead, boolean canWrite) + throws NotFoundServiceEx, BadRequestServiceEx; + /** * Persist the special UserGroups, those that implies special behavior - * - * For obvious reasons this Method MUST NOT exposed through the rest interface. - * - * @return true if the persist operation finish with success, false otherwise + * + *

    For obvious reasons this Method MUST NOT exposed through the rest interface. + * + * @return true if the persist operation finish with success, false otherwise */ public boolean insertSpecialUsersGroups(); + + /** + * Remove the special UserGroups, those that implies special behavior + * + *

    For obvious reasons this Method MUST NOT exposed through the rest interface. + * + * @return true if the removal operation finish with success, false otherwise + */ + public boolean removeSpecialUsersGroups(); /** * Get The UserGroup from the name + * * @param name */ public UserGroup get(String name); /** - * Returns the amount of groups that match searching criteria. - * The 'everyone' group is never included. + * Returns the amount of groups that match searching criteria. The 'everyone' group is never + * included. * * @param authUser the user that performs the research * @param nameLike a sub-string to search in group name * @return the amount of groups that match searching criteria - * * @throws BadRequestServiceEx */ long getCount(User authUser, String nameLike) throws BadRequestServiceEx; @@ -133,10 +138,27 @@ public interface UserGroupService { * * @param authUser the user that performs the research * @param nameLike a sub-string to search in group name - * @param all if true adds to result the 'everyone' group if it matches the searching criteria + * @param all if true adds to result the 'everyone' group if it matches the + * searching criteria * @return the amount of groups that match searching criteria - * * @throws BadRequestServiceEx */ long getCount(User authUser, String nameLike, boolean all) throws BadRequestServiceEx; + + /** + * @param id + * @param attributes + * @throws NotFoundServiceEx + */ + void updateAttributes(long id, List attributes) throws NotFoundServiceEx; + + /** + * @param group + * @return long + * @throws NotFoundServiceEx + * @throws BadRequestServiceEx + */ + long update(UserGroup group) throws NotFoundServiceEx, BadRequestServiceEx; + + Collection findByAttribute(String name, List values, boolean ignoreCase); } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/UserService.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/UserService.java index bca2c375..7338c4cf 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/UserService.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/UserService.java @@ -5,7 +5,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -17,7 +17,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -33,15 +33,13 @@ import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; - import java.util.Collection; import java.util.List; /** * Class UserInterface. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public interface UserService { @@ -111,23 +109,23 @@ List getAll(Integer page, Integer entries, String nameLike, boolean includ * @throws NotFoundServiceEx */ void updateAttributes(long id, List attributes) throws NotFoundServiceEx; - + /** * Persist the special Users, those that implies special behavior (Like GUEST) - * - * For obvious reasons this Method MUST NOT exposed through the rest interface. - * - * @return true if the persist operation finish with success, false otherwise + * + *

    For obvious reasons this Method MUST NOT exposed through the rest interface. + * + * @return true if the persist operation finish with success, false otherwise */ public boolean insertSpecialUsers(); - + /** * Returns all user with the specified attribute (name / value). - * + * * @param attribute * @return */ - public Collection getByAttribute(UserAttribute attribute); - public Collection getByGroup(UserGroup group); + public Collection getByAttribute(UserAttribute attribute); + public Collection getByGroup(UserGroup group); } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/UserSessionService.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/UserSessionService.java index 98aee73a..498f7c18 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/UserSessionService.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/UserSessionService.java @@ -31,68 +31,66 @@ import it.geosolutions.geostore.services.dto.UserSession; /** - * Basic interface for a UserSession service. - * The service should allow registering new sessions, verifying them, removing and automatic expiring. - * + * Basic interface for a UserSession service. The service should allow registering new sessions, + * verifying them, removing and automatic expiring. + * * @author Mauro Bartolomeoli * @author Lorenzo Natali */ public interface UserSessionService { - - /** - * Gets user data for the given session id (if existing). - * - * @param sessionId - * @return - */ + + /** + * Gets user data for the given session id (if existing). + * + * @param sessionId + * @return + */ public User getUserData(String sessionId); - - /** - * Gets refresh token for a given session id (if existing). - * - * @param sessionId - * @return - */ + + /** + * Gets refresh token for a given session id (if existing). + * + * @param sessionId + * @return + */ public String getRefreshToken(String sessionId); - + /** * Refresh an expiring session by the given interval. - * + * * @param sessionId */ public UserSession refreshSession(String sessionId, String refreshToken); - + /** * Register a new session. The session id is given. - * + * * @param sessionId * @param session */ public void registerNewSession(String sessionId, UserSession session); - + /** * Register a new session. The session id is automatically created and returned. - * + * * @param session * @return the generated session id */ public String registerNewSession(UserSession session); - + /** * Remove a session, given its id. + * * @param sessionId */ public void removeSession(String sessionId); - - - /** - * Remove all the sessions. - */ + + /** Remove all the sessions. */ public void removeAllSessions(); - + /** * Checks that owner is the user bound to the given sessionId. - * + * * @param sessionId * @param owner * @return diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/ShortAttribute.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/ShortAttribute.java index d01dbc0b..936ddd8f 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/ShortAttribute.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/ShortAttribute.java @@ -21,13 +21,13 @@ import it.geosolutions.geostore.core.model.Attribute; import it.geosolutions.geostore.core.model.enums.DataType; - import java.io.Serializable; +import java.text.SimpleDateFormat; import java.util.Date; /** * Class ShortAttribute. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ public class ShortAttribute implements Serializable { @@ -64,23 +64,20 @@ public ShortAttribute(String name, String value, DataType type) { } public static ShortAttribute createDateAttribute(String name, Date date) { - return new ShortAttribute(name, Attribute.DATE_FORMAT.format(date), DataType.DATE); + return new ShortAttribute( + name, new SimpleDateFormat(Attribute.DATE_FORMAT).format(date), DataType.DATE); } public static ShortAttribute createStringAttribute(String name, String text) { return new ShortAttribute(name, text, DataType.STRING); } - /** - * @return the attribute - */ + /** @return the attribute */ public String getName() { return name; } - /** - * @param name the attribute to set - */ + /** @param name the attribute to set */ public void setName(String name) { this.name = name; } @@ -89,30 +86,24 @@ public String getValue() { return this.value; } - /** - * @param value - */ + /** @param value */ public void setValue(String value) { this.value = value; } - /** - * @return the type - */ + /** @return the type */ public DataType getType() { return type; } - /** - * @param type the type to set - */ + /** @param type the type to set */ public void setType(DataType type) { this.type = type; } /* * (non-Javadoc) - * + * * @see java.lang.Object#toString() */ @Override @@ -120,17 +111,13 @@ public String toString() { StringBuilder builder = new StringBuilder(); builder.append(getClass().getSimpleName()).append('['); - if (name != null) - builder.append("name=").append(name).append(", "); + if (name != null) builder.append("name=").append(name).append(", "); - if (value != null) - builder.append("value=").append(value).append(", "); + if (value != null) builder.append("value=").append(value).append(", "); - if (type != null) - builder.append("type=").append(type); + if (type != null) builder.append("type=").append(type); builder.append(']'); return builder.toString(); } - } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/ShortResource.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/ShortResource.java index 9220ded6..3050ebda 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/ShortResource.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/ShortResource.java @@ -5,7 +5,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -17,7 +17,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -29,16 +29,14 @@ package it.geosolutions.geostore.services.dto; import it.geosolutions.geostore.core.model.Resource; - import java.io.Serializable; import java.util.Date; import javax.xml.bind.annotation.XmlRootElement; /** * Class ShortResource. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ @XmlRootElement(name = "ShortResource") public class ShortResource implements Serializable { @@ -60,122 +58,129 @@ public class ShortResource implements Serializable { private boolean canDelete = false; - public ShortResource() { + private String creator; - } + private String editor; - /** - * @param resource - */ + private boolean advertised = true; + + public ShortResource() {} + + /** @param resource */ public ShortResource(Resource resource) { this.id = resource.getId(); this.name = resource.getName(); this.creation = resource.getCreation(); this.description = resource.getDescription(); this.lastUpdate = resource.getLastUpdate(); + this.creator = resource.getCreator(); + this.editor = resource.getEditor(); + this.advertised = resource.isAdvertised(); } - /** - * @return the id - */ + /** @return the id */ public long getId() { return id; } - /** - * @param id the id to set - */ + /** @param id the id to set */ public void setId(long id) { this.id = id; } - /** - * @return the name - */ + /** @return the name */ public String getName() { return name; } - /** - * @param name the name to set - */ + /** @param name the name to set */ public void setName(String name) { this.name = name; } - /** - * @return the description - */ + /** @return the description */ public String getDescription() { return description; } - /** - * @param descrition the description to set - */ + /** @param description the description to set */ public void setDescription(String description) { this.description = description; } - /** - * @return the creation - */ + /** @return the creation */ public Date getCreation() { return creation; } - /** - * @param creation the creation to set - */ + /** @param creation the creation to set */ public void setCreation(Date creation) { this.creation = creation; } - /** - * @return the lastUpdate - */ + /** @return the lastUpdate */ public Date getLastUpdate() { return lastUpdate; } - /** - * @param lastUpdate the lastUpdate to set - */ + /** @param lastUpdate the lastUpdate to set */ public void setLastUpdate(Date lastUpdate) { this.lastUpdate = lastUpdate; } - /** - * @return the canEdit - */ + /** @return the canEdit */ public boolean isCanEdit() { return canEdit; } - /** - * @param canEdit the canEdit to set - */ + /** @param canEdit the canEdit to set */ public void setCanEdit(boolean canEdit) { this.canEdit = canEdit; } - /** - * @return the canDelete - */ + /** @return the canDelete */ public boolean isCanDelete() { return canDelete; } - /** - * @param canDelete the canDelete to set - */ + /** @param canDelete the canDelete to set */ public void setCanDelete(boolean canDelete) { this.canDelete = canDelete; } + /** @return */ + public String getCreator() { + return creator; + } + + /** @param creator */ + public void setCreator(String creator) { + this.creator = creator; + } + + /** @return */ + public String getEditor() { + return editor; + } + + /** @param editor */ + public void setEditor(String editor) { + this.editor = editor; + } + + /** @return the advertised */ + public Boolean isAdvertised() { + return advertised; + } + + /** @param advertised the advertised to set */ + public void setAdvertised(Boolean advertised) { + this.advertised = advertised; + } + /* * (non-Javadoc) - * + * * @see java.lang.Object#toString() */ @Override @@ -183,26 +188,25 @@ public String toString() { StringBuilder builder = new StringBuilder(); builder.append(getClass().getSimpleName()).append('['); - if (name != null) - builder.append("name=").append(name); + if (name != null) builder.append("name=").append(name); - if (description != null) - builder.append("description=").append(description).append(", "); + if (description != null) builder.append("description=").append(description).append(", "); - if (creation != null) - builder.append("creation=").append(creation).append(", "); + if (creation != null) builder.append("creation=").append(creation).append(", "); - if (lastUpdate != null) - builder.append("lastUpdate=").append(lastUpdate).append(", "); + if (lastUpdate != null) builder.append("lastUpdate=").append(lastUpdate).append(", "); - if (canEdit) - builder.append("canEdit=").append(canEdit).append(", "); + if (canEdit) builder.append("canEdit=").append(true).append(", "); - if (canDelete) - builder.append("canDelete=").append(canDelete); + if (canDelete) builder.append("canDelete=").append(true); + + if (creator != null) builder.append("creator=").append(creator).append(", "); + + if (editor != null) builder.append("editor=").append(editor).append(", "); + + if (advertised) builder.append("advertised=").append(advertised); builder.append(']'); return builder.toString(); } - } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/UserSession.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/UserSession.java index f797f041..8a1e8cde 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/UserSession.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/UserSession.java @@ -30,42 +30,37 @@ import it.geosolutions.geostore.core.model.User; /** - * Basic interface for a UserSession. - * The session has a unique identifier, contains user data and allows for - * expiration check. - * + * Basic interface for a UserSession. The session has a unique identifier, contains user data and + * allows for expiration check. + * * @author Mauro Bartolomeoli * @author Lorenzo Natali */ public interface UserSession { - - public void setId(String id); - + + public void setId(String id); + public String getId(); - + public void setUser(User user); - + public User getUser(); - + public void setRefreshToken(String refreshToken); - + public String getRefreshToken(); void setExpirationInterval(long expirationInterval); - long getExpirationInterval(); - - /** - * Check if the token has expired - * @return true if it is expired - */ - public boolean isExpired(); - - + long getExpirationInterval(); + /** - * Update expirationDate - * adding expiration time (in seconds) + * Check if the token has expired + * + * @return true if it is expired */ - public void refresh(); + public boolean isExpired(); + /** Update expirationDate adding expiration time (in seconds) */ + public void refresh(); } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/UserSessionImpl.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/UserSessionImpl.java index ac0c269b..0aeb38f7 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/UserSessionImpl.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/UserSessionImpl.java @@ -27,42 +27,39 @@ */ package it.geosolutions.geostore.services.dto; -import java.util.Calendar; - import it.geosolutions.geostore.core.model.User; +import java.util.Calendar; /** * Basic implementation of UserSession. - * - * @author Mauro Bartolomeoli * + * @author Mauro Bartolomeoli */ public class UserSessionImpl implements UserSession { private String id; - + private User user; - + private Calendar expiration; - + private long expirationInterval = 0l; - - private String refreshToken; + private String refreshToken; - public UserSessionImpl(String id, User user, Calendar expiration) { + public UserSessionImpl(String id, User user, Calendar expiration) { super(); this.id = id; this.user = user; this.expiration = expiration; } - + public UserSessionImpl(User user, Calendar expiration) { super(); this.user = user; this.setExpiration(expiration); } - + public void setId(String id) { this.id = id; } @@ -72,54 +69,52 @@ public void setUser(User user) { } public String getRefreshToken() { - return refreshToken; - } + return refreshToken; + } - public void setRefreshToken(String refreshToken) { - this.refreshToken = refreshToken; - } + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } @Override public String getId() { return id; } - + @Override public User getUser() { return user; } - + @Override public boolean isExpired() { - if(expiration != null) { + if (expiration != null) { return expiration.getTime().before(Calendar.getInstance().getTime()); } return false; } - @Override - public void refresh() { - if(expiration != null) { - Calendar newExpiration = Calendar.getInstance(); - newExpiration.setTimeInMillis(newExpiration.getTimeInMillis() + expirationInterval* 1000); - setExpiration(newExpiration); - } - - } - public void setExpiration(Calendar expiration) { - this.expiration = expiration; + @Override + public void refresh() { + if (expiration != null) { + Calendar newExpiration = Calendar.getInstance(); + newExpiration.setTimeInMillis( + newExpiration.getTimeInMillis() + expirationInterval * 1000); + setExpiration(newExpiration); + } } - - @Override - public long getExpirationInterval() { - return expirationInterval; - } - @Override - public void setExpirationInterval(long expirationInterval) { - this.expirationInterval = expirationInterval; - } + public void setExpiration(Calendar expiration) { + this.expiration = expiration; + } + @Override + public long getExpirationInterval() { + return expirationInterval; + } - + @Override + public void setExpirationInterval(long expirationInterval) { + this.expirationInterval = expirationInterval; + } } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/AndFilter.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/AndFilter.java index b7a95a7e..e2b34875 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/AndFilter.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/AndFilter.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,7 +21,6 @@ import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.InternalErrorServiceEx; - import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -30,9 +29,8 @@ import javax.xml.bind.annotation.XmlRootElement; /** - * * Class AndFilter. - * + * * @author ETj (etj at geo-solutions.it) */ @XmlRootElement(name = "AND") @@ -43,8 +41,7 @@ public class AndFilter extends SearchFilter { private List filters = new ArrayList(); - public AndFilter() { - } + public AndFilter() {} public AndFilter(SearchFilter f1, SearchFilter f2, SearchFilter... other) { filters.add(f1); @@ -53,11 +50,13 @@ public AndFilter(SearchFilter f1, SearchFilter f2, SearchFilter... other) { } // molto molto brutto, da cambiare se possibile - @XmlElements({ @XmlElement(name = "ATTRIBUTE", type = AttributeFilter.class), - @XmlElement(name = "OR", type = OrFilter.class), - @XmlElement(name = "AND", type = AndFilter.class), - @XmlElement(name = "FIELD", type = FieldFilter.class), - @XmlElement(name = "CATEGORY", type = CategoryFilter.class) }) + @XmlElements({ + @XmlElement(name = "ATTRIBUTE", type = AttributeFilter.class), + @XmlElement(name = "OR", type = OrFilter.class), + @XmlElement(name = "AND", type = AndFilter.class), + @XmlElement(name = "FIELD", type = FieldFilter.class), + @XmlElement(name = "CATEGORY", type = CategoryFilter.class) + }) public List getFilters() { return filters; } @@ -79,5 +78,4 @@ public void accept(FilterVisitor visitor) throws BadRequestServiceEx, InternalEr public String toString() { return getClass().getSimpleName() + "[" + filters + '}'; } - } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/AttributeFilter.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/AttributeFilter.java index 321beba7..f87be554 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/AttributeFilter.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/AttributeFilter.java @@ -22,14 +22,12 @@ import it.geosolutions.geostore.core.model.enums.DataType; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.InternalErrorServiceEx; - import java.io.Serializable; - import javax.xml.bind.annotation.XmlRootElement; /** * Class Search. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ @XmlRootElement(name = "Attribute") @@ -46,12 +44,8 @@ public class AttributeFilter extends SearchFilter implements Serializable { private SearchOperator operator; - /** - * - */ - public AttributeFilter() { - - } + /** */ + public AttributeFilter() {} /** * @param name @@ -66,58 +60,42 @@ public AttributeFilter(String name, String value, DataType type, SearchOperator this.operator = operator; } - /** - * @return the name - */ + /** @return the name */ public String getName() { return name; } - /** - * @param name the name to set - */ + /** @param name the name to set */ public void setName(String name) { this.name = name; } - /** - * @return the value - */ + /** @return the value */ public String getValue() { return value; } - /** - * @param value the value to set - */ + /** @param value the value to set */ public void setValue(String value) { this.value = value; } - /** - * @return the type - */ + /** @return the type */ public DataType getType() { return type; } - /** - * @param type the type to set - */ + /** @param type the type to set */ public void setType(DataType type) { this.type = type; } - /** - * @return the operator - */ + /** @return the operator */ public SearchOperator getOperator() { return operator; } - /** - * @param operator the operator to set - */ + /** @param operator the operator to set */ public void setOperator(SearchOperator operator) { this.operator = operator; } @@ -129,7 +107,7 @@ public void accept(FilterVisitor visitor) throws BadRequestServiceEx, InternalEr /* * (non-Javadoc) - * + * * @see java.lang.Object#toString() */ @Override @@ -145,5 +123,4 @@ public String toString() { builder.append(']'); return builder.toString(); } - } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/BaseField.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/BaseField.java index ae22f4bb..5b8717f8 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/BaseField.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/BaseField.java @@ -1,40 +1,41 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.services.dto.search; import java.util.Date; - import javax.xml.bind.annotation.XmlType; /** * Enum BaseField. - * + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ @XmlType public enum BaseField { - - CREATION("creation", Date.class), ID("id", Long.class), LASTUPDATE("lastUpdate", Date.class), NAME( - "name", String.class), DESCRIPTION("description", String.class), METADATA("metadata", - String.class); + CREATION("creation", Date.class), + ID("id", Long.class), + LASTUPDATE("lastUpdate", Date.class), + NAME("name", String.class), + DESCRIPTION("description", String.class), + METADATA("metadata", String.class); ; private String fieldName; @@ -56,5 +57,4 @@ public Class getType() { public String getFieldName() { return fieldName; } - } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/CategoryFilter.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/CategoryFilter.java index 81be0d76..4adbeecd 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/CategoryFilter.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/CategoryFilter.java @@ -21,14 +21,12 @@ import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.InternalErrorServiceEx; - import java.io.Serializable; - import javax.xml.bind.annotation.XmlRootElement; /** * Filter by category name - * + * * @author ETj (etj at geo-solutions.it) */ @XmlRootElement(name = "CategoryFilter") @@ -38,11 +36,8 @@ public class CategoryFilter extends SearchFilter implements Serializable { private SearchOperator operator; - /** - * - */ - public CategoryFilter() { - } + /** */ + public CategoryFilter() {} /** * @param name @@ -53,30 +48,22 @@ public CategoryFilter(String name, SearchOperator operator) { setOperator(operator); } - /** - * @return the name - */ + /** @return the name */ public String getName() { return name; } - /** - * @param name the name to set - */ + /** @param name the name to set */ public void setName(String name) { this.name = name; } - /** - * @return the operator - */ + /** @return the operator */ public SearchOperator getOperator() { return operator; } - /** - * @param operator the operator to set - */ + /** @param operator the operator to set */ public final void setOperator(SearchOperator operator) { checkOperator(operator); this.operator = operator; @@ -94,7 +81,7 @@ public void accept(FilterVisitor visitor) throws BadRequestServiceEx, InternalEr /* * (non-Javadoc) - * + * * @see java.lang.Object#toString() */ @Override @@ -107,5 +94,4 @@ public String toString() { builder.append(']'); return builder.toString(); } - } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/FieldFilter.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/FieldFilter.java index 96357e7d..4fdee367 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/FieldFilter.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/FieldFilter.java @@ -20,14 +20,12 @@ package it.geosolutions.geostore.services.dto.search; import it.geosolutions.geostore.services.exception.InternalErrorServiceEx; - import java.io.Serializable; - import javax.xml.bind.annotation.XmlRootElement; /** * Class FieldFilter. - * + * * @author ETj (etj at geo-solutions.it) */ @XmlRootElement(name = "Field") @@ -42,12 +40,8 @@ public class FieldFilter extends SearchFilter implements Serializable { private SearchOperator operator; - /** - * - */ - public FieldFilter() { - - } + /** */ + public FieldFilter() {} public FieldFilter(BaseField field, String value, SearchOperator operator) { this.field = field; @@ -96,5 +90,4 @@ public String toString() { builder.append(']'); return builder.toString(); } - } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/FilterVisitor.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/FilterVisitor.java index 7244086e..ec9eecc3 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/FilterVisitor.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/FilterVisitor.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -24,7 +24,7 @@ /** * Interface FilterVisitor. - * + * * @author ETj (etj at geo-solutions.it) */ public interface FilterVisitor { @@ -40,5 +40,4 @@ public interface FilterVisitor { void visit(NotFilter filter) throws BadRequestServiceEx, InternalErrorServiceEx; void visit(OrFilter filter) throws BadRequestServiceEx, InternalErrorServiceEx; - } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/NotFilter.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/NotFilter.java index 1785a98a..209b2f42 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/NotFilter.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/NotFilter.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,12 +21,11 @@ import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.InternalErrorServiceEx; - import javax.xml.bind.annotation.XmlRootElement; /** * Class NotFilter. - * + * * @author ETj (etj at geo-solutions.it) */ @XmlRootElement(name = "NOT") @@ -37,8 +36,7 @@ public class NotFilter extends SearchFilter { private SearchFilter filter; - public NotFilter() { - } + public NotFilter() {} public NotFilter(SearchFilter filter) { this.filter = filter; @@ -61,5 +59,4 @@ public void accept(FilterVisitor visitor) throws BadRequestServiceEx, InternalEr public String toString() { return getClass().getSimpleName() + "[" + filter + '}'; } - } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/OrFilter.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/OrFilter.java index 23ecee27..1b135283 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/OrFilter.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/OrFilter.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,7 +21,6 @@ import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.InternalErrorServiceEx; - import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -31,7 +30,7 @@ /** * Class OrFilter. - * + * * @author ETj (etj at geo-solutions.it) */ @XmlRootElement(name = "OR") @@ -42,8 +41,7 @@ public class OrFilter extends SearchFilter { private List filters = new ArrayList(); - public OrFilter() { - } + public OrFilter() {} public OrFilter(SearchFilter f1, SearchFilter f2, SearchFilter... other) { filters.add(f1); @@ -52,11 +50,13 @@ public OrFilter(SearchFilter f1, SearchFilter f2, SearchFilter... other) { } // molto molto brutto, da cambiare se possibile - @XmlElements({ @XmlElement(name = "ATTRIBUTE", type = AttributeFilter.class), - @XmlElement(name = "OR", type = OrFilter.class), - @XmlElement(name = "AND", type = AndFilter.class), - @XmlElement(name = "FIELD", type = FieldFilter.class), - @XmlElement(name = "CATEGORY", type = CategoryFilter.class) }) + @XmlElements({ + @XmlElement(name = "ATTRIBUTE", type = AttributeFilter.class), + @XmlElement(name = "OR", type = OrFilter.class), + @XmlElement(name = "AND", type = AndFilter.class), + @XmlElement(name = "FIELD", type = FieldFilter.class), + @XmlElement(name = "CATEGORY", type = CategoryFilter.class) + }) public List getFilters() { return filters; } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/SearchFilter.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/SearchFilter.java index 26d8b468..902bbd72 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/SearchFilter.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/SearchFilter.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,14 +21,12 @@ import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.InternalErrorServiceEx; - import java.io.Serializable; - import javax.xml.bind.annotation.XmlRootElement; /** * Abstract Class SearchFilter. - * + * * @author ETj (etj at geo-solutions.it) */ @XmlRootElement @@ -37,6 +35,6 @@ public abstract class SearchFilter implements Serializable { /** The Constant serialVersionUID. */ private static final long serialVersionUID = -3525374410342234805L; - abstract public void accept(FilterVisitor visitor) throws BadRequestServiceEx, - InternalErrorServiceEx; + public abstract void accept(FilterVisitor visitor) + throws BadRequestServiceEx, InternalErrorServiceEx; } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/SearchOperator.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/SearchOperator.java index 7475613d..acdf0733 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/SearchOperator.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/dto/search/SearchOperator.java @@ -23,12 +23,11 @@ /** * Enum SearchOperator. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ @XmlType public enum SearchOperator { - GREATER_THAN_OR_EQUAL_TO, GREATER_THAN, diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/BadRequestServiceEx.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/BadRequestServiceEx.java index bbc538e8..a6932ddd 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/BadRequestServiceEx.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/BadRequestServiceEx.java @@ -1,28 +1,25 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.services.exception; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class BadRequestServiceEx extends GeoStoreServiceException { /** The Constant serialVersionUID. */ @@ -31,5 +28,4 @@ public class BadRequestServiceEx extends GeoStoreServiceException { public BadRequestServiceEx(String message) { super(message); } - } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/DuplicatedResourceNameServiceEx.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/DuplicatedResourceNameServiceEx.java index b5762829..a5ad8cd6 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/DuplicatedResourceNameServiceEx.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/DuplicatedResourceNameServiceEx.java @@ -1,12 +1,11 @@ package it.geosolutions.geostore.services.exception; public class DuplicatedResourceNameServiceEx extends GeoStoreServiceException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = -4463223126322915755L; - public DuplicatedResourceNameServiceEx(String message) { - super(message); - } - + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = -4463223126322915755L; + + public DuplicatedResourceNameServiceEx(String message) { + super(message); + } } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/GeoStoreServiceException.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/GeoStoreServiceException.java index 463dd884..863cbd21 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/GeoStoreServiceException.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/GeoStoreServiceException.java @@ -1,28 +1,25 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.services.exception; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public abstract class GeoStoreServiceException extends Exception { /** The Constant serialVersionUID. */ @@ -35,5 +32,4 @@ public GeoStoreServiceException(String message) { public GeoStoreServiceException(String message, Throwable cause) { super(message, cause); } - } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/InternalErrorServiceEx.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/InternalErrorServiceEx.java index 1831e27a..66e3fb54 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/InternalErrorServiceEx.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/InternalErrorServiceEx.java @@ -1,28 +1,25 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.services.exception; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class InternalErrorServiceEx extends GeoStoreServiceException { /** The Constant serialVersionUID. */ @@ -31,5 +28,4 @@ public class InternalErrorServiceEx extends GeoStoreServiceException { public InternalErrorServiceEx(String message) { super(message); } - } diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/NotFoundServiceEx.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/NotFoundServiceEx.java index 6bf8d3cb..ebd08840 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/NotFoundServiceEx.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/NotFoundServiceEx.java @@ -1,28 +1,25 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.services.exception; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class NotFoundServiceEx extends GeoStoreServiceException { /** The Constant serialVersionUID. */ diff --git a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/ReservedUserGroupNameEx.java b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/ReservedUserGroupNameEx.java index 788ddb42..3d324406 100644 --- a/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/ReservedUserGroupNameEx.java +++ b/src/core/services-api/src/main/java/it/geosolutions/geostore/services/exception/ReservedUserGroupNameEx.java @@ -19,19 +19,13 @@ */ package it.geosolutions.geostore.services.exception; -/** - * @author DamianoG - * - */ +/** @author DamianoG */ public class ReservedUserGroupNameEx extends BadRequestServiceEx { private static final long serialVersionUID = -4948918988222070783L; - /** - * @param message - */ + /** @param message */ public ReservedUserGroupNameEx(String message) { super(message); } - } diff --git a/src/core/services-api/src/test/java/it/geosolutions/geostore/services/dto/search/SearchFilterTest.java b/src/core/services-api/src/test/java/it/geosolutions/geostore/services/dto/search/SearchFilterTest.java index 217645d8..4ef88101 100644 --- a/src/core/services-api/src/test/java/it/geosolutions/geostore/services/dto/search/SearchFilterTest.java +++ b/src/core/services-api/src/test/java/it/geosolutions/geostore/services/dto/search/SearchFilterTest.java @@ -1,76 +1,69 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.services.dto.search; import it.geosolutions.geostore.core.model.enums.DataType; - import java.io.StringReader; import java.io.StringWriter; import java.util.Iterator; import java.util.List; - import javax.xml.bind.JAXB; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; - import junit.framework.TestCase; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.Test; /** * Class SearchFilterTest. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ public class SearchFilterTest extends TestCase { - protected final Logger LOGGER = Logger.getLogger(this.getClass()); + protected final Logger LOGGER = LogManager.getLogger(this.getClass()); - public SearchFilterTest() { - } + public SearchFilterTest() {} @Override protected void setUp() throws Exception { super.setUp(); - if (LOGGER.isInfoEnabled()) - LOGGER.info("=============================== " + getName()); + if (LOGGER.isInfoEnabled()) LOGGER.info("=============================== " + getName()); - if (LOGGER.isDebugEnabled()) - LOGGER.debug("=============================== " + getName()); + if (LOGGER.isDebugEnabled()) LOGGER.debug("=============================== " + getName()); } @Test public void testAndMarshallUnmarshall() throws JAXBException { SearchFilter base1 = new FieldFilter(BaseField.NAME, "*test*", SearchOperator.LIKE); - SearchFilter att1 = new AttributeFilter("att1", "0.0", DataType.NUMBER, - SearchOperator.GREATER_THAN); - SearchFilter att2 = new AttributeFilter("att2", "attval", DataType.STRING, - SearchOperator.EQUAL_TO); + SearchFilter att1 = + new AttributeFilter("att1", "0.0", DataType.NUMBER, SearchOperator.GREATER_THAN); + SearchFilter att2 = + new AttributeFilter("att2", "attval", DataType.STRING, SearchOperator.EQUAL_TO); SearchFilter andatt = new AndFilter(att1, att2); SearchFilter orfinal = new AndFilter(base1, andatt); - if (LOGGER.isDebugEnabled()) - LOGGER.debug("FILTER: " + orfinal); + if (LOGGER.isDebugEnabled()) LOGGER.debug("FILTER: " + orfinal); marshallUnmarshallSearch(orfinal); } @@ -79,50 +72,65 @@ public void testAndMarshallUnmarshall() throws JAXBException { public void testFieldMarshallUnmarshall() throws JAXBException { SearchFilter sf = new FieldFilter(BaseField.NAME, "*test*", SearchOperator.LIKE); - if (LOGGER.isDebugEnabled()) - LOGGER.debug("FILTER: " + sf); + if (LOGGER.isDebugEnabled()) LOGGER.debug("FILTER: " + sf); marshallUnmarshallSearch(sf); } private void marshallUnmarshallSearch(SearchFilter sf) throws JAXBException { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Marshalling: " + sf); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Marshalling: " + sf); StringWriter sw = new StringWriter(); JAXB.marshal(sf, sw); - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Marshalled into: " + sw.toString()); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Marshalled into: " + sw.toString()); StringReader sr = new StringReader(sw.getBuffer().toString()); - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Unmarshalling..."); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Unmarshalling..."); // // create context by hand // - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Creating JAXB context by hand..."); - - JAXBContext jc = JAXBContext.newInstance(SearchFilter.class, AndFilter.class, - AttributeFilter.class, FieldFilter.class, NotFilter.class, OrFilter.class); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Creating JAXB context by hand..."); + + JAXBContext jc = + JAXBContext.newInstance( + SearchFilter.class, + AndFilter.class, + AttributeFilter.class, + FieldFilter.class, + NotFilter.class, + OrFilter.class); SearchFilter sfOut = (SearchFilter) jc.createUnmarshaller().unmarshal(sr); - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Unmarshalled: " + sfOut); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Unmarshalled: " + sfOut); } @Test public void testXMLParsing() { - String xmlFilter = "" + "" + "NAME" - + "LIKE" + "*test*" + "" + "" - + "" + "attr1" + "EQUAL_TO" - + "STRING" + "value2" + "" + "" - + "attr2" + "GREATER_THAN" - + "NUMBER" + "1.0" + "" + "" - + ""; + String xmlFilter = + "" + + "" + + "NAME" + + "LIKE" + + "*test*" + + "" + + "" + + "" + + "attr1" + + "EQUAL_TO" + + "STRING" + + "value2" + + "" + + "" + + "attr2" + + "GREATER_THAN" + + "NUMBER" + + "1.0" + + "" + + "" + + ""; StringReader reader = new StringReader(xmlFilter); AndFilter searchFilter = JAXB.unmarshal(reader, AndFilter.class); @@ -173,9 +181,6 @@ public void testXMLParsing() { } else { fail("Wrong type instance!"); } - } - } - } diff --git a/src/core/services-api/src/test/java/it/geosolutions/geostore/services/dto/search/UserSessionTest.java b/src/core/services-api/src/test/java/it/geosolutions/geostore/services/dto/search/UserSessionTest.java index 534af8e4..bf5628a6 100644 --- a/src/core/services-api/src/test/java/it/geosolutions/geostore/services/dto/search/UserSessionTest.java +++ b/src/core/services-api/src/test/java/it/geosolutions/geostore/services/dto/search/UserSessionTest.java @@ -1,73 +1,65 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.services.dto.search; -import java.util.GregorianCalendar; - -import javax.xml.bind.JAXBException; - -import org.apache.log4j.Logger; -import org.junit.Test; - import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.services.dto.UserSession; import it.geosolutions.geostore.services.dto.UserSessionImpl; +import java.util.GregorianCalendar; +import javax.xml.bind.JAXBException; import junit.framework.TestCase; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.Test; /** * Class SearchFilterTest. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ public class UserSessionTest extends TestCase { - protected final Logger LOGGER = Logger.getLogger(this.getClass()); - - public UserSessionTest() { - } - + protected final Logger LOGGER = LogManager.getLogger(this.getClass()); + public UserSessionTest() {} @Test public void testUserSession() throws JAXBException { - User u = new User(); - u.setId((long) 1); - u.setName("test"); - UserSession session= new UserSessionImpl(u, new GregorianCalendar(3000,1,1)); - assertEquals(u, session.getUser()); - assertFalse(session.isExpired()); - User u2 = new User(); - u.setId((long) 2); - u.setName("test2"); - session.setUser(u2); - assertEquals(u2, session.getUser()); - session.refresh(); - session= new UserSessionImpl(u, new GregorianCalendar(1900,1,1)); - assertTrue(session.isExpired()); - session.setExpirationInterval(100); - assertEquals(session.getExpirationInterval(), 100); - session.refresh(); - assertFalse(session.isExpired()); + User u = new User(); + u.setId((long) 1); + u.setName("test"); + UserSession session = new UserSessionImpl(u, new GregorianCalendar(3000, 1, 1)); + assertEquals(u, session.getUser()); + assertFalse(session.isExpired()); + User u2 = new User(); + u.setId((long) 2); + u.setName("test2"); + session.setUser(u2); + assertEquals(u2, session.getUser()); + session.refresh(); + session = new UserSessionImpl(u, new GregorianCalendar(1900, 1, 1)); + assertTrue(session.isExpired()); + session.setExpirationInterval(100); + assertEquals(session.getExpirationInterval(), 100); + session.refresh(); + assertFalse(session.isExpired()); } - - - } diff --git a/src/core/services-impl/pom.xml b/src/core/services-impl/pom.xml index 9afafe34..dd1e6bc9 100644 --- a/src/core/services-impl/pom.xml +++ b/src/core/services-impl/pom.xml @@ -25,7 +25,7 @@ it.geosolutions.geostore geostore-core - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-services-impl @@ -56,7 +56,6 @@ - commons-lang commons-lang @@ -98,72 +97,23 @@ - - - dom4j - dom4j - - - log4j - log4j + org.apache.logging.log4j + log4j-core - - - - com.googlecode.genericdao - - dao - - - org.slf4j - slf4j-api - - - - com.trg - trg-search-jpa-hibernate - - - HIBERNATE-SPATIAL - - org.hibernatespatial - hibernate-spatial-postgis + com.googlecode.genericdao + dao-hibernate org.slf4j slf4j-api - --> - - - - - - org.aspectj aspectjrt @@ -175,21 +125,10 @@ javax.servlet - servlet-api + javax.servlet-api provided - - - - - @@ -198,6 +137,18 @@ org.apache.cxf cxf-rt-frontend-jaxrs + + + org.apache.cxf + cxf-rt-rs-extension-providers + + + org.apache.cxf + cxf-rt-rs-json-basic + + + org.codehaus.jettison + jettison @@ -210,7 +161,6 @@ - src/main/resources @@ -231,7 +181,7 @@ org.codehaus.mojo cobertura-maven-plugin - 2.4 + 2.7 diff --git a/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/CategoryServiceImpl.java b/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/CategoryServiceImpl.java index 5c74f1cc..a062ecc4 100644 --- a/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/CategoryServiceImpl.java +++ b/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/CategoryServiceImpl.java @@ -19,50 +19,45 @@ */ package it.geosolutions.geostore.services; +import com.googlecode.genericdao.search.Search; import it.geosolutions.geostore.core.dao.CategoryDAO; import it.geosolutions.geostore.core.dao.SecurityDAO; import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.core.model.SecurityRule; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; - import java.util.List; - -import org.apache.log4j.Logger; - -import com.googlecode.genericdao.search.Search; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * Class CategoryServiceImpl. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ +@SuppressWarnings("PMD.UnusedPrivateField") public class CategoryServiceImpl implements CategoryService { - private static final Logger LOGGER = Logger.getLogger(CategoryServiceImpl.class); + private static final Logger LOGGER = LogManager.getLogger(CategoryServiceImpl.class); private CategoryDAO categoryDAO; private SecurityDAO securityDAO; - /** - * @param securityDAO the securityDAO to set - */ + /** @param securityDAO the securityDAO to set */ public void setSecurityDAO(SecurityDAO securityDAO) { this.securityDAO = securityDAO; } - /** - * @param categoryDAO the categoryDAO to set - */ + /** @param categoryDAO the categoryDAO to set */ public void setCategoryDAO(CategoryDAO categoryDAO) { this.categoryDAO = categoryDAO; } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.CategoryService#insert(it.geosolutions.geostore.core.model.Category) */ @Override @@ -97,7 +92,7 @@ public long insert(Category category) throws BadRequestServiceEx, NotFoundServic /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.CategoryService#update(it.geosolutions.geostore.core.model.Category) */ @Override @@ -107,7 +102,7 @@ public long update(Category category) throws BadRequestServiceEx { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.CategoryService#get(long) */ @Override @@ -119,7 +114,7 @@ public Category get(long id) { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.CategoryService#get(long) */ @Override @@ -140,7 +135,7 @@ public Category get(String name) throws BadRequestServiceEx { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.CategoryService#delete(long) */ @Override @@ -150,7 +145,7 @@ public boolean delete(long id) { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.CategoryService#getAll(java.lang.Integer, java.lang.Integer) */ @Override @@ -176,7 +171,7 @@ public List getAll(Integer page, Integer entries) throws BadRequestSer /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.CategoryService#getCount(java.lang.String) */ @Override @@ -192,7 +187,7 @@ public long getCount(String nameLike) { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.CategoryService#getUserSecurityRule(java.lang.String, long) */ @Override diff --git a/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/InMemoryUserSessionServiceImpl.java b/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/InMemoryUserSessionServiceImpl.java index 375bb353..7ada2227 100644 --- a/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/InMemoryUserSessionServiceImpl.java +++ b/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/InMemoryUserSessionServiceImpl.java @@ -27,6 +27,8 @@ */ package it.geosolutions.geostore.services; +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.services.dto.UserSession; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -34,56 +36,48 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; - -import it.geosolutions.geostore.core.model.User; -import it.geosolutions.geostore.services.UserSessionService; -import it.geosolutions.geostore.services.dto.UserSession; - /** * In memory implementation of a UserSessionService. - * + * * @author Mauro Bartolomeoli * @author Lorenzo Natali - * */ public class InMemoryUserSessionServiceImpl implements UserSessionService { private Map sessions = new ConcurrentHashMap(); private int cleanUpSeconds = 60; - private final ScheduledExecutorService scheduler = Executors - .newScheduledThreadPool(1); - - private Runnable evictionTask = new Runnable() { - @Override - public void run() { - for(String sessionId : sessions.keySet()) { - UserSession session = sessions.get(sessionId); - if(session.isExpired()) { - removeSession(sessionId); + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + + private Runnable evictionTask = + new Runnable() { + @Override + public void run() { + for (String sessionId : sessions.keySet()) { + UserSession session = sessions.get(sessionId); + if (session.isExpired()) { + removeSession(sessionId); + } + } } - } - } - }; - + }; + public InMemoryUserSessionServiceImpl() { super(); // schedule eviction thread - scheduler.scheduleAtFixedRate(evictionTask, cleanUpSeconds, cleanUpSeconds, - TimeUnit.SECONDS); + scheduler.scheduleAtFixedRate( + evictionTask, cleanUpSeconds, cleanUpSeconds, TimeUnit.SECONDS); } - + public void setCleanUpSeconds(int cleanUpSeconds) { this.cleanUpSeconds = cleanUpSeconds; } - - @Override public User getUserData(String sessionId) { - if(sessions.containsKey(sessionId)) { + if (sessions.containsKey(sessionId)) { UserSession session = sessions.get(sessionId); - if(session.isExpired()) { + if (session.isExpired()) { removeSession(sessionId); return null; } @@ -91,12 +85,12 @@ public User getUserData(String sessionId) { } return null; } - + @Override public void registerNewSession(String sessionId, UserSession session) { sessions.put(sessionId, session); } - + @Override public String registerNewSession(UserSession session) { String sessionId = createSessionId(); @@ -106,7 +100,7 @@ public String registerNewSession(UserSession session) { registerNewSession(sessionId, session); return sessionId; } - + private String createSessionId() { return UUID.randomUUID().toString(); } @@ -115,50 +109,45 @@ private String createSessionId() { public void removeSession(String sessionId) { sessions.remove(sessionId); } - - + @Override public void removeAllSessions() { sessions.clear(); } - + /** - * Checks that owner is the user bound to the given sessionId. - * Ownership is checked by: - * - userData equality to the given object - * - username equality to the string representation of ownwer - * + * Checks that owner is the user bound to the given sessionId. Ownership is checked by: - + * userData equality to the given object - username equality to the string representation of + * ownwer + * * @param sessionId * @param owner * @return */ public boolean isOwner(String sessionId, Object owner) { UserSession session = sessions.get(sessionId); - if(session != null) { - return owner.toString().equals(session.getUser().getId()) + if (session != null) { + return owner.toString().equals(String.valueOf(session.getUser().getId())) || owner.equals(session.getUser()); } return false; } - @Override - public UserSession refreshSession(String sessionId, String refreshToken) { - if(sessions.containsKey(sessionId)) { - UserSession sess = sessions.get(sessionId); - if(sess.getRefreshToken().equals(refreshToken)); - sess.refresh(); - return sess; - } - return null; - - } - - @Override - public String getRefreshToken(String sessionId) { - if(sessions.containsKey(sessionId)) { - return sessions.get(sessionId).getRefreshToken(); - } - return null; - } + @Override + public UserSession refreshSession(String sessionId, String refreshToken) { + if (sessions.containsKey(sessionId)) { + UserSession sess = sessions.get(sessionId); + if (sess != null && sess.getRefreshToken().equals(refreshToken)) sess.refresh(); + return sess; + } + return null; + } + @Override + public String getRefreshToken(String sessionId) { + if (sessions.containsKey(sessionId)) { + return sessions.get(sessionId).getRefreshToken(); + } + return null; + } } diff --git a/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/ResourceServiceImpl.java b/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/ResourceServiceImpl.java index 8717bda2..e0aa36d8 100644 --- a/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/ResourceServiceImpl.java +++ b/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/ResourceServiceImpl.java @@ -27,6 +27,8 @@ */ package it.geosolutions.geostore.services; +import com.googlecode.genericdao.search.Search; +import com.googlecode.genericdao.search.Sort; import it.geosolutions.geostore.core.dao.AttributeDAO; import it.geosolutions.geostore.core.dao.CategoryDAO; import it.geosolutions.geostore.core.dao.ResourceDAO; @@ -50,34 +52,28 @@ import it.geosolutions.geostore.services.exception.InternalErrorServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; import it.geosolutions.geostore.util.SearchConverter; - import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; +import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.dao.DataIntegrityViolationException; -import com.googlecode.genericdao.search.Search; -import com.googlecode.genericdao.search.Sort; - -import java.util.LinkedList; - /** * Class ResourceServiceImpl. * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ -public class ResourceServiceImpl implements ResourceService -{ +public class ResourceServiceImpl implements ResourceService { - private static final Logger LOGGER = Logger.getLogger(ResourceServiceImpl.class); + private static final Logger LOGGER = LogManager.getLogger(ResourceServiceImpl.class); private UserGroupDAO userGroupDAO; @@ -91,62 +87,44 @@ public class ResourceServiceImpl implements ResourceService private SecurityDAO securityDAO; - /** - * @param securityDAO the securityDAO to set - */ - public void setSecurityDAO(SecurityDAO securityDAO) - { + /** @param securityDAO the securityDAO to set */ + public void setSecurityDAO(SecurityDAO securityDAO) { this.securityDAO = securityDAO; } - /** - * @param storedDataDAO the storedDataDAO to set - */ - public void setStoredDataDAO(StoredDataDAO storedDataDAO) - { + /** @param storedDataDAO the storedDataDAO to set */ + public void setStoredDataDAO(StoredDataDAO storedDataDAO) { this.storedDataDAO = storedDataDAO; } - /** - * @param resourceDAO - */ - public void setResourceDAO(ResourceDAO resourceDAO) - { + /** @param resourceDAO */ + public void setResourceDAO(ResourceDAO resourceDAO) { this.resourceDAO = resourceDAO; } - /** - * @param attributeDAO - */ - public void setAttributeDAO(AttributeDAO attributeDAO) - { + /** @param attributeDAO */ + public void setAttributeDAO(AttributeDAO attributeDAO) { this.attributeDAO = attributeDAO; } - /** - * @param categoryDAO the categoryDAO to set - */ - public void setCategoryDAO(CategoryDAO categoryDAO) - { + /** @param categoryDAO the categoryDAO to set */ + public void setCategoryDAO(CategoryDAO categoryDAO) { this.categoryDAO = categoryDAO; } - /** - * @param userGroupDAO the userGroupDAO to set - */ - public void setUserGroupDAO(UserGroupDAO userGroupDAO) - { + /** @param userGroupDAO the userGroupDAO to set */ + public void setUserGroupDAO(UserGroupDAO userGroupDAO) { this.userGroupDAO = userGroupDAO; } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.ResourceService#insert(it.geosolutions.geostore.core.model.Resource) */ @Override - public long insert(Resource resource) throws BadRequestServiceEx, NotFoundServiceEx, DuplicatedResourceNameServiceEx - { + public long insert(Resource resource) + throws BadRequestServiceEx, NotFoundServiceEx, DuplicatedResourceNameServiceEx { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Persisting Resource ... "); } @@ -166,8 +144,8 @@ public long insert(Resource resource) throws BadRequestServiceEx, NotFoundServic if (category.getId() != null) { loadedCategory = categoryDAO.find(category.getId()); if (loadedCategory == null) { - throw new NotFoundServiceEx("Resource Category not found [id:" + category.getId() - + "]"); + throw new NotFoundServiceEx( + "Resource Category not found [id:" + category.getId() + "]"); } } else if (category.getName() != null) { @@ -176,8 +154,8 @@ public long insert(Resource resource) throws BadRequestServiceEx, NotFoundServic List categories = categoryDAO.search(searchCriteria); if (categories.isEmpty()) { - throw new NotFoundServiceEx("Resource Category not found [name:" - + category.getName() + "]"); + throw new NotFoundServiceEx( + "Resource Category not found [name:" + category.getName() + "]"); } loadedCategory = categories.get(0); } @@ -188,6 +166,31 @@ public long insert(Resource resource) throws BadRequestServiceEx, NotFoundServic r.setMetadata(resource.getMetadata()); r.setName(resource.getName()); r.setCategory(loadedCategory); + r.setAdvertised(resource.isAdvertised()); + + // Extract "owner"/"creator" and "editor" values + List rules = resource.getSecurity(); + + if (rules != null) { + for (SecurityRule securityRule : rules) { + if ((securityRule.getUser() != null || securityRule.getUsername() != null) + && securityRule.isCanWrite()) { + final String owner = + securityRule.getUser() != null + ? securityRule.getUser().getName() + : securityRule.getUsername(); + r.setCreator(owner); + if (resource.getEditor() != null) { + r.setEditor(owner); + } else { + r.setEditor(resource.getEditor()); + } + } + } + } else { + r.setCreator(resource.getCreator()); + r.setEditor(resource.getEditor()); + } try { resourceDAO.persist(r); @@ -219,8 +222,6 @@ public long insert(Resource resource) throws BadRequestServiceEx, NotFoundServic // // Persisting SecurityRule // - List rules = resource.getSecurity(); - if (rules != null) { for (SecurityRule rule : rules) { rule.setResource(r); @@ -229,7 +230,7 @@ public long insert(Resource resource) throws BadRequestServiceEx, NotFoundServic } return r.getId(); - } catch(Exception e) { + } catch (Exception e) { // remove resource if we cannot persist other objects bound to the resource // (attributes, data, etc.) delete(r.getId()); @@ -239,12 +240,12 @@ public long insert(Resource resource) throws BadRequestServiceEx, NotFoundServic /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.ResourceService#update(it.geosolutions.geostore.core.model.Resource) */ @Override - public long update(Resource resource) throws NotFoundServiceEx, DuplicatedResourceNameServiceEx - { + public long update(Resource resource) + throws NotFoundServiceEx, DuplicatedResourceNameServiceEx { Resource orig = resourceDAO.find(resource.getId()); if (orig == null) { throw new NotFoundServiceEx("Resource not found " + resource.getId()); @@ -263,12 +264,11 @@ public long update(Resource resource) throws NotFoundServiceEx, DuplicatedResour /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.ResourceService#updateAttributes(long, java.util.List) */ @Override - public void updateAttributes(long id, List attributes) throws NotFoundServiceEx - { + public void updateAttributes(long id, List attributes) throws NotFoundServiceEx { Resource resource = resourceDAO.find(id); if (resource == null) { throw new NotFoundServiceEx("Resource not found " + id); @@ -299,10 +299,11 @@ public void updateAttributes(long id, List attributes) throws NotFoun * if a conflict is found, the method throws a DuplicatedResourceNameServiceEx * exception, putting an alternative, non-conflicting resource name in the exception's message */ - private void validateResourceName(Resource resource, boolean isUpdate) throws DuplicatedResourceNameServiceEx - { + private void validateResourceName(Resource resource, boolean isUpdate) + throws DuplicatedResourceNameServiceEx { Resource existentResource = resourceDAO.findByName(resource.getName()); - if (existentResource != null && !(isUpdate && existentResource.getId().equals(resource.getId()))) { + if (existentResource != null + && !(isUpdate && existentResource.getId().equals(resource.getId()))) { String validResourceName = suggestValidResourceName(resource.getName()); throw new DuplicatedResourceNameServiceEx(validResourceName); @@ -316,8 +317,7 @@ private void validateResourceName(Resource resource, boolean isUpdate) throws Du * The maximum employed counter is then established and a unique resource name following the * aforementioned pattern is constructed. */ - private String suggestValidResourceName(String baseResourceName) - { + private String suggestValidResourceName(String baseResourceName) { final String COUNTER_SEPARATOR = " - "; final String BASE_PATTERN = baseResourceName + COUNTER_SEPARATOR; @@ -325,7 +325,8 @@ private String suggestValidResourceName(String baseResourceName) final Pattern RESOURCE_NAME_REGEX_PATTERN = Pattern.compile(BASE_PATTERN + "(\\d+)"); int maxCounter = 0, initialCounter = 2; - List resourceNames = resourceDAO.findResourceNamesMatchingPattern(RESOURCE_NAME_LIKE_PATTERN); + List resourceNames = + resourceDAO.findResourceNamesMatchingPattern(RESOURCE_NAME_LIKE_PATTERN); for (String resourceName : resourceNames) { Matcher matcher = RESOURCE_NAME_REGEX_PATTERN.matcher(resourceName); if (matcher.matches()) { @@ -350,14 +351,13 @@ private String suggestValidResourceName(String baseResourceName) /* * @param id - * + * * @return the Resource or null if none was found with given id - * + * * @see it.geosolutions.geostore.services.ResourceService#get(long) */ @Override - public Resource get(long id) - { + public Resource get(long id) { Resource resource = resourceDAO.find(id); return resource; @@ -365,12 +365,11 @@ public Resource get(long id) /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.ResourceService#delete(long) */ @Override - public boolean delete(long id) - { + public boolean delete(long id) { // // data on ancillary tables should be deleted by cascading // @@ -379,26 +378,25 @@ public boolean delete(long id) /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.ResourceService#delete(long) */ @Override - public void deleteResources(SearchFilter filter) throws BadRequestServiceEx, - InternalErrorServiceEx - { + public void deleteResources(SearchFilter filter) + throws BadRequestServiceEx, InternalErrorServiceEx { Search searchCriteria = SearchConverter.convert(filter); this.resourceDAO.removeResources(searchCriteria); } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.ResourceService#getList(java.lang.String, java.lang.Integer, java.lang.Integer) */ @Override - public List getList(String nameLike, Integer page, Integer entries, User authUser) - throws BadRequestServiceEx - { + public List getList( + String nameLike, Integer page, Integer entries, User authUser) + throws BadRequestServiceEx { if (((page != null) && (entries == null)) || ((page == null) && (entries != null))) { throw new BadRequestServiceEx("Page and entries params should be declared together"); @@ -423,7 +421,7 @@ public List getList(String nameLike, Integer page, Integer entrie securityDAO.addReadSecurityConstraints(searchCriteria, authUser); - if(LOGGER.isTraceEnabled()) { + if (LOGGER.isTraceEnabled()) { LOGGER.trace("Get Resource List: " + searchCriteria); } List found = search(searchCriteria); @@ -433,13 +431,12 @@ public List getList(String nameLike, Integer page, Integer entrie /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.ResourceService#getList(java.lang.Integer, java.lang.Integer) */ @Override public List getAll(Integer page, Integer entries, User authUser) - throws BadRequestServiceEx - { + throws BadRequestServiceEx { if (((page != null) && (entries == null)) || ((page == null) && (entries != null))) { throw new BadRequestServiceEx("Page and entries params should be declared together"); @@ -459,7 +456,7 @@ public List getAll(Integer page, Integer entries, User authUser) searchCriteria.setDistinct(true); securityDAO.addReadSecurityConstraints(searchCriteria, authUser); - + List found = search(searchCriteria); return convertToShortList(found, authUser); @@ -467,12 +464,11 @@ public List getAll(Integer page, Integer entries, User authUser) /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.ResourceService#getCount(java.lang.String) */ @Override - public long getCount(String nameLike) - { + public long getCount(String nameLike) { Search searchCriteria = new Search(Resource.class); if (nameLike != null) { @@ -484,13 +480,12 @@ public long getCount(String nameLike) /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.ResourceService#getCount(java.lang.String) */ @Override - public long getCountByFilter(SearchFilter filter) throws InternalErrorServiceEx, - BadRequestServiceEx - { + public long getCountByFilter(SearchFilter filter) + throws InternalErrorServiceEx, BadRequestServiceEx { Search searchCriteria = SearchConverter.convert(filter); return resourceDAO.count(searchCriteria); } @@ -499,8 +494,7 @@ public long getCountByFilter(SearchFilter filter) throws InternalErrorServiceEx, * @param list * @return List */ - private List convertToShortList(List list, User authUser) - { + private List convertToShortList(List list, User authUser) { List swList = new LinkedList<>(); for (Resource resource : list) { ShortResource shortResource = new ShortResource(resource); @@ -510,16 +504,19 @@ private List convertToShortList(List list, User authUse // the loaded resource (and associated attributes and stored data). // This to inform the client in HTTP response result. // /////////////////////////////////////////////////////////////////////// + boolean resourceCanBeListed = true; if (authUser != null) { if (authUser.getRole().equals(Role.ADMIN)) { shortResource.setCanEdit(true); shortResource.setCanDelete(true); } else { + boolean authUserIsOwner = false; for (SecurityRule rule : resource.getSecurity()) { User owner = rule.getUser(); UserGroup userGroup = rule.getGroup(); if (owner != null) { if (owner.getId().equals(authUser.getId())) { + authUserIsOwner = true; if (rule.isCanWrite()) { shortResource.setCanEdit(true); shortResource.setCanDelete(true); @@ -528,7 +525,7 @@ private List convertToShortList(List list, User authUse } } } else if (userGroup != null) { - List groups = extratcGroupNames(authUser.getGroups()); + List groups = extractGroupNames(authUser.getGroups()); if (groups.contains(userGroup.getGroupName())) { if (rule.isCanWrite()) { shortResource.setCanEdit(true); @@ -539,17 +536,19 @@ private List convertToShortList(List list, User authUse } } } + if (!authUserIsOwner) resourceCanBeListed = resource.isAdvertised(); } + } else { + resourceCanBeListed = resource.isAdvertised(); } - swList.add(shortResource); + if (resourceCanBeListed) swList.add(shortResource); } return swList; } - public static List extratcGroupNames(Set groups) - { + public static List extractGroupNames(Set groups) { List groupNames = new ArrayList(); if (groups == null) { return groupNames; @@ -562,12 +561,11 @@ public static List extratcGroupNames(Set groups) /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.ResourceService#getListAttributes(long) */ @Override - public List getAttributes(long id) - { + public List getAttributes(long id) { Search searchCriteria = new Search(Attribute.class); searchCriteria.addFilterEqual("resource.id", id); @@ -581,8 +579,7 @@ public List getAttributes(long id) * @param list * @return List */ - private List convertToShortAttributeList(List list) - { + private List convertToShortAttributeList(List list) { List swList = new ArrayList(list.size()); for (Attribute attribute : list) { swList.add(new ShortAttribute(attribute)); @@ -593,12 +590,11 @@ private List convertToShortAttributeList(List list) /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.ResourceService#getAttribute(long, java.lang.String) */ @Override - public ShortAttribute getAttribute(long id, String name) - { + public ShortAttribute getAttribute(long id, String name) { Search searchCriteria = new Search(Attribute.class); searchCriteria.addFilterEqual("resource.id", id); searchCriteria.addFilterEqual("name", name); @@ -614,12 +610,11 @@ public ShortAttribute getAttribute(long id, String name) /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.ResourceService#updateAttribute(long, java.lang.String, java.lang.String) */ @Override - public long updateAttribute(long id, String name, String value) throws InternalErrorServiceEx - { + public long updateAttribute(long id, String name, String value) throws InternalErrorServiceEx { Search searchCriteria = new Search(Attribute.class); searchCriteria.addFilterEqual("resource.id", id); searchCriteria.addFilterEqual("name", name); @@ -630,7 +625,6 @@ public long updateAttribute(long id, String name, String value) throws InternalE switch (attribute.getType()) { case DATE: - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); try { attribute.setDateValue(sdf.parse(value)); @@ -654,19 +648,18 @@ public long updateAttribute(long id, String name, String value) throws InternalE } @Override - public long insertAttribute(long id, String name, String value, DataType type) throws InternalErrorServiceEx - { + public long insertAttribute(long id, String name, String value, DataType type) + throws InternalErrorServiceEx { Search searchCriteria = new Search(Attribute.class); searchCriteria.addFilterEqual("resource.id", id); searchCriteria.addFilterEqual("name", name); List attributes = this.attributeDAO.search(searchCriteria); - //if the attribute exist, update id (note: it must have the same type) + // if the attribute exist, update id (note: it must have the same type) if (attributes.size() > 0) { Attribute attribute = attributes.get(0); switch (attribute.getType()) { case DATE: - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); try { attribute.setDateValue(sdf.parse(value)); @@ -688,7 +681,7 @@ public long insertAttribute(long id, String name, String value, DataType type) t return attribute.getId(); } else { - //create the new attribute + // create the new attribute Attribute attribute = new Attribute(); attribute.setType(type); attribute.setName(name); @@ -696,7 +689,6 @@ public long insertAttribute(long id, String name, String value, DataType type) t switch (type) { case DATE: - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); try { attribute.setDateValue(sdf.parse(value)); @@ -715,34 +707,34 @@ public long insertAttribute(long id, String name, String value, DataType type) t } return this.attributeDAO.merge(attribute).getId(); - } - } - /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.ResourceService#getResourcesByFilter(it.geosolutions.geostore.services.dto.SearchFilter) */ @Override public List getResources(SearchFilter filter, User authUser) - throws BadRequestServiceEx, InternalErrorServiceEx - { + throws BadRequestServiceEx, InternalErrorServiceEx { return getResources(filter, null, null, authUser); } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.ResourceService#getResources(it.geosolutions.geostore.services.dto.search.SearchFilter, * java.lang.Integer, java.lang.Integer, boolean, boolean, it.geosolutions.geostore.core.model.User) */ - public List getResources(SearchFilter filter, Integer page, Integer entries, - boolean includeAttributes, boolean includeData, User authUser) - throws BadRequestServiceEx, InternalErrorServiceEx - { + public List getResources( + SearchFilter filter, + Integer page, + Integer entries, + boolean includeAttributes, + boolean includeData, + User authUser) + throws BadRequestServiceEx, InternalErrorServiceEx { if (((page != null) && (entries == null)) || ((page == null) && (entries != null))) { throw new BadRequestServiceEx("Page and entries params should be declared together"); @@ -772,9 +764,8 @@ public List getResources(SearchFilter filter, Integer page, Integer en * @param authUser * @return List */ - private List configResourceList(List list, boolean includeAttributes, - boolean includeData, User authUser) - { + private List configResourceList( + List list, boolean includeAttributes, boolean includeData, User authUser) { List rList = new LinkedList<>(); for (Resource resource : list) { @@ -783,9 +774,12 @@ private List configResourceList(List list, boolean includeAt res.setCategory(resource.getCategory()); res.setCreation(resource.getCreation()); res.setDescription(resource.getDescription()); + res.setAdvertised(resource.isAdvertised()); res.setId(resource.getId()); res.setLastUpdate(resource.getLastUpdate()); res.setName(resource.getName()); + res.setCreator(resource.getCreator()); + res.setEditor(resource.getEditor()); if (includeData) { res.setData(resource.getData()); @@ -802,9 +796,9 @@ private List configResourceList(List list, boolean includeAt } @Override - public List getResources(SearchFilter filter, Integer page, Integer entries, - User authUser) throws BadRequestServiceEx, InternalErrorServiceEx - { + public List getResources( + SearchFilter filter, Integer page, Integer entries, User authUser) + throws BadRequestServiceEx, InternalErrorServiceEx { if (((page != null) && (entries == null)) || ((page == null) && (entries != null))) { throw new BadRequestServiceEx("Page and entries params should be declared together"); @@ -844,8 +838,7 @@ public List getResources(SearchFilter filter, Integer page, Integ */ @Override public List getResourcesFull(SearchFilter filter, User authUser) - throws BadRequestServiceEx, InternalErrorServiceEx - { + throws BadRequestServiceEx, InternalErrorServiceEx { Search searchCriteria = SearchConverter.convert(filter); searchCriteria.addFetch("security"); @@ -853,33 +846,31 @@ public List getResourcesFull(SearchFilter filter, User authUser) searchCriteria.addFetch("data"); securityDAO.addReadSecurityConstraints(searchCriteria, authUser); - List resources = this.search(searchCriteria); - - return resources; + return this.search(searchCriteria); } /** * Internal method to apply default search options and search - * @param searchCriteria search criteria + * + * @param searchCriteria search criteria * @return results of the search */ private List search(Search searchCriteria) { - // apply defaults for sorting - if(searchCriteria != null) { - searchCriteria.addSort(new Sort("name")); - } - - // search - return resourceDAO.search(searchCriteria); - } - - /* + // apply defaults for sorting + if (searchCriteria != null) { + searchCriteria.addSort(new Sort("name")); + } + + // search + return resourceDAO.search(searchCriteria); + } + + /* * (non-Javadoc) * @see it.geosolutions.geostore.services.SecurityService#getUserSecurityRule(java.lang.String, long) */ @Override - public List getUserSecurityRule(String userName, long resourceId) - { + public List getUserSecurityRule(String userName, long resourceId) { return securityDAO.findUserSecurityRule(userName, resourceId); } @@ -887,22 +878,19 @@ public List getUserSecurityRule(String userName, long resourceId) * @see it.geosolutions.geostore.services.SecurityService#getGroupSecurityRule(java.lang.String, long) */ @Override - public List getGroupSecurityRule(List groupNames, long resourceId) - { + public List getGroupSecurityRule(List groupNames, long resourceId) { return securityDAO.findGroupSecurityRule(groupNames, resourceId); } @Override public List getSecurityRules(long id) - throws BadRequestServiceEx, InternalErrorServiceEx - { + throws BadRequestServiceEx, InternalErrorServiceEx { return securityDAO.findSecurityRules(id); } @Override public void updateSecurityRules(long id, List rules) - throws BadRequestServiceEx, InternalErrorServiceEx, NotFoundServiceEx - { + throws BadRequestServiceEx, InternalErrorServiceEx, NotFoundServiceEx { Resource resource = resourceDAO.find(id); if (resource != null) { @@ -940,15 +928,15 @@ public void updateSecurityRules(long id, List rules) * @throws InternalErrorServiceEx * @throws BadRequestServiceEx */ - public long getCountByFilterAndUser(SearchFilter filter, User user) throws BadRequestServiceEx, InternalErrorServiceEx - { + public long getCountByFilterAndUser(SearchFilter filter, User user) + throws BadRequestServiceEx, InternalErrorServiceEx { Search searchCriteria = SearchConverter.convert(filter); - securityDAO.addReadSecurityConstraints(searchCriteria, user); + securityDAO.addAdvertisedSecurityConstraints(searchCriteria, user); return resourceDAO.count(searchCriteria); } /** - * Get filter count by namerLike and user + * Get filter count by nameLike and user * * @param nameLike * @param user @@ -956,8 +944,7 @@ public long getCountByFilterAndUser(SearchFilter filter, User user) throws BadRe * @throws InternalErrorServiceEx * @throws BadRequestServiceEx */ - public long getCountByFilterAndUser(String nameLike, User user) throws BadRequestServiceEx - { + public long getCountByFilterAndUser(String nameLike, User user) throws BadRequestServiceEx { Search searchCriteria = new Search(Resource.class); @@ -965,7 +952,7 @@ public long getCountByFilterAndUser(String nameLike, User user) throws BadReques searchCriteria.addFilterILike("name", nameLike); } - securityDAO.addReadSecurityConstraints(searchCriteria, user); + securityDAO.addAdvertisedSecurityConstraints(searchCriteria, user); return resourceDAO.count(searchCriteria); } diff --git a/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/StoredDataServiceImpl.java b/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/StoredDataServiceImpl.java index f64f50ac..c094a104 100644 --- a/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/StoredDataServiceImpl.java +++ b/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/StoredDataServiceImpl.java @@ -19,9 +19,6 @@ */ package it.geosolutions.geostore.services; -import java.util.Date; -import java.util.List; -import org.apache.log4j.Logger; import it.geosolutions.geostore.core.dao.ResourceDAO; import it.geosolutions.geostore.core.dao.SecurityDAO; import it.geosolutions.geostore.core.dao.StoredDataDAO; @@ -30,37 +27,37 @@ import it.geosolutions.geostore.core.model.SecurityRule; import it.geosolutions.geostore.core.model.StoredData; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; +import java.util.Date; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * Class StoredDataServiceImpl. - * + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ public class StoredDataServiceImpl implements StoredDataService { - private static final Logger LOGGER = Logger.getLogger(StoredDataServiceImpl.class); + private static final Logger LOGGER = LogManager.getLogger(StoredDataServiceImpl.class); private StoredDataDAO storedDataDAO; private ResourceDAO resourceDAO; - + private SecurityDAO securityDAO; - /** - * @param resourceDAO the resourceDAO to set - */ + /** @param resourceDAO the resourceDAO to set */ public void setResourceDAO(ResourceDAO resourceDAO) { this.resourceDAO = resourceDAO; } - /** - * @param storedDataDAO - */ + /** @param storedDataDAO */ public void setStoredDataDAO(StoredDataDAO storedDataDAO) { this.storedDataDAO = storedDataDAO; } - + public void setSecurityDAO(SecurityDAO securityDAO) { this.securityDAO = securityDAO; } @@ -97,7 +94,6 @@ public long update(long id, String data) throws NotFoundServiceEx { } /** - * * @param id * @return the StoredData or null if none was found with given id * @throws NotFoundServiceEx @@ -164,5 +160,4 @@ public List getUserSecurityRule(String name, long storedDataId) { public List getGroupSecurityRule(List groupNames, long storedDataId) { return securityDAO.findGroupSecurityRule(groupNames, storedDataId); } - } diff --git a/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/UserGroupServiceImpl.java b/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/UserGroupServiceImpl.java index 384341d3..fc08226d 100644 --- a/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/UserGroupServiceImpl.java +++ b/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/UserGroupServiceImpl.java @@ -19,79 +19,73 @@ */ package it.geosolutions.geostore.services; +import com.googlecode.genericdao.search.Filter; +import com.googlecode.genericdao.search.Search; import it.geosolutions.geostore.core.dao.ResourceDAO; import it.geosolutions.geostore.core.dao.SecurityDAO; import it.geosolutions.geostore.core.dao.UserDAO; +import it.geosolutions.geostore.core.dao.UserGroupAttributeDAO; import it.geosolutions.geostore.core.dao.UserGroupDAO; import it.geosolutions.geostore.core.model.Resource; import it.geosolutions.geostore.core.model.SecurityRule; import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.UserGroupAttribute; import it.geosolutions.geostore.core.model.enums.GroupReservedNames; import it.geosolutions.geostore.core.model.enums.Role; import it.geosolutions.geostore.services.dto.ShortResource; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; import it.geosolutions.geostore.services.exception.ReservedUserGroupNameEx; - import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; - +import java.util.stream.Collectors; import org.apache.commons.lang.StringUtils; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -import com.googlecode.genericdao.search.Filter; -import com.googlecode.genericdao.search.Search; - -/** - * @author DamianoG - * - */ +/** @author DamianoG */ public class UserGroupServiceImpl implements UserGroupService { - private static final Logger LOGGER = Logger.getLogger(UserGroupServiceImpl.class); + private static final Logger LOGGER = LogManager.getLogger(UserGroupServiceImpl.class); private UserGroupDAO userGroupDAO; - + private UserDAO userDAO; private ResourceDAO resourceDAO; - + private SecurityDAO securityDAO; - - /** - * @param userGroupDAO the userGroupDAO to set - */ + + private UserGroupAttributeDAO userGroupAttributeDAO; + + /** @param userGroupDAO the userGroupDAO to set */ public void setUserGroupDAO(UserGroupDAO userGroupDAO) { this.userGroupDAO = userGroupDAO; } - - /** - * - * @param userDAO the userDAO to set - */ + + /** @param userDAO the userDAO to set */ public void setUserDAO(UserDAO userDAO) { this.userDAO = userDAO; } - - /** - * - * @param resourceDAO the resourceDAO to set - */ + + /** @param resourceDAO the resourceDAO to set */ public void setResourceDAO(ResourceDAO resourceDAO) { this.resourceDAO = resourceDAO; } - - /** - * @param securityDAO the securityDAO to set - */ + + /** @param securityDAO the securityDAO to set */ public void setSecurityDAO(SecurityDAO securityDAO) { this.securityDAO = securityDAO; } + public void setUserGroupAttributeDAO(UserGroupAttributeDAO userGroupAttributeDAO) { + this.userGroupAttributeDAO = userGroupAttributeDAO; + } + /* (non-Javadoc) * @see it.geosolutions.geostore.services.UserGroupService#insert(it.geosolutions.geostore.core.model.UserGroup) */ @@ -102,20 +96,32 @@ public long insert(UserGroup userGroup) throws BadRequestServiceEx { } if (userGroup == null || StringUtils.isEmpty(userGroup.getGroupName())) { - throw new BadRequestServiceEx("The provided UserGroup instance is null or group Name is not specified!"); + throw new BadRequestServiceEx( + "The provided UserGroup instance is null or group Name is not specified!"); } - - if(!GroupReservedNames.isAllowedName(userGroup.getGroupName())){ - throw new ReservedUserGroupNameEx("The usergroup name you try to save: '" + userGroup.getGroupName() + "' is a reserved name!"); + + if (!GroupReservedNames.isAllowedName(userGroup.getGroupName())) { + throw new ReservedUserGroupNameEx( + "The usergroup name you try to save: '" + + userGroup.getGroupName() + + "' is a reserved name!"); } - + userGroup.setGroupName(userGroup.getGroupName()); - + userGroupDAO.persist(userGroup); if (LOGGER.isDebugEnabled()) { LOGGER.debug("UserGroup '" + userGroup.getGroupName() + "' persisted!"); } - + + List attributes = userGroup.getAttributes(); + if (attributes != null && !attributes.isEmpty()) { + for (UserGroupAttribute attr : attributes) { + attr.setUserGroup(userGroup); + userGroupAttributeDAO.persist(attr); + } + } + return userGroup.getId(); } @@ -125,15 +131,18 @@ public long insert(UserGroup userGroup) throws BadRequestServiceEx { @Override public boolean delete(long id) throws NotFoundServiceEx, BadRequestServiceEx { UserGroup group = userGroupDAO.find(id); - if(group == null){ + if (group == null) { LOGGER.error("Can't find usergroup with id '" + id + "'"); throw new NotFoundServiceEx("Can't find usergroup with id '" + id + "'"); } - if(!GroupReservedNames.isAllowedName(group.getGroupName())){ - throw new BadRequestServiceEx("Delete a special usergroup ('" + group.getGroupName() + "' in this case) isn't possible"); + if (!GroupReservedNames.isAllowedName(group.getGroupName())) { + throw new BadRequestServiceEx( + "Delete a special usergroup ('" + + group.getGroupName() + + "' in this case) isn't possible"); } - for(User u : getUsersByGroup(id)){ + for (User u : getUsersByGroup(id)) { u.removeGroup(id); userDAO.merge(u); } @@ -152,44 +161,45 @@ private Collection getUsersByGroup(long groupId) { * @see it.geosolutions.geostore.services.UserGroupService#assignUserGroup(long, long) */ @Override - public void assignUserGroup(long userId, long groupId) throws NotFoundServiceEx{ + public void assignUserGroup(long userId, long groupId) throws NotFoundServiceEx { UserGroup groupToAssign = userGroupDAO.find(groupId); - // Check if the group user want to assign is an allowed one - if(!GroupReservedNames.isAllowedName(groupToAssign.getGroupName())){ - throw new NotFoundServiceEx("You can't re-assign the group EVERYONE or any other reserved groups..."); - } + User targetUser = userDAO.find(userId); - if(groupToAssign == null || targetUser == null){ + if (groupToAssign == null || targetUser == null) { throw new NotFoundServiceEx("The userGroup or the user you provide doesn't exist"); } - if(targetUser.getGroups() == null){ + // Check if the group user want to assign is an allowed one + if (!GroupReservedNames.isAllowedName(groupToAssign.getGroupName())) { + throw new NotFoundServiceEx( + "You can't re-assign the group EVERYONE or any other reserved groups..."); + } + if (targetUser.getGroups() == null) { Set groups = new HashSet(); groups.add(groupToAssign); targetUser.setGroups(groups); - userDAO.merge(targetUser); - } - else{ + } else { targetUser.getGroups().add(groupToAssign); - userDAO.merge(targetUser); } + userDAO.merge(targetUser); } - + /* (non-Javadoc) * @see it.geosolutions.geostore.services.UserGroupService#deassignUserGroup(long, long) */ @Override - public void deassignUserGroup(long userId, long groupId) throws NotFoundServiceEx{ + public void deassignUserGroup(long userId, long groupId) throws NotFoundServiceEx { UserGroup groupToAssign = userGroupDAO.find(groupId); // Check if the group user want to remove is an allowed one - if(!GroupReservedNames.isAllowedName(groupToAssign.getGroupName())){ - throw new NotFoundServiceEx("You can't remove the group EVERYONE or any other reserved groups from the users group list..."); + if (!GroupReservedNames.isAllowedName(groupToAssign.getGroupName())) { + throw new NotFoundServiceEx( + "You can't remove the group EVERYONE or any other reserved groups from the users group list..."); } User targetUser = userDAO.find(userId); - if(groupToAssign == null || targetUser == null){ + if (groupToAssign == null || targetUser == null) { throw new NotFoundServiceEx("The userGroup or the user you provide doesn't exist"); } - if(targetUser.removeGroup(groupId)) { + if (targetUser.removeGroup(groupId)) { userDAO.merge(targetUser); } } @@ -209,13 +219,14 @@ public List getAll(Integer page, Integer entries) throws BadRequestSe } searchCriteria.addSortAsc("groupName"); List found = userGroupDAO.search(searchCriteria); - return found; + return remapWithoutAttributes(found); } @Override - public List getAllAllowed(User user, Integer page, Integer entries, String nameLike, boolean all) throws BadRequestServiceEx { - if (user == null) - throw new BadRequestServiceEx("User must be defined."); + public List getAllAllowed( + User user, Integer page, Integer entries, String nameLike, boolean all) + throws BadRequestServiceEx { + if (user == null) throw new BadRequestServiceEx("User must be defined."); if (((page != null) && (entries == null)) || ((page == null) && (entries != null))) { throw new BadRequestServiceEx("Page and entries params should be declared together."); @@ -231,50 +242,61 @@ public List getAllAllowed(User user, Integer page, Integer entries, S if (user.getRole() == Role.USER) { Set userGrp = user.getGroups(); List grpIds = new ArrayList<>(userGrp.size()); - for(UserGroup grp : userGrp){ + for (UserGroup grp : userGrp) { grpIds.add(grp.getId()); } searchCriteria.addFilterIn("id", grpIds); } - if (nameLike != null) - searchCriteria.addFilterILike("groupName", nameLike); + if (nameLike != null) searchCriteria.addFilterILike("groupName", nameLike); - if(!all) + if (!all) searchCriteria.addFilterNotEqual("groupName", GroupReservedNames.EVERYONE.groupName()); List found = userGroupDAO.search(searchCriteria); - return found; + return remapWithoutAttributes(found); } /* (non-Javadoc) * @see it.geosolutions.geostore.services.UserGroupService#updateSecurityRules(java.lang.Long, java.util.List, boolean, boolean) */ @Override - public List updateSecurityRules(Long groupId, List resourcesIds, boolean canRead, - boolean canWrite) throws NotFoundServiceEx, BadRequestServiceEx { - + public List updateSecurityRules( + Long groupId, List resourcesIds, boolean canRead, boolean canWrite) + throws NotFoundServiceEx, BadRequestServiceEx { + List updated = new ArrayList(); UserGroup group = userGroupDAO.find(groupId); - - if(group == null){ + + if (group == null) { throw new NotFoundServiceEx("The usergroup id you provide doesn't exist!"); } - - if(group.getGroupName().equals(GroupReservedNames.EVERYONE.groupName())){ - if(!canRead || canWrite){ - LOGGER.error("You are trying to assign to a resource the following permissions for the group EVERYONE: [canRead='" + canRead + "',canWrite'" + canWrite + "'] but..."); - LOGGER.error("...the group EVERYONE can be set only in this way: [canRead='true',canWrite='false'] ."); - throw new BadRequestServiceEx("GroupEveryone cannot be set with this grants [canRead='" + canRead + "',canWrite'" + canWrite + "']"); + + if (group.getGroupName().equals(GroupReservedNames.EVERYONE.groupName())) { + if (!canRead || canWrite) { + LOGGER.error( + "You are trying to assign to a resource the following permissions for the group EVERYONE: [canRead='" + + canRead + + "',canWrite'" + + canWrite + + "'] but..."); + LOGGER.error( + "...the group EVERYONE can be set only in this way: [canRead='true',canWrite='false'] ."); + throw new BadRequestServiceEx( + "GroupEveryone cannot be set with this grants [canRead='" + + canRead + + "',canWrite'" + + canWrite + + "']"); } } - + List resourceToSet = resourceDAO.findResources(resourcesIds); - - for(Resource resource : resourceToSet){ + + for (Resource resource : resourceToSet) { SecurityRule sr = getRuleForGroup(resource.getSecurity(), group); - if(sr == null){ + if (sr == null) { // Create new rule SecurityRule newSR = new SecurityRule(); newSR.setCanRead(canRead); @@ -283,47 +305,51 @@ public List updateSecurityRules(Long groupId, List resource newSR.setResource(resource); securityDAO.persist(newSR); resource.getSecurity().add(newSR); - + ShortResource out = new ShortResource(resource); - // In this case the short resource to return is not related to the permission available by the user - // who call the service (like in other service) but can Delete/Edit are set as the rule updated. + // In this case the short resource to return is not related to the permission + // available by the user + // who call the service (like in other service) but can Delete/Edit are set as the + // rule updated. out.setCanDelete(canWrite); out.setCanEdit(canWrite); updated.add(out); - } - else{ + } else { // Update the existing rule sr.setCanRead(canRead); sr.setCanWrite(canWrite); securityDAO.merge(sr); - + ShortResource out = new ShortResource(resource); - // In this case the short resource to return is not related to the permission available by the user - // who call the sevrice (like in other service) but can Delete/Edit are set as the rule updated. + // In this case the short resource to return is not related to the permission + // available by the user + // who call the service (like in other service) but can Delete/Edit are set as the + // rule updated. out.setCanDelete(canWrite); out.setCanEdit(canWrite); updated.add(out); } } - + return updated; } - - private SecurityRule getRuleForGroup(List securityList, UserGroup group){ - for(SecurityRule sr : securityList){ - if(sr.getGroup() != null && sr.getGroup().getGroupName() != null && sr.getGroup().getGroupName().equals(group.getGroupName())){ + + private SecurityRule getRuleForGroup(List securityList, UserGroup group) { + for (SecurityRule sr : securityList) { + if (sr.getGroup() != null + && sr.getGroup().getGroupName() != null + && sr.getGroup().getGroupName().equals(group.getGroupName())) { return sr; } } return null; } - - public boolean insertSpecialUsersGroups(){ + public boolean insertSpecialUsersGroups() { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Persisting Reserved UsersGroup... "); } - + UserGroup ug = new UserGroup(); ug.setGroupName(GroupReservedNames.EVERYONE.groupName()); userGroupDAO.persist(ug); @@ -333,6 +359,25 @@ public boolean insertSpecialUsersGroups(){ return true; } + public boolean removeSpecialUsersGroups() { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Removing Reserved UsersGroup... "); + } + + Search search = new Search(); + search.addFilterEqual("groupName", GroupReservedNames.EVERYONE.groupName()); + List ugEveryone = userGroupDAO.search(search); + if (ugEveryone.size() == 1) { + UserGroup ug = ugEveryone.get(0); + boolean res = userGroupDAO.removeById(ug.getId()); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Special UserGroup '" + ug.getGroupName() + "' removed!"); + } + return res; + } + return false; + } + @Override public UserGroup get(long id) throws BadRequestServiceEx { return userGroupDAO.find(id); @@ -340,29 +385,21 @@ public UserGroup get(long id) throws BadRequestServiceEx { @Override public UserGroup get(String name) { - Search searchCriteria = new Search(UserGroup.class); - searchCriteria.addFilterEqual("groupName", name); - - List existingGroups = userGroupDAO.search(searchCriteria); - if (existingGroups.size() > 0) { - return existingGroups.get(0); - } - return null; + return userGroupDAO.findByName(name); } @Override public long getCount(User user, String nameLike, boolean all) throws BadRequestServiceEx { - if (user == null) - throw new BadRequestServiceEx("User must be defined."); + if (user == null) throw new BadRequestServiceEx("User must be defined."); Search searchCriteria = new Search(UserGroup.class); searchCriteria.addSortAsc("groupName"); - if (user.getRole() == Role.USER){ + if (user.getRole() == Role.USER) { Set userGrp = user.getGroups(); Collection grpIds = new ArrayList<>(); - for(UserGroup grp :userGrp){ + for (UserGroup grp : userGrp) { grpIds.add(grp.getId()); } searchCriteria.addFilterIn("id", grpIds); @@ -372,15 +409,82 @@ public long getCount(User user, String nameLike, boolean all) throws BadRequestS searchCriteria.addFilterILike("groupName", nameLike); } - if(!all) + if (!all) searchCriteria.addFilterNotEqual("groupName", GroupReservedNames.EVERYONE.groupName()); return userGroupDAO.count(searchCriteria); - - } public long getCount(User user, String nameLike) throws BadRequestServiceEx { return getCount(user, nameLike, false); } + + @Override + public void updateAttributes(long id, List attributes) + throws NotFoundServiceEx { + UserGroup group = userGroupDAO.find(id); + if (group == null) { + throw new NotFoundServiceEx("User not found " + id); + } + + // + // Removing old attributes + // + List oldList = group.getAttributes(); + // Iterator iterator; + + if (oldList != null) { + for (UserGroupAttribute a : oldList) { + userGroupAttributeDAO.removeById(a.getId()); + } + } + // + // Saving old attributes + // + for (UserGroupAttribute a : attributes) { + a.setUserGroup(group); + userGroupAttributeDAO.persist(a); + } + } + + @Override + public long update(UserGroup group) throws NotFoundServiceEx, BadRequestServiceEx { + UserGroup old = get(group.getId()); + if (old == null) old = get(group.getGroupName()); + group.setId(old.getId()); + userGroupDAO.merge(group); + return old.getId(); + } + + @Override + public Collection findByAttribute( + String name, List values, boolean ignoreCase) { + Search searchCriteria = new Search(UserGroupAttribute.class); + if (!ignoreCase) { + searchCriteria.addFilterEqual("name", name); + } else { + searchCriteria.addFilterILike("name", name); + } + if (values.size() > 1) searchCriteria.addFilterIn("value", values); + else searchCriteria.addFilterEqual("value", values.get(0)); + searchCriteria.addFetch("userGroup"); + List attributes = userGroupAttributeDAO.search(searchCriteria); + return attributes.stream() + .map(a -> a.getUserGroup()) + .filter(u -> u != null) + .collect(Collectors.toSet()); + } + + private List remapWithoutAttributes(Collection groups) { + List newList = new ArrayList<>(groups.size()); + for (UserGroup old : groups) { + UserGroup newGroup = new UserGroup(); + newGroup.setId(old.getId()); + newGroup.setGroupName(old.getGroupName()); + newGroup.setDescription(old.getDescription()); + newGroup.setEnabled(old.isEnabled()); + newList.add(newGroup); + } + return newList; + } } diff --git a/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/UserServiceImpl.java b/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/UserServiceImpl.java index 3f98e4df..b10c2fb9 100644 --- a/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/UserServiceImpl.java +++ b/src/core/services-impl/src/main/java/it/geosolutions/geostore/services/UserServiceImpl.java @@ -19,6 +19,8 @@ */ package it.geosolutions.geostore.services; +import com.googlecode.genericdao.search.Filter; +import com.googlecode.genericdao.search.Search; import it.geosolutions.geostore.core.dao.UserAttributeDAO; import it.geosolutions.geostore.core.dao.UserDAO; import it.geosolutions.geostore.core.dao.UserGroupDAO; @@ -30,28 +32,24 @@ import it.geosolutions.geostore.core.model.enums.UserReservedNames; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; - import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; - import org.apache.cxf.common.util.StringUtils; -import org.apache.log4j.Logger; - -import com.googlecode.genericdao.search.Filter; -import com.googlecode.genericdao.search.Search; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * Class UserServiceImpl. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ public class UserServiceImpl implements UserService { - private static final Logger LOGGER = Logger.getLogger(UserServiceImpl.class); + private static final Logger LOGGER = LogManager.getLogger(UserServiceImpl.class); private UserDAO userDAO; @@ -59,30 +57,24 @@ public class UserServiceImpl implements UserService { private UserGroupDAO userGroupDAO; - /** - * @param userGroupDAO the userGroupDAO to set - */ + /** @param userGroupDAO the userGroupDAO to set */ public void setUserGroupDAO(UserGroupDAO userGroupDAO) { this.userGroupDAO = userGroupDAO; } - /** - * @param userAttributeDAO the userAttributeDAO to set - */ + /** @param userAttributeDAO the userAttributeDAO to set */ public void setUserAttributeDAO(UserAttributeDAO userAttributeDAO) { this.userAttributeDAO = userAttributeDAO; } - /** - * @param userDAO the userDAO to set - */ + /** @param userDAO the userDAO to set */ public void setUserDAO(UserDAO userDAO) { this.userDAO = userDAO; } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#insert(it.geosolutions.geostore.core.model.User) */ @Override @@ -94,7 +86,7 @@ public long insert(User user) throws BadRequestServiceEx, NotFoundServiceEx { if (user == null) { throw new BadRequestServiceEx("User type must be specified !"); } - if(!UserReservedNames.isAllowedName(user.getName())){ + if (!UserReservedNames.isAllowedName(user.getName())) { throw new BadRequestServiceEx("User name '" + user.getName() + "' is not allowed..."); } @@ -111,7 +103,7 @@ public long insert(User user) throws BadRequestServiceEx, NotFoundServiceEx { List groupNames = new ArrayList(); List existingGroups = new ArrayList(); if (groups != null && groups.size() > 0) { - for(UserGroup group : groups){ + for (UserGroup group : groups) { String groupName = group.getGroupName(); groupNames.add(groupName); if (StringUtils.isEmpty(groupName)) { @@ -127,18 +119,22 @@ public long insert(User user) throws BadRequestServiceEx, NotFoundServiceEx { existingGroups = userGroupDAO.search(searchCriteria); if (existingGroups != null && groups.size() != existingGroups.size()) { - throw new NotFoundServiceEx("At least one User group not found; review the groups associated to the user you want to insert" + user.getId()); - } + throw new NotFoundServiceEx( + "At least one User group not found; review the groups associated to the user you want to insert" + + user.getId()); + } } // Special Usergroup EVERYONE management Search search = new Search(); search.addFilterEqual("groupName", GroupReservedNames.EVERYONE.groupName()); List ugEveryone = userGroupDAO.search(search); - if(ugEveryone == null || ugEveryone.size() != 1){ - // Only log the error at ERROR level and avoid to block the user creation... - LOGGER.error("No UserGroup EVERYONE found, or more than 1 results has been found... skip the EVERYONE group associations for user '" + user.getName() + "'"); - } - else{ + if (ugEveryone == null || ugEveryone.size() != 1) { + // Only log the error at ERROR level and avoid to block the user creation... + LOGGER.error( + "No UserGroup EVERYONE found, or more than 1 results has been found... skip the EVERYONE group associations for user '" + + user.getName() + + "'"); + } else { existingGroups.add(ugEveryone.get(0)); } u.setGroups(new HashSet(existingGroups)); @@ -162,7 +158,7 @@ public long insert(User user) throws BadRequestServiceEx, NotFoundServiceEx { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#update(it.geosolutions.geostore.core.model.User) */ @Override @@ -170,22 +166,22 @@ public long update(User user) throws NotFoundServiceEx, BadRequestServiceEx { User orig = get(user.getId()); if (orig == null) { - orig = get(user.getName()); - user.setId(orig.getId()); + orig = get(user.getName()); + user.setId(orig.getId()); } - + if (orig == null) { - throw new NotFoundServiceEx("User not found " + user.getId()); - } + throw new NotFoundServiceEx("User not found " + user.getId()); + } // // Checking User Group // - Set groups = user.getGroups(); + Set groups = GroupReservedNames.checkReservedGroups(user.getGroups()); List groupNames = new ArrayList(); Set existingGroups = new HashSet(); if (groups != null && groups.size() > 0) { - for(UserGroup group : groups){ + for (UserGroup group : groups) { String groupName = group.getGroupName(); groupNames.add(groupName); if (StringUtils.isEmpty(groupName)) { @@ -198,27 +194,32 @@ public long update(User user) throws NotFoundServiceEx, BadRequestServiceEx { Search searchCriteria = new Search(UserGroup.class); searchCriteria.addFilterIn("groupName", groupNames); - existingGroups = GroupReservedNames.checkReservedGroups(userGroupDAO.search(searchCriteria)); + existingGroups = + GroupReservedNames.checkReservedGroups(userGroupDAO.search(searchCriteria)); - if (existingGroups == null || (existingGroups != null && groups.size() != existingGroups.size())) { - throw new NotFoundServiceEx("At least one User group not found; review the groups associated to the user you want to insert" + user.getId()); + if (existingGroups == null + || (existingGroups != null && groups.size() != existingGroups.size())) { + throw new NotFoundServiceEx( + "At least one User group not found; review the groups associated to the user you want to insert" + + user.getId()); } - } // Special Usergroup EVERYONE management Search search = new Search(); search.addFilterEqual("groupName", GroupReservedNames.EVERYONE.groupName()); List ugEveryone = userGroupDAO.search(search); - if(ugEveryone == null || ugEveryone.size() != 1){ - // Only log the error at ERROR level and avoid to block the user creation... - LOGGER.error("No UserGroup EVERYONE found, or more than 1 results has been found... skip the EVERYONE group associations for user '" + user.getName() + "'"); - } - else{ + if (ugEveryone == null || ugEveryone.size() != 1) { + // Only log the error at ERROR level and avoid to block the user creation... + LOGGER.error( + "No UserGroup EVERYONE found, or more than 1 results has been found... skip the EVERYONE group associations for user '" + + user.getName() + + "'"); + } else { existingGroups.add(ugEveryone.get(0)); } user.getGroups().clear(); user.getGroups().addAll(existingGroups); - + userDAO.merge(user); return orig.getId(); @@ -226,7 +227,7 @@ public long update(User user) throws NotFoundServiceEx, BadRequestServiceEx { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#updateAttributes(long, java.util.List) */ @Override @@ -259,7 +260,7 @@ public void updateAttributes(long id, List attributes) throws Not /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#get(long) */ @Override @@ -271,7 +272,7 @@ public User get(long id) { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#get(java.lang.String) */ @Override @@ -290,7 +291,7 @@ public User get(String name) throws NotFoundServiceEx { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#delete(long) */ @Override @@ -300,7 +301,7 @@ public boolean delete(long id) { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#getAll(java.lang.Integer, java.lang.Integer) */ @Override @@ -326,12 +327,13 @@ public List getAll(Integer page, Integer entries) throws BadRequestService /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#getAll(java.lang.Integer, java.lang.Integer) */ @Override - public List getAll(Integer page, Integer entries, String nameLike, - boolean includeAttributes) throws BadRequestServiceEx { + public List getAll( + Integer page, Integer entries, String nameLike, boolean includeAttributes) + throws BadRequestServiceEx { if (((page != null) && (entries == null)) || ((page == null) && (entries != null))) { throw new BadRequestServiceEx("Page and entries params should be declared together."); @@ -385,7 +387,7 @@ private List configUserList(List list, boolean includeAttributes) { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#getCount(java.lang.String) */ @Override @@ -407,14 +409,14 @@ public boolean insertSpecialUsers() { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Persisting Reserved Users... "); } - + User u = new User(); u.setName(UserReservedNames.GUEST.userName()); u.setRole(Role.GUEST); Search search = new Search(); search.addFilterEqual("groupName", GroupReservedNames.EVERYONE.groupName()); List userGroup = userGroupDAO.search(search); - if(userGroup.size() != 1){ + if (userGroup.size() != 1) { LOGGER.warn("More than EVERYONE group is found..."); } u.setGroups(new HashSet(userGroup)); @@ -427,17 +429,17 @@ public boolean insertSpecialUsers() { @Override public Collection getByAttribute(UserAttribute attribute) { - + Search searchCriteria = new Search(UserAttribute.class); searchCriteria.addFilterEqual("name", attribute.getName()); searchCriteria.addFilterEqual("value", attribute.getValue()); searchCriteria.addFetch("user"); List attributes = userAttributeDAO.search(searchCriteria); - + Set users = new HashSet(); - for(UserAttribute userAttr : attributes) { - if(userAttr.getUser() != null) { + for (UserAttribute userAttr : attributes) { + if (userAttr.getUser() != null) { users.add(userAttr.getUser()); } } diff --git a/src/core/services-impl/src/main/java/it/geosolutions/geostore/util/CategorizedCircularBuffer.java b/src/core/services-impl/src/main/java/it/geosolutions/geostore/util/CategorizedCircularBuffer.java index 79424005..ccab0929 100644 --- a/src/core/services-impl/src/main/java/it/geosolutions/geostore/util/CategorizedCircularBuffer.java +++ b/src/core/services-impl/src/main/java/it/geosolutions/geostore/util/CategorizedCircularBuffer.java @@ -26,12 +26,9 @@ import java.util.List; import java.util.Map; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class CategorizedCircularBuffer { - // protected final static Logger LOGGER = Logger.getLogger(CategorizedCircularBuffer.class); + // protected final static Logger LOGGER = LogManager.getLogger(CategorizedCircularBuffer.class); private final int maxSize; @@ -72,8 +69,8 @@ private void removeLastEntry() { T remove = typedList.removeLast(); if (!lastEntry.value.equals(remove)) { - throw new IllegalStateException("Internal error - mismatching values " - + lastEntry.value + " , " + remove); + throw new IllegalStateException( + "Internal error - mismatching values " + lastEntry.value + " , " + remove); } if (typedList.size() == 0) { diff --git a/src/core/services-impl/src/main/java/it/geosolutions/geostore/util/SearchConverter.java b/src/core/services-impl/src/main/java/it/geosolutions/geostore/util/SearchConverter.java index 61378d76..ce03985b 100644 --- a/src/core/services-impl/src/main/java/it/geosolutions/geostore/util/SearchConverter.java +++ b/src/core/services-impl/src/main/java/it/geosolutions/geostore/util/SearchConverter.java @@ -21,14 +21,6 @@ import com.googlecode.genericdao.search.Filter; import com.googlecode.genericdao.search.Search; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Collections; -import java.util.Date; -import java.util.EnumMap; -import java.util.Map; - import it.geosolutions.geostore.core.model.Resource; import it.geosolutions.geostore.services.dto.search.AndFilter; import it.geosolutions.geostore.services.dto.search.AttributeFilter; @@ -41,12 +33,18 @@ import it.geosolutions.geostore.services.dto.search.SearchOperator; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.InternalErrorServiceEx; - -import org.apache.log4j.Logger; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.EnumMap; +import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * Class SearchConverter. - * + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ @@ -54,11 +52,11 @@ public class SearchConverter implements FilterVisitor { private static final Map ops_rest_trg; - private static final Logger LOGGER = Logger.getLogger(SearchConverter.class); + private static final Logger LOGGER = LogManager.getLogger(SearchConverter.class); static { - Map ops = new EnumMap( - SearchOperator.class); + Map ops = + new EnumMap(SearchOperator.class); ops.put(SearchOperator.EQUAL_TO, Filter.OP_EQUAL); ops.put(SearchOperator.GREATER_THAN_OR_EQUAL_TO, Filter.OP_GREATER_OR_EQUAL); ops.put(SearchOperator.GREATER_THAN, Filter.OP_GREATER_THAN); @@ -78,8 +76,8 @@ public class SearchConverter implements FilterVisitor { * @throws BadRequestServiceEx * @throws InternalErrorServiceEx */ - public static Search convert(SearchFilter filter) throws BadRequestServiceEx, - InternalErrorServiceEx { + public static Search convert(SearchFilter filter) + throws BadRequestServiceEx, InternalErrorServiceEx { SearchConverter sc = new SearchConverter(); filter.accept(sc); @@ -95,8 +93,7 @@ public static Search convert(SearchFilter filter) throws BadRequestServiceEx, private Filter trgFilter; - private SearchConverter() { - } + private SearchConverter() {} /* * (non-Javadoc) @see it.geosolutions.geostore.services.dto.search.FilterVisitor#visit(it.geosolutions.geostore.services.dto.search.AndFilter) @@ -114,15 +111,17 @@ public void visit(AndFilter filter) throws BadRequestServiceEx, InternalErrorSer /** * This is a leaf filter. - * + * * @throws BadRequestServiceEx * @throws InternalErrorServiceEx */ @Override public void visit(AttributeFilter filter) throws BadRequestServiceEx, InternalErrorServiceEx { - if ((filter.getType() != null) && (filter.getName() != null) - && (filter.getOperator() != null) && (filter.getValue() != null)) { + if ((filter.getType() != null) + && (filter.getName() != null) + && (filter.getOperator() != null) + && (filter.getValue() != null)) { Integer trg_op = ops_rest_trg.get(filter.getOperator()); @@ -134,39 +133,43 @@ public void visit(AttributeFilter filter) throws BadRequestServiceEx, InternalEr Object value = null; switch (filter.getType()) { - case DATE: - fieldValueName = "dateValue"; - - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - - try { - value = sdf.parse(filter.getValue()); - } catch (ParseException e) { - throw new InternalErrorServiceEx("Error parsing attribute date value"); - } - - break; - case NUMBER: - fieldValueName = "numberValue"; - - try { - value = Double.valueOf(filter.getValue()); - } catch (NumberFormatException ex) { - throw new InternalErrorServiceEx("Error parsing attribute number value"); - } - - break; - case STRING: - fieldValueName = "textValue"; - value = filter.getValue(); - - break; - default: - throw new IllegalStateException("Unknown type " + filter.getType()); + case DATE: + fieldValueName = "dateValue"; + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + try { + value = sdf.parse(filter.getValue()); + } catch (ParseException e) { + throw new InternalErrorServiceEx("Error parsing attribute date value"); + } + + break; + case NUMBER: + fieldValueName = "numberValue"; + + try { + value = Double.valueOf(filter.getValue()); + } catch (NumberFormatException ex) { + throw new InternalErrorServiceEx("Error parsing attribute number value"); + } + + break; + case STRING: + fieldValueName = "textValue"; + value = filter.getValue(); + + break; + default: + throw new IllegalStateException("Unknown type " + filter.getType()); } - trgFilter = Filter.some("attribute", Filter.and(Filter.equal("name", filter.getName()), - new Filter(fieldValueName, value, trg_op))); + trgFilter = + Filter.some( + "attribute", + Filter.and( + Filter.equal("name", filter.getName()), + new Filter(fieldValueName, value, trg_op))); } else { throw new BadRequestServiceEx("Bad payload. One or more field are missing"); @@ -175,7 +178,7 @@ public void visit(AttributeFilter filter) throws BadRequestServiceEx, InternalEr /** * This is a leaf filter. - * + * * @throws InternalErrorServiceEx */ @SuppressWarnings("rawtypes") @@ -218,7 +221,7 @@ public void visit(FieldFilter filter) throws InternalErrorServiceEx { /** * This is a leaf filter. - * + * * @throws InternalErrorServiceEx */ @Override diff --git a/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/CategoryServiceImplTest.java b/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/CategoryServiceImplTest.java index f5486379..c5d5bf98 100644 --- a/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/CategoryServiceImplTest.java +++ b/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/CategoryServiceImplTest.java @@ -20,29 +20,24 @@ package it.geosolutions.geostore.services; import it.geosolutions.geostore.core.model.Category; - import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; /** * Class CategoryServiceImplTest. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class CategoryServiceImplTest extends ServiceTestBase { @BeforeClass - public static void setUpClass() throws Exception { - } + public static void setUpClass() throws Exception {} @AfterClass - public static void tearDownClass() throws Exception { - } + public static void tearDownClass() throws Exception {} - public CategoryServiceImplTest() { - } + public CategoryServiceImplTest() {} @Test public void testInsertDeleteCategory() throws Exception { @@ -103,6 +98,5 @@ public void testUpdateLoadData() throws Exception { categoryService.delete(categoryId); assertEquals(0, categoryService.getCount(null)); } - } } diff --git a/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/InMemoryUserSessionServiceImplTest.java b/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/InMemoryUserSessionServiceImplTest.java index 259c6991..d03451e5 100644 --- a/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/InMemoryUserSessionServiceImplTest.java +++ b/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/InMemoryUserSessionServiceImplTest.java @@ -19,78 +19,69 @@ */ package it.geosolutions.geostore.services; -import java.util.GregorianCalendar; import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.services.dto.UserSession; import it.geosolutions.geostore.services.dto.UserSessionImpl; - +import java.util.GregorianCalendar; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; /** * Class InMemoryUserSessionServiceImplTest. - * + * * @author Lorenzo Natali (lorenzo.natali at geo-solutions.it) - * */ public class InMemoryUserSessionServiceImplTest extends ServiceTestBase { @BeforeClass - public static void setUpClass() throws Exception { - } + public static void setUpClass() throws Exception {} @AfterClass - public static void tearDownClass() throws Exception { - } - - public InMemoryUserSessionServiceImplTest() { + public static void tearDownClass() throws Exception {} - } + public InMemoryUserSessionServiceImplTest() {} @Test public void testInitialization() { - InMemoryUserSessionServiceImpl service = new InMemoryUserSessionServiceImpl(); - service.setCleanUpSeconds(500); - assertTrue(true); + InMemoryUserSessionServiceImpl service = new InMemoryUserSessionServiceImpl(); + service.setCleanUpSeconds(500); + assertTrue(true); } + @Test public void testSessionStorage() throws Exception { - UserSessionService service = new InMemoryUserSessionServiceImpl(); - User u = new User(); - u.setId(1L); - u.setName("TEST"); - UserSession session = new UserSessionImpl(u, new GregorianCalendar(3000, 1,1)); - String sessionId = service.registerNewSession(session); - User sessUser = service.getUserData(sessionId); - assertEquals(sessUser, u); - assertTrue(service.isOwner(sessionId, u)); - UserSession session2 = new UserSessionImpl(u, new GregorianCalendar(3000, 1,1)); - service.registerNewSession("ID_SESSION", session2); - assertTrue(service.isOwner("ID_SESSION", u)); - service.refreshSession(sessionId, service.getRefreshToken(sessionId)); - service.removeSession("ID_SESSION"); - assertFalse(service.isOwner("ID_SESSION", u)); - service.removeAllSessions(); - assertFalse(service.isOwner(sessionId, u)); - + UserSessionService service = new InMemoryUserSessionServiceImpl(); + User u = new User(); + u.setId(1L); + u.setName("TEST"); + UserSession session = new UserSessionImpl(u, new GregorianCalendar(3000, 1, 1)); + String sessionId = service.registerNewSession(session); + User sessUser = service.getUserData(sessionId); + assertEquals(sessUser, u); + assertTrue(service.isOwner(sessionId, u)); + UserSession session2 = new UserSessionImpl(u, new GregorianCalendar(3000, 1, 1)); + service.registerNewSession("ID_SESSION", session2); + assertTrue(service.isOwner("ID_SESSION", u)); + service.refreshSession(sessionId, service.getRefreshToken(sessionId)); + service.removeSession("ID_SESSION"); + assertFalse(service.isOwner("ID_SESSION", u)); + service.removeAllSessions(); + assertFalse(service.isOwner(sessionId, u)); } + @Test public void testSessionMissing() throws Exception { - UserSessionService service = new InMemoryUserSessionServiceImpl(); - User u = new User(); - u.setId(1L); - u.setName("TEST"); - UserSession session = new UserSessionImpl(u, new GregorianCalendar(3000, 1,1)); - String sessionId = service.registerNewSession(session); - User sessUser = service.getUserData(sessionId); - assertEquals(sessUser, u); - assertTrue(service.isOwner(sessionId, u)); - assertNull(service.getRefreshToken("NOT_A SESSION")); - assertNull(service.getUserData("NOT_A SESSION")); - + UserSessionService service = new InMemoryUserSessionServiceImpl(); + User u = new User(); + u.setId(1L); + u.setName("TEST"); + UserSession session = new UserSessionImpl(u, new GregorianCalendar(3000, 1, 1)); + String sessionId = service.registerNewSession(session); + User sessUser = service.getUserData(sessionId); + assertEquals(sessUser, u); + assertTrue(service.isOwner(sessionId, u)); + assertNull(service.getRefreshToken("NOT_A SESSION")); + assertNull(service.getUserData("NOT_A SESSION")); } - - - } diff --git a/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/ResourceServiceImplTest.java b/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/ResourceServiceImplTest.java index cc4cfbbb..66693a8e 100644 --- a/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/ResourceServiceImplTest.java +++ b/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/ResourceServiceImplTest.java @@ -19,14 +19,6 @@ */ package it.geosolutions.geostore.services; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.core.model.Resource; import it.geosolutions.geostore.core.model.SecurityRule; @@ -40,29 +32,28 @@ import it.geosolutions.geostore.services.dto.search.SearchFilter; import it.geosolutions.geostore.services.dto.search.SearchOperator; import it.geosolutions.geostore.services.exception.DuplicatedResourceNameServiceEx; +import java.util.*; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; /** * Class ResourceServiceImplTest. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class ResourceServiceImplTest extends ServiceTestBase { @BeforeClass - public static void setUpClass() throws Exception { - } + public static void setUpClass() throws Exception {} @AfterClass - public static void tearDownClass() throws Exception { - } + public static void tearDownClass() throws Exception {} - public ResourceServiceImplTest() { - } + public ResourceServiceImplTest() {} @Test public void testInsertDeleteResource() throws Exception { - long resourceId = createResource("name1", "description1", "MAP"); assertEquals(1, resourceService.getCount(null)); @@ -120,18 +111,21 @@ public void testGetAllData() throws Exception { assertEquals(10, resourceService.getCount("name%")); assertEquals(10, resourceService.getList("name%", null, null, buildFakeAdminUser()).size()); assertEquals(20, resourceService.getCount("%name%")); - assertEquals(20, resourceService.getList("%name%", null, null, buildFakeAdminUser()).size()); + assertEquals( + 20, resourceService.getList("%name%", null, null, buildFakeAdminUser()).size()); assertEquals(2, resourceService.getCount("%name1%")); - assertEquals(2, resourceService.getList("%name1%", null, null, buildFakeAdminUser()).size()); + assertEquals( + 2, resourceService.getList("%name1%", null, null, buildFakeAdminUser()).size()); } /** * Tests if the results are sorted by name + * * @throws Exception */ @Test public void testSorting() throws Exception { - assertEquals(0, resourceService.getAll(null, null, buildFakeAdminUser()).size()); - // setup data. First set is ordered + assertEquals(0, resourceService.getAll(null, null, buildFakeAdminUser()).size()); + // setup data. First set is ordered for (int i = 0; i < 20; i++) { createResource("FIRST SET - " + i, "description" + i, "MAP1" + i); } @@ -145,42 +139,50 @@ public void testSorting() throws Exception { List getAllResult = resourceService.getAll(null, null, buildFakeAdminUser()); assertEquals(40, getAllResult.size()); assertTrue(isSorted(getAllResult)); - + // // check getResources, various filters - // - - // category like + // + + // category like SearchFilter MAPCategoryFilter = new CategoryFilter("MAP%", SearchOperator.LIKE); - List getResourcesMAPResult = resourceService.getResources(MAPCategoryFilter, buildFakeAdminUser()); + List getResourcesMAPResult = + resourceService.getResources(MAPCategoryFilter, buildFakeAdminUser()); assertEquals(40, getResourcesMAPResult.size()); assertTrue(isSorted(getResourcesMAPResult)); SearchFilter MAP1CategoryFilter = new CategoryFilter("MAP1%", SearchOperator.LIKE); - List getResourcesMAP1Result = resourceService.getResources(MAP1CategoryFilter, buildFakeAdminUser()); + List getResourcesMAP1Result = + resourceService.getResources(MAP1CategoryFilter, buildFakeAdminUser()); assertEquals(20, getResourcesMAP1Result.size()); assertTrue(isSorted(getResourcesMAP1Result)); SearchFilter MAP2CategoryFilter = new CategoryFilter("MAP2%", SearchOperator.LIKE); - List getResourcesMAP2Result = resourceService.getResources(MAP2CategoryFilter, buildFakeAdminUser()); + List getResourcesMAP2Result = + resourceService.getResources(MAP2CategoryFilter, buildFakeAdminUser()); assertEquals(20, getResourcesMAP2Result.size()); assertTrue(isSorted(getResourcesMAP2Result)); - + // name like - SearchFilter nameContain1Filter = new FieldFilter(BaseField.NAME, "%1%", SearchOperator.LIKE); - List nameContain1Result = resourceService.getResources(nameContain1Filter, buildFakeAdminUser()); - // 22 resources contain 1 in the name: "FIRST SET - 1" + "FIRST SET - 10" ... "FIRST SET - 19", same for second set + SearchFilter nameContain1Filter = + new FieldFilter(BaseField.NAME, "%1%", SearchOperator.LIKE); + List nameContain1Result = + resourceService.getResources(nameContain1Filter, buildFakeAdminUser()); + // 22 resources contain 1 in the name: "FIRST SET - 1" + "FIRST SET - 10" ... "FIRST SET - + // 19", same for second set assertEquals(22, nameContain1Result.size()); assertTrue(isSorted(nameContain1Result)); - - SearchFilter nameContain2Filter = new FieldFilter(BaseField.NAME, "%2%", SearchOperator.LIKE); - List nameContain2Result = resourceService.getResources(nameContain2Filter, buildFakeAdminUser()); - // 4 resources contain 1 in the name: "FIRST SET - 2" + "FIRST SET - 12" + + SearchFilter nameContain2Filter = + new FieldFilter(BaseField.NAME, "%2%", SearchOperator.LIKE); + List nameContain2Result = + resourceService.getResources(nameContain2Filter, buildFakeAdminUser()); + // 4 resources contain 1 in the name: "FIRST SET - 2" + "FIRST SET - 12" assertEquals(4, nameContain2Result.size()); assertTrue(isSorted(nameContain2Result)); - } - + /** - * Check if the List passed is sorted by name + * Check if the List passed is sorted by name + * * @param resourcesList * @return */ @@ -188,7 +190,7 @@ private static boolean isSorted(List resourcesList) { if (resourcesList.size() == 1) { return true; } - + Iterator iter = resourcesList.iterator(); ShortResource current, previous = iter.next(); while (iter.hasNext()) { @@ -244,111 +246,110 @@ public void testCategoryFilter() throws Exception { assertEquals(3, list.size()); } } - + @Test public void testGetSecurityRules() throws Exception { - long userId = createUser("user1", Role.USER, "password"); - User user = new User(); - user.setId(userId); - - long groupId = createGroup("group1"); - UserGroup group = new UserGroup(); - group.setId(groupId); - - List rules = new ArrayList<>(); - - SecurityRule rule = new SecurityRule(); - rule.setUser(user); - rule.setCanRead(true); - rules.add(rule); - - rule = new SecurityRule(); - rule.setCanRead(true); - rule.setCanWrite(true); - rule.setGroup(group); - rules.add(rule); - - long resourceId = createResource("name1", "description1", "MAP", rules); - - List writtenRules = resourceService.getSecurityRules(resourceId); - - assertEquals(2, writtenRules.size()); - - SecurityRule userRule = writtenRules.get(0); - assertNotNull(userRule.getUser()); - assertNull(userRule.getGroup()); - assertEquals((Long)userId, userRule.getUser().getId()); - assertEquals((Long)resourceId, userRule.getResource().getId()); - - SecurityRule groupRule = writtenRules.get(1); - assertNotNull(groupRule.getGroup()); - assertNull(groupRule.getUser()); - assertEquals((Long)groupId, groupRule.getGroup().getId()); - assertEquals((Long)resourceId, groupRule.getResource().getId()); - + long userId = createUser("user1", Role.USER, "password"); + User user = new User(); + user.setId(userId); + + long groupId = createGroup("group1"); + UserGroup group = new UserGroup(); + group.setId(groupId); + + List rules = new ArrayList<>(); + + SecurityRule rule = new SecurityRule(); + rule.setUser(user); + rule.setCanRead(true); + rules.add(rule); + + rule = new SecurityRule(); + rule.setCanRead(true); + rule.setCanWrite(true); + rule.setGroup(group); + rules.add(rule); + + long resourceId = createResource("name1", "description1", "MAP", rules); + + List writtenRules = resourceService.getSecurityRules(resourceId); + + assertEquals(2, writtenRules.size()); + + SecurityRule userRule = writtenRules.get(0); + assertNotNull(userRule.getUser()); + assertNull(userRule.getGroup()); + assertEquals((Long) userId, userRule.getUser().getId()); + assertEquals((Long) resourceId, userRule.getResource().getId()); + + SecurityRule groupRule = writtenRules.get(1); + assertNotNull(groupRule.getGroup()); + assertNull(groupRule.getUser()); + assertEquals((Long) groupId, groupRule.getGroup().getId()); + assertEquals((Long) resourceId, groupRule.getResource().getId()); } - + @Test public void testUpdateSecurityRules() throws Exception { - long resourceId = createResource("name1", "description1", "MAP"); - - List writtenRules = resourceService.getSecurityRules(resourceId); - assertEquals(0, writtenRules.size()); - - List rules = new ArrayList(); - - long userId = createUser("user1", Role.USER, "password"); - User user = new User(); - user.setId(userId); - - long groupId = createGroup("group1"); - UserGroup group = new UserGroup(); - group.setId(groupId); - - long otherGroupId = createGroup("group2"); - UserGroup othergroup = new UserGroup(); - othergroup.setId(otherGroupId); - - SecurityRule rule = new SecurityRule(); - rule.setUser(user); - rule.setCanRead(true); - rules.add(rule); - - rule = new SecurityRule(); - rule.setCanRead(true); - rule.setCanWrite(true); - rule.setGroup(group); - rules.add(rule); - - resourceService.updateSecurityRules(resourceId, rules); - - writtenRules = resourceService.getSecurityRules(resourceId); - assertEquals(2, writtenRules.size()); - - rules.clear(); - - rule = new SecurityRule(); - rule.setUser(user); - rule.setCanRead(true); - rules.add(rule); - - rule = new SecurityRule(); - rule.setCanRead(true); - rule.setCanWrite(true); - rule.setGroup(group); - rules.add(rule); - rule = new SecurityRule(); - rule.setCanRead(true); - rule.setCanWrite(true); - rule.setGroup(othergroup); - rules.add(rule); - - resourceService.updateSecurityRules(resourceId, rules); - - writtenRules = resourceService.getSecurityRules(resourceId); - assertEquals(3, writtenRules.size()); + long resourceId = createResource("name1", "description1", "MAP"); + + List writtenRules = resourceService.getSecurityRules(resourceId); + assertEquals(0, writtenRules.size()); + + List rules = new ArrayList(); + + long userId = createUser("user1", Role.USER, "password"); + User user = new User(); + user.setId(userId); + + long groupId = createGroup("group1"); + UserGroup group = new UserGroup(); + group.setId(groupId); + + long otherGroupId = createGroup("group2"); + UserGroup othergroup = new UserGroup(); + othergroup.setId(otherGroupId); + + SecurityRule rule = new SecurityRule(); + rule.setUser(user); + rule.setCanRead(true); + rules.add(rule); + + rule = new SecurityRule(); + rule.setCanRead(true); + rule.setCanWrite(true); + rule.setGroup(group); + rules.add(rule); + + resourceService.updateSecurityRules(resourceId, rules); + + writtenRules = resourceService.getSecurityRules(resourceId); + assertEquals(2, writtenRules.size()); + + rules.clear(); + + rule = new SecurityRule(); + rule.setUser(user); + rule.setCanRead(true); + rules.add(rule); + + rule = new SecurityRule(); + rule.setCanRead(true); + rule.setCanWrite(true); + rule.setGroup(group); + rules.add(rule); + rule = new SecurityRule(); + rule.setCanRead(true); + rule.setCanWrite(true); + rule.setGroup(othergroup); + rules.add(rule); + + resourceService.updateSecurityRules(resourceId, rules); + + writtenRules = resourceService.getSecurityRules(resourceId); + assertEquals(3, writtenRules.size()); } - + @Test public void testInsertTooBigResource() throws Exception { final String ORIG_RES_NAME = "testRes"; @@ -359,13 +360,13 @@ public void testInsertTooBigResource() throws Exception { assertEquals(0, resourceService.getCount(null)); try { createResource(ORIG_RES_NAME, DESCRIPTION, CATEGORY_NAME, bigData); - } catch(Exception e) { + } catch (Exception e) { error = true; } assertEquals(0, resourceService.getCount(null)); assertTrue(error); } - + private static String createDataSize(int msgSize) { StringBuilder sb = new StringBuilder(msgSize); for (int i = 0; i < msgSize; i++) { @@ -373,83 +374,193 @@ private static String createDataSize(int msgSize) { } return sb.toString(); } - + @Test public void testInsertUpdateDuplicatedResource() throws Exception { - final String ORIG_RES_NAME = "testRes"; - final String DESCRIPTION = "description"; - final String CATEGORY_NAME = "MAP"; - final int NUM_COPIES = 3; - final long[] COPY_IDS = new long[NUM_COPIES]; - - long origResourceId = createResource(ORIG_RES_NAME, DESCRIPTION, CATEGORY_NAME); - Category category = categoryService.get(CATEGORY_NAME); + final String ORIG_RES_NAME = "testRes"; + final String DESCRIPTION = "description"; + final String CATEGORY_NAME = "MAP"; + final int NUM_COPIES = 3; + final long[] COPY_IDS = new long[NUM_COPIES]; + + long origResourceId = createResource(ORIG_RES_NAME, DESCRIPTION, CATEGORY_NAME); + Category category = categoryService.get(CATEGORY_NAME); assertEquals(1, resourceService.getCount(null)); assertNotNull(category); - - for (int i=0; i 0); - assertEquals(i+2, resourceService.getCount(null)); - + assertEquals(i + 2, resourceService.getCount(null)); + // ////////////////////// // test update // ////////////////////// - + Resource copy = resourceService.get(copyId); assertNotNull(copy); copy.setName(ORIG_RES_NAME); try { - resourceService.update(copy); - fail("DuplicatedResourceNameServiceEx was not thrown as expected"); + resourceService.update(copy); + fail("DuplicatedResourceNameServiceEx was not thrown as expected"); } catch (DuplicatedResourceNameServiceEx ex) { - // OK, exception was thrown: exception message be a valid resource name - String validCopyName = ex.getMessage(); - - assertNotNull("Thrown DuplicatedResourceNameServiceEx exception's message was null", validCopyName); - assertFalse("Thrown DuplicatedResourceNameServiceEx exception's message was empty", validCopyName.isEmpty()); - + // OK, exception was thrown: exception message be a valid resource name + String validCopyName = ex.getMessage(); + + assertNotNull( + "Thrown DuplicatedResourceNameServiceEx exception's message was null", + validCopyName); + assertFalse( + "Thrown DuplicatedResourceNameServiceEx exception's message was empty", + validCopyName.isEmpty()); + copy.setName(validCopyName); // should throw no exception try { - resourceService.update(copy); - - // update description - copy.setDescription(DESCRIPTION + " modified"); - resourceService.update(copy); + resourceService.update(copy); + + // update description + copy.setDescription(DESCRIPTION + " modified"); + resourceService.update(copy); } catch (Exception e) { - fail("Exception was thrown during update: " + e.getMessage()); - } + fail("Exception was thrown during update: " + e.getMessage()); + } } - + COPY_IDS[i] = copyId; } - + // cleanup assertTrue("Could not delete resource", resourceService.delete(origResourceId)); - for (int i=0; i(Collections.singletonList(group))); + + long user2Id = createUser("user2", Role.USER, "password", otherGroupId); + User user2 = new User(); + user2.setId(user2Id); + user2.setName("user2"); + user2.setRole(Role.USER); + user2.setGroups(new HashSet<>(Collections.singletonList(otherGroup))); + + List rules1 = + new ArrayList<>( + Arrays.asList( + new SecurityRuleBuilder().user(user1).canRead(true).build(), + new SecurityRuleBuilder().group(group).canRead(true).build(), + new SecurityRuleBuilder().group(otherGroup).canRead(true).build())); + + long resourceId = createResource("name1", "description1", "MAP1", false, rules1); + + List writtenRules = resourceService.getSecurityRules(resourceId); + + assertEquals(3, writtenRules.size()); + + // name like + SearchFilter nameContains1Filter = + new FieldFilter(BaseField.NAME, "%name1%", SearchOperator.LIKE); + resourceService.getResources(nameContains1Filter, null, null, user2); + assertEquals( + 1, + resourceService + .getResources(nameContains1Filter, null, null, buildFakeAdminUser()) + .size()); + assertEquals( + 1, resourceService.getResources(nameContains1Filter, null, null, user1).size()); + assertEquals( + 0, resourceService.getResources(nameContains1Filter, null, null, user2).size()); + + List rules2 = + new ArrayList<>( + Arrays.asList( + new SecurityRuleBuilder().user(user1).canRead(true).build(), + new SecurityRuleBuilder().group(group).canRead(true).build(), + new SecurityRuleBuilder().group(otherGroup).canRead(true).build())); + + resourceId = createResource("name2", "description2", "MAP2", true, rules2); + + writtenRules = resourceService.getSecurityRules(resourceId); + + assertEquals(3, writtenRules.size()); + + // name like + SearchFilter nameContains2Filter = + new FieldFilter(BaseField.NAME, "%name2%", SearchOperator.LIKE); + assertEquals( + 1, + resourceService + .getResources(nameContains2Filter, null, null, buildFakeAdminUser()) + .size()); + assertEquals( + 1, resourceService.getResources(nameContains2Filter, null, null, user1).size()); + assertEquals( + 1, resourceService.getResources(nameContains2Filter, null, null, user2).size()); } - } diff --git a/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/SearchConverterTest.java b/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/SearchConverterTest.java index 0fe01827..a2f4be56 100644 --- a/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/SearchConverterTest.java +++ b/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/SearchConverterTest.java @@ -19,13 +19,6 @@ */ package it.geosolutions.geostore.services; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import javax.xml.bind.JAXB; - import it.geosolutions.geostore.core.model.Attribute; import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.core.model.Resource; @@ -33,29 +26,29 @@ import it.geosolutions.geostore.services.dto.ShortAttribute; import it.geosolutions.geostore.services.dto.ShortResource; import it.geosolutions.geostore.services.dto.search.AndFilter; - +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import javax.xml.bind.JAXB; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; /** * Class SearchConverterTest. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class SearchConverterTest extends ServiceTestBase { @BeforeClass - public static void setUpClass() throws Exception { - } + public static void setUpClass() throws Exception {} @AfterClass - public static void tearDownClass() throws Exception { - } + public static void tearDownClass() throws Exception {} - public SearchConverterTest() { - } + public SearchConverterTest() {} @Test public void testFilterConverter() throws Exception { @@ -102,27 +95,19 @@ public void testFilterConverter() throws Exception { assertEquals(3, sAttributes.size()); assertNotNull(resourceService.get(resourceId)); - assertTrue(resourceService.getAttributes(resourceId).size() == 3); + assertEquals(3, resourceService.getAttributes(resourceId).size()); } // // Complex filter with AND // { - String xmlFilter = "" + "" + "NAME" - + "LIKE" + "%resource%" + "" - + "" + "" + "attr1" - + "EQUAL_TO" + "STRING" - + "value2" + "" + "" - + "attr2" + "GREATER_THAN" - + "NUMBER" + "1.0" + "" + "" - + ""; - - StringReader reader = new StringReader(xmlFilter); + StringReader reader = getFilterConverterFilterAND(); AndFilter searchFilter = JAXB.unmarshal(reader, AndFilter.class); assertNotNull(searchFilter); - List resources = resourceService.getResources(searchFilter, buildFakeAdminUser()); + List resources = + resourceService.getResources(searchFilter, buildFakeAdminUser()); assertEquals(1, resources.size()); } @@ -130,26 +115,78 @@ public void testFilterConverter() throws Exception { // Complex filter with AND OR // { - String xmlFilter = "" + "" + "NAME" - + "LIKE" + "%resource%" + "" - + "" + "" + "attr2" - + "GREATER_THAN" + "NUMBER" - + "1.0" + "" + "" + "" - + "attr1" + "EQUAL_TO" - + "STRING" + "value2" + "" - + "" + "attr1" + "EQUAL_TO" - + "STRING" + "value3" + "" + "" - + "" + ""; - - StringReader reader = new StringReader(xmlFilter); + StringReader reader = getFilterConverterFilterANDOR(); AndFilter searchFilter = JAXB.unmarshal(reader, AndFilter.class); assertNotNull(searchFilter); - List resources = resourceService.getResources(searchFilter, buildFakeAdminUser()); + List resources = + resourceService.getResources(searchFilter, buildFakeAdminUser()); assertEquals(2, resources.size()); } } + private static StringReader getFilterConverterFilterAND() { + String xmlFilter = + "" + + "" + + "NAME" + + "LIKE" + + "%resource%" + + "" + + "" + + "" + + "attr1" + + "EQUAL_TO" + + "STRING" + + "value2" + + "" + + "" + + "attr2" + + "GREATER_THAN" + + "NUMBER" + + "1.0" + + "" + + "" + + ""; + + return new StringReader(xmlFilter); + } + + private static StringReader getFilterConverterFilterANDOR() { + String xmlFilter = + "" + + "" + + "NAME" + + "LIKE" + + "%resource%" + + "" + + "" + + "" + + "attr2" + + "GREATER_THAN" + + "NUMBER" + + "1.0" + + "" + + "" + + "" + + "attr1" + + "EQUAL_TO" + + "STRING" + + "value2" + + "" + + "" + + "attr1" + + "EQUAL_TO" + + "STRING" + + "value3" + + "" + + "" + + "" + + ""; + + return new StringReader(xmlFilter); + } + @Test public void testSearch() throws Exception { // @@ -177,7 +214,7 @@ public void testSearch() throws Exception { Attribute attr2 = new Attribute(); attr2.setName("attr2"); - attr2.setNumberValue(Double.valueOf(i)); + attr2.setNumberValue((double) i); attr2.setType(DataType.NUMBER); attributes.add(attr2); @@ -193,10 +230,10 @@ public void testSearch() throws Exception { List sAttributes = resourceService.getAttributes(resourceId); assertNotNull(sAttributes); - assertTrue(sAttributes.size() == 3); + assertEquals(3, sAttributes.size()); assertNotNull(resourceService.get(resourceId)); - assertTrue(resourceService.getAttributes(resourceId).size() == 3); + assertEquals(3, resourceService.getAttributes(resourceId).size()); long id = createData("data" + i, resourceService.get(resourceId)); @@ -207,24 +244,34 @@ public void testSearch() throws Exception { // Search with paging, filter excluding Data // { - String xmlFilter = "" + "" + "METADATA" - + "LIKE" + "%resource%" + "" - + "" + "attr1" + "LIKE" - + "STRING" + "%value%" + "" + ""; + String xmlFilter = + "" + + "" + + "METADATA" + + "LIKE" + + "%resource%" + + "" + + "" + + "attr1" + + "LIKE" + + "STRING" + + "%value%" + + "" + + ""; StringReader reader = new StringReader(xmlFilter); AndFilter searchFilter = JAXB.unmarshal(reader, AndFilter.class); assertNotNull(searchFilter); - - - List resources = resourceService.getResources(searchFilter, 0, 5, true, false, buildFakeAdminUser()); + List resources = + resourceService.getResources( + searchFilter, 0, 5, true, false, buildFakeAdminUser()); assertEquals(5, resources.size()); Resource res = resources.get(0); assertNotNull(res.getAttribute()); - assertTrue(res.getAttribute().size() == 3); + assertEquals(3, res.getAttribute().size()); assertNull(res.getData()); } @@ -233,16 +280,28 @@ public void testSearch() throws Exception { // Search with paging, filter excluding attributes // { - String xmlFilter = "" + "" + "METADATA" - + "LIKE" + "%resource%" + "" - + "" + "attr1" + "LIKE" - + "STRING" + "%value%" + "" + ""; + String xmlFilter = + "" + + "" + + "METADATA" + + "LIKE" + + "%resource%" + + "" + + "" + + "attr1" + + "LIKE" + + "STRING" + + "%value%" + + "" + + ""; StringReader reader = new StringReader(xmlFilter); AndFilter searchFilter = JAXB.unmarshal(reader, AndFilter.class); assertNotNull(searchFilter); - List resources = resourceService.getResources(searchFilter, 0, 5, false, true, buildFakeAdminUser()); + List resources = + resourceService.getResources( + searchFilter, 0, 5, false, true, buildFakeAdminUser()); assertEquals(5, resources.size()); Resource res = resources.get(0); @@ -255,16 +314,28 @@ public void testSearch() throws Exception { // Search with paging, filter // { - String xmlFilter = "" + "" + "METADATA" - + "LIKE" + "%resource%" + "" - + "" + "attr1" + "LIKE" - + "STRING" + "%value%" + "" + ""; + String xmlFilter = + "" + + "" + + "METADATA" + + "LIKE" + + "%resource%" + + "" + + "" + + "attr1" + + "LIKE" + + "STRING" + + "%value%" + + "" + + ""; StringReader reader = new StringReader(xmlFilter); AndFilter searchFilter = JAXB.unmarshal(reader, AndFilter.class); assertNotNull(searchFilter); - List resources = resourceService.getResources(searchFilter, 0, 5, true, true, buildFakeAdminUser()); + List resources = + resourceService.getResources( + searchFilter, 0, 5, true, true, buildFakeAdminUser()); assertEquals(5, resources.size()); Resource res = resources.get(0); @@ -272,7 +343,7 @@ public void testSearch() throws Exception { assertNotNull(res.getData()); assertNotNull(res.getAttribute()); - assertTrue(res.getAttribute().size() == 3); + assertEquals(3, res.getAttribute().size()); } } } diff --git a/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/ServiceTestBase.java b/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/ServiceTestBase.java index 6d882422..b6391e06 100644 --- a/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/ServiceTestBase.java +++ b/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/ServiceTestBase.java @@ -19,12 +19,6 @@ */ package it.geosolutions.geostore.services; -import java.util.ArrayList; -import java.util.List; - -import org.apache.log4j.Logger; -import org.springframework.context.support.ClassPathXmlApplicationContext; - import it.geosolutions.geostore.core.dao.ResourceDAO; import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.core.model.Resource; @@ -33,18 +27,22 @@ import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserAttribute; import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.enums.GroupReservedNames; import it.geosolutions.geostore.core.model.enums.Role; import it.geosolutions.geostore.services.dto.ShortResource; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; +import java.util.List; import junit.framework.TestCase; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Class ServiceTestBase. - * + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class ServiceTestBase extends TestCase { @@ -55,23 +53,21 @@ public class ServiceTestBase extends TestCase { protected static CategoryService categoryService; protected static UserService userService; - + protected static UserGroupService userGroupService; - + protected static ResourceDAO resourceDAO; protected static ClassPathXmlApplicationContext ctx = null; - protected final Logger LOGGER = Logger.getLogger(getClass()); + protected final Logger LOGGER = LogManager.getLogger(getClass()); - /** - * - */ + /** */ public ServiceTestBase() { synchronized (ServiceTestBase.class) { if (ctx == null) { - String[] paths = { "classpath*:applicationContext.xml" - // ,"applicationContext-test.xml" + String[] paths = {"classpath*:applicationContext.xml" + // ,"applicationContext-test.xml" }; ctx = new ClassPathXmlApplicationContext(paths); @@ -95,9 +91,7 @@ protected void setUp() throws Exception { removeAll(); } - /** - * - */ + /** */ public void testCheckServices() { assertNotNull(storedDataService); assertNotNull(resourceService); @@ -121,24 +115,22 @@ protected void removeAll() throws NotFoundServiceEx, BadRequestServiceEx { /** * @throws BadRequestServiceEx - * @throws NotFoundServiceEx + * @throws NotFoundServiceEx */ private void removeAllUserGroup() throws BadRequestServiceEx, NotFoundServiceEx { List list = userGroupService.getAll(null, null); for (UserGroup item : list) { LOGGER.info("Removing User: " + item.getGroupName()); - - boolean ret = userGroupService.delete(item.getId()); - assertTrue("Group not removed", ret); + if (GroupReservedNames.isAllowedName(item.getGroupName())) { + boolean ret = userGroupService.delete(item.getId()); + assertTrue("Group not removed", ret); + } } - + boolean res = userGroupService.removeSpecialUsersGroups(); assertEquals("Group have not been properly deleted", 0, userService.getCount(null)); } - - /** - * @throws BadRequestServiceEx - */ + /** @throws BadRequestServiceEx */ private void removeAllUser() throws BadRequestServiceEx { List list = userService.getAll(null, null); for (User item : list) { @@ -151,9 +143,7 @@ private void removeAllUser() throws BadRequestServiceEx { assertEquals("User have not been properly deleted", 0, userService.getCount(null)); } - /** - * @throws BadRequestServiceEx - */ + /** @throws BadRequestServiceEx */ private void removeAllCategory() throws BadRequestServiceEx { List list = categoryService.getAll(null, null); for (Category item : list) { @@ -166,9 +156,7 @@ private void removeAllCategory() throws BadRequestServiceEx { assertEquals("Category have not been properly deleted", 0, categoryService.getCount(null)); } - /** - * @throws NotFoundServiceEx - */ + /** @throws NotFoundServiceEx */ protected void removeAllStoredData() throws NotFoundServiceEx { List list = storedDataService.getAll(); for (StoredData item : list) { @@ -179,10 +167,7 @@ protected void removeAllStoredData() throws NotFoundServiceEx { } } - /** - * @throws BadRequestServiceEx - * - */ + /** @throws BadRequestServiceEx */ private void removeAllResource() throws BadRequestServiceEx { List list = resourceService.getAll(null, null, buildFakeAdminUser()); for (ShortResource item : list) { @@ -196,8 +181,8 @@ private void removeAllResource() throws BadRequestServiceEx { } /** - * @param name * @param data + * @param resource * @return long * @throws Exception */ @@ -207,14 +192,13 @@ protected long createData(String data, Resource resource) throws Exception { /** * @param name - * @param creation * @param description - * @param storedData + * @param catName * @return long * @throws Exception */ - protected long createResource(String name, String description, String catName) throws Exception { - + protected long createResource(String name, String description, String catName) + throws Exception { Category category = new Category(); category.setName(catName); @@ -224,21 +208,22 @@ protected long createResource(String name, String description, String catName) t resource.setName(name); resource.setDescription(description); resource.setCategory(category); + resource.setCreator("USER1"); + resource.setEditor("USER2"); return resourceService.insert(resource); } - + /** * @param name - * @param creation * @param description + * @param catName * @param data - * * @return long * @throws Exception */ - protected long createResource(String name, String description, String catName, String data) throws Exception { - + protected long createResource(String name, String description, String catName, String data) + throws Exception { Category category = new Category(); category.setName(catName); @@ -251,20 +236,53 @@ protected long createResource(String name, String description, String catName, S StoredData storedData = new StoredData(); storedData.setData(data); resource.setData(storedData); + resource.setCreator("USER1"); + resource.setEditor("USER2"); return resourceService.insert(resource); } - + /** * @param name - * @param creation * @param description - * @param storedData + * @param catName + * @param rules * @return long * @throws Exception */ - protected long createResource(String name, String description, String catName, List rules) throws Exception { + protected long createResource( + String name, String description, String catName, List rules) + throws Exception { + Category category = new Category(); + category.setName(catName); + + categoryService.insert(category); + + Resource resource = new Resource(); + resource.setName(name); + resource.setDescription(description); + resource.setCategory(category); + resource.setSecurity(rules); + + return resourceService.insert(resource); + } + /** + * @param name + * @param description + * @param catName + * @param advertised + * @param rules + * @return long + * @throws Exception + */ + protected long createResource( + String name, + String description, + String catName, + boolean advertised, + List rules) + throws Exception { Category category = new Category(); category.setName(catName); @@ -274,18 +292,29 @@ protected long createResource(String name, String description, String catName, L resource.setName(name); resource.setDescription(description); resource.setCategory(category); + resource.setAdvertised(advertised); resource.setSecurity(rules); + resource.setCreator("USER1"); + resource.setEditor("USER2"); return resourceService.insert(resource); } + /** + * @param name + * @param description + * @param category + * @return + * @throws Exception + */ protected long createResource(String name, String description, Category category) throws Exception { - Resource resource = new Resource(); resource.setName(name); resource.setDescription(description); resource.setCategory(category); + resource.setCreator("USER1"); + resource.setEditor("USER2"); return resourceService.insert(resource); } @@ -317,19 +346,25 @@ protected long createUser(String name, Role role, String password) throws Except return userService.insert(user); } - + protected long createUserGroup(String name, long[] usersId) throws Exception { - UserGroup group = new UserGroup(); - group.setGroupName(name); - group.setDescription(""); - long groupId = userGroupService.insert(group); - for (long userId : usersId) { - userGroupService.assignUserGroup(userId, groupId); - } - return groupId; + UserGroup group = new UserGroup(); + group.setGroupName(name); + group.setDescription(""); + long groupId = userGroupService.insert(group); + for (long userId : usersId) { + userGroupService.assignUserGroup(userId, groupId); + } + return groupId; + } + + protected void createSpecialUserGroups() { + userGroupService.insertSpecialUsersGroups(); } - - protected long createUser(String name, Role role, String password, List attributes) throws Exception { + + protected long createUser( + String name, Role role, String password, List attributes) + throws Exception { User user = new User(); user.setName(name); user.setRole(role); @@ -337,8 +372,9 @@ protected long createUser(String name, Role role, String password, List groups = userGroupService.getAll(null, null); assertEquals("Saved 4 groups but retrieved less or more groups...", 4, groups.size()); - + userGroupService.delete(ug4.getId()); groups = userGroupService.getAll(null, null); assertEquals("Removed 1 group of 4 but retrieved less or more groups...", 3, groups.size()); } - + @Test - public void testAssignGroupToUser() throws BadRequestServiceEx, NotFoundServiceEx{ + public void testAssignGroupToUser() throws BadRequestServiceEx, NotFoundServiceEx { UserGroup ug1 = new UserGroup(); ug1.setGroupName("ug1"); long gid = userGroupService.insert(ug1); - + User u = new User(); u.setName("u1"); u.setPassword("password"); u.setRole(Role.USER); long uid = userService.insert(u); - + userGroupService.assignUserGroup(uid, gid); - + User uu = userService.get(uid); Set groups = uu.getGroups(); assertEquals("GroupSize must be 1!", 1, groups.size()); } - + /** - * Test the case of updating permissions on rules based on resource/group when the group isn't assigned yet to the resource - * Test the case of updating permissions on rules based on resource/group when the group is already assigned to the resource - * + * Test the case of updating permissions on rules based on resource/group when the group isn't + * assigned yet to the resource Test the case of updating permissions on rules based on + * resource/group when the group is already assigned to the resource + * * @throws BadRequestServiceEx * @throws NotFoundServiceEx - * @throws DuplicatedResourceNameServiceEx + * @throws DuplicatedResourceNameServiceEx */ @Test - public void testChangeGroupPermissionsOnResources() throws BadRequestServiceEx, NotFoundServiceEx, DuplicatedResourceNameServiceEx{ + public void testChangeGroupPermissionsOnResources() + throws BadRequestServiceEx, NotFoundServiceEx, DuplicatedResourceNameServiceEx { UserGroup ug1 = new UserGroup(); ug1.setGroupName("ug1"); long gid = userGroupService.insert(ug1); - + User u = new User(); u.setName("u1"); u.setPassword("password"); @@ -111,7 +111,7 @@ public void testChangeGroupPermissionsOnResources() throws BadRequestServiceEx, group.add(ug1); u.setGroups(group); long uid = userService.insert(u); - + Resource r = new Resource(); List attributeList = new ArrayList(); Attribute a1 = new Attribute(); @@ -128,30 +128,166 @@ public void testChangeGroupPermissionsOnResources() throws BadRequestServiceEx, categoryService.insert(cat); long id = resourceService.insert(r); r = resourceService.get(id); - + List idList = new ArrayList(); idList.add(id); List resourcelist = resourceDAO.findResources(idList); List listSecurity = resourcelist.get(0).getSecurity(); - assertEquals(0, listSecurity.size()); //shouldn't be any rule... - + assertEquals(0, listSecurity.size()); // shouldn't be any rule... + List listR = new ArrayList(); listR.add(r.getId()); - - List listsr = userGroupService.updateSecurityRules(ug1.getId(), listR, true, true); + + List listsr = + userGroupService.updateSecurityRules(ug1.getId(), listR, true, true); assertEquals(1, listsr.size()); assertTrue("Expected TRUE", listsr.get(0).isCanDelete()); assertTrue("Expected TRUE", listsr.get(0).isCanEdit()); - + idList = new ArrayList(); idList.add(id); resourcelist = resourceDAO.findResources(idList); listSecurity = resourcelist.get(0).getSecurity(); assertEquals(1, listSecurity.size()); // now the rules should be 1: one for the group added - + listsr = userGroupService.updateSecurityRules(ug1.getId(), listR, false, false); assertEquals(1, listsr.size()); assertTrue("Expected FALSE", !listsr.get(0).isCanDelete()); assertTrue("Expected FALSE", !listsr.get(0).isCanEdit()); } + + /** + * Test the insertion of a UserGroup with UserGroupAttributes. + * + * @throws BadRequestServiceEx + */ + @Test + public void testInsertGroupWithAttributes() throws BadRequestServiceEx { + UserGroup group = new UserGroup(); + group.setGroupName("GroupWithAttrs"); + UserGroupAttribute attribute = new UserGroupAttribute(); + attribute.setName("attr1"); + attribute.setValue("value,value2,value3"); + + UserGroupAttribute attribute2 = new UserGroupAttribute(); + attribute2.setName("attr2"); + attribute2.setValue("value4,value5,value6"); + + group.setAttributes(Arrays.asList(attribute, attribute2)); + + long id = userGroupService.insert(group); + + UserGroup ug = userGroupService.get(id); + List attributes = ug.getAttributes(); + assertEquals(2, attributes.size()); + assertEquals("attr1", attributes.get(0).getName()); + assertEquals("attr2", attributes.get(1).getName()); + } + + /** + * Test the updating of UserGroupAttributes. + * + * @throws BadRequestServiceEx + * @throws NotFoundServiceEx + */ + @Test + public void testUpdateGroup() throws BadRequestServiceEx, NotFoundServiceEx { + UserGroup group = new UserGroup(); + group.setGroupName("GroupWithAttrs2"); + UserGroupAttribute attribute = new UserGroupAttribute(); + attribute.setName("attr1"); + attribute.setValue("value,value2,value3"); + + UserGroupAttribute attribute2 = new UserGroupAttribute(); + attribute2.setName("attr2"); + attribute2.setValue("value4,value5,value6"); + + group.setAttributes(Arrays.asList(attribute, attribute2)); + + long id = userGroupService.insert(group); + + UserGroup toUpdate = userGroupService.get(id); + toUpdate.setDescription("Updated Description"); + + long idUpdated = userGroupService.update(toUpdate); + + UserGroup updated = userGroupService.get(idUpdated); + + assertEquals(id, idUpdated); + assertEquals("Updated Description", updated.getDescription()); + } + + @Test + public void testUpdateUserGroupAttributes() throws BadRequestServiceEx, NotFoundServiceEx { + UserGroup group = new UserGroup(); + group.setGroupName("GroupWithAttrs2"); + UserGroupAttribute attribute = new UserGroupAttribute(); + attribute.setName("attr1"); + attribute.setValue("value,value2,value3"); + + UserGroupAttribute attribute2 = new UserGroupAttribute(); + attribute2.setName("attr2"); + attribute2.setValue("value4,value5,value6"); + + group.setAttributes(Arrays.asList(attribute, attribute2)); + + long id = userGroupService.insert(group); + + UserGroupAttribute attributeToUpdate1 = new UserGroupAttribute(); + attributeToUpdate1.setName(attribute.getName()); + attributeToUpdate1.setValue(attribute.getValue()); + UserGroupAttribute attributeToUpdate2 = new UserGroupAttribute(); + attributeToUpdate2.setName("updated"); + attributeToUpdate2.setValue("updatedValue"); + List attributes = Arrays.asList(attributeToUpdate1, attributeToUpdate2); + + userGroupService.updateAttributes(id, attributes); + UserGroup groupUpdated = userGroupService.get(id); + List updatedList = groupUpdated.getAttributes(); + assertTrue(updatedList.stream().anyMatch(g -> g.getName().equals(attribute.getName()))); + assertTrue( + updatedList.stream() + .anyMatch(g -> g.getName().equals(attributeToUpdate2.getName()))); + assertFalse(updatedList.stream().anyMatch(g -> g.getName().equals(attribute2.getName()))); + } + + @Test + public void testgetByAttributes() throws BadRequestServiceEx { + UserGroup group = new UserGroup(); + group.setGroupName("GroupWithAttrs"); + UserGroupAttribute attribute = new UserGroupAttribute(); + attribute.setName("organization"); + attribute.setValue("value"); + + UserGroupAttribute attribute2 = new UserGroupAttribute(); + attribute2.setName("attr2"); + attribute2.setValue("value4,value5,value6"); + + group.setAttributes(Arrays.asList(attribute, attribute2)); + + long id = userGroupService.insert(group); + + UserGroup group2 = new UserGroup(); + group2.setGroupName("GroupWithAttrs2"); + UserGroupAttribute attribute21 = new UserGroupAttribute(); + attribute21.setName("Organization"); + attribute21.setValue("value"); + + UserGroupAttribute attribute22 = new UserGroupAttribute(); + attribute22.setName("attr2"); + attribute22.setValue("value4,value5,value6"); + + group2.setAttributes(Arrays.asList(attribute21, attribute22)); + + userGroupService.insert(group2); + UserGroupAttribute groupAttribute = new UserGroupAttribute(); + groupAttribute.setName("organization"); + groupAttribute.setValue("value"); + Collection groups = + userGroupService.findByAttribute("organization", Arrays.asList("value"), true); + assertEquals(2, groups.size()); + + groups = userGroupService.findByAttribute("organization", Arrays.asList("value"), false); + assertEquals(1, groups.size()); + } } diff --git a/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/UserServiceImplTest.java b/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/UserServiceImplTest.java index 12c97683..47e48253 100644 --- a/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/UserServiceImplTest.java +++ b/src/core/services-impl/src/test/java/it/geosolutions/geostore/services/UserServiceImplTest.java @@ -19,37 +19,32 @@ */ package it.geosolutions.geostore.services; +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.UserAttribute; +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.enums.Role; +import it.geosolutions.geostore.core.security.password.PwEncoder; import java.util.Arrays; import java.util.Collection; import java.util.UUID; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; -import it.geosolutions.geostore.core.model.User; -import it.geosolutions.geostore.core.model.UserAttribute; -import it.geosolutions.geostore.core.model.UserGroup; -import it.geosolutions.geostore.core.model.enums.Role; -import it.geosolutions.geostore.core.security.password.PwEncoder; /** * Class UserServiceImplTest. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class UserServiceImplTest extends ServiceTestBase { @BeforeClass - public static void setUpClass() throws Exception { - } + public static void setUpClass() throws Exception {} @AfterClass - public static void tearDownClass() throws Exception { - } + public static void tearDownClass() throws Exception {} - public UserServiceImplTest() { - - } + public UserServiceImplTest() {} @Test public void testInsertDeleteUser() throws Exception { @@ -79,7 +74,7 @@ public void testUpdateLoadData() throws Exception { User loaded = userService.get(userId); assertNotNull(loaded); assertEquals(NAME, loaded.getName()); - assertTrue( PwEncoder.isPasswordValid(loaded.getPassword(),"testPW")); + assertTrue(PwEncoder.isPasswordValid(loaded.getPassword(), "testPW")); assertEquals(Role.USER, loaded.getRole()); loaded.setNewPassword("testPW2"); @@ -92,7 +87,7 @@ public void testUpdateLoadData() throws Exception { { User loaded = userService.get(userId); assertNotNull(loaded); - assertTrue(PwEncoder.isPasswordValid(loaded.getPassword(),"testPW2")); + assertTrue(PwEncoder.isPasswordValid(loaded.getPassword(), "testPW2")); } // @@ -103,9 +98,8 @@ public void testUpdateLoadData() throws Exception { userService.delete(userId); assertEquals(0, userService.getCount(null)); } - } - + @Test public void testGetByAttribute() throws Exception { UserAttribute attribute = new UserAttribute(); @@ -113,7 +107,7 @@ public void testGetByAttribute() throws Exception { attribute.setName("UUID"); attribute.setValue(token); createUser("test", Role.USER, "tesPW", Arrays.asList(attribute)); - + assertEquals(1, userService.getByAttribute(attribute).size()); } @@ -126,7 +120,7 @@ public void testGetByGroupId() throws Exception { Collection users = userService.getByGroup(group); assertEquals(1, users.size()); } - + @Test public void testGetByGroupName() throws Exception { long groupId = createGroup("testgroup"); @@ -136,7 +130,7 @@ public void testGetByGroupName() throws Exception { Collection users = userService.getByGroup(group); assertEquals(1, users.size()); } - + @Test public void testUpdateByUserId() throws Exception { final String NAME = "name1"; @@ -144,21 +138,73 @@ public void testUpdateByUserId() throws Exception { long userId = createUser(NAME, Role.USER, "testPW"); assertEquals(1, userService.getCount(null)); - + User loaded = userService.get(userId); assertNotNull(loaded); assertEquals(NAME, loaded.getName()); - assertTrue( PwEncoder.isPasswordValid(loaded.getPassword(),"testPW")); + assertTrue(PwEncoder.isPasswordValid(loaded.getPassword(), "testPW")); assertEquals(Role.USER, loaded.getRole()); loaded.setNewPassword("testPW2"); userService.update(loaded); - + loaded = userService.get(userId); assertNotNull(loaded); - assertTrue(PwEncoder.isPasswordValid(loaded.getPassword(),"testPW2")); + assertTrue(PwEncoder.isPasswordValid(loaded.getPassword(), "testPW2")); } - + + @Test + public void testUpdateWithGroups() throws Exception { + final String NAME = "name1"; + + long userId = createUser(NAME, Role.USER, "testPW"); + assertEquals(1, userService.getCount(null)); + + createUserGroup("testgroup", new long[] {userId}); + + User loaded = userService.get(userId); + assertNotNull(loaded); + assertEquals(NAME, loaded.getName()); + assertTrue(PwEncoder.isPasswordValid(loaded.getPassword(), "testPW")); + assertEquals(Role.USER, loaded.getRole()); + assertEquals(1, loaded.getGroups().size()); + + loaded.setNewPassword("testPW2"); + userService.update(loaded); + + loaded = userService.get(userId); + assertNotNull(loaded); + assertTrue(PwEncoder.isPasswordValid(loaded.getPassword(), "testPW2")); + assertEquals(1, loaded.getGroups().size()); + } + + @Test + public void testUpdateWithGroupsAndEveryone() throws Exception { + final String NAME = "name1"; + + createSpecialUserGroups(); + + long userId = createUser(NAME, Role.USER, "testPW"); + assertEquals(1, userService.getCount(null)); + + createUserGroup("testgroup", new long[] {userId}); + + User loaded = userService.get(userId); + assertNotNull(loaded); + assertEquals(NAME, loaded.getName()); + assertTrue(PwEncoder.isPasswordValid(loaded.getPassword(), "testPW")); + assertEquals(Role.USER, loaded.getRole()); + assertEquals(2, loaded.getGroups().size()); + + loaded.setNewPassword("testPW2"); + userService.update(loaded); + + loaded = userService.get(userId); + assertNotNull(loaded); + assertTrue(PwEncoder.isPasswordValid(loaded.getPassword(), "testPW2")); + assertEquals(2, loaded.getGroups().size()); + } + @Test public void testUpdateByUserName() throws Exception { final String NAME = "name1"; @@ -166,20 +212,19 @@ public void testUpdateByUserName() throws Exception { long userId = createUser(NAME, Role.USER, "testPW"); assertEquals(1, userService.getCount(null)); - + User loaded = userService.get(userId); assertNotNull(loaded); assertEquals(NAME, loaded.getName()); - assertTrue( PwEncoder.isPasswordValid(loaded.getPassword(),"testPW")); + assertTrue(PwEncoder.isPasswordValid(loaded.getPassword(), "testPW")); assertEquals(Role.USER, loaded.getRole()); loaded.setNewPassword("testPW2"); loaded.setId(-1L); userService.update(loaded); - + loaded = userService.get(userId); assertNotNull(loaded); - assertTrue(PwEncoder.isPasswordValid(loaded.getPassword(),"testPW2")); + assertTrue(PwEncoder.isPasswordValid(loaded.getPassword(), "testPW2")); } - } diff --git a/src/core/services-impl/src/test/java/it/geosolutions/geostore/util/CategorizedCircularBufferTest.java b/src/core/services-impl/src/test/java/it/geosolutions/geostore/util/CategorizedCircularBufferTest.java index dfd08782..3d9f7187 100644 --- a/src/core/services-impl/src/test/java/it/geosolutions/geostore/util/CategorizedCircularBufferTest.java +++ b/src/core/services-impl/src/test/java/it/geosolutions/geostore/util/CategorizedCircularBufferTest.java @@ -20,22 +20,16 @@ package it.geosolutions.geostore.util; import java.util.List; - -import it.geosolutions.geostore.util.CategorizedCircularBuffer; - import junit.framework.TestCase; - import org.junit.Test; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class CategorizedCircularBufferTest extends TestCase { @Test public void testAdd() { - CategorizedCircularBuffer ccb = new CategorizedCircularBuffer(4); + CategorizedCircularBuffer ccb = + new CategorizedCircularBuffer(4); ccb.add(1L, "e0"); assertEquals(1, ccb.size()); @@ -65,7 +59,8 @@ public void testAdd() { @Test public void testSubList() { - CategorizedCircularBuffer ccb = new CategorizedCircularBuffer(4); + CategorizedCircularBuffer ccb = + new CategorizedCircularBuffer(4); ccb.add(0L, "e0"); // 3 ccb.add(1L, "e2"); // 2 @@ -104,7 +99,8 @@ public void testSubList() { @Test public void testSubListByKey() { - CategorizedCircularBuffer ccb = new CategorizedCircularBuffer(6); + CategorizedCircularBuffer ccb = + new CategorizedCircularBuffer(6); ccb.add(1L, "e0"); // 5 ibk2 ccb.add(2L, "e1"); // 4 diff --git a/src/core/services-impl/src/test/java/it/geosolutions/test/AbstractSpringContextTest.java b/src/core/services-impl/src/test/java/it/geosolutions/test/AbstractSpringContextTest.java index a4adb20c..c4c57bcd 100644 --- a/src/core/services-impl/src/test/java/it/geosolutions/test/AbstractSpringContextTest.java +++ b/src/core/services-impl/src/test/java/it/geosolutions/test/AbstractSpringContextTest.java @@ -21,24 +21,22 @@ package it.geosolutions.test; import junit.framework.TestCase; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Base class for tests with a spring context loaded from the classpath. - * + * * @author Nate Sammons */ public abstract class AbstractSpringContextTest extends TestCase { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); protected ClassPathXmlApplicationContext context = null; - /** - * Get the filename to use for this context. - */ + /** Get the filename to use for this context. */ protected abstract String[] getContextFilenames(); @Override diff --git a/src/core/services-impl/src/test/resources/log4j.properties b/src/core/services-impl/src/test/resources/log4j.properties deleted file mode 100644 index ffc17041..00000000 --- a/src/core/services-impl/src/test/resources/log4j.properties +++ /dev/null @@ -1,8 +0,0 @@ -log4j.rootLogger=INFO, consoleAppender - -log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender -log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout -log4j.appender.consoleAppender.layout.ConversionPattern=%p %d{yyyy-MM-dd HH:mm:ss.SSS} %C{1}.%M() - %m %n - -log4j.logger.org.hibernate=WARN -log4j.logger.com.trg=INFO diff --git a/src/core/services-impl/src/test/resources/log4j2.properties b/src/core/services-impl/src/test/resources/log4j2.properties new file mode 100644 index 00000000..360f6464 --- /dev/null +++ b/src/core/services-impl/src/test/resources/log4j2.properties @@ -0,0 +1,15 @@ +rootLogger.level = INFO +appenders= console + + +appender.console.type = Console +appender.console.name = LogToConsole +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = %p %d{yyyy-MM-dd HH:mm:ss.SSS} %c::%M:%L - %m%n +rootLogger.appenderRef.stdout.ref = LogToConsole +rootLogger.appenderRef.console.ref = LogToConsole + +logger.hibernate1.name=org.hibernate +logger.hibernate1.level=WARN +logger.trg1.name=com.trg +logger.trg1.level=INFO diff --git a/src/modules/pom.xml b/src/modules/pom.xml index 589ee1d9..d61734b4 100644 --- a/src/modules/pom.xml +++ b/src/modules/pom.xml @@ -18,14 +18,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - + 4.0.0 it.geosolutions.geostore geostore-root - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-modules diff --git a/src/modules/rest/api/pom.xml b/src/modules/rest/api/pom.xml index 86aed2fc..50214253 100644 --- a/src/modules/rest/api/pom.xml +++ b/src/modules/rest/api/pom.xml @@ -25,7 +25,7 @@ it.geosolutions.geostore geostore-rest-root - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-rest-api @@ -60,6 +60,18 @@ org.apache.cxf cxf-rt-frontend-jaxrs + + + org.apache.cxf + cxf-rt-rs-extension-providers + + + org.apache.cxf + cxf-rt-rs-json-basic + + + org.codehaus.jettison + jettison @@ -109,10 +112,14 @@ - jsr311-api - javax.ws.rs - jar - 1.1.1 + javax.xml.ws + jaxws-api + + + + javax.servlet + javax.servlet-api + provided @@ -124,17 +131,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - - 1.7 - 1.7 - - - - - true @@ -155,5 +151,4 @@ - diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/providers/StringTextProvider.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/providers/StringTextProvider.java index d3afe509..f2a1e2d1 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/providers/StringTextProvider.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/providers/StringTextProvider.java @@ -1,20 +1,16 @@ /** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + *

    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. + *

    Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. */ package it.geosolutions.geostore.services.providers; @@ -24,49 +20,64 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; - import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.MessageBodyWriter; - import org.apache.cxf.helpers.IOUtils; import org.apache.cxf.jaxrs.provider.AbstractConfigurableProvider; import org.apache.cxf.jaxrs.utils.HttpUtils; /** - * This is a porting of org.apache.cxf.jaxrs.provider.StringTextProvider, created in a recent version of cxf. - * It's used in geostore to read the raw request body in in rest service's StoredDataService to retrieve the correct encoding. - * - * @author Lorenzo Natali, GeoSolutions S.a.s. + * This is a porting of org.apache.cxf.jaxrs.provider.StringTextProvider, created in a recent + * version of cxf. It's used in geostore to read the raw request body in in rest service's + * StoredDataService to retrieve the correct encoding. * + * @author Lorenzo Natali, GeoSolutions S.a.s. */ public class StringTextProvider extends AbstractConfigurableProvider - implements MessageBodyReader, MessageBodyWriter { + implements MessageBodyReader, MessageBodyWriter { private int bufferSize = 4096; - public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mt) { + + public boolean isReadable( + Class type, Type genericType, Annotation[] annotations, MediaType mt) { return String.class == type; } - public String readFrom(Class type, Type genType, Annotation[] anns, MediaType mt, - MultivaluedMap headers, InputStream is) throws IOException { - - return IOUtils.toString(is, HttpUtils.getSetEncoding(mt, null, StandardCharsets.UTF_8.name())); + public String readFrom( + Class type, + Type genType, + Annotation[] anns, + MediaType mt, + MultivaluedMap headers, + InputStream is) + throws IOException { + + return IOUtils.toString( + is, HttpUtils.getSetEncoding(mt, null, StandardCharsets.UTF_8.name())); } - public long getSize(String t, Class type, Type genericType, Annotation[] annotations, MediaType mt) { + public long getSize( + String t, Class type, Type genericType, Annotation[] annotations, MediaType mt) { return -1; } - public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mt) { + public boolean isWriteable( + Class type, Type genericType, Annotation[] annotations, MediaType mt) { return String.class == type; } - public void writeTo(String obj, Class type, Type genType, Annotation[] anns, - MediaType mt, MultivaluedMap headers, - OutputStream os) throws IOException { + public void writeTo( + String obj, + Class type, + Type genType, + Annotation[] anns, + MediaType mt, + MultivaluedMap headers, + OutputStream os) + throws IOException { String encoding = HttpUtils.getSetEncoding(mt, headers, StandardCharsets.UTF_8.name()); - //REVISIT try to avoid instantiating the whole byte array + // REVISIT try to avoid instantiating the whole byte array byte[] bytes = obj.getBytes(encoding); if (bytes.length > bufferSize) { int pos = 0; @@ -82,7 +93,8 @@ public void writeTo(String obj, Class type, Type genType, Annotation[] anns, os.write(bytes); } } + public void setBufferSize(int bufferSize) { this.bufferSize = bufferSize; } -} \ No newline at end of file +} diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/IdPLoginRest.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/IdPLoginRest.java new file mode 100644 index 00000000..602671c8 --- /dev/null +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/IdPLoginRest.java @@ -0,0 +1,69 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest; + +import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; +import it.geosolutions.geostore.services.rest.model.SessionToken; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Response; +import org.springframework.security.access.annotation.Secured; + +/** Base interface providing entry points to login using on an external Identity provider. */ +public interface IdPLoginRest { + + @GET + @Path("/{provider}/login") + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + void login(@PathParam("provider") String provider) throws NotFoundWebEx; + + @GET + @Path("/{provider}/callback") + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + Response callback(@PathParam("provider") String provider) throws NotFoundWebEx; + + @GET + @Path("/{provider}/tokens") + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + SessionToken getTokensByTokenIdentifier( + @PathParam("provider") String provider, + @QueryParam("identifier") String tokenIdentifier) + throws NotFoundWebEx; + + /** + * Registers an IdP loginService with a key equal to the provider name value. + * + * @param providerName the provider name to which is associated the {@link IdPLoginService} + * instance. + * @param service the {@link IdPLoginService} instance to resgister and associate to the + * provider name value. + */ + void registerService(String providerName, IdPLoginService service); +} diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/IdPLoginService.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/IdPLoginService.java new file mode 100644 index 00000000..cf6cf766 --- /dev/null +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/IdPLoginService.java @@ -0,0 +1,43 @@ +package it.geosolutions.geostore.services.rest; + +import it.geosolutions.geostore.services.rest.model.SessionToken; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.Response; + +/** + * Extension point to customize the login and redirect after login performed from the {@link + * IdPLoginRest}; + */ +public interface IdPLoginService { + + /** + * Perform the login to an external IdP. + * + * @param request the request. + * @param response the response. + * @param provider the provider name. + */ + void doLogin(HttpServletRequest request, HttpServletResponse response, String provider); + + /** + * Perform a redirect to an application url. Useful if the external IdP redirect to an app url + * when login is successful. + * + * @param request the request. + * @param response the response. + * @param provider the provider name. + * @return a {@link Response instance}. + */ + Response doInternalRedirect( + HttpServletRequest request, HttpServletResponse response, String provider); + + /** + * Return the SessionToken if any exists for the provided key. + * + * @param provider the auth provider + * @param tokenIdentifier the token identifier to use to retrieve stored tokens. + * @return the {@link SessionToken} if found, null otherwise. + */ + SessionToken getTokenByIdentifier(String provider, String tokenIdentifier); +} diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTBackupService.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTBackupService.java index 340427bc..af3d626a 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTBackupService.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTBackupService.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -31,7 +31,6 @@ import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; import it.geosolutions.geostore.services.rest.model.RESTQuickBackup; - import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; @@ -40,17 +39,16 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; - import org.apache.cxf.jaxrs.ext.multipart.Multipart; import org.springframework.security.access.annotation.Secured; /** * Backup/restore REST service - * + * * @author ETj (etj at geo-solutions.it) */ // @RolesAllowed({ "ADMIN" }) -@Secured({ "ROLE_ADMIN" }) +@Secured({"ROLE_ADMIN"}) public interface RESTBackupService { /** @@ -60,41 +58,42 @@ public interface RESTBackupService { */ @GET @Path("/full") - @Produces({ MediaType.TEXT_PLAIN }) + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN" }) - @Secured({ "ROLE_ADMIN" }) + @Secured({"ROLE_ADMIN"}) String backup(@Context SecurityContext sc); @PUT @Path("/full/{token}") - @Produces({ MediaType.TEXT_PLAIN }) + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN" }) - @Secured({ "ROLE_ADMIN" }) + @Secured({"ROLE_ADMIN"}) String restore(@Context SecurityContext sc, @PathParam("token") String token); /** - * Quick backup is a backup that is built in memory. It can only be issued when the data base in the store is not very big. Furthermore, most - * internal params are not backup/restored (creation time, ...) Most important, neither users or authentication info are backupped. - * + * Quick backup is a backup that is built in memory. It can only be issued when the data base in + * the store is not very big. Furthermore, most internal params are not backup/restored + * (creation time, ...) Most important, neither users or authentication info are + * backupped. */ @GET @Path("/quick") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) // @RolesAllowed({ "ADMIN" }) - @Secured({ "ROLE_ADMIN" }) + @Secured({"ROLE_ADMIN"}) RESTQuickBackup quickBackup(@Context SecurityContext sc) throws BadRequestServiceEx; /** - * Quick backup is a backup that is built in memory. It can only be issued when the data base in the store is not very big. Furthermore, most - * internal params are not backup/restored (creation time, ...) Most important, neither users or authentication info are backupped. - * + * Quick backup is a backup that is built in memory. It can only be issued when the data base in + * the store is not very big. Furthermore, most internal params are not backup/restored + * (creation time, ...) Most important, neither users or authentication info are + * backupped. */ @PUT @Path("/quick") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN" }) - @Secured({ "ROLE_ADMIN" }) + @Secured({"ROLE_ADMIN"}) String quickRestore(@Context SecurityContext sc, @Multipart("backup") RESTQuickBackup backup) throws BadRequestServiceEx; - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTCategoryService.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTCategoryService.java index a832739a..d5174038 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTCategoryService.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTCategoryService.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -33,7 +33,6 @@ import it.geosolutions.geostore.services.rest.exception.BadRequestWebEx; import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; import it.geosolutions.geostore.services.rest.model.CategoryList; - import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; @@ -46,13 +45,12 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; - import org.apache.cxf.jaxrs.ext.multipart.Multipart; import org.springframework.security.access.annotation.Secured; /** * Interface RESTCategoryService. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ @@ -66,10 +64,10 @@ public interface RESTCategoryService { */ @POST @Path("/") - @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN" }) - @Secured({ "ROLE_ADMIN" }) + @Secured({"ROLE_ADMIN"}) long insert(@Context SecurityContext sc, @Multipart("category") Category category) throws BadRequestServiceEx, NotFoundServiceEx; @@ -81,11 +79,15 @@ long insert(@Context SecurityContext sc, @Multipart("category") Category categor */ @PUT @Path("/category/{id}") - @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) + @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN" }) - @Secured({ "ROLE_ADMIN" }) - long update(@Context SecurityContext sc, @PathParam("id") long id, - @Multipart("category") Category category) throws NotFoundWebEx; + @Secured({"ROLE_ADMIN"}) + long update( + @Context SecurityContext sc, + @PathParam("id") long id, + @Multipart("category") Category category) + throws NotFoundWebEx; /** * @param id @@ -94,7 +96,7 @@ long update(@Context SecurityContext sc, @PathParam("id") long id, @DELETE @Path("/category/{id}") // @RolesAllowed({ "ADMIN" }) - @Secured({ "ROLE_ADMIN" }) + @Secured({"ROLE_ADMIN"}) void delete(@Context SecurityContext sc, @PathParam("id") long id) throws NotFoundWebEx; /** @@ -104,9 +106,9 @@ long update(@Context SecurityContext sc, @PathParam("id") long id, */ @GET @Path("/category/{id}") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) Category get(@Context SecurityContext sc, @PathParam("id") long id) throws NotFoundWebEx; /** @@ -117,11 +119,14 @@ long update(@Context SecurityContext sc, @PathParam("id") long id, */ @GET @Path("/") - @Produces({ MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) - CategoryList getAll(@Context SecurityContext sc, @QueryParam("page") Integer page, - @QueryParam("entries") Integer entries) throws BadRequestWebEx; + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + CategoryList getAll( + @Context SecurityContext sc, + @QueryParam("page") Integer page, + @QueryParam("entries") Integer entries) + throws BadRequestWebEx; /** * @param nameLike @@ -129,8 +134,8 @@ CategoryList getAll(@Context SecurityContext sc, @QueryParam("page") Integer pag */ @GET @Path("/count/{nameLike}") + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) long getCount(@Context SecurityContext sc, @PathParam("nameLike") String nameLike); - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTMiscService.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTMiscService.java index 39afe6d9..beacf04b 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTMiscService.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTMiscService.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -34,7 +34,6 @@ import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; import it.geosolutions.geostore.services.rest.model.ResourceList; import it.geosolutions.geostore.services.rest.model.ShortResourceList; - import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.Path; @@ -44,61 +43,62 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; - import org.springframework.security.access.annotation.Secured; /** * Interface RESTMiscService. Experimental operations go here. - * + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ - public interface RESTMiscService { @GET @Path("/category/name/{cname}/resource/name/{rname}/data") + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) - String getData(@Context SecurityContext sc, @PathParam("cname") String cname, - @PathParam("rname") String rname) throws NotFoundWebEx, ConflictWebEx, BadRequestWebEx, - InternalErrorWebEx; + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + String getData( + @Context SecurityContext sc, + @PathParam("cname") String cname, + @PathParam("rname") String rname) + throws NotFoundWebEx, ConflictWebEx, BadRequestWebEx, InternalErrorWebEx; @GET @Path("/category/name/{cname}/resource/name/{rname}") - @Produces({ MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) - Resource getResource(@Context SecurityContext sc, @PathParam("cname") String cname, - @PathParam("rname") String rname) throws NotFoundWebEx, ConflictWebEx, BadRequestWebEx, - InternalErrorWebEx; + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + Resource getResource( + @Context SecurityContext sc, + @PathParam("cname") String cname, + @PathParam("rname") String rname) + throws NotFoundWebEx, ConflictWebEx, BadRequestWebEx, InternalErrorWebEx; @GET @Path("/category/name/{cname}/resources/") - @Produces({ MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) - ShortResourceList getResourcesByCategory(@Context SecurityContext sc, - @PathParam("cname") String cname) throws NotFoundWebEx, ConflictWebEx, BadRequestWebEx, - InternalErrorWebEx; - + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + ShortResourceList getResourcesByCategory( + @Context SecurityContext sc, @PathParam("cname") String cname) + throws NotFoundWebEx, ConflictWebEx, BadRequestWebEx, InternalErrorWebEx; + @GET @Path("/category/name/{cname}/fullresources/") - @Produces({ MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) - ResourceList getResourcesByCategory(@Context SecurityContext sc, + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + ResourceList getResourcesByCategory( + @Context SecurityContext sc, @PathParam("cname") String cname, @QueryParam("includeAttributes") @DefaultValue("false") boolean includeAttributes, @QueryParam("includeAttributes") @DefaultValue("false") boolean includeData) throws NotFoundWebEx, ConflictWebEx, BadRequestWebEx, InternalErrorWebEx; - @GET @Path("/reload/{service}") - @Secured({ "ROLE_ADMIN" }) - void reload(@Context SecurityContext sc, @PathParam("service") String service) throws BadRequestWebEx; - - + @Secured({"ROLE_ADMIN"}) + void reload(@Context SecurityContext sc, @PathParam("service") String service) + throws BadRequestWebEx; } - diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTResourceService.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTResourceService.java index 96ec2439..669d6679 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTResourceService.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTResourceService.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -28,6 +28,19 @@ package it.geosolutions.geostore.services.rest; +import it.geosolutions.geostore.core.model.Resource; +import it.geosolutions.geostore.core.model.enums.DataType; +import it.geosolutions.geostore.services.dto.search.SearchFilter; +import it.geosolutions.geostore.services.exception.InternalErrorServiceEx; +import it.geosolutions.geostore.services.rest.exception.BadRequestWebEx; +import it.geosolutions.geostore.services.rest.exception.InternalErrorWebEx; +import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; +import it.geosolutions.geostore.services.rest.model.RESTAttribute; +import it.geosolutions.geostore.services.rest.model.RESTResource; +import it.geosolutions.geostore.services.rest.model.ResourceList; +import it.geosolutions.geostore.services.rest.model.SecurityRuleList; +import it.geosolutions.geostore.services.rest.model.ShortAttributeList; +import it.geosolutions.geostore.services.rest.model.ShortResourceList; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; @@ -41,32 +54,17 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; - import org.apache.cxf.jaxrs.ext.multipart.Multipart; import org.springframework.security.access.annotation.Secured; -import it.geosolutions.geostore.core.model.Resource; -import it.geosolutions.geostore.core.model.enums.DataType; -import it.geosolutions.geostore.services.dto.search.SearchFilter; -import it.geosolutions.geostore.services.exception.InternalErrorServiceEx; -import it.geosolutions.geostore.services.rest.exception.BadRequestWebEx; -import it.geosolutions.geostore.services.rest.exception.InternalErrorWebEx; -import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; -import it.geosolutions.geostore.services.rest.model.RESTAttribute; -import it.geosolutions.geostore.services.rest.model.RESTResource; -import it.geosolutions.geostore.services.rest.model.ResourceList; -import it.geosolutions.geostore.services.rest.model.SecurityRuleList; -import it.geosolutions.geostore.services.rest.model.ShortAttributeList; -import it.geosolutions.geostore.services.rest.model.ShortResourceList; - /** * Interface RESTResourceService. - * + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ // @RolesAllowed({ "ADMIN" }) -@Secured({ "ROLE_ADMIN" }) +@Secured({"ROLE_ADMIN"}) public interface RESTResourceService { /** @@ -76,11 +74,11 @@ public interface RESTResourceService { */ @POST @Path("/") - @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) + @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) // @Produces({MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) - @Produces({ MediaType.TEXT_PLAIN }) + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN", "USER" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN" }) + @Secured({"ROLE_USER", "ROLE_ADMIN"}) long insert(@Context SecurityContext sc, @Multipart("resource") RESTResource resource) throws InternalErrorWebEx; @@ -93,11 +91,15 @@ long insert(@Context SecurityContext sc, @Multipart("resource") RESTResource res */ @PUT @Path("/resource/{id}") - @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) + @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN", "USER" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN" }) - long update(@Context SecurityContext sc, @PathParam("id") long id, - @Multipart("resource") RESTResource resource) throws NotFoundWebEx, BadRequestWebEx; + @Secured({"ROLE_USER", "ROLE_ADMIN"}) + long update( + @Context SecurityContext sc, + @PathParam("id") long id, + @Multipart("resource") RESTResource resource) + throws NotFoundWebEx, BadRequestWebEx; /** * @param id @@ -107,7 +109,7 @@ long update(@Context SecurityContext sc, @PathParam("id") long id, @DELETE @Path("/resource/{id}") // @RolesAllowed({ "ADMIN", "USER" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN" }) + @Secured({"ROLE_USER", "ROLE_ADMIN"}) void delete(@Context SecurityContext sc, @PathParam("id") long id) throws NotFoundWebEx; /** @@ -117,7 +119,7 @@ long update(@Context SecurityContext sc, @PathParam("id") long id, @DELETE @Path("/") // @RolesAllowed({ "ADMIN" }) - @Secured({ "ROLE_ADMIN" }) + @Secured({"ROLE_ADMIN"}) void deleteResources(@Context SecurityContext sc, @Multipart("filter") SearchFilter filter) throws BadRequestWebEx, InternalErrorWebEx; @@ -128,13 +130,14 @@ void deleteResources(@Context SecurityContext sc, @Multipart("filter") SearchFil */ @GET @Path("/resource/{id}") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) - Resource get(@Context SecurityContext sc, @PathParam("id") long id, + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + Resource get( + @Context SecurityContext sc, + @PathParam("id") long id, @QueryParam("full") @DefaultValue("false") boolean full) - - throws NotFoundWebEx; + throws NotFoundWebEx; /** * @param page @@ -144,11 +147,14 @@ Resource get(@Context SecurityContext sc, @PathParam("id") long id, */ @GET @Path("/") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) - ShortResourceList getAll(@Context SecurityContext sc, @QueryParam("page") Integer page, - @QueryParam("entries") Integer entries) throws BadRequestWebEx; + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + ShortResourceList getAll( + @Context SecurityContext sc, + @QueryParam("page") Integer page, + @QueryParam("entries") Integer entries) + throws BadRequestWebEx; /** * @param nameLike @@ -159,11 +165,14 @@ ShortResourceList getAll(@Context SecurityContext sc, @QueryParam("page") Intege */ @GET @Path("/search/{nameLike}") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) - ShortResourceList getList(@Context SecurityContext sc, @PathParam("nameLike") String nameLike, - @QueryParam("page") Integer page, @QueryParam("entries") Integer entries) + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + ShortResourceList getList( + @Context SecurityContext sc, + @PathParam("nameLike") String nameLike, + @QueryParam("page") Integer page, + @QueryParam("entries") Integer entries) throws BadRequestWebEx; /** @@ -173,13 +182,14 @@ ShortResourceList getList(@Context SecurityContext sc, @PathParam("nameLike") St @POST @GET @Path("/search") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) - @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) @Deprecated - ShortResourceList getResources(@Context SecurityContext sc, - @Multipart("filter") SearchFilter filter) throws BadRequestWebEx, InternalErrorWebEx; + ShortResourceList getResources( + @Context SecurityContext sc, @Multipart("filter") SearchFilter filter) + throws BadRequestWebEx, InternalErrorWebEx; /** * @param sc @@ -195,15 +205,18 @@ ShortResourceList getResources(@Context SecurityContext sc, @POST @GET @Path("/search/list") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) - @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) - ResourceList getResourcesList(@Context SecurityContext sc, @QueryParam("page") Integer page, + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + ResourceList getResourcesList( + @Context SecurityContext sc, + @QueryParam("page") Integer page, @QueryParam("entries") Integer entries, @QueryParam("includeAttributes") @DefaultValue("false") boolean includeAttributes, @QueryParam("includeData") @DefaultValue("false") boolean includeData, - @Multipart("filter") SearchFilter filter) throws BadRequestWebEx, InternalErrorWebEx; + @Multipart("filter") SearchFilter filter) + throws BadRequestWebEx, InternalErrorWebEx; /** * @param nameLike @@ -211,8 +224,9 @@ ResourceList getResourcesList(@Context SecurityContext sc, @QueryParam("page") I */ @GET @Path("/count/{nameLike}") + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) long getCount(@Context SecurityContext sc, @PathParam("nameLike") String nameLike); /** @@ -222,9 +236,9 @@ ResourceList getResourcesList(@Context SecurityContext sc, @QueryParam("page") I */ @GET @Path("/resource/{id}/attributes") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) ShortAttributeList getAttributes(@Context SecurityContext sc, @PathParam("id") long id) throws NotFoundWebEx; @@ -236,11 +250,12 @@ ShortAttributeList getAttributes(@Context SecurityContext sc, @PathParam("id") l */ @GET @Path("/resource/{id}/attributes/{name}") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) - String getAttribute(@Context SecurityContext sc, @PathParam("id") long id, - @PathParam("name") String name) throws NotFoundWebEx; + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + String getAttribute( + @Context SecurityContext sc, @PathParam("id") long id, @PathParam("name") String name) + throws NotFoundWebEx; /** * Updates the attribute using the PUT request body (JSON). @@ -253,14 +268,11 @@ String getAttribute(@Context SecurityContext sc, @PathParam("id") long id, */ @PUT @Path("/resource/{id}/attributes/") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_PLAIN}) @Consumes(MediaType.APPLICATION_JSON) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) long updateAttribute( - @Context SecurityContext sc, - @PathParam("id") long id, - RESTAttribute content - ); + @Context SecurityContext sc, @PathParam("id") long id, RESTAttribute content); /** * @param id @@ -272,11 +284,14 @@ long updateAttribute( */ @PUT @Path("/resource/{id}/attributes/{name}/{value}") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN", "USER" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) - long updateAttribute(@Context SecurityContext sc, @PathParam("id") long id, - @PathParam("name") String name, @PathParam("value") String value); + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + long updateAttribute( + @Context SecurityContext sc, + @PathParam("id") long id, + @PathParam("name") String name, + @PathParam("value") String value); /** * @param id @@ -289,29 +304,32 @@ long updateAttribute(@Context SecurityContext sc, @PathParam("id") long id, */ @PUT @Path("/resource/{id}/attributes/{name}/{value}/{type}") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN", "USER" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) - long updateAttribute(@Context SecurityContext sc, @PathParam("id") long id, - @PathParam("name") String name, @PathParam("value") String value,@PathParam("type" ) DataType type); + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + long updateAttribute( + @Context SecurityContext sc, + @PathParam("id") long id, + @PathParam("name") String name, + @PathParam("value") String value, + @PathParam("type") DataType type); /** - * * @param sc * @param id * @param securityRules */ @POST @Path("/resource/{id}/permissions") - @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) - @Secured({ "ROLE_USER", "ROLE_ADMIN" }) - void updateSecurityRules(@Context SecurityContext sc, @PathParam("id") long id, @Multipart("rules") SecurityRuleList securityRules); - - + @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Secured({"ROLE_USER", "ROLE_ADMIN"}) + void updateSecurityRules( + @Context SecurityContext sc, + @PathParam("id") long id, + @Multipart("rules") SecurityRuleList securityRules); + @GET @Path("/resource/{id}/permissions") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) - @Secured({ "ROLE_USER", "ROLE_ADMIN" }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Secured({"ROLE_USER", "ROLE_ADMIN"}) SecurityRuleList getSecurityRules(@Context SecurityContext sc, @PathParam("id") long id); - - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTSessionService.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTSessionService.java index 3207a730..b0b9985c 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTSessionService.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTSessionService.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -27,9 +27,10 @@ */ package it.geosolutions.geostore.services.rest; - +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.services.rest.model.SessionToken; import java.text.ParseException; - +import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; @@ -42,103 +43,120 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; - import org.springframework.security.access.annotation.Secured; -import it.geosolutions.geostore.core.model.User; -import it.geosolutions.geostore.services.rest.model.SessionToken; - public interface RESTSessionService { - - /** + + /** * Gets the User object associated to the given sessionId (if it exists). - * + * * @param sessionId * @param refresh flag to automatically refresh the session (only if enabled) * @return */ - @GET - @Path("/user/{sessionId}") - @Produces({MediaType.APPLICATION_JSON}) - @Secured({ "ROLE_ADMIN", "ROLE_USER", "ROLE_ANONYMOUS" }) - User getUser( - @PathParam("sessionId") String sessionId, - @DefaultValue("true") @QueryParam("refresh") boolean refresh); - - /** + @GET + @Path("/user/{sessionId}") + @Produces({MediaType.APPLICATION_JSON}) + @Secured({"ROLE_ADMIN", "ROLE_USER", "ROLE_ANONYMOUS"}) + User getUser( + @PathParam("sessionId") String sessionId, + @DefaultValue("true") @QueryParam("refresh") boolean refresh); + + /** * Gets the username associated to the given sessionId (if it exists). - * + * * @param sessionId * @param refresh flag to automatically refresh the session (only if enabled) * @return */ - @GET + @GET @Path("/username/{sessionId}") - @Produces({MediaType.TEXT_PLAIN}) - @Secured({ "ROLE_ADMIN", "ROLE_USER", "ROLE_ANONYMOUS" }) + @Produces({MediaType.TEXT_PLAIN}) + @Secured({"ROLE_ADMIN", "ROLE_USER", "ROLE_ANONYMOUS"}) public String getUserName( - @PathParam("sessionId") String sessionId, - @DefaultValue("true") @QueryParam("refresh") boolean refresh); - - /** + @PathParam("sessionId") String sessionId, + @DefaultValue("true") @QueryParam("refresh") boolean refresh); + + /** * Creates a new session for the User in SecurityContext. - * + * * @return the session key - * @throws ParseException + * @throws ParseException */ - @PUT @Path("/") @Produces({MediaType.TEXT_PLAIN}) - @Secured({ "ROLE_ADMIN", "ROLE_USER" }) + @Secured({"ROLE_ADMIN", "ROLE_USER"}) public String createSession( - @DefaultValue("") @QueryParam("expires") String expires, @Context SecurityContext sc) throws ParseException; - - /** + @DefaultValue("") @QueryParam("expires") String expires, @Context SecurityContext sc) + throws ParseException; + + /** * Creates a new session for the User in SecurityContext. - * + * * @return The session token with expiring time (in seconds and refresh token. - * @throws ParseException + * @throws ParseException */ - @POST @Path("/login") @Produces({MediaType.APPLICATION_JSON}) - @Secured({ "ROLE_ADMIN", "ROLE_USER" }) + @Secured({"ROLE_ADMIN", "ROLE_USER"}) public SessionToken login(@Context SecurityContext sc) throws ParseException; - - /** + + /** * Refresh the session token - * + * * @param sessionId the current session token * @param refreshToken the token that allow you to refresh the session - * * @return the new session token with the new informations - * @throws ParseException + * @throws ParseException */ - @POST @Path("/refresh/{sessionId}/{refreshToken}") @Produces({MediaType.APPLICATION_JSON}) - @Secured({ "ROLE_ADMIN", "ROLE_USER" }) - public SessionToken refresh(@Context SecurityContext sc, @PathParam("sessionId") String sessionId, @PathParam("refreshToken") String refreshToken) throws ParseException; + @Secured({"ROLE_ADMIN", "ROLE_USER"}) + @Deprecated + public SessionToken refresh( + @Context SecurityContext sc, + @PathParam("sessionId") String sessionId, + @PathParam("refreshToken") String refreshToken) + throws ParseException; /** * Removes the given session. - * + * * @return */ @DELETE @Path("/{sessionId}") - @Secured({ "ROLE_ADMIN", "ROLE_USER" }) + @Secured({"ROLE_ADMIN", "ROLE_USER"}) + @Deprecated public void removeSession(@PathParam("sessionId") String sessionId); - + + @POST + @Path("/refreshToken") + @Produces({MediaType.APPLICATION_JSON}) + @Consumes({MediaType.APPLICATION_JSON}) + @Secured({"ROLE_ADMIN", "ROLE_USER", "ROLE_ANONYMOUS"}) + public SessionToken refresh(SessionToken token) throws ParseException; + /** + * Removes the given session. + * + * @return + */ + @DELETE + @Path("/logout") + @Secured({"ROLE_ADMIN", "ROLE_USER"}) + public void removeSession(); + /** * Removes all sessions. - * + * * @return */ @DELETE @Path("/") - @Secured({ "ROLE_ADMIN" }) + @Secured({"ROLE_ADMIN"}) public void clear(); + + void registerDelegate(String key, SessionServiceDelegate delegate); } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTStoredDataService.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTStoredDataService.java index 4e55e732..f421cdb6 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTStoredDataService.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTStoredDataService.java @@ -21,7 +21,6 @@ import it.geosolutions.geostore.core.model.StoredData; import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; - import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; @@ -35,13 +34,12 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; - import org.apache.cxf.jaxrs.ext.multipart.Multipart; import org.springframework.security.access.annotation.Secured; /** * Interface RESTStoredDataService.Operations on {@link StoredData StoredData}s. - * + * * @author Emanuele Tajariol (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ @@ -55,17 +53,20 @@ public interface RESTStoredDataService { */ @PUT @Path("/{id}") - @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.TEXT_PLAIN, - MediaType.APPLICATION_JSON }) - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Consumes({ + MediaType.APPLICATION_XML, + MediaType.TEXT_XML, + MediaType.TEXT_PLAIN, + MediaType.APPLICATION_JSON + }) + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN", "USER" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN" }) - long update(@Context SecurityContext sc, @PathParam("id") long id, - @Multipart("data") String data) throws NotFoundWebEx; + @Secured({"ROLE_USER", "ROLE_ADMIN"}) + long update( + @Context SecurityContext sc, @PathParam("id") long id, @Multipart("data") String data) + throws NotFoundWebEx; - /** - * @return StoredDataList - */ + /** @return StoredDataList */ // @GET // @Path("/") // @Produces({MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) @@ -79,7 +80,7 @@ long update(@Context SecurityContext sc, @PathParam("id") long id, @DELETE @Path("/{id}") // @RolesAllowed({ "ADMIN", "USER" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN" }) + @Secured({"ROLE_USER", "ROLE_ADMIN"}) void delete(@Context SecurityContext sc, @PathParam("id") long id) throws NotFoundWebEx; /** @@ -89,20 +90,20 @@ long update(@Context SecurityContext sc, @PathParam("id") long id, */ @GET @Path("/{id}") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) String get(@Context SecurityContext sc, @Context HttpHeaders headers, @PathParam("id") long id) throws NotFoundWebEx; @GET @Path("/{id}/raw") - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) Response getRaw( - @Context SecurityContext sc, - @Context HttpHeaders headers, - @PathParam("id") long id, - @QueryParam("decode") String decodeFormat - ) throws NotFoundWebEx; - + @Context SecurityContext sc, + @Context HttpHeaders headers, + @PathParam("id") long id, + @QueryParam("decode") String decodeFormat) + throws NotFoundWebEx; } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTUserGroupService.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTUserGroupService.java index e307f54c..21a95fdd 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTUserGroupService.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTUserGroupService.java @@ -19,13 +19,12 @@ */ package it.geosolutions.geostore.services.rest; -import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.services.rest.exception.BadRequestWebEx; import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; import it.geosolutions.geostore.services.rest.model.RESTUserGroup; import it.geosolutions.geostore.services.rest.model.ShortResourceList; import it.geosolutions.geostore.services.rest.model.UserGroupList; - +import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; @@ -39,66 +38,116 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; - import org.apache.cxf.jaxrs.ext.multipart.Multipart; import org.springframework.security.access.annotation.Secured; -/** - * @author DamianoG - * - */ +/** @author DamianoG */ public interface RESTUserGroupService { - - @POST @Path("/") - @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML,MediaType.APPLICATION_JSON }) - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) - @Secured({ "ROLE_ADMIN" }) - long insert(@Context SecurityContext sc, @Multipart("userGroup") UserGroup userGroup) + @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Produces({MediaType.TEXT_PLAIN}) + @Secured({"ROLE_ADMIN"}) + long insert(@Context SecurityContext sc, @Multipart("userGroup") RESTUserGroup userGroup) throws BadRequestWebEx; @DELETE @Path("/group/{id}") - @Secured({ "ROLE_ADMIN" }) + @Secured({"ROLE_ADMIN"}) void delete(@Context SecurityContext sc, @PathParam("id") long id) throws NotFoundWebEx; @GET - @Path("/group/{id}") - @Secured({ "ROLE_ADMIN" }) - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) - RESTUserGroup get(@Context SecurityContext sc, @PathParam("id") long id) throws NotFoundWebEx; - + @Path("/group/{id}") + @Secured({"ROLE_ADMIN"}) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + RESTUserGroup get( + @Context SecurityContext sc, + @PathParam("id") long id, + @QueryParam("includeattributes") @DefaultValue("true") boolean includeAttributes) + throws NotFoundWebEx; + @GET - @Path("/group/name/{name}") - @Secured({ "ROLE_ADMIN" }) - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) - RESTUserGroup get(@Context SecurityContext sc, @PathParam("id") String name) throws NotFoundWebEx; - + @Path("/group/name/{name}") + @Secured({"ROLE_ADMIN"}) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + RESTUserGroup get( + @Context SecurityContext sc, + @PathParam("name") String name, + @QueryParam("includeattributes") @DefaultValue("true") boolean includeAttributes) + throws NotFoundWebEx; + @POST @Path("/group/{userid}/{groupid}") - @Secured({ "ROLE_ADMIN" }) - void assignUserGroup(@Context SecurityContext sc, @PathParam("userid") long userId, @PathParam("groupid") long groupId) + @Secured({"ROLE_ADMIN"}) + void assignUserGroup( + @Context SecurityContext sc, + @PathParam("userid") long userId, + @PathParam("groupid") long groupId) throws NotFoundWebEx; - + @DELETE @Path("/group/{userid}/{groupid}") - @Secured({ "ROLE_ADMIN" }) - void deassignUserGroup(@Context SecurityContext sc, @PathParam("userid") long userId, @PathParam("groupid") long groupId) + @Secured({"ROLE_ADMIN"}) + void deassignUserGroup( + @Context SecurityContext sc, + @PathParam("userid") long userId, + @PathParam("groupid") long groupId) throws NotFoundWebEx; - + @GET @Path("/") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) - @Secured({ "ROLE_ADMIN" }) - UserGroupList getAll(@Context SecurityContext sc, @QueryParam("page") Integer page, - @QueryParam("entries") Integer entries, @QueryParam("all") @DefaultValue("false") boolean all, @QueryParam("users") @DefaultValue("true") boolean includeUsers) throws BadRequestWebEx; - + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Secured({"ROLE_ADMIN"}) + UserGroupList getAll( + @Context SecurityContext sc, + @QueryParam("page") Integer page, + @QueryParam("entries") Integer entries, + @QueryParam("all") @DefaultValue("false") boolean all, + @QueryParam("users") @DefaultValue("true") boolean includeUsers) + throws BadRequestWebEx; + @PUT @Path("/update_security_rules/{groupId}/{canRead}/{canWrite}") - @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) - @Secured({ "ROLE_ADMIN" }) - ShortResourceList updateSecurityRules(@Context SecurityContext sc, @Multipart("resourcelist")ShortResourceList resourcesToSet, @PathParam("groupId") Long groupId, @PathParam("canRead") Boolean canRead, @PathParam("canWrite") Boolean canWrite) throws BadRequestWebEx, NotFoundWebEx; + @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Secured({"ROLE_ADMIN"}) + ShortResourceList updateSecurityRules( + @Context SecurityContext sc, + @Multipart("resourcelist") ShortResourceList resourcesToSet, + @PathParam("groupId") Long groupId, + @PathParam("canRead") Boolean canRead, + @PathParam("canWrite") Boolean canWrite) + throws BadRequestWebEx, NotFoundWebEx; + + @PUT + @Path("/group/{id}") + @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Produces({MediaType.TEXT_PLAIN}) + @Secured({"ROLE_ADMIN"}) + long update( + @Context SecurityContext sc, + @PathParam("id") long id, + @Multipart("userGroup") RESTUserGroup userGroup) + throws NotFoundWebEx; + + @GET + @Path("/search/attribute/{name}/{value}") + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Secured({"ROLE_ADMIN", "ROLE_USER"}) + UserGroupList getByAttribute( + @Context SecurityContext sc, + @PathParam("name") String name, + @PathParam("value") String value, + @QueryParam("ignoreCase") @DefaultValue("false") boolean ignoreCase); + + @GET + @Path("/search/attribute/{name}") + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Secured({"ROLE_ADMIN", "ROLE_USER"}) + UserGroupList getByAttribute( + @Context SecurityContext sc, + @PathParam("name") String name, + @QueryParam("values") List values, + @QueryParam("ignoreCase") @DefaultValue("false") boolean ignoreCase); } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTUserService.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTUserService.java index bd402118..0e825772 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTUserService.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/RESTUserService.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -33,7 +33,6 @@ import it.geosolutions.geostore.services.rest.exception.BadRequestWebEx; import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; import it.geosolutions.geostore.services.rest.model.UserList; - import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; @@ -47,88 +46,99 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; - import org.apache.cxf.jaxrs.ext.multipart.Multipart; import org.springframework.security.access.annotation.Secured; /** * Interface RESTUserInterface. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public interface RESTUserService { @POST @Path("/") - @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN" }) - @Secured({ "ROLE_ADMIN" }) + @Secured({"ROLE_ADMIN"}) long insert(@Context SecurityContext sc, @Multipart("user") User user) throws BadRequestServiceEx, NotFoundServiceEx; @PUT @Path("/user/{id}") - @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML,MediaType.APPLICATION_JSON }) + @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Produces({MediaType.TEXT_PLAIN}) // @RolesAllowed({ "ADMIN", "USER" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN" }) + @Secured({"ROLE_USER", "ROLE_ADMIN"}) long update(@Context SecurityContext sc, @PathParam("id") long id, @Multipart("user") User user) throws NotFoundWebEx; @DELETE @Path("/user/{id}") // @RolesAllowed({ "ADMIN" }) - @Secured({ "ROLE_ADMIN" }) + @Secured({"ROLE_ADMIN"}) void delete(@Context SecurityContext sc, @PathParam("id") long id) throws NotFoundWebEx; @GET @Path("/user/{id}") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) // @RolesAllowed({ "ADMIN" }) - @Secured({ "ROLE_ADMIN" }) - User get(@Context SecurityContext sc, @PathParam("id") long id, + @Secured({"ROLE_ADMIN"}) + User get( + @Context SecurityContext sc, + @PathParam("id") long id, @QueryParam("includeattributes") @DefaultValue("false") boolean includeAttributes) throws NotFoundWebEx; @GET @Path("/search/{name}") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) // @RolesAllowed({ "ADMIN" }) - @Secured({ "ROLE_ADMIN" }) - User get(@Context SecurityContext sc, @PathParam("name") String name, + @Secured({"ROLE_ADMIN"}) + User get( + @Context SecurityContext sc, + @PathParam("name") String name, @QueryParam("includeattributes") @DefaultValue("false") boolean includeAttributes) throws NotFoundWebEx; @GET @Path("/") // @RolesAllowed({ "ADMIN" }) - @Secured({ "ROLE_ADMIN" }) - UserList getAll(@Context SecurityContext sc, @QueryParam("page") Integer page, - @QueryParam("entries") Integer entries) throws BadRequestWebEx; + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Secured({"ROLE_ADMIN"}) + UserList getAll( + @Context SecurityContext sc, + @QueryParam("page") Integer page, + @QueryParam("entries") Integer entries) + throws BadRequestWebEx; + @Produces({MediaType.TEXT_PLAIN}) @GET @Path("/count/{nameLike}") // @RolesAllowed({ "ADMIN" }) - @Secured({ "ROLE_ADMIN" }) + @Secured({"ROLE_ADMIN"}) long getCount(@Context SecurityContext sc, @PathParam("nameLike") String nameLike); @GET @Path("/user/details/") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) // @RolesAllowed({ "ADMIN", "USER" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN" }) - User getAuthUserDetails(@Context SecurityContext sc, + @Secured({"ROLE_USER", "ROLE_ADMIN"}) + User getAuthUserDetails( + @Context SecurityContext sc, @QueryParam("includeattributes") @DefaultValue("false") boolean includeAttributes); @GET @Path("/search/list/{nameLike}") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) // @RolesAllowed({ "ADMIN", "USER" }) - @Secured({ "ROLE_USER", "ROLE_ADMIN" }) - UserList getUserList(@Context SecurityContext sc, @PathParam("nameLike") String nameLike, - @QueryParam("page") Integer page, @QueryParam("entries") Integer entries, + @Secured({"ROLE_USER", "ROLE_ADMIN"}) + UserList getUserList( + @Context SecurityContext sc, + @PathParam("nameLike") String nameLike, + @QueryParam("page") Integer page, + @QueryParam("entries") Integer entries, @QueryParam("includeattributes") @DefaultValue("false") boolean includeAttributes) throws BadRequestWebEx; - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/SessionServiceDelegate.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/SessionServiceDelegate.java new file mode 100644 index 00000000..de622b22 --- /dev/null +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/SessionServiceDelegate.java @@ -0,0 +1,49 @@ +package it.geosolutions.geostore.services.rest; + +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.services.rest.model.SessionToken; + +/** + * Base interface for a SessionServiceDelegate. Instances of this type are meant to customize + * refresh token and logout operations. + */ +public interface SessionServiceDelegate { + + String PROVIDER_KEY = "PROVIDER"; + + /** + * Refresh a token, + * + * @param refreshToken the refresh token. + * @param accessToken the current access token. + * @return a Session Token instance holding the new token and the refresh token. + */ + SessionToken refresh(String refreshToken, String accessToken); + + /** + * Get the user by sessionId. + * + * @param sessionId the session identifier. + * @param refresh refresh flag. + * @param autorefresh autorefresh flag. + * @return the user if found, null otherwise. + */ + User getUser(String sessionId, boolean refresh, boolean autorefresh); + + /** + * Get the username by sessionId. + * + * @param sessionId the session identifier. + * @param refresh refresh flag. + * @param autorefresh autorefresh flag. + * @return the username if found, null otherwise. + */ + String getUserName(String sessionId, boolean refresh, boolean autorefresh); + + /** + * Do the logout. + * + * @param sessionId the current session token. + */ + void doLogout(String sessionId); +} diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/BadRequestWebEx.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/BadRequestWebEx.java index 2e90ec5b..a6d8eb97 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/BadRequestWebEx.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/BadRequestWebEx.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,10 +21,7 @@ import javax.ws.rs.core.Response; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class BadRequestWebEx extends GeoStoreWebEx { /** The Constant serialVersionUID. */ @@ -33,5 +30,4 @@ public class BadRequestWebEx extends GeoStoreWebEx { public BadRequestWebEx(String message) { super(Response.Status.BAD_REQUEST, message); } - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/ConflictWebEx.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/ConflictWebEx.java index 711db2eb..f697f914 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/ConflictWebEx.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/ConflictWebEx.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2012 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,10 +21,7 @@ import javax.ws.rs.core.Response; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class ConflictWebEx extends GeoStoreWebEx { public ConflictWebEx(String message) { diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/ForbiddenErrorWebEx.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/ForbiddenErrorWebEx.java index 68b671a6..ddd4b7f3 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/ForbiddenErrorWebEx.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/ForbiddenErrorWebEx.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -23,9 +23,8 @@ /** * Class ForbiddenErrorWebEx. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class ForbiddenErrorWebEx extends GeoStoreWebEx { @@ -35,5 +34,4 @@ public class ForbiddenErrorWebEx extends GeoStoreWebEx { public ForbiddenErrorWebEx(String message) { super(Response.Status.FORBIDDEN, message); } - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/GeoStoreWebEx.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/GeoStoreWebEx.java index 9278c466..fdb8b200 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/GeoStoreWebEx.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/GeoStoreWebEx.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -23,10 +23,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public abstract class GeoStoreWebEx extends WebApplicationException { private String message; @@ -40,5 +37,4 @@ protected GeoStoreWebEx(Status status, String message) { public String getMessage() { return message; } - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/InternalErrorWebEx.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/InternalErrorWebEx.java index b72e57fa..098a50dd 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/InternalErrorWebEx.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/InternalErrorWebEx.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,10 +21,7 @@ import javax.ws.rs.core.Response; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class InternalErrorWebEx extends GeoStoreWebEx { /** The Constant serialVersionUID. */ diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/NotFoundWebEx.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/NotFoundWebEx.java index 1fcf9e09..c5658f83 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/NotFoundWebEx.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/exception/NotFoundWebEx.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,10 +21,7 @@ import javax.ws.rs.core.Response; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class NotFoundWebEx extends GeoStoreWebEx { /** The Constant serialVersionUID. */ diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/CategoryList.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/CategoryList.java index a3b4848c..c1d9c4e7 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/CategoryList.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/CategoryList.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -31,45 +31,34 @@ import it.geosolutions.geostore.core.model.Category; import java.util.Collections; import java.util.Iterator; - import java.util.List; - import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; /** * Class CategoryList. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ @XmlRootElement(name = "CategoryList") public class CategoryList implements Iterable { private List list; - public CategoryList() { + public CategoryList() {} - } - - /** - * @param list - */ + /** @param list */ public CategoryList(List list) { this.list = list; } - /** - * @return List - */ + /** @return List */ @XmlElement(name = "Category") public List getList() { return list; } - /** - * @param list - */ + /** @param list */ public void setList(List list) { this.list = list; } @@ -78,5 +67,4 @@ public void setList(List list) { public Iterator iterator() { return list == null ? Collections.EMPTY_LIST.iterator() : list.iterator(); } - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTAttribute.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTAttribute.java index e9647314..6ea3b810 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTAttribute.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTAttribute.java @@ -1,20 +1,18 @@ package it.geosolutions.geostore.services.rest.model; +import it.geosolutions.geostore.services.dto.ShortAttribute; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; -import it.geosolutions.geostore.services.dto.ShortAttribute; - /** * DTO for attribute object - * @author Lorenzo Natali, GeoSolutions s.a.s. * + * @author Lorenzo Natali, GeoSolutions s.a.s. */ @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class RESTAttribute extends ShortAttribute { - private static final long serialVersionUID = 1L; - + private static final long serialVersionUID = 1L; } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTCategory.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTCategory.java index 5abf0ada..32142d18 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTCategory.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTCategory.java @@ -21,7 +21,6 @@ package it.geosolutions.geostore.services.rest.model; import java.io.Serializable; - import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "Category") @@ -35,8 +34,7 @@ public class RESTCategory implements Serializable { private String name; - public RESTCategory() { - } + public RESTCategory() {} public RESTCategory(Long id) { this.id = id; @@ -46,37 +44,29 @@ public RESTCategory(String name) { this.name = name; } - /** - * @return the id - */ + /** @return the id */ public Long getId() { return id; } - /** - * @param id the id to set - */ + /** @param id the id to set */ public void setId(Long id) { this.id = id; } - /** - * @return the name - */ + /** @return the name */ public String getName() { return name; } - /** - * @param name the name to set - */ + /** @param name the name to set */ public void setName(String name) { this.name = name; } /* * (non-Javadoc) - * + * * @see java.lang.Object#toString() */ @Override @@ -96,5 +86,4 @@ public String toString() { builder.append(']'); return builder.toString(); } - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTQuickBackup.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTQuickBackup.java index a130f0fe..b29c68a6 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTQuickBackup.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTQuickBackup.java @@ -33,20 +33,15 @@ import java.util.LinkedList; import java.util.List; import javax.xml.bind.annotation.XmlElement; - import javax.xml.bind.annotation.XmlRootElement; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ @XmlRootElement(name = "Backup") public class RESTQuickBackup implements Serializable { private Collection categories = new LinkedList(); - public RESTQuickBackup() { - } + public RESTQuickBackup() {} @XmlElement(name = "category") public Collection getCategories() { @@ -75,13 +70,12 @@ public String toString() { } sb.append('}'); } - } sb.append(']'); return sb.toString(); } - static public class RESTBackupCategory { + public static class RESTBackupCategory implements Serializable { Long id; String name; @@ -125,14 +119,13 @@ public void setResources(List resources) { public void addResource(RESTBackupResource resource) { resources.add(resource); } - } - static public class RESTBackupAuth { + public static class RESTBackupAuth implements Serializable { // TODO } - static public class RESTBackupResource { + public static class RESTBackupResource implements Serializable { RESTResource resource; // TODO: add auth info @@ -144,6 +137,5 @@ public RESTResource getResource() { public void setResource(RESTResource resource) { this.resource = resource; } - } } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTResource.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTResource.java index 576a1f12..07b4943c 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTResource.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTResource.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -28,18 +28,16 @@ package it.geosolutions.geostore.services.rest.model; import it.geosolutions.geostore.services.dto.ShortAttribute; - import java.io.Serializable; import java.util.Date; import java.util.List; - import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; /** * Class RESTResource. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ @@ -62,157 +60,153 @@ public class RESTResource implements Serializable { private String metadata; + private String creator; + + private String editor; + private List attribute; private RESTStoredData store; private RESTCategory category; - /** - * @return the id - */ + private boolean advertised = true; + + /** @return the id */ public Long getId() { return id; } - /** - * @param id the id to set - */ + /** @param id the id to set */ public void setId(Long id) { this.id = id; } - /** - * @return the name - */ + /** @return the name */ public String getName() { return name; } - /** - * @param name the name to set - */ + /** @param name the name to set */ public void setName(String name) { this.name = name; } - /** - * @return the description - */ + /** @return the description */ public String getDescription() { return description; } - /** - * @param description the description to set - */ + /** @param description the description to set */ public void setDescription(String description) { this.description = description; } - /** - * @return the creation - */ + /** @return the creation */ public Date getCreation() { return creation; } - /** - * @param creation the creation to set - */ + /** @param creation the creation to set */ public void setCreation(Date creation) { this.creation = creation; } - /** - * @return the lastUpdate - */ + /** @return the lastUpdate */ public Date getLastUpdate() { return lastUpdate; } - /** - * @param lastUpdate the lastUpdate to set - */ + /** @param lastUpdate the lastUpdate to set */ public void setLastUpdate(Date lastUpdate) { this.lastUpdate = lastUpdate; } - /** - * @return the metadata - */ + /** @return the metadata */ public String getMetadata() { return metadata; } - /** - * @param metadata the metadata to set - */ + /** @param metadata the metadata to set */ public void setMetadata(String metadata) { this.metadata = metadata; } - /** - * @return the attribute - */ + /** @return the attribute */ @XmlElementWrapper(name = "Attributes") public List getAttribute() { return attribute; } - /** - * @param attribute the attribute to set - */ + /** @param attribute the attribute to set */ public void setAttribute(List attribute) { this.attribute = attribute; } - /** - * @return the store - */ + /** @return the store */ public RESTStoredData getStore() { return store; } - /** - * @param store the store to set - */ + /** @param store the store to set */ public void setStore(RESTStoredData store) { this.store = store; } - /** - * Shortcut for reading data - */ + /** Shortcut for reading data */ @XmlTransient public String getData() { return store == null ? null : store.getData(); } - /** - * Shortcut for setting data - */ + /** Shortcut for setting data */ public void setData(String data) { this.store = data == null ? null : new RESTStoredData(data); } - /** - * @return the category - */ + /** @return the category */ public RESTCategory getCategory() { return category; } - /** - * @param category the category to set - */ + /** @param category the category to set */ public void setCategory(RESTCategory category) { this.category = category; } + /** @return the creator username */ + public String getCreator() { + return creator; + } + + /** @param creator the creator username */ + public void setCreator(String creator) { + this.creator = creator; + } + + /** @return the editor username */ + public String getEditor() { + return editor; + } + + /** @param editor the creator username */ + public void setEditor(String editor) { + this.editor = editor; + } + + /** @param advertised weather the resource is advertised or not */ + public void setAdvertised(boolean advertised) { + this.advertised = advertised; + } + + /** @return advertised weather the resource is advertised or not */ + public boolean isAdvertised() { + return this.advertised; + } + /* * (non-Javadoc) - * + * * @see java.lang.Object#toString() */ @Override @@ -222,32 +216,28 @@ public String toString() { builder.append("id=").append(id); - if (name != null) - builder.append(", name=").append(name); + if (name != null) builder.append(", name=").append(name); + + if (description != null) builder.append(", descr=").append(description); - if (description != null) - builder.append(", descr=").append(description); + if (creation != null) builder.append(", created=").append(creation); - if (creation != null) - builder.append(", created=").append(creation); + if (lastUpdate != null) builder.append(", updated=").append(lastUpdate); - if (lastUpdate != null) - builder.append(", updated=").append(lastUpdate); + if (metadata != null) builder.append(", meta=").append(metadata); - if (metadata != null) - builder.append(", meta=").append(metadata); + if (attribute != null) builder.append(", attr=").append(attribute); - if (attribute != null) - builder.append(", attr=").append(attribute); + if (store != null) builder.append(", store=").append(store); - if (store != null) - builder.append(", store=").append(store.toString()); + if (category != null) builder.append(", cat=").append(category); - if (category != null) - builder.append(", cat=").append(category.toString()); + if (creator != null) builder.append(", creator=").append(creator); + if (editor != null) builder.append(", editor=").append(editor); + + builder.append(", advertised=").append(advertised); builder.append(']'); return builder.toString(); } - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTSecurityRule.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTSecurityRule.java index 8ebbcf9e..dfcdc243 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTSecurityRule.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTSecurityRule.java @@ -5,7 +5,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -17,7 +17,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -31,94 +31,86 @@ import it.geosolutions.geostore.core.model.SecurityRule; import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserGroup; - import javax.xml.bind.annotation.XmlRootElement; /** * Class RESTSecurityRule. - * + * * @author Mauro Bartolomeoli (mauro.bartolomeoli at geo-solutions.it) - * */ @XmlRootElement(name = "SecurityRule") public class RESTSecurityRule { - - private RESTUser user = null; - - private RESTUserGroup group = null; - - private boolean canRead = false; - - private boolean canWrite = false; - - public RESTSecurityRule(SecurityRule rule) { - if(rule.getUser() != null) { - User ruleUser = rule.getUser(); - user = new RESTUser(); - user.setId(ruleUser.getId()); - user.setName(ruleUser.getName()); - } - if(rule.getGroup() != null) { - UserGroup ruleGroup = rule.getGroup(); - group = new RESTUserGroup(); - group.setId(ruleGroup.getId()); - group.setGroupName(ruleGroup.getGroupName()); - } - canRead = rule.isCanRead(); - canWrite = rule.isCanWrite(); - } - - public RESTSecurityRule() { - - } - - public RESTSecurityRule(RESTUser user, boolean canRead, boolean canWrite) { - super(); - this.user = user; - this.canRead = canRead; - this.canWrite = canWrite; - } - - public RESTSecurityRule(RESTUserGroup group, boolean canRead, boolean canWrite) { - super(); - this.group = group; - this.canRead = canRead; - this.canWrite = canWrite; - } - - - public RESTUser getUser() { - return user; - } - - public void setUser(RESTUser user) { - this.user = user; - } - - public RESTUserGroup getGroup() { - return group; - } - - public void setGroup(RESTUserGroup group) { - this.group = group; - } - - public boolean isCanRead() { - return canRead; - } - - public void setCanRead(boolean canRead) { - this.canRead = canRead; - } - - public boolean isCanWrite() { - return canWrite; - } - - public void setCanWrite(boolean canWrite) { - this.canWrite = canWrite; - } - - - + + private RESTUser user = null; + + private RESTUserGroup group = null; + + private boolean canRead = false; + + private boolean canWrite = false; + + public RESTSecurityRule(SecurityRule rule) { + if (rule.getUser() != null) { + User ruleUser = rule.getUser(); + user = new RESTUser(); + user.setId(ruleUser.getId()); + user.setName(ruleUser.getName()); + } + if (rule.getGroup() != null) { + UserGroup ruleGroup = rule.getGroup(); + group = new RESTUserGroup(); + group.setId(ruleGroup.getId()); + group.setGroupName(ruleGroup.getGroupName()); + } + canRead = rule.isCanRead(); + canWrite = rule.isCanWrite(); + } + + public RESTSecurityRule() {} + + public RESTSecurityRule(RESTUser user, boolean canRead, boolean canWrite) { + super(); + this.user = user; + this.canRead = canRead; + this.canWrite = canWrite; + } + + public RESTSecurityRule(RESTUserGroup group, boolean canRead, boolean canWrite) { + super(); + this.group = group; + this.canRead = canRead; + this.canWrite = canWrite; + } + + public RESTUser getUser() { + return user; + } + + public void setUser(RESTUser user) { + this.user = user; + } + + public RESTUserGroup getGroup() { + return group; + } + + public void setGroup(RESTUserGroup group) { + this.group = group; + } + + public boolean isCanRead() { + return canRead; + } + + public void setCanRead(boolean canRead) { + this.canRead = canRead; + } + + public boolean isCanWrite() { + return canWrite; + } + + public void setCanWrite(boolean canWrite) { + this.canWrite = canWrite; + } } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTStoredData.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTStoredData.java index cb7e0ef6..acee40d8 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTStoredData.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTStoredData.java @@ -5,7 +5,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -17,7 +17,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -29,14 +29,12 @@ package it.geosolutions.geostore.services.rest.model; import java.io.Serializable; - import javax.xml.bind.annotation.XmlRootElement; /** * Class RESTStoredData. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ @XmlRootElement(name = "StoredData") public class RESTStoredData implements Serializable { @@ -49,51 +47,37 @@ public class RESTStoredData implements Serializable { private String data; - /** - * Instantiates a new instance. - */ - public RESTStoredData() { - - } + /** Instantiates a new instance. */ + public RESTStoredData() {} - /** - * Instantiates a new instance. - */ + /** Instantiates a new instance. */ public RESTStoredData(String data) { this.data = data; } - /** - * @return the id - */ + /** @return the id */ public Long getId() { return id; } - /** - * @param id the id to set - */ + /** @param id the id to set */ public void setId(long id) { this.id = id; } - /** - * @return the data - */ + /** @return the data */ public String getData() { return data; } - /** - * @param data the data to set - */ + /** @param data the data to set */ public void setData(String data) { this.data = data; } /* * (non-Javadoc) - * + * * @see java.lang.Object#toString() */ @Override @@ -103,11 +87,9 @@ public String toString() { builder.append("id=").append(id).append(", "); - if (data != null) - builder.append("data=").append(data); + if (data != null) builder.append("data=").append(data); builder.append(']'); return builder.toString(); } - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTUser.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTUser.java index 1bc5e01f..33b3563f 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTUser.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTUser.java @@ -5,7 +5,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -17,7 +17,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -31,19 +31,16 @@ import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.core.model.enums.GroupReservedNames; import it.geosolutions.geostore.core.model.enums.Role; - import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Set; - import javax.xml.bind.annotation.XmlRootElement; /** * Class RESTUser. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ @XmlRootElement(name = "User") public class RESTUser implements Serializable { @@ -59,20 +56,15 @@ public class RESTUser implements Serializable { private Role role; private List groupsNames; - - public RESTUser() { - } - /** - * @param id - */ + public RESTUser() {} + + /** @param id */ public RESTUser(Long id) { this.id = id; } - /** - * @param name - */ + /** @param name */ public RESTUser(String name) { this.name = name; } @@ -88,74 +80,58 @@ public RESTUser(Long id, String name, Role role, Set groups, boolean this.name = name; this.role = role; groupsNames = new ArrayList(); - if(groups != null){ - for(UserGroup ug : groups){ - if(allGroups || GroupReservedNames.isAllowedName(ug.getGroupName())){ + if (groups != null) { + for (UserGroup ug : groups) { + if (allGroups || GroupReservedNames.isAllowedName(ug.getGroupName())) { groupsNames.add(ug.getGroupName()); } } } } - /** - * @return the id - */ + /** @return the id */ public Long getId() { return id; } - /** - * @param id the id to set - */ + /** @param id the id to set */ public void setId(Long id) { this.id = id; } - /** - * @return the name - */ + /** @return the name */ public String getName() { return name; } - /** - * @param name the name to set - */ + /** @param name the name to set */ public void setName(String name) { this.name = name; } - /** - * @return the role - */ + /** @return the role */ public Role getRole() { return role; } - /** - * @param role the role to set - */ + /** @param role the role to set */ public void setRole(Role role) { this.role = role; } - - /** - * @return the groupsNames - */ + + /** @return the groupsNames */ public List getGroupsNames() { return groupsNames; } - /** - * @param groupsNames the groupsNames to set - */ + /** @param groupsNames the groupsNames to set */ public void setGroupsNames(List groupsNames) { this.groupsNames = groupsNames; } /* * (non-Javadoc) - * + * * @see java.lang.Object#toString() */ @Override @@ -180,5 +156,4 @@ public String toString() { builder.append(']'); return builder.toString(); } - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTUserGroup.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTUserGroup.java index 358528f0..1a55a240 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTUserGroup.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/RESTUserGroup.java @@ -20,32 +20,30 @@ package it.geosolutions.geostore.services.rest.model; import it.geosolutions.geostore.core.model.User; - +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.UserGroupAttribute; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Set; - import javax.xml.bind.annotation.XmlRootElement; -/** - * @author DamianoG - * - */ - +/** @author DamianoG */ @XmlRootElement(name = "UserGroup") -public class RESTUserGroup implements Serializable{ +public class RESTUserGroup implements Serializable { private static final long serialVersionUID = 7681963958796864207L; private Long id; - + private String groupName; - + private UserList restUsers; - + private String description; - + + private List attributes; + public RESTUserGroup() {} /** @@ -56,72 +54,66 @@ public RESTUserGroup(Long id, String groupName, Set users, String descript this.id = id; this.groupName = groupName; List list = new ArrayList(); - for(User u : users){ + for (User u : users) { list.add(new RESTUser(u.getId(), u.getName(), u.getRole(), u.getGroups(), true)); } - this.restUsers = new UserList(list); + this.restUsers = new UserList(list); this.description = description; } - /** - * @return the id - */ + public RESTUserGroup(UserGroup group, Set users) { + this(group.getId(), group.getGroupName(), users, group.getDescription()); + } + + /** @return the id */ public Long getId() { return id; } - /** - * @param id the id to set - */ + /** @param id the id to set */ public void setId(Long id) { this.id = id; } - /** - * @return the groupName - */ + /** @return the groupName */ public String getGroupName() { return groupName; } - /** - * @param groupName the groupName to set - */ + /** @param groupName the groupName to set */ public void setGroupName(String groupName) { this.groupName = groupName; } - - /** - * @return the restUsers - */ + + /** @return the restUsers */ public UserList getRestUsers() { return restUsers; } - /** - * @param restUsers the restUsers to set - */ + /** @param restUsers the restUsers to set */ public void setRestUsers(UserList restUsers) { this.restUsers = restUsers; } - - /** - * @return the description - */ + + /** @return the description */ public String getDescription() { return description; } - /** - * @param description the description to set - */ + /** @param description the description to set */ public void setDescription(String description) { this.description = description; } - /** - * @return the serialversionuid - */ + public List getAttributes() { + return attributes; + } + + public void setAttributes(List attributes) { + this.attributes = attributes; + } + + /** @return the serialversionuid */ public static long getSerialversionuid() { return serialVersionUID; } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/ResourceList.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/ResourceList.java index d53684bb..dfbd9a18 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/ResourceList.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/ResourceList.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -29,46 +29,35 @@ package it.geosolutions.geostore.services.rest.model; import it.geosolutions.geostore.core.model.Resource; - import java.util.List; - import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; /** * Class ResourceList. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ @XmlRootElement(name = "ResourceList") public class ResourceList { private List list; - public ResourceList() { + public ResourceList() {} - } - - /** - * @param list - */ + /** @param list */ public ResourceList(List list) { this.list = list; } - /** - * @return List - */ + /** @return List */ @XmlElement(name = "Resource") public List getList() { return list; } - /** - * @param list - */ + /** @param list */ public void setList(List list) { this.list = list; } @@ -77,5 +66,4 @@ public void setList(List list) { public boolean isEmpty() { return list == null || list.isEmpty(); } - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/SecurityRuleList.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/SecurityRuleList.java index 82313937..760c0f4b 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/SecurityRuleList.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/SecurityRuleList.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -29,51 +29,40 @@ package it.geosolutions.geostore.services.rest.model; import it.geosolutions.geostore.core.model.SecurityRule; - import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; - import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; /** * Class SecurityRuleList. - * + * * @author Mauro Bartolomeoli (mauro.bartolomeoli at geo-solutions.it) - * */ @XmlRootElement(name = "SecurityRuleList") public class SecurityRuleList implements Iterable { private List list; - public SecurityRuleList() { + public SecurityRuleList() {} - } - - /** - * @param list - */ + /** @param list */ public SecurityRuleList(List list) { this.list = new ArrayList(); - for(SecurityRule rule : list) { - this.list.add(new RESTSecurityRule(rule)); + for (SecurityRule rule : list) { + this.list.add(new RESTSecurityRule(rule)); } } - /** - * @return List - */ + /** @return List */ @XmlElement(name = "SecurityRule") public List getList() { return list; } - /** - * @param list - */ + /** @param list */ public void setList(List list) { this.list = list; } @@ -82,5 +71,4 @@ public void setList(List list) { public Iterator iterator() { return list == null ? Collections.EMPTY_LIST.iterator() : list.iterator(); } - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/SessionToken.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/SessionToken.java index ae8bfdeb..12674768 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/SessionToken.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/SessionToken.java @@ -3,41 +3,46 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; - @XmlRootElement public class SessionToken { - String token_type; - String access_token; - String refresh_token; - Long expires; - - @XmlElement(name="token_type") - public String getTokenType() { - return token_type; - } - public void setTokenType(String token_type) { - this.token_type = token_type; - } - @XmlElement(name="access_token") - public String getAccessToken() { - return access_token; - } - public void setAccessToken(String access_token) { - this.access_token = access_token; - } - @XmlElement(name="expires") - public Long getExpires() { - return expires; - } - public void setExpires(Long expires) { - this.expires = expires; - } - @XmlElement(name="refresh_token") - public void setRefreshToken(String refresh_token) { - this.refresh_token = refresh_token; - - } - public String getRefreshToken() { - return refresh_token; - } + String token_type; + String access_token; + String refresh_token; + Long expires; + + @XmlElement(name = "token_type") + public String getTokenType() { + return token_type; + } + + public void setTokenType(String token_type) { + this.token_type = token_type; + } + + @XmlElement(name = "access_token") + public String getAccessToken() { + return access_token; + } + + public void setAccessToken(String access_token) { + this.access_token = access_token; + } + + @XmlElement(name = "expires") + public Long getExpires() { + return expires; + } + + public void setExpires(Long expires) { + this.expires = expires; + } + + @XmlElement(name = "refresh_token") + public void setRefreshToken(String refresh_token) { + this.refresh_token = refresh_token; + } + + public String getRefreshToken() { + return refresh_token; + } } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/ShortAttributeList.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/ShortAttributeList.java index 2f543e37..b6ea66f4 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/ShortAttributeList.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/ShortAttributeList.java @@ -5,7 +5,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -17,7 +17,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -29,47 +29,35 @@ package it.geosolutions.geostore.services.rest.model; import it.geosolutions.geostore.services.dto.ShortAttribute; - import java.util.List; - import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; /** * Class ShortAttributeList. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ @XmlRootElement(name = "AttributeList") public class ShortAttributeList { private List list; - public ShortAttributeList() { + public ShortAttributeList() {} - } - - /** - * @param list - */ + /** @param list */ public ShortAttributeList(List list) { this.list = list; } - /** - * @return List - */ + /** @return List */ @XmlElement(name = "Attribute") public List getList() { return list; } - /** - * @param list - */ + /** @param list */ public void setList(List list) { this.list = list; } - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/ShortResourceList.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/ShortResourceList.java index 7eb1c262..c0d69881 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/ShortResourceList.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/ShortResourceList.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -29,46 +29,35 @@ package it.geosolutions.geostore.services.rest.model; import it.geosolutions.geostore.services.dto.ShortResource; - import java.util.List; - import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; /** * Class ShortResourceList. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ @XmlRootElement(name = "ResourceList") public class ShortResourceList { private List list; - public ShortResourceList() { + public ShortResourceList() {} - } - - /** - * @param list - */ + /** @param list */ public ShortResourceList(List list) { this.list = list; } - /** - * @return List - */ + /** @return List */ @XmlElement(name = "Resource") public List getList() { return list; } - /** - * @param list - */ + /** @param list */ public void setList(List list) { this.list = list; } @@ -77,5 +66,4 @@ public void setList(List list) { public boolean isEmpty() { return list == null || list.isEmpty(); } - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/StoredDataList.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/StoredDataList.java index c404e5ad..b12648a0 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/StoredDataList.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/StoredDataList.java @@ -1,46 +1,41 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.services.rest.model; import it.geosolutions.geostore.core.model.StoredData; - import java.util.List; - import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; /** * Class StoredDataList. - * + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ @XmlRootElement(name = "StoredDataList") public class StoredDataList { private List list; - public StoredDataList() { - - } + public StoredDataList() {} public StoredDataList(List list) { this.list = list; @@ -54,5 +49,4 @@ public List getList() { public void setList(List list) { this.list = list; } - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/UserGroupList.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/UserGroupList.java index 578a6333..77e4b015 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/UserGroupList.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/UserGroupList.java @@ -23,14 +23,10 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; - import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -/** - * @author DamianoG - * - */ +/** @author DamianoG */ @XmlRootElement(name = "UserGroupList") public class UserGroupList implements Iterable { @@ -40,30 +36,23 @@ public UserGroupList() { this.list = new ArrayList(); } - /** - * @param list - */ + /** @param list */ public UserGroupList(List list) { super(); - if(list != null){ + if (list != null) { this.list = list; - } - else{ + } else { this.list = new ArrayList(); } } - /** - * @return the userGroup - */ + /** @return the userGroup */ @XmlElement(name = "UserGroup") public List getUserGroupList() { return list; } - /** - * @param userGroup the userGroup to set - */ + /** @param userGroup the userGroup to set */ public void setUserGroupList(List userGroup) { this.list = userGroup; } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/UserList.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/UserList.java index 045f8609..be3f141e 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/UserList.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/UserList.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -27,46 +27,37 @@ */ package it.geosolutions.geostore.services.rest.model; +import java.io.Serializable; import java.util.Collections; import java.util.Iterator; import java.util.List; - import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; /** * Class UserList. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ @XmlRootElement(name = "UserList") -public class UserList implements Iterable { +public class UserList implements Iterable, Serializable { private List list; - public UserList() { + public UserList() {} - } - - /** - * @param list - */ + /** @param list */ public UserList(List list) { this.list = list; } - /** - * @return List - */ + /** @return List */ @XmlElement(name = "User") public List getList() { return list; } - /** - * @param list - */ + /** @param list */ public void setList(List list) { this.list = list; } @@ -75,5 +66,4 @@ public void setList(List list) { public Iterator iterator() { return list == null ? Collections.EMPTY_LIST.iterator() : list.iterator(); } - } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/enums/RawFormat.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/enums/RawFormat.java index f99d8242..16cb1e90 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/enums/RawFormat.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/model/enums/RawFormat.java @@ -27,13 +27,8 @@ */ package it.geosolutions.geostore.services.rest.model.enums; -/** - * - * @author ETj - */ +/** @author ETj */ public enum RawFormat { - - BASE64, - DATAURI - + BASE64, + DATAURI } diff --git a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/utils/GeoStoreJAXBContext.java b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/utils/GeoStoreJAXBContext.java index 530539b3..2b928e01 100644 --- a/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/utils/GeoStoreJAXBContext.java +++ b/src/modules/rest/api/src/main/java/it/geosolutions/geostore/services/rest/utils/GeoStoreJAXBContext.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -28,33 +28,35 @@ import java.util.Arrays; import java.util.Enumeration; import java.util.List; - import javax.xml.bind.JAXBContext; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * A JAXBContext of public GeoStore classes. Useful when unmarhasalling geostore classes. - * + * * @author ETj (etj at geo-solutions.it) */ +@SuppressWarnings("PMD.UnusedPrivateMethod") public class GeoStoreJAXBContext { - private final static Logger LOGGER = Logger.getLogger(GeoStoreJAXBContext.class); + private static final Logger LOGGER = LogManager.getLogger(GeoStoreJAXBContext.class); - private final static JAXBContext context; + private static final JAXBContext context; static { context = createNewContext(); } /** - * Use this method if you need to extend your context, or use the getContext() method if you need the standard context. + * Use this method if you need to extend your context, or use the getContext() method if you + * need the standard context. */ public static JAXBContext createNewContext() { JAXBContext tmpContext = null; try { - // This procedure has been commented out since it has problems in dealing with classes inside jar files. + // This procedure has been commented out since it has problems in dealing with classes + // inside jar files. // An explicit enumeration of the classes has been implemented instead. // String coreModelPackage = Resource.class.getPackage().getName(); // String serviceApiPackage = ShortResource.class.getPackage().getName(); @@ -67,8 +69,11 @@ public static JAXBContext createNewContext() { List allClasses = getGeoStoreClasses(); if (LOGGER.isDebugEnabled()) - LOGGER.debug("Initializing GeoStoreJAXBContext with " + allClasses.size() - + " classes " + allClasses); + LOGGER.debug( + "Initializing GeoStoreJAXBContext with " + + allClasses.size() + + " classes " + + allClasses); tmpContext = JAXBContext.newInstance(allClasses.toArray(new Class[allClasses.size()])); } catch (Exception ex) { @@ -85,8 +90,9 @@ public static List getGeoStoreClasses() { List restApiClasses = getRESTclasses(); // merge the classes and create the context - List allClasses = new ArrayList(coreModelClasses.size() - + serviceApiClasses.size() + restApiClasses.size()); + List allClasses = + new ArrayList( + coreModelClasses.size() + serviceApiClasses.size() + restApiClasses.size()); allClasses.addAll(coreModelClasses); allClasses.addAll(serviceApiClasses); allClasses.addAll(restApiClasses); @@ -95,7 +101,8 @@ public static List getGeoStoreClasses() { } private static List> getModelClasses() { - return Arrays.asList(it.geosolutions.geostore.core.model.Attribute.class, + return Arrays.asList( + it.geosolutions.geostore.core.model.Attribute.class, it.geosolutions.geostore.core.model.Category.class, it.geosolutions.geostore.core.model.UserGroup.class, it.geosolutions.geostore.core.model.Resource.class, @@ -110,7 +117,8 @@ private static List> getModelClasses() { } private static List> getAPIclasses() { - return Arrays.asList(it.geosolutions.geostore.services.dto.ShortAttribute.class, + return Arrays.asList( + it.geosolutions.geostore.services.dto.ShortAttribute.class, it.geosolutions.geostore.services.dto.ShortResource.class, it.geosolutions.geostore.services.dto.search.AndFilter.class, it.geosolutions.geostore.services.dto.search.AttributeFilter.class, @@ -121,23 +129,24 @@ private static List> getAPIclasses() { it.geosolutions.geostore.services.dto.search.OrFilter.class, it.geosolutions.geostore.services.dto.search.SearchFilter.class, it.geosolutions.geostore.services.dto.search.SearchOperator.class); - } private static List> getRESTclasses() { - return Arrays - .asList(it.geosolutions.geostore.services.rest.model.CategoryList.class, - it.geosolutions.geostore.services.rest.model.UserGroupList.class, - it.geosolutions.geostore.services.rest.model.RESTResource.class, - it.geosolutions.geostore.services.rest.model.RESTCategory.class, - it.geosolutions.geostore.services.rest.model.StoredDataList.class, - it.geosolutions.geostore.services.rest.model.ShortAttributeList.class, - it.geosolutions.geostore.services.rest.model.UserList.class, - it.geosolutions.geostore.services.rest.model.ShortResourceList.class, - it.geosolutions.geostore.services.rest.model.RESTStoredData.class, - it.geosolutions.geostore.services.rest.model.RESTQuickBackup.class, - it.geosolutions.geostore.services.rest.model.RESTQuickBackup.RESTBackupCategory.class, - it.geosolutions.geostore.services.rest.model.RESTQuickBackup.RESTBackupResource.class); + return Arrays.asList( + it.geosolutions.geostore.services.rest.model.CategoryList.class, + it.geosolutions.geostore.services.rest.model.UserGroupList.class, + it.geosolutions.geostore.services.rest.model.RESTResource.class, + it.geosolutions.geostore.services.rest.model.RESTCategory.class, + it.geosolutions.geostore.services.rest.model.StoredDataList.class, + it.geosolutions.geostore.services.rest.model.ShortAttributeList.class, + it.geosolutions.geostore.services.rest.model.UserList.class, + it.geosolutions.geostore.services.rest.model.ShortResourceList.class, + it.geosolutions.geostore.services.rest.model.RESTStoredData.class, + it.geosolutions.geostore.services.rest.model.RESTQuickBackup.class, + it.geosolutions.geostore.services.rest.model.RESTQuickBackup.RESTBackupCategory + .class, + it.geosolutions.geostore.services.rest.model.RESTQuickBackup.RESTBackupResource + .class); } public static JAXBContext getContext() { @@ -147,19 +156,19 @@ public static JAXBContext getContext() { } /** - * Scans all classes accessible from the context class loader which belong to the given package and subpackages. - * + * Scans all classes accessible from the context class loader which belong to the given package + * and subpackages. + * * @param packageName The base package * @return The classes * @throws ClassNotFoundException * @throws IOException */ @SuppressWarnings("unchecked") - private static List getClasses(String packageName) throws ClassNotFoundException, - IOException { + private static List getClasses(String packageName) + throws ClassNotFoundException, IOException { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Loading classes from " + packageName); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Loading classes from " + packageName); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); assert classLoader != null; String path = packageName.replace('.', '/'); @@ -169,8 +178,7 @@ private static List getClasses(String packageName) throws ClassNotFoundEx URL resource = resources.nextElement(); String fileName = resource.getFile(); - if (LOGGER.isDebugEnabled()) - LOGGER.debug(" adding resource " + fileName); + if (LOGGER.isDebugEnabled()) LOGGER.debug(" adding resource " + fileName); String fileNameDecoded = URLDecoder.decode(fileName, "UTF-8"); dirs.add(new File(fileNameDecoded)); @@ -184,9 +192,9 @@ private static List getClasses(String packageName) throws ClassNotFoundEx /** * Recursive method used to find all classes in a given directory and subdirs. - * - * This method is not useful if classes are inside a jar file. - * + * + *

    This method is not useful if classes are inside a jar file. + * * @param directory The base directory * @param packageName The package name for classes found inside the base directory * @return The classes @@ -210,19 +218,26 @@ private static List findClasses(File directory, String packageName) } else if (fileName.endsWith(".class") && !fileName.contains("$")) { Class _class; try { - _class = Class.forName(packageName + '.' - + fileName.substring(0, fileName.length() - 6)); + _class = + Class.forName( + packageName + + '.' + + fileName.substring(0, fileName.length() - 6)); } catch (ExceptionInInitializerError e) { // happen, for example, in classes, which depend on // Spring to inject some beans, and which fail, // if dependency is not fulfilled - _class = Class.forName( - packageName + '.' + fileName.substring(0, fileName.length() - 6), - false, Thread.currentThread().getContextClassLoader()); + _class = + Class.forName( + packageName + + '.' + + fileName.substring(0, fileName.length() - 6), + false, + Thread.currentThread().getContextClassLoader()); } classes.add(_class); } } return classes; } -} \ No newline at end of file +} diff --git a/src/modules/rest/api/src/test/java/it/geosolutions/geostore/services/providers/StringConversionTest.java b/src/modules/rest/api/src/test/java/it/geosolutions/geostore/services/providers/StringConversionTest.java index 4543943d..74350153 100644 --- a/src/modules/rest/api/src/test/java/it/geosolutions/geostore/services/providers/StringConversionTest.java +++ b/src/modules/rest/api/src/test/java/it/geosolutions/geostore/services/providers/StringConversionTest.java @@ -1,18 +1,18 @@ /* Copyright (C) 2007 - 2012 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -20,32 +20,24 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; - import javax.ws.rs.core.MediaType; - +import junit.framework.TestCase; import org.jdom.JDOMException; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; -import junit.framework.TestCase; - -/** - * - * @author Lorenzo Natali, GeoSolutions S.a.s. - */ +/** @author Lorenzo Natali, GeoSolutions S.a.s. */ public class StringConversionTest extends TestCase { - final String TEST_STRING = "àòèòù"; - public StringConversionTest() { - } + final String TEST_STRING = "àòèòù"; + + public StringConversionTest() {} @BeforeClass - public static void setUpClass() throws Exception { - } + public static void setUpClass() throws Exception {} @AfterClass - public static void tearDownClass() throws Exception { - } + public static void tearDownClass() throws Exception {} @Test public void testStringConversion() throws JDOMException, IOException { diff --git a/src/modules/rest/api/src/test/java/it/geosolutions/geostore/services/rest/utils/FilterUnmarshalTest.java b/src/modules/rest/api/src/test/java/it/geosolutions/geostore/services/rest/utils/FilterUnmarshalTest.java index 1f382346..d49fa563 100644 --- a/src/modules/rest/api/src/test/java/it/geosolutions/geostore/services/rest/utils/FilterUnmarshalTest.java +++ b/src/modules/rest/api/src/test/java/it/geosolutions/geostore/services/rest/utils/FilterUnmarshalTest.java @@ -1,18 +1,18 @@ /* Copyright (C) 2007 - 2012 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -49,30 +49,25 @@ import org.junit.BeforeClass; import org.junit.Test; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class FilterUnmarshalTest extends TestCase { - public FilterUnmarshalTest() { - } + public FilterUnmarshalTest() {} @BeforeClass - public static void setUpClass() throws Exception { - } + public static void setUpClass() throws Exception {} @AfterClass - public static void tearDownClass() throws Exception { - } + public static void tearDownClass() throws Exception {} @Test public void testGetContext() throws JDOMException, IOException { - SearchFilter searchFilter = new AndFilter(new FieldFilter(BaseField.NAME, "%", - SearchOperator.LIKE), - new CategoryFilter("theCategoryName", SearchOperator.EQUAL_TO), - new AttributeFilter("theLayerName", "layer", DataType.STRING, - SearchOperator.EQUAL_TO)); + SearchFilter searchFilter = + new AndFilter( + new FieldFilter(BaseField.NAME, "%", SearchOperator.LIKE), + new CategoryFilter("theCategoryName", SearchOperator.EQUAL_TO), + new AttributeFilter( + "theLayerName", "layer", DataType.STRING, SearchOperator.EQUAL_TO)); StringWriter writer = new StringWriter(); JAXB.marshal(searchFilter, writer); String xml = writer.toString(); @@ -97,11 +92,14 @@ public void testPrintRESTResource() throws JDOMException, IOException { rr.setId(42L); rr.setName("TestResource"); rr.setDescription("This is a sample RESTResource"); + rr.setAdvertised(true); rr.setCreation(new Date()); rr.setLastUpdate(new Date()); rr.setCategory(new RESTCategory("TestCategory")); rr.setData("Sample data content"); rr.setMetadata("Sample metadata content"); + rr.setCreator("User1"); + rr.setEditor("User2"); List attr = new ArrayList(); attr.add(new ShortAttribute("attname1", "attvalue1", DataType.STRING)); @@ -139,7 +137,6 @@ public void testPrintBackup() throws JDOMException, IOException, JAXBException { String xml = writer.toString(); System.out.println("Marshalled Backup is " + xml); - } protected static RESTBackupResource createBKResource(String name, String catName) { @@ -156,5 +153,4 @@ protected static RESTResource createRESTResource(String name, String catName) { rr1.getAttribute().add(new ShortAttribute("att_x_" + name, "test", DataType.STRING)); return rr1; } - } diff --git a/src/modules/rest/api/src/test/java/it/geosolutions/geostore/services/rest/utils/GeoStoreJAXBContextTest.java b/src/modules/rest/api/src/test/java/it/geosolutions/geostore/services/rest/utils/GeoStoreJAXBContextTest.java index 79ae737d..0224464d 100644 --- a/src/modules/rest/api/src/test/java/it/geosolutions/geostore/services/rest/utils/GeoStoreJAXBContextTest.java +++ b/src/modules/rest/api/src/test/java/it/geosolutions/geostore/services/rest/utils/GeoStoreJAXBContextTest.java @@ -1,45 +1,40 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.services.rest.utils; +import static org.junit.Assert.*; + import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; -import static org.junit.Assert.*; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class GeoStoreJAXBContextTest { - public GeoStoreJAXBContextTest() { - } + public GeoStoreJAXBContextTest() {} @BeforeClass - public static void setUpClass() throws Exception { - } + public static void setUpClass() throws Exception {} @AfterClass - public static void tearDownClass() throws Exception { - } + public static void tearDownClass() throws Exception {} @Test public void testGetContext() { diff --git a/src/modules/rest/api/src/test/resources/log4j.properties b/src/modules/rest/api/src/test/resources/log4j.properties deleted file mode 100644 index e8e0887e..00000000 --- a/src/modules/rest/api/src/test/resources/log4j.properties +++ /dev/null @@ -1,7 +0,0 @@ -log4j.rootLogger=INFO, consoleAppender - -log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender -log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout -log4j.appender.consoleAppender.layout.ConversionPattern=%p %d{yyyy-MM-dd HH:mm:ss.SSS} %C{1}.%M() - %m %n - -log4j.logger.it.geosolutions.geostore.services.rest.utils=DEBUG diff --git a/src/modules/rest/api/src/test/resources/log4j2.properties b/src/modules/rest/api/src/test/resources/log4j2.properties new file mode 100644 index 00000000..fb29abb7 --- /dev/null +++ b/src/modules/rest/api/src/test/resources/log4j2.properties @@ -0,0 +1,13 @@ +rootLogger.level = INFO +appenders= console + + +appender.console.type = Console +appender.console.name = LogToConsole +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = %p %d{yyyy-MM-dd HH:mm:ss.SSS} %c::%M:%L - %m%n +rootLogger.appenderRef.stdout.ref = LogToConsole +rootLogger.appenderRef.console.ref = LogToConsole + +logger.restsrvutils.name=it.geosolutions.geostore.services.rest.utils +logger.restsrvutils.level= DEBUG diff --git a/src/modules/rest/auditing/pom.xml b/src/modules/rest/auditing/pom.xml index 897fc29a..7d8f0d1e 100644 --- a/src/modules/rest/auditing/pom.xml +++ b/src/modules/rest/auditing/pom.xml @@ -23,18 +23,18 @@ it.geosolutions.geostore geostore-rest-root - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-rest-auditing GeoStore - Modules - REST auditing org.apache.cxf - cxf-rt-core + cxf-rt-transports-http - log4j - log4j + org.apache.logging.log4j + log4j-core commons-io @@ -43,7 +43,6 @@ org.freemarker freemarker - 2.3.20 junit @@ -51,7 +50,7 @@ javax.servlet - servlet-api + javax.servlet-api org.apache.cxf diff --git a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditInfo.java b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditInfo.java index eefe4cc3..3a9f9ad3 100644 --- a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditInfo.java +++ b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditInfo.java @@ -28,7 +28,6 @@ package it.geosolutions.geostore.services.rest.auditing; enum AuditInfo { - HTTP_METHOD("httpMethod"), PATH("path"), BASE_PATH("basePath"), @@ -59,4 +58,4 @@ enum AuditInfo { String getKey() { return key; } -} \ No newline at end of file +} diff --git a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditInfoExtractor.java b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditInfoExtractor.java index ba51cb07..b66362b9 100644 --- a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditInfoExtractor.java +++ b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditInfoExtractor.java @@ -29,13 +29,6 @@ import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserGroup; -import org.apache.cxf.io.CachedOutputStream; -import org.apache.cxf.message.Message; -import org.apache.cxf.transport.http.AbstractHTTPDestination; -import org.apache.log4j.Logger; -import org.springframework.security.core.Authentication; - -import javax.servlet.http.HttpServletRequest; import java.io.InputStream; import java.io.OutputStream; import java.security.Principal; @@ -44,15 +37,21 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.servlet.http.HttpServletRequest; +import org.apache.cxf.io.CachedOutputStream; +import org.apache.cxf.message.Message; +import org.apache.cxf.transport.http.AbstractHTTPDestination; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.security.core.Authentication; final class AuditInfoExtractor { - private static final Logger LOGGER = Logger.getLogger(AuditInfoExtractor.class); + private static final Logger LOGGER = LogManager.getLogger(AuditInfoExtractor.class); private static final Pattern geoStorePath = Pattern.compile("/geostore/(.*)"); - private AuditInfoExtractor() { - } + private AuditInfoExtractor() {} static Map extract(Message message) { Map auditInfo = new HashMap(); @@ -67,13 +66,10 @@ static Map extract(Message message) { return auditInfo; } - static Integer - getResponseLength(Message message) { - try { - CachedOutputStream outputStream = (CachedOutputStream) message.getContent(OutputStream.class); - if (outputStream != null) { - return outputStream.size(); - } + static Long getResponseLength(Message message) { + try (CachedOutputStream outputStream = + (CachedOutputStream) message.getContent(OutputStream.class)) { + if (outputStream != null) return outputStream.size(); } catch (Exception exception) { LogUtils.error(LOGGER, exception, "Error obtaining response length."); } @@ -86,19 +82,36 @@ private static void handleInMessage(Map auditInfo, Message messa return; } try { - auditInfo.put(AuditInfo.HTTP_METHOD.getKey(), safeToString(message.get(Message.HTTP_REQUEST_METHOD))); - auditInfo.put(AuditInfo.PATH.getKey(), removeGeoStore((String) message.get(Message.PATH_INFO))); - auditInfo.put(AuditInfo.BASE_PATH.getKey(), removeGeoStore((String) message.get(Message.BASE_PATH))); - auditInfo.put(AuditInfo.QUERY_STRING.getKey(), safeToString(message.get(Message.QUERY_STRING))); - HttpServletRequest httpServletRequest = (HttpServletRequest) message.get(AbstractHTTPDestination.HTTP_REQUEST); - auditInfo.put(AuditInfo.REMOTE_ADDR.getKey(), safeToString(httpServletRequest.getRemoteAddr())); - auditInfo.put(AuditInfo.REMOTE_HOST.getKey(), safeToString(httpServletRequest.getRemoteHost())); - auditInfo.put(AuditInfo.REMOTE_USER.getKey(), safeToString(httpServletRequest.getRemoteUser())); - auditInfo.put(AuditInfo.HOST.getKey(), safeToString(httpServletRequest.getServerName())); + auditInfo.put( + AuditInfo.HTTP_METHOD.getKey(), + safeToString(message.get(Message.HTTP_REQUEST_METHOD))); + auditInfo.put( + AuditInfo.PATH.getKey(), + removeGeoStore((String) message.get(Message.PATH_INFO))); + auditInfo.put( + AuditInfo.BASE_PATH.getKey(), + removeGeoStore((String) message.get(Message.BASE_PATH))); + auditInfo.put( + AuditInfo.QUERY_STRING.getKey(), + safeToString(message.get(Message.QUERY_STRING))); + HttpServletRequest httpServletRequest = + (HttpServletRequest) message.get(AbstractHTTPDestination.HTTP_REQUEST); + auditInfo.put( + AuditInfo.REMOTE_ADDR.getKey(), + safeToString(httpServletRequest.getRemoteAddr())); + auditInfo.put( + AuditInfo.REMOTE_HOST.getKey(), + safeToString(httpServletRequest.getRemoteHost())); + auditInfo.put( + AuditInfo.REMOTE_USER.getKey(), + safeToString(httpServletRequest.getRemoteUser())); + auditInfo.put( + AuditInfo.HOST.getKey(), safeToString(httpServletRequest.getServerName())); fillAuthInfo(auditInfo, httpServletRequest); - auditInfo.put(AuditInfo.BODY_AS_STRING.getKey(), getPaylod(message)); + auditInfo.put(AuditInfo.BODY_AS_STRING.getKey(), getPayload(message)); } catch (Exception exception) { - LogUtils.error(LOGGER, exception, "Error obtaining auditing information for input message."); + LogUtils.error( + LOGGER, exception, "Error obtaining auditing information for input message."); } } @@ -106,9 +119,14 @@ private static void handleOutSuccessMessage(Map auditInfo, Messa if (message == null) { return; } - auditInfo.put(AuditInfo.RESPONSE_STATUS_CODE.getKey(), safeToString(message.get(Message.RESPONSE_CODE))); - auditInfo.put(AuditInfo.RESPONSE_CONTENT_TYPE.getKey(), safeToString(message.get(Message.CONTENT_TYPE))); - auditInfo.put(AuditInfo.RESPONSE_LENGTH.getKey(), + auditInfo.put( + AuditInfo.RESPONSE_STATUS_CODE.getKey(), + safeToString(message.get(Message.RESPONSE_CODE))); + auditInfo.put( + AuditInfo.RESPONSE_CONTENT_TYPE.getKey(), + safeToString(message.get(Message.CONTENT_TYPE))); + auditInfo.put( + AuditInfo.RESPONSE_LENGTH.getKey(), safeToString(message.getExchange().get(AuditInfo.RESPONSE_LENGTH.getKey()))); } @@ -123,9 +141,13 @@ private static void handleOutFaultMessage(Map auditInfo, Message } else { auditInfo.put(AuditInfo.ERROR_MESSAGE.getKey(), ""); } - auditInfo.put(AuditInfo.RESPONSE_CONTENT_TYPE.getKey(), safeToString(message.get(Message.CONTENT_TYPE))); + auditInfo.put( + AuditInfo.RESPONSE_CONTENT_TYPE.getKey(), + safeToString(message.get(Message.CONTENT_TYPE))); auditInfo.put(AuditInfo.RESPONSE_LENGTH.getKey(), safeToString(getResponseLength(message))); - auditInfo.put(AuditInfo.RESPONSE_STATUS_CODE.getKey(), safeToString(safeToString(message.get(Message.RESPONSE_CODE)))); + auditInfo.put( + AuditInfo.RESPONSE_STATUS_CODE.getKey(), + safeToString(safeToString(message.get(Message.RESPONSE_CODE)))); } private static void handleTime(Map auditInfo, Object startTimeObject) { @@ -146,12 +168,11 @@ private static String safeToString(Object value) { return value.toString(); } - private static String getPaylod(Message message) { - InputStream inputStream = message.getContent(InputStream.class); - if (inputStream == null) { - return ""; - } - try { + private static String getPayload(Message message) { + try (InputStream inputStream = message.getContent(InputStream.class)) { + if (inputStream == null) { + return ""; + } return inputStream.toString(); } catch (Exception exception) { LogUtils.error(LOGGER, exception, "Error reading payload."); @@ -159,7 +180,8 @@ private static String getPaylod(Message message) { return ""; } - private static void fillAuthInfo(Map info, HttpServletRequest httpServletRequest) { + private static void fillAuthInfo( + Map info, HttpServletRequest httpServletRequest) { Principal userPrincipal = httpServletRequest.getUserPrincipal(); String userName = ""; String userRole = ""; diff --git a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingConfiguration.java b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingConfiguration.java index 8c649922..6edd01ea 100644 --- a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingConfiguration.java +++ b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingConfiguration.java @@ -27,16 +27,16 @@ */ package it.geosolutions.geostore.services.rest.auditing; -import org.apache.commons.io.FileUtils; -import org.apache.log4j.Logger; - import java.io.File; import java.io.FileInputStream; import java.util.Properties; +import org.apache.commons.io.FileUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; final class AuditingConfiguration { - private static final Logger LOGGER = Logger.getLogger(AuditingConfiguration.class); + private static final Logger LOGGER = LogManager.getLogger(AuditingConfiguration.class); static final String CONFIGURATION_PATH = "GEOSTORE_AUDITING_CONF"; @@ -112,7 +112,8 @@ AuditingConfiguration checkForNewConfiguration() { long candidateConfigurationFileChecksum = checksum(candidateConfigurationFile); if (configurationFile.compareTo(candidateConfigurationFile) != 0 || configurationFileChecksum != candidateConfigurationFileChecksum) { - return new AuditingConfiguration(candidateConfigurationFile, candidateConfigurationFileChecksum); + return new AuditingConfiguration( + candidateConfigurationFile, candidateConfigurationFileChecksum); } return null; } @@ -121,7 +122,8 @@ private long checksum(File file) { try { return FileUtils.checksumCRC32(file); } catch (Exception exception) { - throw new AuditingException(exception, "Error computing checksum of file '%s'.", file.getPath()); + throw new AuditingException( + exception, "Error computing checksum of file '%s'.", file.getPath()); } } @@ -136,20 +138,22 @@ private static File findConfigurationFile() { } File configurationFile = new File(configurationFilePath); if (!configurationFile.exists()) { - throw new AuditingException("Configuration file '%s' does not exists.", configurationFile.getPath()); + throw new AuditingException( + "Configuration file '%s' does not exists.", configurationFile.getPath()); } return configurationFile; } private Properties readProperties() { - try { - FileInputStream input = new FileInputStream(configurationFile); + try (FileInputStream input = new FileInputStream(configurationFile)) { Properties properties = new Properties(); properties.load(input); input.close(); return properties; } catch (Exception exception) { - throw new AuditingException(exception, "Error reading properties from configuration file '%s'.", + throw new AuditingException( + exception, + "Error reading properties from configuration file '%s'.", configurationFile.getPath()); } } @@ -161,4 +165,4 @@ private static String getProperty(Properties properties, String propertyName) { } return propertyValue; } -} \ No newline at end of file +} diff --git a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingFilesManager.java b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingFilesManager.java index 9b17c292..a270ab76 100644 --- a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingFilesManager.java +++ b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingFilesManager.java @@ -27,9 +27,6 @@ */ package it.geosolutions.geostore.services.rest.auditing; -import org.apache.commons.io.FileUtils; -import org.apache.log4j.Logger; - import java.io.File; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -38,10 +35,13 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.commons.io.FileUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; final class AuditingFilesManager { - private static final Logger logger = Logger.getLogger(AuditingFilesManager.class); + private static final Logger logger = LogManager.getLogger(AuditingFilesManager.class); private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); @@ -78,7 +78,7 @@ String getCurrentDayTag() { } void makeOutputFileExists() { - if(!outputFile.exists()) { + if (!outputFile.exists()) { createOutputFile(); } } @@ -94,7 +94,8 @@ private void init() { try { FileUtils.forceMkdir(outputDirectory); } catch (Exception exception) { - throw new AuditingException(exception, "Error creating output directory '%s'.", outputDirectory); + throw new AuditingException( + exception, "Error creating output directory '%s'.", outputDirectory); } } } @@ -110,23 +111,34 @@ private void handleExistingOutputDirectory() { private void updateCurrentDay() { String dayTag = dateFormat.format(new Date()); if (currentDayTag == null || !currentDayTag.equals(dayTag)) { - LogUtils.debug(logger, "Current day '%s' will be updates to '%s'.", currentDayTag, dayTag); + LogUtils.debug( + logger, "Current day '%s' will be updates to '%s'.", currentDayTag, dayTag); currentDayTag = dayTag; - filePattern = Pattern.compile("audit-geostore-" + currentDayTag + "-(\\d+)\\." + fileExtension + "$"); + filePattern = + Pattern.compile( + "audit-geostore-" + currentDayTag + "-(\\d+)\\." + fileExtension + "$"); } } private String getRollinFileName(int nextRollingValue) { - return String.format("audit-geostore-%s-%d.%s", currentDayTag, nextRollingValue, fileExtension); + return String.format( + "audit-geostore-%s-%d.%s", currentDayTag, nextRollingValue, fileExtension); } private void moveOutputFile(File rollingFile) { - LogUtils.info(logger, "Rolling output file '%s' to '%s'.", outputFile.getPath(), rollingFile.getPath()); + LogUtils.info( + logger, + "Rolling output file '%s' to '%s'.", + outputFile.getPath(), + rollingFile.getPath()); try { FileUtils.moveFile(outputFile, rollingFile); } catch (Exception exception) { - throw new AuditingException(exception, "Error moving output file '%s' to rolling file '%s'.", - outputFile.getPath(), rollingFile.getPath()); + throw new AuditingException( + exception, + "Error moving output file '%s' to rolling file '%s'.", + outputFile.getPath(), + rollingFile.getPath()); } } @@ -135,7 +147,8 @@ private void createOutputFile() { LogUtils.info(logger, "Creating output file '%s'.", outputFile.getPath()); FileUtils.touch(outputFile); } catch (Exception exception) { - throw new AuditingException(exception, "Error creating output file '%s'.", outputFile.getPath()); + throw new AuditingException( + exception, "Error creating output file '%s'.", outputFile.getPath()); } } @@ -146,7 +159,11 @@ private int getNextRollingValue() { Collections.sort(rollingValues); nextRollingValue = rollingValues.get(rollingValues.size() - 1) + 1; } - LogUtils.debug(logger, "Next rolling value for day '%s' will be '%d'.", currentDayTag, nextRollingValue); + LogUtils.debug( + logger, + "Next rolling value for day '%s' will be '%d'.", + currentDayTag, + nextRollingValue); return nextRollingValue; } @@ -165,9 +182,11 @@ private List getRollingValues() { private String[] listOutputDirectoryFiles() { String[] files = outputDirectory.list(); if (files == null) { - throw new AuditingException("Error listing files of output directory '%s'.", outputDirectory); + throw new AuditingException( + "Error listing files of output directory '%s'.", outputDirectory); } - LogUtils.debug(logger, "Output directory current files: %s.", LogUtils.arrayToString(files)); + LogUtils.debug( + logger, "Output directory current files: %s.", LogUtils.arrayToString(files)); return files; } } diff --git a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingInterceptorPostMarshall.java b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingInterceptorPostMarshall.java index a1eb6fef..b3dd19e6 100644 --- a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingInterceptorPostMarshall.java +++ b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingInterceptorPostMarshall.java @@ -31,21 +31,19 @@ import org.apache.cxf.message.Message; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; -import org.apache.log4j.Logger; public final class AuditingInterceptorPostMarshall extends AbstractPhaseInterceptor { - private static final Logger LOGGER = Logger.getLogger(AuditingInterceptorPostMarshall.class); - public AuditingInterceptorPostMarshall() { super(Phase.POST_MARSHAL); } @Override public void handleMessage(Message message) throws Fault { - Integer responseLength = AuditInfoExtractor.getResponseLength(message); + Long responseLength = AuditInfoExtractor.getResponseLength(message); if (responseLength != null) { - message.getExchange().put(AuditInfo.RESPONSE_LENGTH.getKey(), responseLength.toString()); + message.getExchange() + .put(AuditInfo.RESPONSE_LENGTH.getKey(), responseLength.toString()); } } } diff --git a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingOutput.java b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingOutput.java index e9dbb1dd..f89e5b2a 100644 --- a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingOutput.java +++ b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingOutput.java @@ -27,8 +27,6 @@ */ package it.geosolutions.geostore.services.rest.auditing; -import org.apache.log4j.Logger; - import java.io.FileWriter; import java.util.ArrayList; import java.util.Collections; @@ -36,12 +34,15 @@ import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; final class AuditingOutput { - private static final Logger LOGGER = Logger.getLogger(AuditingOutput.class); + private static final Logger LOGGER = LogManager.getLogger(AuditingOutput.class); - private final BlockingQueue> messagesQueue = new ArrayBlockingQueue>(10000); + private final BlockingQueue> messagesQueue = + new ArrayBlockingQueue>(10000); private AuditingConfiguration configuration; @@ -61,24 +62,30 @@ final class AuditingOutput { LOGGER.info("Auditing enable."); auditEnable = true; templates = new AuditingTemplates(configuration.getTemplatesDirectory()); - auditingFilesManager = new AuditingFilesManager( - configuration.getOutputDirectory(), configuration.getOutputFilesExtension()); + auditingFilesManager = + new AuditingFilesManager( + configuration.getOutputDirectory(), + configuration.getOutputFilesExtension()); openWriter(); final Consumer consumer = new Consumer(); final Thread consumerThread = new Thread(consumer); consumerThread.start(); - Runtime.getRuntime().addShutdownHook(new Thread() { - public void run() { - consumer.running = false; - try { - consumerThread.interrupt(); - consumerThread.join(500); - } catch (InterruptedException exception) { - LOGGER.error("Interrupted when waiting for consumer thread.", exception); - } - closeWriter(); - } - }); + Runtime.getRuntime() + .addShutdownHook( + new Thread() { + public void run() { + consumer.running = false; + try { + consumerThread.interrupt(); + consumerThread.join(500); + } catch (InterruptedException exception) { + LOGGER.error( + "Interrupted when waiting for consumer thread.", + exception); + } + closeWriter(); + } + }); } else { LOGGER.info("Auditing not enable."); } @@ -109,14 +116,18 @@ private void openWriter() { try { writer = new FileWriter(auditingFilesManager.getOutputFile()); } catch (Exception exception) { - throw new AuditingException(exception, "Error open writer for file output '%s'.", + throw new AuditingException( + exception, + "Error open writer for file output '%s'.", auditingFilesManager.getOutputFile().getPath()); } try { templates.getHeaderTemplate().process(Collections.EMPTY_MAP, writer); } catch (Exception exception) { - throw new AuditingException(exception, "Error writing header to file '%s'.", + throw new AuditingException( + exception, + "Error writing header to file '%s'.", auditingFilesManager.getOutputFile().getPath()); } } @@ -126,13 +137,15 @@ private void closeWriter() { templates.getFooterTemplate().process(Collections.EMPTY_MAP, writer); } catch (Exception exception) { - throw new AuditingException("Error writing footer to file output '%s'.", + throw new AuditingException( + "Error writing footer to file output '%s'.", auditingFilesManager.getOutputFile().getPath()); } try { writer.close(); } catch (Exception exception) { - throw new AuditingException("Error closing writer for file output '%s'.", + throw new AuditingException( + "Error closing writer for file output '%s'.", auditingFilesManager.getOutputFile().getPath()); } } diff --git a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingTemplates.java b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingTemplates.java index 95ca509c..3d87b467 100644 --- a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingTemplates.java +++ b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/AuditingTemplates.java @@ -30,9 +30,8 @@ import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateExceptionHandler; -import org.apache.commons.io.FileUtils; - import java.io.File; +import org.apache.commons.io.FileUtils; final class AuditingTemplates { @@ -54,19 +53,28 @@ final class AuditingTemplates { this(templatesDirectoryPath, null, null, null); } - AuditingTemplates(String templatesDirectoryPath, Long headerTemplateChecksum, - Long bodyTemplateChecksum, Long footerTemplateChecksum) { + AuditingTemplates( + String templatesDirectoryPath, + Long headerTemplateChecksum, + Long bodyTemplateChecksum, + Long footerTemplateChecksum) { templatesDirectory = getTemplatesDirectory(templatesDirectoryPath); Configuration configuration = getConfiguration(); headerTemplate = getTemplate(configuration, HEADER); bodyTemplate = getTemplate(configuration, BODY); footerTemplate = getTemplate(configuration, FOOTER); - this.headerTemplateChecksum = headerTemplateChecksum == null ? - checksum(new File(templatesDirectory, "header.ftl")) : headerTemplateChecksum; - this.bodyTemplateChecksum = bodyTemplateChecksum == null ? - checksum(new File(templatesDirectory, "body.ftl")) : bodyTemplateChecksum; - this.footerTemplateChecksum = footerTemplateChecksum == null ? - checksum(new File(templatesDirectory, "footer.ftl")) : footerTemplateChecksum; + this.headerTemplateChecksum = + headerTemplateChecksum == null + ? checksum(new File(templatesDirectory, "header.ftl")) + : headerTemplateChecksum; + this.bodyTemplateChecksum = + bodyTemplateChecksum == null + ? checksum(new File(templatesDirectory, "body.ftl")) + : bodyTemplateChecksum; + this.footerTemplateChecksum = + footerTemplateChecksum == null + ? checksum(new File(templatesDirectory, "footer.ftl")) + : footerTemplateChecksum; } Template getHeaderTemplate() { @@ -90,8 +98,11 @@ AuditingTemplates checkForNewTemplates(String candidateTemplatesDirectoryPath) { || headerTemplateChecksum == candidateHeaderTemplateChecksum || bodyTemplateChecksum == candidateBodyTemplateChecksum || footerTemplateChecksum == candidateFooterTemplateChecksum) { - return new AuditingTemplates(candidateTemplatesDirectoryPath, candidateHeaderTemplateChecksum, - candidateBodyTemplateChecksum, candidateFooterTemplateChecksum); + return new AuditingTemplates( + candidateTemplatesDirectoryPath, + candidateHeaderTemplateChecksum, + candidateBodyTemplateChecksum, + candidateFooterTemplateChecksum); } return null; } @@ -99,7 +110,8 @@ AuditingTemplates checkForNewTemplates(String candidateTemplatesDirectoryPath) { private File getTemplatesDirectory(String templatesDirectoryPath) { File file = new File(templatesDirectoryPath); if (!file.exists()) { - throw new AuditingException("Templates directory '%s' does not exists.", templatesDirectoryPath); + throw new AuditingException( + "Templates directory '%s' does not exists.", templatesDirectoryPath); } return file; } @@ -112,7 +124,9 @@ private Configuration getConfiguration() { configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); return configuration; } catch (Exception exception) { - throw new AuditingException(exception, "Error initiating templates configuration from directory '%s'.", + throw new AuditingException( + exception, + "Error initiating templates configuration from directory '%s'.", templatesDirectory.getPath()); } } @@ -129,7 +143,8 @@ private long checksum(File file) { try { return FileUtils.checksumCRC32(file); } catch (Exception exception) { - throw new AuditingException(exception, "Error computign checksum of file '%s'.", file.getPath()); + throw new AuditingException( + exception, "Error computign checksum of file '%s'.", file.getPath()); } } } diff --git a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/LogUtils.java b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/LogUtils.java index be6d75b3..6ab960d8 100644 --- a/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/LogUtils.java +++ b/src/modules/rest/auditing/src/main/java/it/geosolutions/geostore/services/rest/auditing/LogUtils.java @@ -27,12 +27,11 @@ */ package it.geosolutions.geostore.services.rest.auditing; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.Logger; final class LogUtils { - private LogUtils() { - } + private LogUtils() {} static void info(Logger logger, String formattedMessage, Object... messageArguments) { if (logger.isInfoEnabled()) { @@ -46,7 +45,11 @@ static void debug(Logger logger, String formattedMessage, Object... messageArgum } } - static void error(Logger logger, Throwable exception, String formattedMessage, Object... messageArguments) { + static void error( + Logger logger, + Throwable exception, + String formattedMessage, + Object... messageArguments) { logger.error(String.format(formattedMessage, messageArguments), exception); } diff --git a/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditInfoExtractorTest.java b/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditInfoExtractorTest.java index 1477a776..ed76d76b 100644 --- a/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditInfoExtractorTest.java +++ b/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditInfoExtractorTest.java @@ -27,9 +27,17 @@ */ package it.geosolutions.geostore.services.rest.auditing; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.core.model.enums.Role; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collections; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; import org.apache.cxf.io.CachedOutputStream; import org.apache.cxf.message.Exchange; import org.apache.cxf.message.Message; @@ -38,23 +46,15 @@ import org.mockito.Mockito; import org.springframework.security.core.Authentication; -import javax.servlet.http.HttpServletRequest; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Collections; -import java.util.Map; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - public final class AuditInfoExtractorTest extends AuditingTestsBase { private static HttpServletRequest getHttpServletRequest() { HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); Mockito.when(httpServletRequest.getRemoteAddr()).thenReturn("127.0.0.1"); Mockito.when(httpServletRequest.getRemoteHost()).thenReturn("127.0.0.1"); - Mockito.when(httpServletRequest.getRemoteUser()). - thenReturn("User[id=2, name=admin, group=[UserGroup[id=1, groupName=everyone]], role=ADMIN]"); + Mockito.when(httpServletRequest.getRemoteUser()) + .thenReturn( + "User[id=2, name=admin, group=[UserGroup[id=1, groupName=everyone]], role=ADMIN]"); Mockito.when(httpServletRequest.getServerName()).thenReturn("localhost"); UserGroup userGroup = Mockito.mock(UserGroup.class); Mockito.when(userGroup.getGroupName()).thenReturn("everyone"); @@ -81,7 +81,8 @@ private static Message getInMessage() { Mockito.when(inMessage.get(Message.BASE_PATH)).thenReturn("/geostore/users"); Mockito.when(inMessage.get(Message.QUERY_STRING)).thenReturn(null); HttpServletRequest httpServletRequest = getHttpServletRequest(); - Mockito.when(inMessage.get(AbstractHTTPDestination.HTTP_REQUEST)).thenReturn(httpServletRequest); + Mockito.when(inMessage.get(AbstractHTTPDestination.HTTP_REQUEST)) + .thenReturn(httpServletRequest); InputStream inputStream = getInputStream("body-content"); Mockito.when(inMessage.getContent(InputStream.class)).thenReturn(inputStream); return inMessage; @@ -90,7 +91,8 @@ private static Message getInMessage() { private static Message getOutSuccessMessage() { Message outSuccessMessage = Mockito.mock(Message.class); Mockito.when(outSuccessMessage.get(Message.RESPONSE_CODE)).thenReturn("200"); - Mockito.when(outSuccessMessage.get(Message.CONTENT_TYPE)).thenReturn("application/octet-stream"); + Mockito.when(outSuccessMessage.get(Message.CONTENT_TYPE)) + .thenReturn("application/octet-stream"); Exchange exchange = Mockito.mock(Exchange.class); Mockito.when(exchange.get(AuditInfo.RESPONSE_LENGTH.getKey())).thenReturn(150); Mockito.when(outSuccessMessage.getExchange()).thenReturn(exchange); @@ -99,15 +101,17 @@ private static Message getOutSuccessMessage() { private static CachedOutputStream getCacheOutputStream() { CachedOutputStream outputStream = Mockito.mock(CachedOutputStream.class); - Mockito.when(outputStream.size()).thenReturn(100); + Mockito.when(outputStream.size()).thenReturn(100L); return outputStream; } private static Message getOutFaultMessage() { Message outFaultMessage = Mockito.mock(Message.class); - Mockito.when(outFaultMessage.getContent(Exception.class)).thenReturn(new Exception("exception-message")); + Mockito.when(outFaultMessage.getContent(Exception.class)) + .thenReturn(new Exception("exception-message")); Mockito.when(outFaultMessage.get(Message.RESPONSE_CODE)).thenReturn("500"); - Mockito.when(outFaultMessage.get(Message.CONTENT_TYPE)).thenReturn("application/octet-stream"); + Mockito.when(outFaultMessage.get(Message.CONTENT_TYPE)) + .thenReturn("application/octet-stream"); CachedOutputStream outputStream = getCacheOutputStream(); Mockito.when(outFaultMessage.getContent(OutputStream.class)).thenReturn(outputStream); return outFaultMessage; @@ -126,7 +130,9 @@ public void testSuccessExecution() { Map auditInfo = AuditInfoExtractor.extract(message); assertEquals(auditInfo.size(), 18); assertEquals(auditInfo.get(AuditInfo.HOST.getKey()), "localhost"); - assertEquals(auditInfo.get(AuditInfo.RESPONSE_CONTENT_TYPE.getKey()), "application/octet-stream"); + assertEquals( + auditInfo.get(AuditInfo.RESPONSE_CONTENT_TYPE.getKey()), + "application/octet-stream"); assertEquals(auditInfo.get(AuditInfo.HTTP_METHOD.getKey()), "GET"); assertEquals(auditInfo.get(AuditInfo.BODY_AS_STRING.getKey()), "body-content"); assertEquals(auditInfo.get(AuditInfo.USER_ROLE.getKey()), "ADMIN"); @@ -140,10 +146,12 @@ public void testSuccessExecution() { assertEquals(auditInfo.get(AuditInfo.PATH.getKey()), "users/user/15"); assertEquals(auditInfo.get(AuditInfo.USER_NAME.getKey()), "admin"); assertEquals(auditInfo.get(AuditInfo.REMOTE_ADDR.getKey()), "127.0.0.1"); - assertEquals(auditInfo.get(AuditInfo.REMOTE_USER.getKey()), + assertEquals( + auditInfo.get(AuditInfo.REMOTE_USER.getKey()), "User[id=2, name=admin, group=[UserGroup[id=1, groupName=everyone]], role=ADMIN]"); assertNotNull(auditInfo.get(AuditInfo.END_TIME.getKey())); - assertEquals(Long.parseLong(auditInfo.get(AuditInfo.TOTAL_TIME.getKey())), + assertEquals( + Long.parseLong(auditInfo.get(AuditInfo.TOTAL_TIME.getKey())), Long.parseLong(auditInfo.get(AuditInfo.END_TIME.getKey())) - 1000); } @@ -160,7 +168,9 @@ public void testFaultExecution() { Map auditInfo = AuditInfoExtractor.extract(message); assertEquals(auditInfo.size(), 20); assertEquals(auditInfo.get(AuditInfo.HOST.getKey()), "localhost"); - assertEquals(auditInfo.get(AuditInfo.RESPONSE_CONTENT_TYPE.getKey()), "application/octet-stream"); + assertEquals( + auditInfo.get(AuditInfo.RESPONSE_CONTENT_TYPE.getKey()), + "application/octet-stream"); assertEquals(auditInfo.get(AuditInfo.HTTP_METHOD.getKey()), "GET"); assertEquals(auditInfo.get(AuditInfo.BODY_AS_STRING.getKey()), "body-content"); assertEquals(auditInfo.get(AuditInfo.USER_ROLE.getKey()), "ADMIN"); @@ -174,12 +184,14 @@ public void testFaultExecution() { assertEquals(auditInfo.get(AuditInfo.PATH.getKey()), "users/user/15"); assertEquals(auditInfo.get(AuditInfo.USER_NAME.getKey()), "admin"); assertEquals(auditInfo.get(AuditInfo.REMOTE_ADDR.getKey()), "127.0.0.1"); - assertEquals(auditInfo.get(AuditInfo.REMOTE_USER.getKey()), + assertEquals( + auditInfo.get(AuditInfo.REMOTE_USER.getKey()), "User[id=2, name=admin, group=[UserGroup[id=1, groupName=everyone]], role=ADMIN]"); assertEquals(auditInfo.get(AuditInfo.ERROR_MESSAGE.getKey()), "exception-message"); assertEquals(auditInfo.get(AuditInfo.FAILED.getKey()), "true"); assertNotNull(auditInfo.get(AuditInfo.END_TIME.getKey())); - assertEquals(Long.parseLong(auditInfo.get(AuditInfo.TOTAL_TIME.getKey())), + assertEquals( + Long.parseLong(auditInfo.get(AuditInfo.TOTAL_TIME.getKey())), Long.parseLong(auditInfo.get(AuditInfo.END_TIME.getKey())) - 1000); } } diff --git a/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingConfigurationTest.java b/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingConfigurationTest.java index 9fd31eca..cb7e203f 100644 --- a/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingConfigurationTest.java +++ b/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingConfigurationTest.java @@ -27,13 +27,12 @@ */ package it.geosolutions.geostore.services.rest.auditing; -import org.junit.Test; - -import java.util.Map; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import java.util.Map; +import org.junit.Test; + public final class AuditingConfigurationTest extends AuditingTestsBase { @Test @@ -42,20 +41,25 @@ public void testSimpleConfiguration() { assertEquals(auditingConfiguration.isAuditEnable(), true); assertEquals(auditingConfiguration.getMaxRequestPerFile(), 3); assertEquals(auditingConfiguration.getTemplatesVersion(), 1); - assertEquals(auditingConfiguration.getOutputDirectory(), OUTPUT_DIRECTORY.getAbsolutePath()); + assertEquals( + auditingConfiguration.getOutputDirectory(), OUTPUT_DIRECTORY.getAbsolutePath()); assertEquals(auditingConfiguration.getOutputFilesExtension(), "txt"); - assertEquals(auditingConfiguration.getTemplatesDirectory(), TEMPLATES_DIRECTORY.getAbsolutePath()); + assertEquals( + auditingConfiguration.getTemplatesDirectory(), + TEMPLATES_DIRECTORY.getAbsolutePath()); } @Test public void testUpdateConfiguration() { AuditingConfiguration auditingConfiguration = new AuditingConfiguration(); assertEquals(auditingConfiguration.isAuditEnable(), true); - Map properties = AuditingTestsUtils.getDefaultProperties(OUTPUT_DIRECTORY, TEMPLATES_DIRECTORY); + Map properties = + AuditingTestsUtils.getDefaultProperties(OUTPUT_DIRECTORY, TEMPLATES_DIRECTORY); properties.put(AuditingConfiguration.AUDIT_ENABLE, "false"); - AuditingTestsUtils.createFile(CONFIGURATION_FILE_PATH, - AuditingTestsUtils.propertiesToString(properties)); - AuditingConfiguration newAuditingConfiguration = auditingConfiguration.checkForNewConfiguration(); + AuditingTestsUtils.createFile( + CONFIGURATION_FILE_PATH, AuditingTestsUtils.propertiesToString(properties)); + AuditingConfiguration newAuditingConfiguration = + auditingConfiguration.checkForNewConfiguration(); assertNotNull(newAuditingConfiguration); } } diff --git a/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingFilesManagerTest.java b/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingFilesManagerTest.java index a848300c..f5612587 100644 --- a/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingFilesManagerTest.java +++ b/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingFilesManagerTest.java @@ -27,11 +27,10 @@ */ package it.geosolutions.geostore.services.rest.auditing; -import org.junit.Test; +import static org.junit.Assert.assertEquals; import java.io.File; - -import static org.junit.Assert.assertEquals; +import org.junit.Test; public final class AuditingFilesManagerTest extends AuditingTestsBase { @@ -47,16 +46,19 @@ public void testCleanInit() { @Test public void testInitWithExistingFiles() { AuditingTestsUtils.createFile( - new File(OUTPUT_DIRECTORY, "audit-geostore.txt"), - "existing1"); + new File(OUTPUT_DIRECTORY, "audit-geostore.txt"), "existing1"); AuditingFilesManager auditingFilesManager = new AuditingFilesManager(OUTPUT_DIRECTORY.getPath(), "txt"); File expectedOutputFile = new File(OUTPUT_DIRECTORY, "audit-geostore.txt"); - File expectedExistingFile = new File(OUTPUT_DIRECTORY, - String.format("audit-geostore-%s-1.txt", auditingFilesManager.getCurrentDayTag())); + File expectedExistingFile = + new File( + OUTPUT_DIRECTORY, + String.format( + "audit-geostore-%s-1.txt", + auditingFilesManager.getCurrentDayTag())); assertEquals(auditingFilesManager.getOutputFile(), expectedOutputFile); - AuditingTestsUtils.checkDirectoryContainsFiles(OUTPUT_DIRECTORY, - expectedOutputFile, expectedExistingFile); + AuditingTestsUtils.checkDirectoryContainsFiles( + OUTPUT_DIRECTORY, expectedOutputFile, expectedExistingFile); AuditingTestsUtils.checkFileExistsWithContent(expectedExistingFile, "existing1"); AuditingTestsUtils.checkFileExistsWithContent(expectedOutputFile, ""); } @@ -70,20 +72,29 @@ public void testRollingFiles() { AuditingTestsUtils.checkDirectoryContainsFiles(OUTPUT_DIRECTORY, expectedOutputFile); AuditingTestsUtils.writeToFile(expectedOutputFile, "rolled1"); auditingFilesManager.rollOutputFile(); - File expectedExistingFile1 = new File(OUTPUT_DIRECTORY, - String.format("audit-geostore-%s-1.txt", auditingFilesManager.getCurrentDayTag())); - AuditingTestsUtils.checkDirectoryContainsFiles(OUTPUT_DIRECTORY, - expectedOutputFile, expectedExistingFile1); + File expectedExistingFile1 = + new File( + OUTPUT_DIRECTORY, + String.format( + "audit-geostore-%s-1.txt", + auditingFilesManager.getCurrentDayTag())); + AuditingTestsUtils.checkDirectoryContainsFiles( + OUTPUT_DIRECTORY, expectedOutputFile, expectedExistingFile1); AuditingTestsUtils.checkFileExistsWithContent(expectedExistingFile1, "rolled1"); AuditingTestsUtils.checkFileExistsWithContent(expectedOutputFile, ""); String previousCurrentTagDay = auditingFilesManager.getCurrentDayTag(); AuditingTestsUtils.writeToFile(expectedOutputFile, "rolled2"); auditingFilesManager.rollOutputFile(); - int rolledFileIndex = previousCurrentTagDay.equals(auditingFilesManager.getCurrentDayTag()) ? 2 : 1; - File expectedExistingFile2 = new File(OUTPUT_DIRECTORY, - String.format("audit-geostore-%s-%d.txt", auditingFilesManager.getCurrentDayTag(), rolledFileIndex)); - AuditingTestsUtils.checkDirectoryContainsFiles(OUTPUT_DIRECTORY, - expectedOutputFile, expectedExistingFile1, expectedExistingFile2); + int rolledFileIndex = + previousCurrentTagDay.equals(auditingFilesManager.getCurrentDayTag()) ? 2 : 1; + File expectedExistingFile2 = + new File( + OUTPUT_DIRECTORY, + String.format( + "audit-geostore-%s-%d.txt", + auditingFilesManager.getCurrentDayTag(), rolledFileIndex)); + AuditingTestsUtils.checkDirectoryContainsFiles( + OUTPUT_DIRECTORY, expectedOutputFile, expectedExistingFile1, expectedExistingFile2); AuditingTestsUtils.checkFileExistsWithContent(expectedExistingFile1, "rolled1"); AuditingTestsUtils.checkFileExistsWithContent(expectedExistingFile2, "rolled2"); AuditingTestsUtils.checkFileExistsWithContent(expectedOutputFile, ""); diff --git a/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingOutputTest.java b/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingOutputTest.java index ecc4379f..c361ae20 100644 --- a/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingOutputTest.java +++ b/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingOutputTest.java @@ -27,13 +27,12 @@ */ package it.geosolutions.geostore.services.rest.auditing; -import org.junit.Assert; -import org.junit.Test; - import java.io.File; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; +import org.junit.Assert; +import org.junit.Test; public final class AuditingOutputTest extends AuditingTestsBase { @@ -48,16 +47,23 @@ public void testAuditOutput() throws InterruptedException { offerMessage(auditingOutput, outputFile, message1); AuditingTestsUtils.checkFileExistsWithContent(outputFile, contentWithoutEnd(message1)); offerMessage(auditingOutput, outputFile, message2); - AuditingTestsUtils.checkFileExistsWithContent(outputFile, contentWithoutEnd(message1, message2)); + AuditingTestsUtils.checkFileExistsWithContent( + outputFile, contentWithoutEnd(message1, message2)); offerMessage(auditingOutput, outputFile, message3); - File rolledFile = new File(OUTPUT_DIRECTORY, - String.format("audit-geostore-%s-1.txt", auditingOutput.getAuditingFilesManager().getCurrentDayTag())); + File rolledFile = + new File( + OUTPUT_DIRECTORY, + String.format( + "audit-geostore-%s-1.txt", + auditingOutput.getAuditingFilesManager().getCurrentDayTag())); AuditingTestsUtils.waitFileExists(rolledFile, 5000); AuditingTestsUtils.checkFileExistsWithContent(outputFile, "*START*"); - AuditingTestsUtils.checkFileExistsWithContent(rolledFile, contentWithEnd(message1, message2, message3)); + AuditingTestsUtils.checkFileExistsWithContent( + rolledFile, contentWithEnd(message1, message2, message3)); } - private void offerMessage(AuditingOutput auditingOutput, File outputFile, Map message) + private void offerMessage( + AuditingOutput auditingOutput, File outputFile, Map message) throws InterruptedException { long checksum = AuditingTestsUtils.checksum(outputFile); auditingOutput.offerMessage(copy(message)); @@ -81,7 +87,8 @@ private static Map createTestMessage(String label) { message.put(AuditInfo.ERROR_MESSAGE.getKey(), "ERROR_MESSAGE" + "-" + label); message.put(AuditInfo.FAILED.getKey(), "FAILED" + "-" + label); message.put(AuditInfo.RESPONSE_STATUS_CODE.getKey(), "RESPONSE_STATUS_CODE" + "-" + label); - message.put(AuditInfo.RESPONSE_CONTENT_TYPE.getKey(), "RESPONSE_CONTENT_TYPE" + "-" + label); + message.put( + AuditInfo.RESPONSE_CONTENT_TYPE.getKey(), "RESPONSE_CONTENT_TYPE" + "-" + label); message.put(AuditInfo.RESPONSE_LENGTH.getKey(), "RESPONSE_LENGTH" + "-" + label); message.put(AuditInfo.START_TIME.getKey(), "START_TIME" + "-" + label); message.put(AuditInfo.END_TIME.getKey(), "END_TIME" + "-" + label); diff --git a/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingTemplatesTest.java b/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingTemplatesTest.java index 26a658cc..a1dadd9b 100644 --- a/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingTemplatesTest.java +++ b/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingTemplatesTest.java @@ -27,17 +27,16 @@ */ package it.geosolutions.geostore.services.rest.auditing; -import freemarker.template.TemplateException; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import freemarker.template.TemplateException; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; - -import static org.junit.Assert.assertEquals; +import org.junit.Test; public final class AuditingTemplatesTest extends AuditingTestsBase { diff --git a/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingTestsBase.java b/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingTestsBase.java index fa131107..6bdd1b78 100644 --- a/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingTestsBase.java +++ b/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingTestsBase.java @@ -27,27 +27,36 @@ */ package it.geosolutions.geostore.services.rest.auditing; -import org.junit.After; -import org.junit.Before; - import java.io.File; import java.net.URISyntaxException; import java.util.UUID; +import org.junit.After; +import org.junit.Before; /** - * Base class that defines the necessary tests directories and make everything is cleaned at the end. + * Base class that defines the necessary tests directories and make everything is cleaned at the + * end. */ -abstract public class AuditingTestsBase { +public abstract class AuditingTestsBase { - protected final File TESTS_ROOT_DIRECTORY = new File(System.getProperty("java.io.tmpdir"), "auditing-tests-" + UUID.randomUUID().toString()); + protected final File TESTS_ROOT_DIRECTORY = + new File( + System.getProperty("java.io.tmpdir"), + "auditing-tests-" + UUID.randomUUID().toString()); protected final File OUTPUT_DIRECTORY = new File(TESTS_ROOT_DIRECTORY, "output"); protected final File TEMPLATES_DIRECTORY = getTemplatesDirectory(); protected final File CONFIGURATION_DIRECTORY = new File(TESTS_ROOT_DIRECTORY, "configuration"); - protected final File CONFIGURATION_FILE_PATH = new File(CONFIGURATION_DIRECTORY, "auditing.properties"); + protected final File CONFIGURATION_FILE_PATH = + new File(CONFIGURATION_DIRECTORY, "auditing.properties"); private File getTemplatesDirectory() { try { - return new File(AuditingTestsUtils.class.getClassLoader().getResource("templates").toURI().getPath()); + return new File( + AuditingTestsUtils.class + .getClassLoader() + .getResource("templates") + .toURI() + .getPath()); } catch (URISyntaxException exception) { throw new RuntimeException("Error getting tests templates directory."); } @@ -56,7 +65,11 @@ private File getTemplatesDirectory() { @Before public void before() { AuditingTestsUtils.initDirectory(TESTS_ROOT_DIRECTORY); - AuditingTestsUtils.createDefaultConfiguration(CONFIGURATION_DIRECTORY, CONFIGURATION_FILE_PATH, OUTPUT_DIRECTORY, TEMPLATES_DIRECTORY); + AuditingTestsUtils.createDefaultConfiguration( + CONFIGURATION_DIRECTORY, + CONFIGURATION_FILE_PATH, + OUTPUT_DIRECTORY, + TEMPLATES_DIRECTORY); AuditingTestsUtils.initDirectory(OUTPUT_DIRECTORY); } diff --git a/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingTestsUtils.java b/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingTestsUtils.java index 4b0b6a74..2f96b3a9 100644 --- a/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingTestsUtils.java +++ b/src/modules/rest/auditing/src/test/java/it/geosolutions/geostore/services/rest/auditing/AuditingTestsUtils.java @@ -27,9 +27,6 @@ */ package it.geosolutions.geostore.services.rest.auditing; -import org.junit.Assert; -import org.apache.commons.io.FileUtils; - import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; @@ -37,6 +34,8 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; +import org.apache.commons.io.FileUtils; +import org.junit.Assert; final class AuditingTestsUtils { @@ -44,10 +43,16 @@ static File randomDirectory(File original) { return new File(original.getPath() + "-" + UUID.randomUUID().toString()); } - static void createDefaultConfiguration(File configurationDirectory, File configurationFilePath, File outputDirectory, File templatesDirectory) { + static void createDefaultConfiguration( + File configurationDirectory, + File configurationFilePath, + File outputDirectory, + File templatesDirectory) { initDirectory(configurationDirectory); - System.setProperty(AuditingConfiguration.CONFIGURATION_PATH, configurationFilePath.getAbsolutePath()); - String properties = propertiesToString(getDefaultProperties(outputDirectory, templatesDirectory)); + System.setProperty( + AuditingConfiguration.CONFIGURATION_PATH, configurationFilePath.getAbsolutePath()); + String properties = + propertiesToString(getDefaultProperties(outputDirectory, templatesDirectory)); createFile(configurationFilePath, properties); } @@ -60,7 +65,8 @@ static void createDirectory(File directory) { try { FileUtils.forceMkdir(directory); } catch (Exception exception) { - throw new AuditingException(exception, "Error creating directory '%s'.", directory.getAbsolutePath()); + throw new AuditingException( + exception, "Error creating directory '%s'.", directory.getAbsolutePath()); } } @@ -70,12 +76,14 @@ static void deleteDirectory(File directory) { static void deleteFile(File file) { if (!file.getAbsolutePath().contains("auditing-tests")) { - throw new AuditingException("This path '%s' requested to delete looks suspicious.", file.getAbsolutePath()); + throw new AuditingException( + "This path '%s' requested to delete looks suspicious.", file.getAbsolutePath()); } try { FileUtils.deleteQuietly(file); } catch (Exception exception) { - throw new AuditingException(exception, "Error deleting file '%s'.", file.getAbsolutePath()); + throw new AuditingException( + exception, "Error deleting file '%s'.", file.getAbsolutePath()); } } @@ -91,7 +99,8 @@ static void writeToFile(File file, String fileContent) { writer.flush(); writer.close(); } catch (Exception exception) { - throw new AuditingException(exception, "Error writing content to file '%s'.", file.getAbsolutePath()); + throw new AuditingException( + exception, "Error writing content to file '%s'.", file.getAbsolutePath()); } } @@ -103,7 +112,8 @@ static String readFile(File file) { input.close(); return new String(data).replaceAll("\\r\\n", "\n"); } catch (Exception exception) { - throw new AuditingException(exception, "Error reading file '%s' content.", file.getAbsolutePath()); + throw new AuditingException( + exception, "Error reading file '%s' content.", file.getAbsolutePath()); } } @@ -111,9 +121,13 @@ static Map getDefaultProperties(File outputDirectory, File templ Map properties = new HashMap(); properties.put(AuditingConfiguration.AUDIT_ENABLE, "true"); properties.put(AuditingConfiguration.MAX_RESQUEST_PER_FILE, "3"); - properties.put(AuditingConfiguration.OUTPUT_DIRECTORY, outputDirectory.getAbsolutePath().replace("\\", "\\\\")); + properties.put( + AuditingConfiguration.OUTPUT_DIRECTORY, + outputDirectory.getAbsolutePath().replace("\\", "\\\\")); properties.put(AuditingConfiguration.OUTPUT_FILES_EXTENSION, "txt"); - properties.put(AuditingConfiguration.TEMPLATES_DIRECTORY, templatesDirectory.getAbsolutePath().replace("\\", "\\\\")); + properties.put( + AuditingConfiguration.TEMPLATES_DIRECTORY, + templatesDirectory.getAbsolutePath().replace("\\", "\\\\")); properties.put(AuditingConfiguration.TEMPLATES_VERSION, "1"); return properties; } @@ -121,7 +135,11 @@ static Map getDefaultProperties(File outputDirectory, File templ static String propertiesToString(Map properties) { StringBuilder stringBuilder = new StringBuilder(); for (Map.Entry property : properties.entrySet()) { - stringBuilder.append(property.getKey()).append('=').append(property.getValue()).append("\n"); + stringBuilder + .append(property.getKey()) + .append('=') + .append(property.getValue()) + .append("\n"); } return stringBuilder.toString(); } @@ -147,11 +165,13 @@ static long checksum(File file) { try { return FileUtils.checksumCRC32(file); } catch (Exception exception) { - throw new AuditingException(exception, "Error computing checksum of file '%s'.", file.getAbsolutePath()); + throw new AuditingException( + exception, "Error computing checksum of file '%s'.", file.getAbsolutePath()); } } - static void waitFileChange(File file, long checksum, long timeoutInMs) throws InterruptedException { + static void waitFileChange(File file, long checksum, long timeoutInMs) + throws InterruptedException { for (int i = 0; i < timeoutInMs / 100; i++) { if (checksum(file) != checksum) { return; diff --git a/src/modules/rest/auditing/src/test/resources/log4j.properties b/src/modules/rest/auditing/src/test/resources/log4j.properties deleted file mode 100644 index 96b80f3d..00000000 --- a/src/modules/rest/auditing/src/test/resources/log4j.properties +++ /dev/null @@ -1,5 +0,0 @@ -log4j.rootLogger=INFO, consoleAppender - -log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender -log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout -log4j.appender.consoleAppender.layout.ConversionPattern=%p %d{yyyy-MM-dd HH:mm:ss.SSS} %c::%M:%L - %m%n \ No newline at end of file diff --git a/src/modules/rest/auditing/src/test/resources/log4j2.properties b/src/modules/rest/auditing/src/test/resources/log4j2.properties new file mode 100644 index 00000000..0af58038 --- /dev/null +++ b/src/modules/rest/auditing/src/test/resources/log4j2.properties @@ -0,0 +1,10 @@ +rootLogger.level = INFO +appenders= console + + +appender.console.type = Console +appender.console.name = LogToConsole +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = %p %d{yyyy-MM-dd HH:mm:ss.SSS} %c::%M:%L - %m%n +rootLogger.appenderRef.stdout.ref = LogToConsole +rootLogger.appenderRef.console.ref = LogToConsole diff --git a/src/modules/rest/client/pom.xml b/src/modules/rest/client/pom.xml index e6ba828d..d3963711 100644 --- a/src/modules/rest/client/pom.xml +++ b/src/modules/rest/client/pom.xml @@ -25,7 +25,7 @@ it.geosolutions.geostore geostore-rest-root - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-rest-client @@ -59,13 +59,11 @@ com.sun.jersey.contribs jersey-apache-client - 1.12 com.sun.jersey jersey-client - 1.12 @@ -80,18 +78,7 @@ - - org.apache.maven.plugins - maven-compiler-plugin - - utf8 - 1.7 - 1.7 - - - - true org.apache.maven.plugins diff --git a/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/rest/AdministratorGeoStoreClient.java b/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/rest/AdministratorGeoStoreClient.java index 3632aa4a..8bc6ab76 100644 --- a/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/rest/AdministratorGeoStoreClient.java +++ b/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/rest/AdministratorGeoStoreClient.java @@ -1,39 +1,36 @@ package it.geosolutions.geostore.services.rest; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.core.util.MultivaluedMapImpl; import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.services.rest.model.RESTUserGroup; import it.geosolutions.geostore.services.rest.model.ShortResourceList; import it.geosolutions.geostore.services.rest.model.UserGroupList; import it.geosolutions.geostore.services.rest.model.UserList; - import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; -import com.sun.jersey.api.client.WebResource; -import com.sun.jersey.core.util.MultivaluedMapImpl; - /** * Advanced GeoStore client for user management - * + * * @author Lorenzo Natali * @author DamianoG - * */ public class AdministratorGeoStoreClient extends GeoStoreClient { // ========================================================================== // === USERS MANAGEMENT // ========================================================================== - + public User getUser(long id) { return getBaseWebResource("users", "user", id).accept(MediaType.TEXT_XML).get(User.class); - } - - public User getUser(String name) { - return getBaseWebResource("users", "search", name).accept(MediaType.TEXT_XML).get(User.class); + public User getUser(String name) { + return getBaseWebResource("users", "search", name) + .accept(MediaType.TEXT_XML) + .get(User.class); } public User getUser(long id, Boolean includeAttributes) { @@ -43,23 +40,24 @@ public User getUser(long id, Boolean includeAttributes) { if (includeAttributes != null) { queryParams.add("includeattributes", includeAttributes.toString()); - } return wr.queryParams(queryParams).get(User.class); - } public UserList getUsers() { - return getBaseWebResource("users").header("Content-Type", MediaType.TEXT_XML).accept(MediaType.TEXT_XML).get(UserList.class); + return getBaseWebResource("users") + .header("Content-Type", MediaType.TEXT_XML) + .accept(MediaType.TEXT_XML) + .get(UserList.class); } public UserList getUsers(Integer page, Integer entries) { WebResource wr = getBaseWebResource("users"); return wr.queryParam("page", page.toString()) - .queryParam("entries", entries.toString()) - .header("Content-Type", MediaType.TEXT_XML) - .accept(MediaType.TEXT_XML) - .get(UserList.class); + .queryParam("entries", entries.toString()) + .header("Content-Type", MediaType.TEXT_XML) + .accept(MediaType.TEXT_XML) + .get(UserList.class); } // TODO move it to the base client to allow login @@ -71,8 +69,11 @@ public Long insert(User user) { // WebResource wr = getBaseWebResource(); // // String sid = wr.path("resources") - String sid = getBaseWebResource("users").header("Content-Type", MediaType.TEXT_XML) - .accept(MediaType.TEXT_PLAIN).post(String.class, user); + String sid = + getBaseWebResource("users") + .header("Content-Type", MediaType.TEXT_XML) + .accept(MediaType.TEXT_PLAIN) + .post(String.class, user); return Long.parseLong(sid); } @@ -82,17 +83,21 @@ public void deleteUser(Long id) { } public void update(Long id, User user) { - getBaseWebResource("users", "user", id).header("Content-Type", MediaType.TEXT_XML) + getBaseWebResource("users", "user", id) + .header("Content-Type", MediaType.TEXT_XML) .put(user); } - + // ========================================================================== // === USER GROUPS MANAGEMENT // ========================================================================== public long insertUserGroup(UserGroup usergroup) { - String sid = getBaseWebResource("usergroups").header("Content-Type", MediaType.TEXT_XML) - .accept(MediaType.TEXT_PLAIN).post(String.class, usergroup); + String sid = + getBaseWebResource("usergroups") + .header("Content-Type", MediaType.TEXT_XML) + .accept(MediaType.TEXT_PLAIN) + .post(String.class, usergroup); return Long.parseLong(sid); } @@ -106,35 +111,36 @@ public void assignUserGroup(long userId, long usergroupId) { } public void deassignUserGroup(long userId, long usergroupId) { - getBaseWebResource("usergroups", "group", userId, usergroupId).delete(); - - } - - public RESTUserGroup getUserGroup(long usergroupId){ - return getBaseWebResource("usergroups", "group", usergroupId).get(RESTUserGroup.class); - } - + getBaseWebResource("usergroups", "group", userId, usergroupId).delete(); + } + + public RESTUserGroup getUserGroup(long usergroupId) { + return getBaseWebResource("usergroups", "group", usergroupId).get(RESTUserGroup.class); + } + public UserGroupList getUserGroups(Integer page, Integer entries, boolean all) { WebResource wr = getBaseWebResource("usergroups"); wr = wr.queryParam("page", page.toString()); wr = wr.queryParam("entries", entries.toString()); - wr = wr.queryParam("all", ""+all); - return wr.header("Content-Type", MediaType.TEXT_XML).accept(MediaType.TEXT_XML) + wr = wr.queryParam("all", "" + all); + return wr.header("Content-Type", MediaType.TEXT_XML) + .accept(MediaType.TEXT_XML) .get(UserGroupList.class); } - public ShortResourceList updateSecurityRules(ShortResourceList resourcesToSet, Long groupId, - boolean canRead, boolean canWrite) { - WebResource wr = getBaseWebResource("usergroups","update_security_rules", groupId, canRead, canWrite); - ShortResourceList updatedResources = wr.header("Content-Type", MediaType.TEXT_XML) - .accept(MediaType.TEXT_XML).put(ShortResourceList.class, resourcesToSet); + public ShortResourceList updateSecurityRules( + ShortResourceList resourcesToSet, Long groupId, boolean canRead, boolean canWrite) { + WebResource wr = + getBaseWebResource( + "usergroups", "update_security_rules", groupId, canRead, canWrite); + ShortResourceList updatedResources = + wr.header("Content-Type", MediaType.TEXT_XML) + .accept(MediaType.TEXT_XML) + .put(ShortResourceList.class, resourcesToSet); return updatedResources; } - public RESTUserGroup getUserGroup(String name) { - return getBaseWebResource("usergroups", "group", "name",name).get(RESTUserGroup.class); - - } - - + public RESTUserGroup getUserGroup(String name) { + return getBaseWebResource("usergroups", "group", "name", name).get(RESTUserGroup.class); + } } diff --git a/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/rest/GeoStoreClient.java b/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/rest/GeoStoreClient.java index 707d0d41..815bfd17 100644 --- a/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/rest/GeoStoreClient.java +++ b/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/rest/GeoStoreClient.java @@ -19,8 +19,12 @@ */ package it.geosolutions.geostore.services.rest; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter; import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.core.model.Resource; +import it.geosolutions.geostore.services.dto.ShortResource; import it.geosolutions.geostore.services.dto.search.SearchFilter; import it.geosolutions.geostore.services.rest.client.model.ExtGroupList; import it.geosolutions.geostore.services.rest.model.CategoryList; @@ -29,51 +33,44 @@ import it.geosolutions.geostore.services.rest.model.ResourceList; import it.geosolutions.geostore.services.rest.model.SecurityRuleList; import it.geosolutions.geostore.services.rest.model.ShortResourceList; - -import javax.ws.rs.core.MediaType; - -import com.sun.jersey.api.client.Client; -import com.sun.jersey.api.client.WebResource; -import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter; -import it.geosolutions.geostore.services.dto.ShortResource; import it.geosolutions.geostore.services.rest.model.enums.RawFormat; +import javax.ws.rs.core.MediaType; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class GeoStoreClient { private String username = null; private String password = null; private String geostoreRestUrl = null; - public GeoStoreClient() { - } + public GeoStoreClient() {} // ========================================================================== // === RESOURCES // ========================================================================== /** - * @deprecated the REST service call is deprecated and should be replaced by - * {@link #searchResources(it.geosolutions.geostore.services.dto.search.SearchFilter, java.lang.Integer, java.lang.Integer, java.lang.Boolean, java.lang.Boolean) } + * @deprecated the REST service call is deprecated and should be replaced by {@link + * #searchResources(it.geosolutions.geostore.services.dto.search.SearchFilter, + * java.lang.Integer, java.lang.Integer, java.lang.Boolean, java.lang.Boolean) } */ @Deprecated - public ShortResourceList searchResources(SearchFilter searchFilter) - { - ShortResourceList resourceList = getBaseWebResource("resources", "search") - .header("Content-Type", MediaType.TEXT_XML) - .accept(MediaType.TEXT_XML) - .post(ShortResourceList.class, searchFilter); + public ShortResourceList searchResources(SearchFilter searchFilter) { + ShortResourceList resourceList = + getBaseWebResource("resources", "search") + .header("Content-Type", MediaType.TEXT_XML) + .accept(MediaType.TEXT_XML) + .post(ShortResourceList.class, searchFilter); return resourceList; } - public ResourceList searchResources(SearchFilter searchFilter, - Integer page, Integer entries, - Boolean includeAttributes, Boolean includeData) - { + public ResourceList searchResources( + SearchFilter searchFilter, + Integer page, + Integer entries, + Boolean includeAttributes, + Boolean includeData) { WebResource wb = getBaseWebResource("resources", "search", "list"); wb = addQParam(wb, "page", page); @@ -87,156 +84,134 @@ public ResourceList searchResources(SearchFilter searchFilter, .post(ResourceList.class, searchFilter); } - protected WebResource addQParam(WebResource wb, String key, Object value) - { - if (value != null) - return wb.queryParam(key, value.toString()); - else - return wb; + protected WebResource addQParam(WebResource wb, String key, Object value) { + if (value != null) return wb.queryParam(key, value.toString()); + else return wb; } - public Long insert(RESTResource resource) - { - String sid = getBaseWebResource("resources") - .header("Content-Type", MediaType.TEXT_XML) - .accept(MediaType.TEXT_PLAIN) - .post(String.class, resource); + public Long insert(RESTResource resource) { + String sid = + getBaseWebResource("resources") + .header("Content-Type", MediaType.TEXT_XML) + .accept(MediaType.TEXT_PLAIN) + .post(String.class, resource); return Long.parseLong(sid); } - public Resource getResource(Long id, boolean full) - { + public Resource getResource(Long id, boolean full) { WebResource resource = getBaseWebResource("resources", "resource", id); - if (full) - resource = resource.queryParam("full", Boolean.toString(full)); + if (full) resource = resource.queryParam("full", Boolean.toString(full)); return resource.header("Content-Type", MediaType.TEXT_XML) .accept(MediaType.TEXT_XML) .get(Resource.class); } - public Resource getResource(Long id) - { + public Resource getResource(Long id) { return getResource(id, false); } - public void deleteResource(Long id) - { + public void deleteResource(Long id) { getBaseWebResource("resources", "resource", id).delete(); } - public void updateResource(Long id, RESTResource resource) - { + public void updateResource(Long id, RESTResource resource) { getBaseWebResource("resources", "resource", id) .header("Content-Type", MediaType.TEXT_XML) .put(resource); } - public ShortResourceList getAllShortResource(Integer page, Integer entries) - { + public ShortResourceList getAllShortResource(Integer page, Integer entries) { WebResource wr = getBaseWebResource("resources"); return wr.queryParam("page", page.toString()) - .queryParam("entries", entries.toString()) - .header("Content-Type", MediaType.TEXT_XML) - .accept(MediaType.TEXT_XML) - .get(ShortResourceList.class); - } - - public SecurityRuleList getSecurityRules(Long resourceId) - { - WebResource wr = getBaseWebResource("resources", "resource", resourceId, "permissions"); - return wr.header("Content-Type", MediaType.TEXT_XML) + .queryParam("entries", entries.toString()) + .header("Content-Type", MediaType.TEXT_XML) + .accept(MediaType.TEXT_XML) + .get(ShortResourceList.class); + } + + public SecurityRuleList getSecurityRules(Long resourceId) { + WebResource wr = getBaseWebResource("resources", "resource", resourceId, "permissions"); + return wr.header("Content-Type", MediaType.TEXT_XML) .accept(MediaType.TEXT_XML) .get(SecurityRuleList.class); } - - public void updateSecurityRules(Long resourceId, SecurityRuleList rules) - { - getBaseWebResource("resources", "resource", resourceId, "permissions") - .header("Content-Type", MediaType.TEXT_XML) - .accept(MediaType.TEXT_PLAIN) - .post(rules); + + public void updateSecurityRules(Long resourceId, SecurityRuleList rules) { + getBaseWebResource("resources", "resource", resourceId, "permissions") + .header("Content-Type", MediaType.TEXT_XML) + .accept(MediaType.TEXT_PLAIN) + .post(rules); } // ========================================================================== // === DATA // ========================================================================== - public String getData(Long id) - { + public String getData(Long id) { return getData(id, MediaType.WILDCARD_TYPE); } - public String getData(Long id, MediaType acceptMediaType) - { - return getBaseWebResource("data", id) - .accept(acceptMediaType) - .get(String.class); + public String getData(Long id, MediaType acceptMediaType) { + return getBaseWebResource("data", id).accept(acceptMediaType).get(String.class); } - public byte[] getRawData(Long id, RawFormat decodeFrom) - { + public byte[] getRawData(Long id, RawFormat decodeFrom) { return getRawData(byte[].class, id, decodeFrom); } - public T getRawData(Class clazz, Long id, RawFormat decodeFrom) - { + public T getRawData(Class clazz, Long id, RawFormat decodeFrom) { WebResource wr = getBaseWebResource("data", id, "raw"); - if(decodeFrom != null) { + if (decodeFrom != null) { wr = wr.queryParam("decode", decodeFrom.name()); } return wr.get(clazz); } - public void setData(Long id, String data) - { + public void setData(Long id, String data) { getBaseWebResource("data", id).put(data); } - public void updateData(Long id, String data) - { - getBaseWebResource("data", id) - .header("Content-Type", MediaType.TEXT_PLAIN) - .put(data); + public void updateData(Long id, String data) { + getBaseWebResource("data", id).header("Content-Type", MediaType.TEXT_PLAIN).put(data); } // ========================================================================== // === CATEGORIES // ========================================================================== - public Long insert(RESTCategory category) - { - String sid = getBaseWebResource("categories") - .header("Content-Type", MediaType.TEXT_XML) - .accept(MediaType.TEXT_PLAIN) - .post(String.class, category); + public Long insert(RESTCategory category) { + String sid = + getBaseWebResource("categories") + .header("Content-Type", MediaType.TEXT_XML) + .accept(MediaType.TEXT_PLAIN) + .post(String.class, category); return Long.parseLong(sid); } - public Category getCategory(Long id) - { - Category category = getBaseWebResource("categories", "category", id) - .header("Content-Type", MediaType.TEXT_XML) - .accept(MediaType.TEXT_XML) - .get(Category.class); + public Category getCategory(Long id) { + Category category = + getBaseWebResource("categories", "category", id) + .header("Content-Type", MediaType.TEXT_XML) + .accept(MediaType.TEXT_XML) + .get(Category.class); return category; } - public Long getCategoryCount(String nameLike) - { - String count = getBaseWebResource("categories", "count", nameLike) - .accept(MediaType.TEXT_PLAIN) - .get(String.class); + public Long getCategoryCount(String nameLike) { + String count = + getBaseWebResource("categories", "count", nameLike) + .accept(MediaType.TEXT_PLAIN) + .get(String.class); return Long.parseLong(count); } - public CategoryList getCategories(Integer page, Integer entries) - { + public CategoryList getCategories(Integer page, Integer entries) { return getBaseWebResource("categories") .queryParam("page", page.toString()) .queryParam("entries", entries.toString()) @@ -245,16 +220,14 @@ public CategoryList getCategories(Integer page, Integer entries) .get(CategoryList.class); } - public CategoryList getCategories() - { + public CategoryList getCategories() { return getBaseWebResource("categories") .header("Content-Type", MediaType.TEXT_XML) .accept(MediaType.TEXT_XML) .get(CategoryList.class); } - public void deleteCategory(Long id) - { + public void deleteCategory(Long id) { getBaseWebResource("categories", "category", id).delete(); } @@ -265,27 +238,25 @@ public void deleteCategory(Long id) // Anyway we can use these methods to test the logic in integrations tests. // ========================================================================== - public ExtGroupList searchUserGroup(Integer start,Integer limit, String nameLike, boolean all) - { + public ExtGroupList searchUserGroup( + Integer start, Integer limit, String nameLike, boolean all) { WebResource wr = getBaseWebResource("extjs", "search", "groups", nameLike); - - wr = wr.queryParam("start", start.toString()) - .queryParam("limit", limit.toString()) - .queryParam("all", Boolean.toString(all)); + + wr = + wr.queryParam("start", start.toString()) + .queryParam("limit", limit.toString()) + .queryParam("all", Boolean.toString(all)); return wr.header("Content-Type", MediaType.TEXT_XML) .accept(MediaType.TEXT_XML) .get(ExtGroupList.class); - } - public ExtGroupList searchUserGroup(Integer start,Integer limit, String nameLike) - { + public ExtGroupList searchUserGroup(Integer start, Integer limit, String nameLike) { return searchUserGroup(start, limit, nameLike, false); } - public ShortResource getShortResource(long id) - { + public ShortResource getShortResource(long id) { WebResource wr = getBaseWebResource("extjs", "resource", id); return wr.get(ShortResource.class); } @@ -293,37 +264,34 @@ public ShortResource getShortResource(long id) // ========================================================================== // ========================================================================== - protected WebResource getBaseWebResource() - { - if (geostoreRestUrl == null) - throw new IllegalStateException("GeoStore URL not set"); + protected WebResource getBaseWebResource() { + if (geostoreRestUrl == null) throw new IllegalStateException("GeoStore URL not set"); Client c = Client.create(); if (username != null || password != null) { - c.addFilter(new HTTPBasicAuthFilter(username != null ? username : "", - password != null ? password : "")); + c.addFilter( + new HTTPBasicAuthFilter( + username != null ? username : "", password != null ? password : "")); } WebResource wr = c.resource(geostoreRestUrl); return wr; } - protected WebResource getBaseWebResource(Object... path) - { - if (geostoreRestUrl == null) - throw new IllegalStateException("GeoStore URL not set"); + protected WebResource getBaseWebResource(Object... path) { + if (geostoreRestUrl == null) throw new IllegalStateException("GeoStore URL not set"); Client c = Client.create(); if (username != null || password != null) { - c.addFilter(new HTTPBasicAuthFilter(username != null ? username : "", - password != null ? password : "")); + c.addFilter( + new HTTPBasicAuthFilter( + username != null ? username : "", password != null ? password : "")); } StringBuilder fullpath = new StringBuilder(geostoreRestUrl); for (Object o : path) { String p = o.toString(); - if (fullpath.charAt(fullpath.length() - 1) != '/') - fullpath.append('/'); + if (fullpath.charAt(fullpath.length() - 1) != '/') fullpath.append('/'); fullpath.append(p); } WebResource wr = c.resource(fullpath.toString()); @@ -355,5 +323,4 @@ public String getGeostoreRestUrl() { public void setGeostoreRestUrl(String geostoreRestUrl) { this.geostoreRestUrl = geostoreRestUrl; } - } diff --git a/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/rest/client/model/ExtGroupList.java b/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/rest/client/model/ExtGroupList.java index ed07815a..1dec3b80 100644 --- a/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/rest/client/model/ExtGroupList.java +++ b/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/rest/client/model/ExtGroupList.java @@ -27,11 +27,8 @@ * */ - import it.geosolutions.geostore.core.model.UserGroup; - import java.util.List; - import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; @@ -40,7 +37,6 @@ * Class ExtGroupList. * * @author Mirco Bertelli (mirco.bertelli at geo-solutions.it) - * */ @XmlRootElement(name = "ExtGroupList") public class ExtGroupList { @@ -49,44 +45,32 @@ public class ExtGroupList { private List list; - public ExtGroupList() { - - } + public ExtGroupList() {} - /** - * @param list - */ + /** @param list */ public ExtGroupList(long count, List list) { this.count = count; this.list = list; } - /** - * @return the count - */ + /** @return the count */ @XmlElement(name = "GroupCount") public long getCount() { return count; } - /** - * @param count the count to set - */ + /** @param count the count to set */ public void setCount(long count) { this.count = count; } - /** - * @return List - */ + /** @return List */ @XmlElement(name = "Group") public List getList() { return list; } - /** - * @param list - */ + /** @param list */ public void setList(List list) { this.list = list; } @@ -95,4 +79,4 @@ public void setList(List list) { public boolean isEmpty() { return list == null || list.isEmpty(); } -} \ No newline at end of file +} diff --git a/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/security/GeoStoreAuthenticationProvider.java b/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/security/GeoStoreAuthenticationProvider.java index 4793d134..468cb4c2 100644 --- a/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/security/GeoStoreAuthenticationProvider.java +++ b/src/modules/rest/client/src/main/java/it/geosolutions/geostore/services/security/GeoStoreAuthenticationProvider.java @@ -1,127 +1,107 @@ package it.geosolutions.geostore.services.security; +import com.sun.jersey.api.client.ClientHandlerException; import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.services.rest.AdministratorGeoStoreClient; - import java.util.ArrayList; import java.util.List; - import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.GrantedAuthorityImpl; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UsernameNotFoundException; -import com.sun.jersey.api.client.ClientHandlerException; /** * Wrap geostore Rest Services to allow Authentication using Geostore Users - * @author Lorenzo Natali * + * @author Lorenzo Natali */ public class GeoStoreAuthenticationProvider implements AuthenticationProvider { - /** - * The rest service - */ - String geoStoreRestURL; - - /** - * a list of allowed Roles - */ - List allowedRoles; - - - - - /** - * Message shown if the user logged haven't got an allowed role. - * TODO: Localize it - */ - public static final String UNAUTHORIZED_MSG = "This user have not enougth permissions to access to the Admin GUI"; - - /** - * Message shown if the user it's not found. - * TODO: Localize it - */ - public static final String USER_NOT_FOUND_MSG = "User not found. Please check your credentials"; - - /** - * Message shown if GeoStore it's unavailable. - * TODO: Localize it - */ - public static final String GEOSTORE_UNAVAILABLE = "GeoStore it's not availabile. Please contact with the administrator"; - - @Override - public boolean supports(Class authentication) { - return authentication.equals(UsernamePasswordAuthenticationToken.class); - } - - @Override - public Authentication authenticate(Authentication authentication) { - String pw = (String) authentication.getCredentials(); - String us = (String) authentication.getPrincipal(); - // We use the credentials for all the session in the GeoStore client - - AdministratorGeoStoreClient geoStoreClient =new AdministratorGeoStoreClient(); - geoStoreClient.setUsername(us); - geoStoreClient.setPassword(pw); - geoStoreClient.setGeostoreRestUrl(geoStoreRestURL); - - User user = null; - try { - user = geoStoreClient.getUserDetails(); - } catch (ClientHandlerException che) { - throw new UsernameNotFoundException(GEOSTORE_UNAVAILABLE); - } catch (Exception e){ - // user not found generic response. - user = null; - } - - if (user != null) { - String role = user.getRole().toString(); - if (!roleAllowed(role)){ - throw new BadCredentialsException(UNAUTHORIZED_MSG); - } -// return null; - List authorities = new ArrayList(); - authorities.add(new GrantedAuthorityImpl("ROLE_" + role)); - Authentication a = new UsernamePasswordAuthenticationToken(us, pw, - authorities); - // a.setAuthenticated(true); - return a; - } else { - throw new UsernameNotFoundException(USER_NOT_FOUND_MSG); - } - - } - - private boolean roleAllowed(String role) { - for (String allowed : allowedRoles) { - if (allowed != null) { - if (allowed.equals(role)) - return true; - } - } - return false; - } - - // GETTERS AND SETTERS - public List getAllowedRoles() { - return allowedRoles; - } - - public void setAllowedRoles(List roleFilter) { - this.allowedRoles = roleFilter; - } - - public String getGeoStoreRestURL() { - return geoStoreRestURL; - } - - public void setGeoStoreRestURL(String geoStoreRestURL) { - this.geoStoreRestURL = geoStoreRestURL; - } - -} \ No newline at end of file + /** The rest service */ + String geoStoreRestURL; + + /** a list of allowed Roles */ + List allowedRoles; + + /** Message shown if the user logged haven't got an allowed role. TODO: Localize it */ + public static final String UNAUTHORIZED_MSG = + "This user have not enougth permissions to access to the Admin GUI"; + + /** Message shown if the user it's not found. TODO: Localize it */ + public static final String USER_NOT_FOUND_MSG = "User not found. Please check your credentials"; + + /** Message shown if GeoStore it's unavailable. TODO: Localize it */ + public static final String GEOSTORE_UNAVAILABLE = + "GeoStore it's not availabile. Please contact with the administrator"; + + @Override + public boolean supports(Class authentication) { + return authentication.equals(UsernamePasswordAuthenticationToken.class); + } + + @Override + public Authentication authenticate(Authentication authentication) { + String pw = (String) authentication.getCredentials(); + String us = (String) authentication.getPrincipal(); + // We use the credentials for all the session in the GeoStore client + + AdministratorGeoStoreClient geoStoreClient = new AdministratorGeoStoreClient(); + geoStoreClient.setUsername(us); + geoStoreClient.setPassword(pw); + geoStoreClient.setGeostoreRestUrl(geoStoreRestURL); + + User user = null; + try { + user = geoStoreClient.getUserDetails(); + } catch (ClientHandlerException che) { + throw new UsernameNotFoundException(GEOSTORE_UNAVAILABLE); + } catch (Exception e) { + // user not found generic response. + user = null; + } + + if (user != null) { + String role = user.getRole().toString(); + if (!roleAllowed(role)) { + throw new BadCredentialsException(UNAUTHORIZED_MSG); + } + // return null; + List authorities = new ArrayList(); + authorities.add(new SimpleGrantedAuthority("ROLE_" + role)); + Authentication a = new UsernamePasswordAuthenticationToken(us, pw, authorities); + // a.setAuthenticated(true); + return a; + } else { + throw new UsernameNotFoundException(USER_NOT_FOUND_MSG); + } + } + + private boolean roleAllowed(String role) { + for (String allowed : allowedRoles) { + if (allowed != null) { + if (allowed.equals(role)) return true; + } + } + return false; + } + + // GETTERS AND SETTERS + public List getAllowedRoles() { + return allowedRoles; + } + + public void setAllowedRoles(List roleFilter) { + this.allowedRoles = roleFilter; + } + + public String getGeoStoreRestURL() { + return geoStoreRestURL; + } + + public void setGeoStoreRestURL(String geoStoreRestURL) { + this.geoStoreRestURL = geoStoreRestURL; + } +} diff --git a/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/AdministratorGeostoreClientTest.java b/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/AdministratorGeostoreClientTest.java index e82add0c..8020c80b 100644 --- a/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/AdministratorGeostoreClientTest.java +++ b/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/AdministratorGeostoreClientTest.java @@ -26,6 +26,8 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; + +import com.sun.jersey.api.client.UniformInterfaceException; import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.core.model.Resource; import it.geosolutions.geostore.core.model.User; @@ -53,31 +55,29 @@ import it.geosolutions.geostore.services.rest.model.ShortResourceList; import it.geosolutions.geostore.services.rest.model.UserGroupList; import it.geosolutions.geostore.services.rest.model.UserList; - import java.net.ConnectException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.Before; import org.junit.Test; -import com.sun.jersey.api.client.UniformInterfaceException; -import java.util.Arrays; - public class AdministratorGeostoreClientTest { AdministratorGeoStoreClient geoStoreClient; GeoStoreClient geoStoreUserClient; - private final static Logger LOGGER = Logger.getLogger(AdministratorGeostoreClientTest.class); + private static final Logger LOGGER = + LogManager.getLogger(AdministratorGeostoreClientTest.class); final String DEFAULTCATEGORYNAME = "TestCategory1"; final String KEY_STRING = "stringAtt"; final String origString = "OrigStringValue"; - + protected AdministratorGeoStoreClient createAdministratorClient() { geoStoreClient = new AdministratorGeoStoreClient(); geoStoreClient.setGeostoreRestUrl("http://localhost:9191/geostore/rest"); @@ -85,7 +85,7 @@ protected AdministratorGeoStoreClient createAdministratorClient() { geoStoreClient.setPassword("admin"); return geoStoreClient; } - + protected GeoStoreClient createUserClient(String username, String password) { geoStoreUserClient = new AdministratorGeoStoreClient(); geoStoreUserClient.setGeostoreRestUrl("http://localhost:9191/geostore/rest"); @@ -113,7 +113,6 @@ protected boolean pingGeoStore(GeoStoreClient client) { } } - @Before public void before() throws Exception { geoStoreClient = createAdministratorClient(); @@ -129,7 +128,7 @@ public void before() throws Exception { // ========================================================================== // === USER MANAGEMENT TESTS // ========================================================================== - + @Test public void getIdTest() { @@ -140,7 +139,6 @@ public void getIdTest() { } catch (Exception e) { fail(); } - } @Test @@ -176,16 +174,15 @@ public void getUsersTest() { user.setGroups(ugs); geoStoreClient.insert(user); users = geoStoreClient.getUsers(0, 3); - for(RESTUser u : users.getList()){ - if("testuser111".equals(u.getName())){ - assertEquals(3,u.getGroupsNames().size()); + for (RESTUser u : users.getList()) { + if ("testuser111".equals(u.getName())) { + assertEquals(3, u.getGroupsNames().size()); } } - + } catch (Exception e) { fail(); } - } @Test @@ -200,7 +197,7 @@ public void createUserTest() { UserAttribute email = new UserAttribute(); email.setName("email"); email.setValue("test@geo-solutions.it"); - + UserGroup ug = new UserGroup(); ug.setGroupName("testgroup1"); ug.setDescription("testGroup1-Description"); @@ -208,24 +205,24 @@ public void createUserTest() { Set ugs = new HashSet(); ugs.add(ug); user.setGroups(ugs); - + List attrs = new ArrayList(); attrs.add(email); user.setAttribute(attrs); Long id = geoStoreClient.insert(user); System.out.println(id); User us = geoStoreClient.getUser(id, true); - //check assigned usergroup - assertEquals(1,us.getGroups().size()); + // check assigned usergroup + assertEquals(1, us.getGroups().size()); UserGroup ugRetrieved = null; - for(UserGroup ugIter : us.getGroups()){ - if("testgroup1".equals(ugIter.getGroupName())){ + for (UserGroup ugIter : us.getGroups()) { + if ("testgroup1".equals(ugIter.getGroupName())) { ugRetrieved = ugIter; } } - assertEquals("testGroup1-Description",ugRetrieved.getDescription()); + assertEquals("testGroup1-Description", ugRetrieved.getDescription()); assertNotNull(ugRetrieved.getId()); - + user.getName().equals("testuser"); attrs = us.getAttribute(); assertNotNull("Missing attribute list", attrs); @@ -245,7 +242,6 @@ public void deleteUserTest() { @Test public void updateUserTest() { - User userOld = new User(); userOld.setName("testuser1"); userOld.setRole(Role.USER); @@ -258,10 +254,10 @@ public void updateUserTest() { attrs.add(email); userOld.setAttribute(attrs); Long id = geoStoreClient.insert(userOld); - + // User user = geoStoreClient.getUser(1); try { - + User user = new User(); user.setName("testuser1"); user.setRole(Role.USER); @@ -283,11 +279,10 @@ public void updateUserTest() { } } - // ========================================================================== // === USER GROUPS MANAGEMENT // ========================================================================== - + @Test public void insertGetDeleteAssign_UserGroupTest() { @@ -302,18 +297,18 @@ public void insertGetDeleteAssign_UserGroupTest() { Set userSet = new HashSet<>(); userSet.add(u1); userSet.add(u2); - + UserGroup ug = new UserGroup(); ug.setGroupName("usergroupTest1"); -//!! ug.setUsers(userSet); + // !! ug.setUsers(userSet); long ugid = geoStoreClient.insertUserGroup(ug); - - //get created group + + // get created group RESTUserGroup restUG1 = geoStoreClient.getUserGroup(ugid); ug = new UserGroup(); ug.setGroupName(restUG1.getGroupName()); assertNotNull(ug); - assertEquals("usergroupTest1",ug.getGroupName()); + assertEquals("usergroupTest1", ug.getGroupName()); UserGroupList ugl = geoStoreClient.getUserGroups(0, 1000, true); List ugll = ugl.getUserGroupList(); assertEquals(2, ugll.size()); @@ -321,8 +316,8 @@ public void insertGetDeleteAssign_UserGroupTest() { assertEquals("usergroupTest1", ug1.getGroupName()); List userAssigned = ug1.getRestUsers().getList(); - assertEquals(null,userAssigned); - + assertEquals(null, userAssigned); + geoStoreClient.assignUserGroup(uid1, ug1.getId()); geoStoreClient.assignUserGroup(uid2, ug1.getId()); ugl = geoStoreClient.getUserGroups(0, 1000, true); @@ -337,11 +332,11 @@ public void insertGetDeleteAssign_UserGroupTest() { geoStoreClient.deassignUserGroup(uid2, ug1.getId()); u = geoStoreClient.getUser(uid2); usergroups = u.getGroups(); - //the null is not a vaild response, the EVERYONE group at least is expected + // the null is not a vaild response, the EVERYONE group at least is expected assertNotNull(usergroups); assertEquals(0, usergroups.size()); // - //reassign + // reassign // geoStoreClient.assignUserGroup(uid2, ug1.getId()); ugl = geoStoreClient.getUserGroups(0, 1000, true); @@ -350,61 +345,61 @@ public void insertGetDeleteAssign_UserGroupTest() { ug1 = ugll.get(1); assertEquals("usergroupTest1", ug1.getGroupName()); userAssigned = ug1.getRestUsers().getList(); - assertEquals(2,userAssigned.size()); - + assertEquals(2, userAssigned.size()); + // // reassign from user // UserGroup ug2 = new UserGroup(); ug2.setGroupName("usergroupTest2"); -//!! ug2.setUsers(userSet); + // !! ug2.setUsers(userSet); ugid = geoStoreClient.insertUserGroup(ug2); RESTUserGroup restUG = geoStoreClient.getUserGroup(ugid); assertNotNull(restUG); assertEquals("usergroupTest2", restUG.getGroupName()); // usergrouptest1 user - User us= geoStoreClient.getUser(userAssigned.get(0).getId()); + User us = geoStoreClient.getUser(userAssigned.get(0).getId()); Set usergroups2 = us.getGroups(); assertEquals(1, usergroups2.size()); ug2 = new UserGroup(); ug2.setGroupName(restUG.getGroupName()); - //test add groups + // test add groups us.getGroups().add(ug2); geoStoreClient.update(us.getId(), us); - us= geoStoreClient.getUser(userAssigned.get(0).getId()); + us = geoStoreClient.getUser(userAssigned.get(0).getId()); assertEquals(2, us.getGroups().size()); - - //test remove groups - for( UserGroup gg: us.getGroups()){ - if(gg.getGroupName().equals(ug2.getGroupName())){ - us.getGroups().remove(gg); - break; - } + + // test remove groups + for (UserGroup gg : us.getGroups()) { + if (gg.getGroupName().equals(ug2.getGroupName())) { + us.getGroups().remove(gg); + break; + } } restUG = geoStoreClient.getUserGroup(ugid); geoStoreClient.update(us.getId(), us); - us= geoStoreClient.getUser(userAssigned.get(0).getId()); + us = geoStoreClient.getUser(userAssigned.get(0).getId()); assertEquals(1, us.getGroups().size()); - + // - // delete + // delete // geoStoreClient.deleteUserGroup(ug1.getId()); geoStoreClient.deleteUserGroup(ugid); ugl = geoStoreClient.getUserGroups(0, 1000, true); - assertEquals(1,ugl.getUserGroupList().size()); + assertEquals(1, ugl.getUserGroupList().size()); UserList ul = geoStoreClient.getUsers(0, 1000); assertEquals(3, ul.getList().size()); // the 2 users added in this test, plus the admin geoStoreClient.deleteUser(uid1); geoStoreClient.deleteUser(uid2); ul = geoStoreClient.getUsers(0, 1000); - assertEquals(1, ul.getList().size()); // only the admin + assertEquals(1, ul.getList().size()); // only the admin } - + @Test - public void testStoredDataServices(){ - + public void testStoredDataServices() { + // Create a resource with Stored Data using the user "User" createDefaultCategory(); @@ -412,377 +407,358 @@ public void testStoredDataServices(){ ShortResource sr = createAResource(); - GeoStoreClient userGeoStoreClient = createUserClient("user","user"); + GeoStoreClient userGeoStoreClient = createUserClient("user", "user"); String data = userGeoStoreClient.getData(sr.getId()); assertEquals("we wish you a merry xmas and a happy new year", data); - - //try to get the related Stored Data with the user "u1", must not be possible due to authorization rules + + // try to get the related Stored Data with the user "u1", must not be possible due to + // authorization rules createUser("u1", Role.USER, "u1"); - userGeoStoreClient = createUserClient("u1","u1"); - try{ + userGeoStoreClient = createUserClient("u1", "u1"); + try { userGeoStoreClient.getData(sr.getId()); fail("Untrapped exception"); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { int u1StatusR = e.getResponse().getStatus(); - assertEquals(403,u1StatusR); + assertEquals(403, u1StatusR); } } - + @Test - public void testEVERYONEassignmentResources(){ + public void testEVERYONEassignmentResources() { // Create a resource with Stored Data using the user "User" createDefaultCategory(); createUser("user", Role.USER, "user"); ShortResource sr = createAResource(); - + SecurityRuleList srlFinal = geoStoreUserClient.getSecurityRules(sr.getId()); assertEquals(1, srlFinal.getList().size()); - + SecurityRuleList srl = new SecurityRuleList(); RESTSecurityRule everyoneRule = new RESTSecurityRule(); Resource r = new Resource(); r.setId(sr.getId()); everyoneRule.setCanRead(true); everyoneRule.setCanWrite(true); - - RESTUserGroup everyoneGroup = geoStoreClient.getUserGroup(GroupReservedNames.EVERYONE.toString()); + + RESTUserGroup everyoneGroup = + geoStoreClient.getUserGroup(GroupReservedNames.EVERYONE.toString()); RESTUserGroup ug = new RESTUserGroup(); ug.setGroupName(GroupReservedNames.EVERYONE.toString()); ug.setId(everyoneGroup.getId()); everyoneRule.setGroup(ug); - + List restSR = new ArrayList(); restSR.add(everyoneRule); restSR.add(srlFinal.getList().get(0)); - + srl.setList(restSR); - + geoStoreUserClient.updateSecurityRules(sr.getId(), srl); - - srlFinal = geoStoreUserClient.getSecurityRules(sr.getId()); + + srlFinal = geoStoreUserClient.getSecurityRules(sr.getId()); assertEquals(2, srlFinal.getList().size()); - } - + @Test - public void testEVERYONEassignmentUsers(){ + public void testEVERYONEassignmentUsers() { RESTUserGroup ug = geoStoreClient.getUserGroup(GroupReservedNames.EVERYONE.toString()); createUser("user", Role.USER, "user"); User u = geoStoreClient.getUser("user"); - + int errorCode = -1; - try{ + try { geoStoreClient.assignUserGroup(u.getId(), ug.getId()); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { errorCode = e.getResponse().getStatus(); } - assertEquals(404,errorCode); - + assertEquals(404, errorCode); + errorCode = -1; - try{ + try { geoStoreClient.deassignUserGroup(u.getId(), ug.getId()); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { errorCode = e.getResponse().getStatus(); } - assertEquals(404,errorCode); + assertEquals(404, errorCode); } - + @Test - public void testUserInitialization(){ - UserList ul = geoStoreClient.getUsers(0,100); + public void testUserInitialization() { + UserList ul = geoStoreClient.getUsers(0, 100); assertEquals(1, ul.getList().size()); // admin only - for(RESTUser u : ul.getList()){ + for (RESTUser u : ul.getList()) { assertNull(u.getGroupsNames()); } } - + @Test - public void everyoneGroupTest(){ - + public void everyoneGroupTest() { + UserGroupList ugl = geoStoreClient.getUserGroups(0, 1000, true); assertEquals(1, ugl.getUserGroupList().size()); assertEquals("everyone", ugl.getUserGroupList().get(0).getGroupName()); - + createUser("user", Role.USER, "user"); createDefaultCategory(); ShortResource sr = createAResource(); long uID = createUser("u1", Role.USER, "u1"); GeoStoreClient userGeoStoreClient = createUserClient("u1", "u1"); - - + int u1StatusR = -1; int u1StatusW = -1; - try{ + try { userGeoStoreClient.getResource(sr.getId()); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { u1StatusR = e.getResponse().getStatus(); } - assertEquals(403,u1StatusR); - try{ + assertEquals(403, u1StatusR); + try { userGeoStoreClient.updateResource(sr.getId(), new RESTResource()); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { u1StatusW = e.getResponse().getStatus(); } - assertEquals(403,u1StatusW); - + assertEquals(403, u1StatusW); + // Trying to assign to user "u1" the special group "everyone" (that should have id = 1) // An error is expected RESTUserGroup ugEveryone = geoStoreClient.getUserGroup("everyone"); assertNotNull(ugEveryone); - assertEquals(0,geoStoreClient.getUser(uID).getGroups().size()); + assertEquals(0, geoStoreClient.getUser(uID).getGroups().size()); int assignError = -1; - try{ + try { geoStoreClient.assignUserGroup(uID, ugEveryone.getId()); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { assignError = e.getResponse().getStatus(); } assertEquals(404, assignError); - assertEquals(0,geoStoreClient.getUser(uID).getGroups().size()); - + assertEquals(0, geoStoreClient.getUser(uID).getGroups().size()); + u1StatusR = -1; u1StatusW = -1; int updateStatus = -1; - + // Going to setup grants for group EVERYONE // Note that canRead=FALSE and canWrite=TRUE so I'm expect a BadRequestException ShortResourceList srl = new ShortResourceList(); List srlArray = new ArrayList(); srlArray.add(sr); srl.setList(srlArray); - try{ + try { geoStoreClient.updateSecurityRules(srl, ugEveryone.getId(), false, true); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { updateStatus = e.getResponse().getStatus(); } - assertEquals(400,updateStatus); + assertEquals(400, updateStatus); // User updateSecurityRule in the right way geoStoreClient.updateSecurityRules(srl, ugEveryone.getId(), true, false); - + // Now "u1" should be able to READ but not to WRITE the created resource. userGeoStoreClient.getResource(sr.getId()); - try{ + try { userGeoStoreClient.updateResource(sr.getId(), new RESTResource()); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { u1StatusW = e.getResponse().getStatus(); } - assertEquals(403,u1StatusW); - + assertEquals(403, u1StatusW); } - + @Test public void ListResourcesTest() { - + // Create a group UserGroup ug = new UserGroup(); ug.setGroupName("g1"); long gid = geoStoreClient.insertUserGroup(ug); - + UserGroup anotherGroup = new UserGroup(); anotherGroup.setGroupName("g2"); long anotherGid = geoStoreClient.insertUserGroup(anotherGroup); - + // Create a user createUser("user", Role.USER, "user", ug); - + // Create a resource with the user "user". So user will be the owner createDefaultCategory(); ShortResource sr = createAResource(); ShortResource sr2 = createAResource(); ShortResource sr3 = createAResource(); ShortResource sr4 = createAResource(); - + ShortResourceList srl = geoStoreUserClient.getAllShortResource(0, 1000); List listG1 = new ArrayList<>(); List listG2 = new ArrayList<>(); int i = 0; - for(ShortResource r : srl.getList()){ - if(i<2){ + for (ShortResource r : srl.getList()) { + if (i < 2) { listG1.add(r); - }else{ + } else { listG2.add(r); } i++; } - + // Ok, now it's time to test something. createUser("u1", Role.USER, "u1", ug); GeoStoreClient u1Client = createUserClient("u1", "u1"); - + // Since all resources inserted belong to user "user" and no groups security rules are added // the user "u1" won't see any resource neither as short resource ShortResourceList srlTmp = u1Client.getAllShortResource(1, 1000); assertNull(srlTmp.getList()); - + // trying to get all the resource list the user will get an empty list SearchFilter filter = new FieldFilter(BaseField.NAME, "rest%", SearchOperator.LIKE); ResourceList rl = u1Client.searchResources(filter, -1, -1, false, false); assertNull(rl.getList()); - + UserGroupList ugl = geoStoreClient.getUserGroups(0, 1000, true); long ug1_id = -1; long ug2_id = -1; - for(RESTUserGroup tmp_ug : ugl.getUserGroupList()){ - if(tmp_ug.getGroupName().equalsIgnoreCase("g1")){ + for (RESTUserGroup tmp_ug : ugl.getUserGroupList()) { + if (tmp_ug.getGroupName().equalsIgnoreCase("g1")) { ug1_id = tmp_ug.getId(); } - if(tmp_ug.getGroupName().equalsIgnoreCase("g2")){ + if (tmp_ug.getGroupName().equalsIgnoreCase("g2")) { ug2_id = tmp_ug.getId(); } } - + // Add group permissions to resources long errorCode = -1; - try{ + try { geoStoreClient.updateSecurityRules(new ShortResourceList(listG1), 787687l, true, true); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { errorCode = e.getResponse().getStatus(); } assertEquals(404, errorCode); - + geoStoreClient.updateSecurityRules(new ShortResourceList(listG1), ug1_id, true, true); - geoStoreClient.updateSecurityRules(new ShortResourceList(listG2), ug2_id, true, true); - + geoStoreClient.updateSecurityRules(new ShortResourceList(listG2), ug2_id, true, true); + // Now the situation should be changed: I should have access to 2 resources srl = u1Client.getAllShortResource(0, 1000); assertEquals(2, srl.getList().size()); - for(ShortResource r : srl.getList()){ - if(r.getId() == listG1.get(0).getId() || r.getId() == listG1.get(1).getId()){ + for (ShortResource r : srl.getList()) { + if (r.getId() == listG1.get(0).getId() || r.getId() == listG1.get(1).getId()) { assertTrue(r.isCanDelete()); assertTrue(r.isCanEdit()); - } - else{ + } else { assertTrue(!r.isCanDelete()); assertTrue(!r.isCanEdit()); } } - + rl = u1Client.searchResources(filter, -1, -1, false, false); - assertEquals(2,rl.getList().size()); - + assertEquals(2, rl.getList().size()); } @Test public void updateSecurityRulesTest() { - + // Create a group UserGroup ug = new UserGroup(); ug.setGroupName("usergroupTest1"); long gid = geoStoreClient.insertUserGroup(ug); - + UserGroup ug2 = new UserGroup(); ug2.setGroupName("unusedGroup"); long gid2 = geoStoreClient.insertUserGroup(ug2); - + UserGroup anotherGroup = new UserGroup(); anotherGroup.setGroupName("anotherGroup"); long anotherGid = geoStoreClient.insertUserGroup(anotherGroup); - + Set ugroups = new HashSet<>(); ugroups.add(ug); ugroups.add(anotherGroup); - + // Create 2 user createUser("u1", Role.USER, "u1"); createUser("u2", Role.USER, "u2", ug, anotherGroup); - + GeoStoreClient u1Client = createUserClient("u1", "u1"); GeoStoreClient u2Client = createUserClient("u2", "u2"); - + // Create a resource with the user "user". So user will be the owner createUser("user", Role.USER, "user"); createDefaultCategory(); ShortResource sr = createAResource(); ShortResource sr2 = createAResource(); - - + // Since "user" is the owner the users - // "u1" and "u2" should not be allowed to READ or WRITE, because they are not the owner of the reosource, it belong to at least one group and the resource doesn't have any groups rule security - try{ + // "u1" and "u2" should not be allowed to READ or WRITE, because they are not the owner of + // the reosource, it belong to at least one group and the resource doesn't have any groups + // rule security + try { u1Client.getResource(sr.getId(), true); fail("Untrapped exception"); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { assertEquals(403, e.getResponse().getStatus()); } - try{ + try { u2Client.getResource(sr.getId(), true); fail("Untrapped exception"); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { assertEquals(403, e.getResponse().getStatus()); } - try{ + try { u1Client.updateResource(sr.getId(), new RESTResource()); fail("Untrapped exception"); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { assertEquals(403, e.getResponse().getStatus()); } - try{ + try { u2Client.updateResource(sr.getId(), new RESTResource()); fail("Untrapped exception"); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { assertEquals(403, e.getResponse().getStatus()); } - // Update permissions for users belong to "usergroupTest1" ("u1") group - // The effect of this service call will be: + // The effect of this service call will be: // 1) The resource will be added with a group rule on usergroupTest1 // 2) "u2" will have WRITE permission but not READ permissions // 3) Nothing will change for "u1" List srl = new ArrayList(); srl.add(sr); ShortResourceList srll = new ShortResourceList(srl); - + ShortResourceList srlf = geoStoreClient.getAllShortResource(0, 1000); assertEquals(2, srlf.getList().size()); geoStoreClient.updateSecurityRules(srll, gid, false, true); geoStoreClient.updateSecurityRules(srll, gid2, false, false); srlf = geoStoreClient.getAllShortResource(0, 1000); assertEquals(2, srlf.getList().size()); - + // READ shouldn't allowed, WRITE allowed - try{ + try { u1Client.getResource(sr.getId(), true); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { assertEquals(403, e.getResponse().getStatus()); } - try{ + try { u2Client.getResource(sr.getId(), true); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { assertEquals(403, e.getResponse().getStatus()); } - try{ + try { u1Client.updateResource(sr.getId(), new RESTResource()); - } - catch(UniformInterfaceException e){ + } catch (UniformInterfaceException e) { assertEquals(403, e.getResponse().getStatus()); } u2Client.updateResource(sr.getId(), new RESTResource()); } - + @Test - public void getAllGroupsWithoutEveryoneTest(){ + public void getAllGroupsWithoutEveryoneTest() { final int GROUP_NUM = 3; addSomeUserGroups(GROUP_NUM, "randomGroups"); @@ -791,28 +767,29 @@ public void getAllGroupsWithoutEveryoneTest(){ } @Test - public void getAllGroupsWithEveryoneTest(){ + public void getAllGroupsWithEveryoneTest() { int grpCount = 3; addSomeUserGroups(grpCount, "randomGroups"); ExtGroupList res = geoStoreClient.searchUserGroup(0, 10, "*", true); - assertEquals(grpCount+1, res.getCount()); // +1: the everyone group + assertEquals(grpCount + 1, res.getCount()); // +1: the everyone group } @Test - public void searchGroupTest(){ + public void searchGroupTest() { int grpNum = 4; int targetGrpNum = 2; String targetGrpPrefix = "target"; addSomeUserGroups(grpNum, "smokeGrp"); addSomeUserGroups(targetGrpNum, targetGrpPrefix); - ExtGroupList searchResult = geoStoreClient.searchUserGroup(0, 10, "*" + targetGrpPrefix + "*"); + ExtGroupList searchResult = + geoStoreClient.searchUserGroup(0, 10, "*" + targetGrpPrefix + "*"); assertTrue(searchResult.getList().size() == targetGrpNum); } @Test - public void noGroupsForNormalUserTest(){ + public void noGroupsForNormalUserTest() { addSomeUserGroups(8, "randomGrp"); @@ -824,7 +801,7 @@ public void noGroupsForNormalUserTest(){ } @Test - public void allGroupsOfAnUserTest(){ + public void allGroupsOfAnUserTest() { int grpTestUserNum = 5, grpUUserNum = 3; String usrTestName = "test"; String usrTestPasswd = "test"; @@ -839,7 +816,6 @@ public void allGroupsOfAnUserTest(){ testUser.setGroups(testUserGroups); geoStoreClient.insert(testUser); - User u = new User(); u.setName("u"); u.setRole(Role.USER); @@ -857,28 +833,29 @@ public void allGroupsOfAnUserTest(){ } @Test - public void userGroupsPaginationTest(){ + public void userGroupsPaginationTest() { int totalGrps = 10; int pageSize = 3; int expectedItems[] = {3, 3, 3, 1}; ExtGroupList result; addSomeUserGroups(totalGrps, "paging"); - for(int page=0; page createSomeGroups(int amount, String namePrefix){ + protected Set createSomeGroups(int amount, String namePrefix) { Set grps = new HashSet(); - for(int i=0; i createSomeGroups(int amount, String namePrefix){ /** * Create n random UserGroups and insert them into db. + * * @param n the amount of random groups that will be created * @param namePrefix a string used as prefix in groups name and descriptions. */ - protected void addSomeUserGroups(int n, String namePrefix){ - for(UserGroup g : createSomeGroups(n, namePrefix)){ + protected void addSomeUserGroups(int n, String namePrefix) { + for (UserGroup g : createSomeGroups(n, namePrefix)) { geoStoreClient.insertUserGroup(g); } } /** * adds some UserGroup into db + * * @param groups set of UserGroups to insert into db. */ - protected void addSomeUserGroups(Set groups){ - for(UserGroup g : groups){ + protected void addSomeUserGroups(Set groups) { + for (UserGroup g : groups) { geoStoreClient.insertUserGroup(g); } } @@ -911,40 +890,38 @@ protected void addSomeUserGroups(Set groups){ protected void removeAllUsers(GeoStoreClient client) { UserList users = geoStoreClient.getUsers(0, 1000); List usersList = users.getList(); - usersList = (usersList == null)?new ArrayList():usersList; - for(RESTUser u : usersList){ - if(!("admin".equals(u.getName()))){ - try{ + usersList = (usersList == null) ? new ArrayList() : usersList; + for (RESTUser u : usersList) { + if (!("admin".equals(u.getName()))) { + try { geoStoreClient.deleteUser(u.getId()); - } - catch(Exception e){ + } catch (Exception e) { LOGGER.error("Error removing " + u); } } } } - + protected void removeAllUserGroup(GeoStoreClient client) { UserGroupList groups = geoStoreClient.getUserGroups(0, 1000, true); List groupList = groups.getUserGroupList(); - groupList = (groupList == null)?new ArrayList():groupList; - for(RESTUserGroup ug : groupList){ - try{ + groupList = (groupList == null) ? new ArrayList() : groupList; + for (RESTUserGroup ug : groupList) { + try { geoStoreClient.deleteUserGroup(ug.getId()); - } - catch(Exception e){ + } catch (Exception e) { LOGGER.error("Error removing " + ug); } } } - + protected Long createDefaultCategory() { Long catid = geoStoreClient.insert(new RESTCategory(DEFAULTCATEGORYNAME)); assertNotNull(catid); return catid; } - - protected ShortResource createAResource(){ + + protected ShortResource createAResource() { RESTStoredData storedData = new RESTStoredData(); storedData.setData("we wish you a merry xmas and a happy new year"); @@ -953,17 +930,15 @@ protected ShortResource createAResource(){ String timeid = Long.toString(System.currentTimeMillis()); - - RESTResource origResource = new RESTResource(); origResource.setCategory(new RESTCategory(DEFAULTCATEGORYNAME)); origResource.setName("rest_test_resource_" + timeid); origResource.setStore(storedData); origResource.setAttribute(attrList); - GeoStoreClient userGeoStoreClient = createUserClient("user","user"); + GeoStoreClient userGeoStoreClient = createUserClient("user", "user"); Long rid = userGeoStoreClient.insert(origResource); - + // Return a short resource represent the resource inserted origResource.setId(rid); ShortResource sr = new ShortResource(); @@ -971,7 +946,7 @@ protected ShortResource createAResource(){ sr.setName("rest_test_resource_" + timeid); return sr; } - + protected void removeAllResources(GeoStoreClient client) { SearchFilter filter = new FieldFilter(BaseField.NAME, "*", SearchOperator.IS_NOT_NULL); { @@ -992,7 +967,7 @@ protected void removeAllResources(GeoStoreClient client) { // assertEquals("Not all resources have been deleted", 0, resources.getList().size()); } } - + protected void removeAllCategories(GeoStoreClient client) { { CategoryList categories = client.getCategories(); @@ -1012,18 +987,17 @@ protected void removeAllCategories(GeoStoreClient client) { // assertEquals("Not all categories have been deleted", 0, categories.getList().size()); } } - - protected long createUser(String name, Role role, String pw, UserGroup ...group) { + + protected long createUser(String name, Role role, String pw, UserGroup... group) { User user = new User(); user.setName(name); user.setRole(role); user.setNewPassword(pw); - if(group != null) { + if (group != null) { user.setGroups(new HashSet(Arrays.asList(group))); } return geoStoreClient.insert(user); } - } diff --git a/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/AutoCreateUsersTest.java b/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/AutoCreateUsersTest.java index f2f53e1b..21a5bd62 100644 --- a/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/AutoCreateUsersTest.java +++ b/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/AutoCreateUsersTest.java @@ -23,29 +23,30 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; -import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.User; import java.net.ConnectException; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; /** - * Auto create users integration test. You need to override this properties on your GeoStore instance (use your geostore-ovr.properties):
    - *
    + * Auto create users integration test. You need to override this properties on your GeoStore + * instance (use your geostore-ovr.properties):
    + *
    * * geostoreAuthInterceptor.autoCreateUsers=true * geostoreAuthInterceptor.newUsersRole=USER * geostoreAuthInterceptor.newUsersPassword=NONE * geostoreAuthInterceptor.newUsersPasswordHeader= * - * + * * @author adiaz (alejandro.diaz at geo-solutions.it) */ public class AutoCreateUsersTest { - private final static Logger LOGGER = Logger.getLogger(AutoCreateUsersTest.class); + private static final Logger LOGGER = LogManager.getLogger(AutoCreateUsersTest.class); AdministratorGeoStoreClient geoStoreClient; @@ -84,9 +85,7 @@ public void before() throws Exception { assumeTrue(pingGeoStore(geoStoreClient)); } - /** - * Test auto create users with GeoStore client - */ + /** Test auto create users with GeoStore client */ @Test @Ignore("Ignore this test until the user autocreation won't be restored") public void testAutoCreateUsers() { @@ -101,7 +100,5 @@ public void testAutoCreateUsers() { } catch (Exception e) { fail("Unable to create user"); } - } - } diff --git a/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/BaseGeoStoreClientTest.java b/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/BaseGeoStoreClientTest.java index aa4260aa..b5e2e244 100644 --- a/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/BaseGeoStoreClientTest.java +++ b/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/BaseGeoStoreClientTest.java @@ -19,6 +19,9 @@ */ package it.geosolutions.geostore.services.rest; +import static org.junit.Assert.*; +import static org.junit.Assume.*; + import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserGroup; @@ -42,36 +45,29 @@ import java.util.Date; import java.util.HashSet; import java.util.List; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; -import static org.junit.Assume.*; -import static org.junit.Assert.*; -/** - * - * @author ETj (etj at geo-solutions.it) - */ -abstract public class BaseGeoStoreClientTest { +/** @author ETj (etj at geo-solutions.it) */ +public abstract class BaseGeoStoreClientTest { - private final static Logger LOGGER = Logger.getLogger(BaseGeoStoreClientTest.class); + private static final Logger LOGGER = LogManager.getLogger(BaseGeoStoreClientTest.class); protected static final String GEOSTORE_REST_URL = "http://localhost:9191/geostore/rest"; protected GeoStoreClient client; protected AdministratorGeoStoreClient adminClient; - public BaseGeoStoreClientTest() { - } + public BaseGeoStoreClientTest() {} @BeforeClass - public static void setUpClass() throws Exception { - } + public static void setUpClass() throws Exception {} @AfterClass - public static void tearDownClass() throws Exception { - } + public static void tearDownClass() throws Exception {} @Before public void before() throws Exception { @@ -164,8 +160,7 @@ protected void removeAllCategories(GeoStoreClient client) { } } - protected void removeAllGroups() - { + protected void removeAllGroups() { UserGroupList userGroups = adminClient.getUserGroups(0, 1000, false); for (RESTUserGroup group : userGroups) { @@ -174,13 +169,12 @@ protected void removeAllGroups() } } - protected void removeAllUsers() - { + protected void removeAllUsers() { UserList users = adminClient.getUsers(); for (RESTUser user : users) { LOGGER.info("Found user " + user + " . Deleting..."); - if(user.getName().equals("admin")) { + if (user.getName().equals("admin")) { LOGGER.info("Skipping main admin"); continue; } @@ -206,27 +200,24 @@ protected boolean pingGeoStore(GeoStoreClient client) { throw new RuntimeException("Unexpected exception: " + ex.getMessage(), ex); } } - - protected long createUser(String name, Role role, String pw, UserGroup ...group) - { + + protected long createUser(String name, Role role, String pw, UserGroup... group) { User user = new User(); user.setName(name); user.setRole(role); user.setNewPassword(pw); - if(group != null) { + if (group != null) { user.setGroups(new HashSet(Arrays.asList(group))); } return adminClient.insert(user); } - protected UserGroup createUserGroup(String name) - { + protected UserGroup createUserGroup(String name) { UserGroup g1 = new UserGroup(); g1.setGroupName(name); long id = adminClient.insertUserGroup(g1); g1.setId(id); return g1; } - } diff --git a/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/GeoStoreClientTest.java b/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/GeoStoreClientTest.java index 5e86844e..830aada9 100644 --- a/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/GeoStoreClientTest.java +++ b/src/modules/rest/client/src/test/java/it/geosolutions/geostore/services/rest/GeoStoreClientTest.java @@ -19,9 +19,11 @@ */ package it.geosolutions.geostore.services.rest; +import static org.junit.Assert.*; + import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.ClientResponse.Status; -import static org.junit.Assert.*; +import com.sun.jersey.api.client.UniformInterfaceException; import it.geosolutions.geostore.core.model.Attribute; import it.geosolutions.geostore.core.model.Resource; import it.geosolutions.geostore.core.model.SecurityRule; @@ -30,6 +32,7 @@ import it.geosolutions.geostore.core.model.enums.DataType; import it.geosolutions.geostore.core.model.enums.Role; import it.geosolutions.geostore.services.dto.ShortAttribute; +import it.geosolutions.geostore.services.dto.ShortResource; import it.geosolutions.geostore.services.dto.search.BaseField; import it.geosolutions.geostore.services.dto.search.CategoryFilter; import it.geosolutions.geostore.services.dto.search.FieldFilter; @@ -43,30 +46,23 @@ import it.geosolutions.geostore.services.rest.model.ShortResourceList; import it.geosolutions.geostore.services.rest.model.enums.RawFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.codec.binary.Base64; import org.apache.cxf.helpers.IOUtils; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.Test; -import com.sun.jersey.api.client.UniformInterfaceException; -import it.geosolutions.geostore.services.dto.ShortResource; -import java.util.Collections; - -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class GeoStoreClientTest extends BaseGeoStoreClientTest { - private final static Logger LOGGER = Logger.getLogger(GeoStoreClientTest.class); + private static final Logger LOGGER = LogManager.getLogger(GeoStoreClientTest.class); final String DEFAULTCATEGORYNAME = "TestCategory1"; - - @Test public void testRemoveAllAttribs() { @@ -133,7 +129,6 @@ public void testRemoveAllAttribs() { List loadedAttrs = loaded.getAttribute(); assertEquals(0, loadedAttrs.size()); } - } @Test @@ -162,6 +157,8 @@ public void testInsertResource() { origResource.setName("rest_test_resource_" + timeid); origResource.setStore(storedData); origResource.setAttribute(attrList); + origResource.setCreator("USER1"); + origResource.setEditor("USER2"); Long rid = client.insert(origResource); System.out.println("RESOURCE has ID " + rid); @@ -192,11 +189,10 @@ public void testInsertResource() { assertEquals(DataType.DATE, datt.getType()); assertEquals(KEY_DATE, datt.getName()); assertEquals(origDate, datt.getDateValue()); - } // test Search - SearchFilter searchFilter = new FieldFilter(BaseField.NAME, "%" + timeid, - SearchOperator.LIKE); + SearchFilter searchFilter = + new FieldFilter(BaseField.NAME, "%" + timeid, SearchOperator.LIKE); ShortResourceList rlist = client.searchResources(searchFilter); assertNotNull(rlist); assertEquals(1, rlist.getList().size()); @@ -232,6 +228,8 @@ public void testUpdateResource() { origResource.setName("rest_test_resource_" + timeid); origResource.setStore(storedData); origResource.setAttribute(attrList); + origResource.setCreator("USER1"); + origResource.setEditor("USER2"); rid = client.insert(origResource); } @@ -250,6 +248,8 @@ public void testUpdateResource() { attrList.add(new ShortAttribute("string4", "value4", DataType.STRING)); // added updResource.setAttribute(attrList); + updResource.setCreator("USER1Updated"); + updResource.setEditor("USER2Updated"); client.updateResource(rid, updResource); } @@ -269,6 +269,9 @@ public void testUpdateResource() { assertEquals("value1", attMap.get("string1")); assertEquals("value2.2", attMap.get("string2")); assertEquals("value4", attMap.get("string4")); + + assertEquals("USER1Updated", loaded.getCreator()); + assertEquals("USER2Updated", loaded.getEditor()); } // try bad update @@ -288,7 +291,6 @@ public void testUpdateResource() { LOGGER.info("Error condition successfully detected: " + response); } catch (Exception e) { LOGGER.info("Error condition successfully detected:" + e.getMessage(), e); - } } @@ -311,7 +313,6 @@ public void testSearchByCategory() { SearchFilter filter = new CategoryFilter(DEFAULTCATEGORYNAME, SearchOperator.EQUAL_TO); ShortResourceList resources = client.searchResources(filter); assertEquals(1, resources.getList().size()); - } // @Test @@ -359,6 +360,8 @@ public void testGetResourceFull() { origResource.setCategory(new RESTCategory(DEFAULTCATEGORYNAME)); origResource.setName("rest_test_resource_getFull"); origResource.setStore(storedData); + origResource.setCreator("USER1"); + origResource.setEditor("USER2"); Long rid = client.insert(origResource); System.out.println("RESOURCE has ID " + rid); @@ -381,7 +384,6 @@ public void testGetResourceFull() { System.out.println("RESOURCE: " + loaded); assertNotNull(loaded.getData()); } - } @Test @@ -390,12 +392,11 @@ public void testGetDataRawBase64() { createDefaultCategory(); final String DATA = "we wish you a merry xmas and a happy new year"; - final String ENCODED = Base64.encodeBase64String(DATA.getBytes()); + final String ENCODED = Base64.encodeBase64String(DATA.getBytes()); RESTStoredData storedData = new RESTStoredData(); storedData.setData(ENCODED); - RESTResource origResource = new RESTResource(); origResource.setCategory(new RESTCategory(DEFAULTCATEGORYNAME)); origResource.setName("rest_test_getrawdata"); @@ -417,7 +418,7 @@ public void testGetDataRawBase64() { byte[] encoded = client.getRawData(rid, null); assertArrayEquals(ENCODED.getBytes(), encoded); } - + { LOGGER.info("RAW data: base64"); byte[] decoded = client.getRawData(rid, RawFormat.BASE64); @@ -431,7 +432,7 @@ public void testGetDataRawDataURI() { createDefaultCategory(); final String DATA = "we wish you a merry xmas and a happy new year"; - final String ENCODED_DATA = Base64.encodeBase64String(DATA.getBytes()); + final String ENCODED_DATA = Base64.encodeBase64String(DATA.getBytes()); final String MEDIATYPE = "test/custom"; final String DATAURIHEADER = "data:" + MEDIATYPE + ";base64,"; final String FULL_DATA = DATAURIHEADER + ENCODED_DATA; @@ -439,7 +440,6 @@ public void testGetDataRawDataURI() { RESTStoredData storedData = new RESTStoredData(); storedData.setData(FULL_DATA); - RESTResource origResource = new RESTResource(); origResource.setCategory(new RESTCategory(DEFAULTCATEGORYNAME)); origResource.setName("rest_test_rawdatauri"); @@ -464,7 +464,8 @@ public void testGetDataRawDataURI() { // Also test the content type { LOGGER.info("RAW data: test content type"); - ClientResponse response = client.getRawData(ClientResponse.class, rid, RawFormat.DATAURI); + ClientResponse response = + client.getRawData(ClientResponse.class, rid, RawFormat.DATAURI); byte[] decoded = response.getEntity(byte[].class); @@ -475,7 +476,6 @@ public void testGetDataRawDataURI() { } } - @Test public void testSearch01() { @@ -519,12 +519,11 @@ public void testSearch01() { assertNotNull(resources.getList().get(0).getData()); assertEquals("pippo", resources.getList().get(0).getData().getData()); } - } @Test public void testDefaultSecurityRules() { - createDefaultCategory(); + createDefaultCategory(); RESTResource res = new RESTResource(); res.setCategory(new RESTCategory(DEFAULTCATEGORYNAME)); @@ -538,11 +537,11 @@ public void testDefaultSecurityRules() { assertNotNull(rules.getList()); assertEquals(1, rules.getList().size()); } - + @Test public void testupdateSecurityRules() { - - createDefaultCategory(); + + createDefaultCategory(); RESTResource res = new RESTResource(); res.setCategory(new RESTCategory(DEFAULTCATEGORYNAME)); @@ -551,40 +550,38 @@ public void testupdateSecurityRules() { res.setName("rest_test_resource_" + timeid); Long userId = createUser("u1_" + timeid, Role.USER, "---"); - + UserGroup g1 = new UserGroup(); - g1.setGroupName("g1_" + timeid); + g1.setGroupName("g1_" + timeid); Long groupId = adminClient.insertUserGroup(g1); - - + Long id = client.insert(res); List ruleList = new ArrayList(); - + SecurityRule rule = new SecurityRule(); rule.setCanRead(true); - rule.setCanWrite(true); + rule.setCanWrite(true); User user = new User(); user.setId(userId); rule.setUser(user); ruleList.add(rule); - + rule = new SecurityRule(); rule.setCanRead(true); - rule.setCanWrite(false); + rule.setCanWrite(false); UserGroup group = new UserGroup(); group.setId(groupId); rule.setGroup(group); ruleList.add(rule); - + SecurityRuleList rules = new SecurityRuleList(ruleList); client.updateSecurityRules(id, rules); - + SecurityRuleList writtenRules = client.getSecurityRules(id); assertNotNull(writtenRules.getList()); assertEquals(2, rules.getList().size()); } - @Test public void testGetShortResource() { @@ -643,11 +640,9 @@ public void testGetShortResource() { assertEquals("r1", loaded.getName()); assertFalse(loaded.isCanEdit()); } - } - private RESTResource buildResource(String name, String catName) - { + private RESTResource buildResource(String name, String catName) { RESTResource res = new RESTResource(); res.setCategory(new RESTCategory(catName)); res.setName(name); @@ -659,5 +654,4 @@ protected Long createDefaultCategory() { assertNotNull(catid); return catid; } - } diff --git a/src/modules/rest/client/src/test/resources/log4j.properties b/src/modules/rest/client/src/test/resources/log4j.properties deleted file mode 100644 index d45f8deb..00000000 --- a/src/modules/rest/client/src/test/resources/log4j.properties +++ /dev/null @@ -1,10 +0,0 @@ -log4j.rootLogger=INFO, consoleAppender - -log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender -log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout -log4j.appender.consoleAppender.layout.ConversionPattern=%p %d{yyyy-MM-dd HH:mm:ss.SSS} %c::%M:%L - %m%n - -log4j.logger.org.hibernate=INFO -log4j.logger.com.trg=INFO - -log4j.logger.it.geosolutions.geostore.services.rest.impl=DEBUG diff --git a/src/modules/rest/client/src/test/resources/log4j2.properties b/src/modules/rest/client/src/test/resources/log4j2.properties new file mode 100644 index 00000000..0e29e48a --- /dev/null +++ b/src/modules/rest/client/src/test/resources/log4j2.properties @@ -0,0 +1,17 @@ +rootLogger.level = INFO +appenders= console + + +appender.console.type = Console +appender.console.name = LogToConsole +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = %p %d{yyyy-MM-dd HH:mm:ss.SSS} %c::%M:%L - %m%n +rootLogger.appenderRef.stdout.ref = LogToConsole +rootLogger.appenderRef.console.ref = LogToConsole + +logger.restsrvimpl.name=it.geosolutions.geostore.services.rest.impl +logger.restsrvimpl.level= DEBUG +logger.hibernate1.name=org.hibernate +logger.hibernate1.level=INFO +logger.trg1.name=com.trg +logger.trg1.level=INFO \ No newline at end of file diff --git a/src/modules/rest/extjs/pom.xml b/src/modules/rest/extjs/pom.xml index 3b9bf59f..f7d2e2ca 100644 --- a/src/modules/rest/extjs/pom.xml +++ b/src/modules/rest/extjs/pom.xml @@ -18,19 +18,20 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - + 4.0.0 it.geosolutions.geostore geostore-rest-root - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-rest-extjs jar - GeoStore - Modules - REST EXTJS services + GeoStore - Modules - REST EXT-JS services @@ -42,7 +43,7 @@ geostore-services-api
    - + it.geosolutions.geostore geostore-rest-impl @@ -84,11 +85,8 @@ net.sf.json-lib json-lib - 2.4 - - jdk15 - + junit junit @@ -97,12 +95,11 @@ org.hamcrest hamcrest-core - 1.3 test javax.servlet - servlet-api + javax.servlet-api test
    @@ -112,32 +109,27 @@ org.apache.maven.plugins maven-compiler-plugin - - utf8 - 1.7 - 1.7 - - + org.apache.maven.plugins maven-surefire-plugin diff --git a/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/model/ExtGroupList.java b/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/model/ExtGroupList.java index 47d236c4..c8e2aefb 100644 --- a/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/model/ExtGroupList.java +++ b/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/model/ExtGroupList.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -28,64 +28,49 @@ package it.geosolutions.geostore.services.model; import it.geosolutions.geostore.core.model.UserGroup; - import java.util.List; - import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; /** * Class ExtGroupList. - * + * * @author Mirco Bertelli (mirco.bertelli at geo-solutions.it) - * */ @XmlRootElement(name = "ExtGroupList") public class ExtGroupList { - - private long count; - private List list; + private long count; - public ExtGroupList() { + private List list; - } + public ExtGroupList() {} - /** - * @param list - */ + /** @param list */ public ExtGroupList(long count, List list) { this.count = count; this.list = list; } - /** - * @return the count - */ + /** @return the count */ @XmlElement(name = "GroupCount") public long getCount() { return count; } - /** - * @param count the count to set - */ + /** @param count the count to set */ public void setCount(long count) { this.count = count; } - /** - * @return List - */ + /** @return List */ @XmlElement(name = "Group") public List getList() { return list; } - /** - * @param list - */ + /** @param list */ public void setList(List list) { this.list = list; } diff --git a/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/model/ExtResourceList.java b/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/model/ExtResourceList.java index 0be57d1e..c1711ed8 100644 --- a/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/model/ExtResourceList.java +++ b/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/model/ExtResourceList.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -29,18 +29,15 @@ package it.geosolutions.geostore.services.model; import it.geosolutions.geostore.core.model.Resource; - import java.util.List; - import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; /** * Class ExtResourceList. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ @XmlRootElement(name = "ExtResourceList") public class ExtResourceList { @@ -49,44 +46,32 @@ public class ExtResourceList { private List list; - public ExtResourceList() { + public ExtResourceList() {} - } - - /** - * @param list - */ + /** @param list */ public ExtResourceList(long count, List list) { this.count = count; this.list = list; } - /** - * @return the count - */ + /** @return the count */ @XmlElement(name = "ResourceCount") public long getCount() { return count; } - /** - * @param count the count to set - */ + /** @param count the count to set */ public void setCount(long count) { this.count = count; } - /** - * @return List - */ + /** @return List */ @XmlElement(name = "Resource") public List getList() { return list; } - /** - * @param list - */ + /** @param list */ public void setList(List list) { this.list = list; } @@ -95,5 +80,4 @@ public void setList(List list) { public boolean isEmpty() { return list == null || list.isEmpty(); } - } diff --git a/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/model/ExtUserList.java b/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/model/ExtUserList.java index 169fc321..57d61715 100644 --- a/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/model/ExtUserList.java +++ b/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/model/ExtUserList.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -29,18 +29,15 @@ package it.geosolutions.geostore.services.model; import it.geosolutions.geostore.core.model.User; - import java.util.List; - import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; /** * Class ExtUserList. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ @XmlRootElement(name = "ExtUserList") public class ExtUserList { @@ -49,44 +46,32 @@ public class ExtUserList { private List list; - public ExtUserList() { + public ExtUserList() {} - } - - /** - * @param list - */ + /** @param list */ public ExtUserList(long count, List list) { this.count = count; this.list = list; } - /** - * @return the count - */ + /** @return the count */ @XmlElement(name = "UserCount") public long getCount() { return count; } - /** - * @param count the count to set - */ + /** @param count the count to set */ public void setCount(long count) { this.count = count; } - /** - * @return List - */ + /** @return List */ @XmlElement(name = "User") public List getList() { return list; } - /** - * @param list - */ + /** @param list */ public void setList(List list) { this.list = list; } @@ -95,5 +80,4 @@ public void setList(List list) { public boolean isEmpty() { return list == null || list.isEmpty(); } - } diff --git a/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/rest/RESTExtJsService.java b/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/rest/RESTExtJsService.java index 305b0bc3..4f8bb7ac 100644 --- a/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/rest/RESTExtJsService.java +++ b/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/rest/RESTExtJsService.java @@ -5,7 +5,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -17,7 +17,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -36,36 +36,27 @@ import it.geosolutions.geostore.services.rest.exception.BadRequestWebEx; import it.geosolutions.geostore.services.rest.exception.InternalErrorWebEx; import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; - import javax.annotation.security.RolesAllowed; -import javax.ws.rs.Consumes; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; +import javax.ws.rs.*; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; - import org.apache.cxf.jaxrs.ext.multipart.Multipart; import org.springframework.security.access.annotation.Secured; /** * Interface RESTExtJsService. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public interface RESTExtJsService { @GET @Path("/search/{nameLike}") - @Produces({ MediaType.APPLICATION_JSON }) - @Secured({ "ROLE_ADMIN", "ROLE_USER", "ROLE_ANONYMOUS" }) - String getAllResources(@Context SecurityContext sc, + @Produces({MediaType.APPLICATION_JSON}) + @Secured({"ROLE_ADMIN", "ROLE_USER", "ROLE_ANONYMOUS"}) + String getAllResources( + @Context SecurityContext sc, @PathParam("nameLike") String nameLike, @QueryParam("start") Integer start, @QueryParam("limit") Integer limit) @@ -73,9 +64,10 @@ String getAllResources(@Context SecurityContext sc, @GET @Path("/search/category/{categoryName}") - @Produces({ MediaType.APPLICATION_JSON }) - @Secured({ "ROLE_ADMIN", "ROLE_USER", "ROLE_ANONYMOUS" }) - String getResourcesByCategory(@Context SecurityContext sc, + @Produces({MediaType.APPLICATION_JSON}) + @Secured({"ROLE_ADMIN", "ROLE_USER", "ROLE_ANONYMOUS"}) + String getResourcesByCategory( + @Context SecurityContext sc, @PathParam("categoryName") String categoryName, @QueryParam("start") Integer start, @QueryParam("limit") Integer limit, @@ -85,10 +77,11 @@ String getResourcesByCategory(@Context SecurityContext sc, @GET @Path("/search/category/{categoryName}/{resourceNameLike}") - @Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) - @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - String getResourcesByCategory(@Context SecurityContext sc, - @PathParam("categoryName") String categoryName, + @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) + @RolesAllowed({"ADMIN", "USER", "GUEST"}) + String getResourcesByCategory( + @Context SecurityContext sc, + @PathParam("categoryName") String categoryName, @PathParam("resourceNameLike") String resourceNameLike, @QueryParam("start") Integer start, @QueryParam("limit") Integer limit, @@ -98,9 +91,10 @@ String getResourcesByCategory(@Context SecurityContext sc, @GET @Path("/search/category/{categoryName}/{resourceNameLike}/{extraAttributes}") - @Produces({ MediaType.APPLICATION_JSON }) - @RolesAllowed({ "ADMIN", "USER", "GUEST" }) - String getResourcesByCategory(@Context SecurityContext sc, + @Produces({MediaType.APPLICATION_JSON}) + @RolesAllowed({"ADMIN", "USER", "GUEST"}) + String getResourcesByCategory( + @Context SecurityContext sc, @PathParam("categoryName") String categoryName, @PathParam("resourceNameLike") String resourceNameLike, @PathParam("extraAttributes") String extraAttributes, @@ -113,10 +107,11 @@ String getResourcesByCategory(@Context SecurityContext sc, @POST @GET @Path("/search/list") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) - @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) - @Secured({ "ROLE_ADMIN", "ROLE_USER", "ROLE_ANONYMOUS" }) - ExtResourceList getExtResourcesList(@Context SecurityContext sc, + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) + @Secured({"ROLE_ADMIN", "ROLE_USER", "ROLE_ANONYMOUS"}) + ExtResourceList getExtResourcesList( + @Context SecurityContext sc, @QueryParam("start") Integer start, @QueryParam("limit") Integer limit, @QueryParam("includeAttributes") @DefaultValue("false") boolean includeAttributes, @@ -126,9 +121,10 @@ ExtResourceList getExtResourcesList(@Context SecurityContext sc, @GET @Path("/search/users/{nameLike}") - @Produces({ MediaType.APPLICATION_JSON }) - @Secured({ "ROLE_ADMIN"}) - ExtUserList getUsersList(@Context SecurityContext sc, + @Produces({MediaType.APPLICATION_JSON}) + @Secured({"ROLE_ADMIN"}) + ExtUserList getUsersList( + @Context SecurityContext sc, @PathParam("nameLike") String nameLike, @QueryParam("start") Integer start, @QueryParam("limit") Integer limit, @@ -137,6 +133,7 @@ ExtUserList getUsersList(@Context SecurityContext sc, /** * Search for groups by name and return paginated results. + * * @param sc security context * @param nameLike a substring in the name * @param start the n-th group shown as first in results. @@ -147,20 +144,20 @@ ExtUserList getUsersList(@Context SecurityContext sc, */ @GET @Path("/search/groups/{nameLike}") - @Produces({ MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) - @Secured({ "ROLE_ADMIN", "ROLE_USER"}) - ExtGroupList getGroupsList(@Context SecurityContext sc, + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Secured({"ROLE_ADMIN", "ROLE_USER"}) + ExtGroupList getGroupsList( + @Context SecurityContext sc, @PathParam("nameLike") String nameLike, - @QueryParam("start") Integer start, + @QueryParam("start") Integer start, @QueryParam("limit") Integer limit, @QueryParam("all") @DefaultValue("false") boolean all) throws BadRequestWebEx; @GET @Path("/resource/{id}") - @Produces({ MediaType.TEXT_XML, MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN }) - @Secured({ "ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS" }) - ShortResource getResource(@Context SecurityContext sc, - @PathParam("id") long id) - throws NotFoundWebEx; + @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON}) + @Secured({"ROLE_USER", "ROLE_ADMIN", "ROLE_ANONYMOUS"}) + ShortResource getResource(@Context SecurityContext sc, @PathParam("id") long id) + throws NotFoundWebEx; } diff --git a/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTExtJsServiceImpl.java b/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTExtJsServiceImpl.java index 73d87c38..06035702 100644 --- a/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTExtJsServiceImpl.java +++ b/src/modules/rest/extjs/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTExtJsServiceImpl.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -27,23 +27,14 @@ */ package it.geosolutions.geostore.services.rest.impl; -import it.geosolutions.geostore.core.model.Attribute; -import it.geosolutions.geostore.core.model.Resource; -import it.geosolutions.geostore.core.model.SecurityRule; -import it.geosolutions.geostore.core.model.User; -import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.*; import it.geosolutions.geostore.core.model.enums.Role; import it.geosolutions.geostore.services.ResourceService; import it.geosolutions.geostore.services.SecurityService; import it.geosolutions.geostore.services.UserGroupService; import it.geosolutions.geostore.services.UserService; import it.geosolutions.geostore.services.dto.ShortResource; -import it.geosolutions.geostore.services.dto.search.AndFilter; -import it.geosolutions.geostore.services.dto.search.BaseField; -import it.geosolutions.geostore.services.dto.search.CategoryFilter; -import it.geosolutions.geostore.services.dto.search.FieldFilter; -import it.geosolutions.geostore.services.dto.search.SearchFilter; -import it.geosolutions.geostore.services.dto.search.SearchOperator; +import it.geosolutions.geostore.services.dto.search.*; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.InternalErrorServiceEx; import it.geosolutions.geostore.services.model.ExtGroupList; @@ -54,21 +45,13 @@ import it.geosolutions.geostore.services.rest.exception.ForbiddenErrorWebEx; import it.geosolutions.geostore.services.rest.exception.InternalErrorWebEx; import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.Iterator; -import java.util.List; - +import java.util.*; import javax.ws.rs.core.SecurityContext; - import net.sf.json.JSON; import net.sf.json.JSONArray; import net.sf.json.JSONObject; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * Class RESTExtJsServiceImpl. @@ -78,7 +61,7 @@ */ public class RESTExtJsServiceImpl extends RESTServiceImpl implements RESTExtJsService { - private final static Logger LOGGER = Logger.getLogger(RESTExtJsServiceImpl.class); + private static final Logger LOGGER = LogManager.getLogger(RESTExtJsServiceImpl.class); private ResourceService resourceService; @@ -86,16 +69,12 @@ public class RESTExtJsServiceImpl extends RESTServiceImpl implements RESTExtJsSe private UserGroupService groupService; - /** - * @param resourceService - */ + /** @param resourceService */ public void setResourceService(ResourceService resourceService) { this.resourceService = resourceService; } - /** - * @param userService the userService to set - */ + /** @param userService the userService to set */ public void setUserService(UserService userService) { this.userService = userService; } @@ -106,13 +85,12 @@ public void setUserGroupService(UserGroupService userGroupService) { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTExtJsService#getAllResources (javax.ws.rs.core.UriInfo, javax.ws.rs.core.SecurityContext, * java.lang.String, java.lang.Integer, java.lang.Integer) */ @Override - public String getAllResources(SecurityContext sc, String nameLike, - Integer start, Integer limit) + public String getAllResources(SecurityContext sc, String nameLike, Integer start, Integer limit) throws BadRequestWebEx { if (start == null || limit == null) { @@ -120,27 +98,30 @@ public String getAllResources(SecurityContext sc, String nameLike, } if (LOGGER.isInfoEnabled()) { - LOGGER.info("Retrieving paginated resource list (start="+start+" limit="+limit+")"); + LOGGER.info("Retrieving paginated resource list (start={} limit={})", start, limit); } User authUser = null; try { authUser = extractAuthUser(sc); } catch (InternalErrorWebEx ie) { - // serch without user information - LOGGER.warn("Error in validating user (this action should probably be aborted)", ie); // why is this exception caught? + // search without user information + LOGGER.warn( + "Error in validating user (this action should probably be aborted)", + ie); // why is this exception caught? } int page = start == 0 ? start : start / limit; try { nameLike = nameLike.replaceAll("[*]", "%"); - // TOOD: implement includeAttributes and includeData + // TODO: implement includeAttributes and includeData - List resources = resourceService.getList(nameLike, page, limit, authUser); + List resources = + resourceService.getList(nameLike, page, limit, authUser); long count = 0; - if (resources != null && resources.size() > 0) { + if (resources != null && !resources.isEmpty()) { count = resourceService.getCountByFilterAndUser(nameLike, authUser); } @@ -157,35 +138,61 @@ public String getAllResources(SecurityContext sc, String nameLike, /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTExtJsService#getResourcesByCategory(javax.ws.rs.core.SecurityContext, java.lang.String, * java.lang.Integer, java.lang.Integer) */ @Override - public String getResourcesByCategory(SecurityContext sc, String categoryName, Integer start, - Integer limit, boolean includeAttributes, - boolean includeData) throws BadRequestWebEx { - return getResourcesByCategory(sc, categoryName, null, start, limit, includeAttributes, includeData); + public String getResourcesByCategory( + SecurityContext sc, + String categoryName, + Integer start, + Integer limit, + boolean includeAttributes, + boolean includeData) + throws BadRequestWebEx { + return getResourcesByCategory( + sc, categoryName, null, start, limit, includeAttributes, includeData); } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTExtJsService#getResourcesByCategory(javax.ws.rs.core.SecurityContext, java.lang.String, * java.lang.Integer, java.lang.Integer) */ @Override - public String getResourcesByCategory(SecurityContext sc, String categoryName, String resourceNameLike, Integer start, - Integer limit, boolean includeAttributes, boolean includeData) throws BadRequestWebEx { - return getResourcesByCategory(sc, categoryName, resourceNameLike, null, start, limit, includeAttributes, includeData); + public String getResourcesByCategory( + SecurityContext sc, + String categoryName, + String resourceNameLike, + Integer start, + Integer limit, + boolean includeAttributes, + boolean includeData) + throws BadRequestWebEx { + return getResourcesByCategory( + sc, + categoryName, + resourceNameLike, + null, + start, + limit, + includeAttributes, + includeData); } @Override - public String getResourcesByCategory(SecurityContext sc, - String categoryName, String resourceNameLike, String extraAttributes, - Integer start, Integer limit, boolean includeAttributes, boolean includeData) - throws BadRequestWebEx, InternalErrorWebEx - { + public String getResourcesByCategory( + SecurityContext sc, + String categoryName, + String resourceNameLike, + String extraAttributes, + Integer start, + Integer limit, + boolean includeAttributes, + boolean includeData) + throws BadRequestWebEx, InternalErrorWebEx { if (((start != null) && (limit == null)) || ((start == null) && (limit != null))) { throw new BadRequestWebEx("start and limit params should be declared together"); } @@ -195,13 +202,20 @@ public String getResourcesByCategory(SecurityContext sc, } // read extra attributes - List extraAttributesList = extraAttributes != null ? - Arrays.asList(extraAttributes.split(",")): - Collections.EMPTY_LIST; + List extraAttributesList = + extraAttributes != null + ? Arrays.asList(extraAttributes.split(",")) + : Collections.EMPTY_LIST; if (LOGGER.isDebugEnabled()) { - LOGGER.debug("getResourcesByCategory(" + categoryName + ", start=" + start + ", limit=" - + limit + (resourceNameLike != null ? ", search=" + resourceNameLike : "")); + LOGGER.debug( + "getResourcesByCategory(" + + categoryName + + ", start=" + + start + + ", limit=" + + limit + + (resourceNameLike != null ? ", search=" + resourceNameLike : "")); } User authUser = null; @@ -209,7 +223,9 @@ public String getResourcesByCategory(SecurityContext sc, authUser = extractAuthUser(sc); } catch (InternalErrorWebEx ie) { // search without user information - LOGGER.warn("Error in validating user (this action should probably be aborted)", ie); // why is this exception caught? + LOGGER.warn( + "Error in validating user (this action should probably be aborted)", + ie); // why is this exception caught? } Integer page = null; @@ -221,31 +237,42 @@ public String getResourcesByCategory(SecurityContext sc, SearchFilter filter = new CategoryFilter(categoryName, SearchOperator.EQUAL_TO); if (resourceNameLike != null) { resourceNameLike = resourceNameLike.replaceAll("[*]", "%"); - filter = new AndFilter(filter, new FieldFilter(BaseField.NAME, resourceNameLike, SearchOperator.ILIKE)); + filter = + new AndFilter( + filter, + new FieldFilter( + BaseField.NAME, resourceNameLike, SearchOperator.ILIKE)); } - List resources = resourceService.getResources(filter, - page, limit, - includeAttributes || (extraAttributes != null && !extraAttributes.isEmpty()), - includeData, + List resources = + filterOutUnadvertisedResources( + resourceService.getResources( + filter, + page, + limit, + includeAttributes + || (extraAttributes != null + && !extraAttributes.isEmpty()), + includeData, + authUser), authUser); long count = 0; - if (resources != null && resources.size() > 0) { + if (!resources.isEmpty()) { count = resourceService.getCountByFilterAndUser(filter, authUser); } - JSONObject result = makeExtendedJSONResult(true, count, resources, - authUser, extraAttributesList, includeAttributes, - includeData); + JSONObject result = + makeExtendedJSONResult( + true, + count, + resources, + authUser, + extraAttributesList, + includeAttributes, + includeData); return result.toString(); - - } catch (InternalErrorServiceEx e) { - LOGGER.warn(e.getMessage(), e); - - JSONObject obj = makeJSONResult(false, 0, null, authUser); - return obj.toString(); - } catch (BadRequestServiceEx e) { + } catch (InternalErrorServiceEx | BadRequestServiceEx e) { LOGGER.warn(e.getMessage(), e); JSONObject obj = makeJSONResult(false, 0, null, authUser); @@ -255,29 +282,42 @@ public String getResourcesByCategory(SecurityContext sc, /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTExtJsService#getResourcesList(javax.ws.rs.core.SecurityContext, java.lang.Integer, * java.lang.Integer, boolean, boolean, it.geosolutions.geostore.services.dto.search.SearchFilter) */ @Override - public ExtResourceList getExtResourcesList(SecurityContext sc, Integer start, Integer limit, - boolean includeAttributes, boolean includeData, SearchFilter filter) throws BadRequestWebEx { + public ExtResourceList getExtResourcesList( + SecurityContext sc, + Integer start, + Integer limit, + boolean includeAttributes, + boolean includeData, + SearchFilter filter) + throws BadRequestWebEx { if (((start != null) && (limit == null)) || ((start == null) && (limit != null))) { throw new BadRequestWebEx("start and limit params should be declared together"); } if (LOGGER.isDebugEnabled()) { - LOGGER.debug("getResourcesList(start=" + start + ", limit=" + limit - + ", includeAttributes=" + includeAttributes); + LOGGER.debug( + "getResourcesList(start=" + + start + + ", limit=" + + limit + + ", includeAttributes=" + + includeAttributes); } User authUser = null; try { authUser = extractAuthUser(sc); } catch (InternalErrorWebEx ie) { - // serch without user information - LOGGER.warn("Error in validating user (this action should probably be aborted)", ie); // why is this exception caught? + // search without user information + LOGGER.warn( + "Error in validating user (this action should probably be aborted)", + ie); // why is this exception caught? } Integer page = null; @@ -286,29 +326,56 @@ public ExtResourceList getExtResourcesList(SecurityContext sc, Integer start, In } try { - List resources = resourceService.getResources(filter, page, limit, - includeAttributes, includeData, authUser); + List resources = + filterOutUnadvertisedResources( + resourceService.getResources( + filter, page, limit, includeAttributes, includeData, authUser), + authUser); - // Here the Read permission on each resource must be checked due to will be returned the full Resource not just a ShortResource - // N.B. This is a bad method to check the permissions on each requested resource, it can perform 2 database access for each resource. - // Possible optimization -> When retrieving the resources, add to "filter" also another part to load only the allowed resources. + // Here the Read permission on each resource must be checked due to will be returned the + // full Resource not just a ShortResource + // N.B. This is a bad method to check the permissions on each requested resource, it can + // perform 2 database access for each resource. + // Possible optimization -> When retrieving the resources, add to "filter" also another + // part to load only the allowed resources. long count = 0; - if (resources != null && resources.size() > 0) { + if (!resources.isEmpty()) { count = resourceService.getCountByFilterAndUser(filter, authUser); } - ExtResourceList list = new ExtResourceList(count, resources); - return list; + return new ExtResourceList(count, resources); - } catch (InternalErrorServiceEx e) { + } catch (InternalErrorServiceEx | BadRequestServiceEx e) { LOGGER.warn(e.getMessage(), e); return null; - } catch (BadRequestServiceEx e) { - LOGGER.warn(e.getMessage(), e); + } + } - return null; + /** + * This internal method allows us to filter out "unadvertised" resources for + * non-admin/non-owners + * + * @param resources + * @param authUser + * @return + */ + private List filterOutUnadvertisedResources(List resources, User authUser) { + List filteredList = new ArrayList<>(); + for (Resource r : resources) { + // get security rules for user + User owner = null; + for (SecurityRule rule : + resourceService.getUserSecurityRule(authUser.getName(), r.getId())) { + owner = rule.getUser(); + } + if (r.isAdvertised() + || (authUser.getRole().equals(Role.ADMIN) + || (owner != null && owner.getId().equals(authUser.getId())))) { + filteredList.add(r); + } } + return filteredList; } /** @@ -318,9 +385,22 @@ public ExtResourceList getExtResourcesList(SecurityContext sc, Integer start, In * @param extraAttributes * @return JSONObject */ - private JSONObject makeExtendedJSONResult(boolean success, long count, List resources, - User authUser, List extraAttributes, boolean includeAttributes, boolean includeData) { - return makeGeneralizedJSONResult(success, count, resources, authUser, extraAttributes, includeAttributes, includeData); + private JSONObject makeExtendedJSONResult( + boolean success, + long count, + List resources, + User authUser, + List extraAttributes, + boolean includeAttributes, + boolean includeData) { + return makeGeneralizedJSONResult( + success, + count, + resources, + authUser, + extraAttributes, + includeAttributes, + includeData); } /** @@ -329,21 +409,26 @@ private JSONObject makeExtendedJSONResult(boolean success, long count, List resources, - User authUser) { + private JSONObject makeJSONResult( + boolean success, long count, List resources, User authUser) { return makeGeneralizedJSONResult(success, count, resources, authUser, null, false, false); } @Override - public ExtUserList getUsersList(SecurityContext sc, String nameLike, Integer start, - Integer limit, boolean includeAttributes) throws BadRequestWebEx { + public ExtUserList getUsersList( + SecurityContext sc, + String nameLike, + Integer start, + Integer limit, + boolean includeAttributes) + throws BadRequestWebEx { if (((start != null) && (limit == null)) || ((start == null) && (limit != null))) { throw new BadRequestWebEx("start and limit params should be declared together"); } if (LOGGER.isDebugEnabled()) { - LOGGER.debug("getUsersList(start=" + start + ", limit=" + limit); + LOGGER.debug("getUsersList(start={}, limit={}", start, limit); } Integer page = null; @@ -356,30 +441,30 @@ public ExtUserList getUsersList(SecurityContext sc, String nameLike, Integer sta List users = userService.getAll(page, limit, nameLike, includeAttributes); long count = 0; - if (users != null && users.size() > 0) { + if (users != null && !users.isEmpty()) { count = userService.getCount(nameLike); } - ExtUserList list = new ExtUserList(count, users); - return list; + return new ExtUserList(count, users); } catch (BadRequestServiceEx e) { LOGGER.warn(e.getMessage(), e); - + return null; } } @Override - public ExtGroupList getGroupsList(SecurityContext sc, String nameLike, Integer start, - Integer limit, boolean all) throws BadRequestWebEx { + public ExtGroupList getGroupsList( + SecurityContext sc, String nameLike, Integer start, Integer limit, boolean all) + throws BadRequestWebEx { if (((start != null) && (limit == null)) || ((start == null) && (limit != null))) { throw new BadRequestWebEx("start and limit params should be declared together"); } if (LOGGER.isDebugEnabled()) { - LOGGER.debug("getGroupsList(start=" + start + ", limit=" + limit); + LOGGER.debug("getGroupsList(start={}, limit={}", start, limit); } Integer page = null; @@ -391,29 +476,30 @@ public ExtGroupList getGroupsList(SecurityContext sc, String nameLike, Integer s try { authUser = extractAuthUser(sc); } catch (InternalErrorWebEx ie) { - // serch without user information - LOGGER.warn("Error in validating user (this action should probably be aborted)", ie); // why is this exception caught? + // search without user information + LOGGER.warn( + "Error in validating user (this action should probably be aborted)", + ie); // why is this exception caught? return null; } try { nameLike = nameLike.replaceAll("[*]", "%"); - List groups = groupService.getAllAllowed(authUser, page, limit, nameLike, all); + List groups = + groupService.getAllAllowed(authUser, page, limit, nameLike, all); long count = 0; - if (groups != null && groups.size() > 0) { + if (groups != null && !groups.isEmpty()) { count = groupService.getCount(authUser, nameLike, all); } - ExtGroupList list = new ExtGroupList(count, groups); - return list; + return new ExtGroupList(count, groups); } catch (BadRequestServiceEx e) { LOGGER.warn(e.getMessage(), e); return null; } - } /* (non-Javadoc) @@ -434,8 +520,14 @@ protected SecurityService getSecurityService() { * @param extraAttributes * @return */ - private JSONObject makeGeneralizedJSONResult(boolean success, long count, List resources, - User authUser, List extraAttributes, boolean includeAttributes, boolean includeData) { + private JSONObject makeGeneralizedJSONResult( + boolean success, + long count, + List resources, + User authUser, + List extraAttributes, + boolean includeAttributes, + boolean includeData) { JSONObject jsonObj = new JSONObject(); jsonObj.put("success", success); jsonObj.put("totalCount", count); @@ -491,7 +583,9 @@ private JSONObject makeGeneralizedJSONResult(boolean success, long count, List groups = new ArrayList(); for (UserGroup g : authUser.getGroups()) { groups.add(g.getGroupName()); } - for (SecurityRule rule : resourceService - .getGroupSecurityRule(groups, - r.getId())) { - //GUEST users can not access to the delete and edit(resource,data blob is editable) services - //so only authenticated users with + for (SecurityRule rule : + resourceService.getGroupSecurityRule(groups, r.getId())) { + // GUEST users can not access to the delete and edit(resource,data blob is + // editable) services + // so only authenticated users with if (rule.isCanWrite() && !authUser.getRole().equals(Role.GUEST)) { canEdit = true; canDelete = true; break; } } - //get security rules for user - for (SecurityRule rule : resourceService - .getUserSecurityRule(authUser.getName(), - r.getId())) { + // get security rules for user + for (SecurityRule rule : + resourceService.getUserSecurityRule(authUser.getName(), r.getId())) { User owner = rule.getUser(); UserGroup userGroup = rule.getGroup(); if (owner != null) { @@ -639,8 +740,13 @@ private void readSecurity() { } } else if (userGroup != null) { if (authUser.getGroups() != null - && authUser.getGroups().contains( // FIXME: Given object cannot contain instances of String (expected UserGroup) - userGroup.getGroupName())) { + && authUser.getGroups().stream() + .noneMatch( + ug -> + ug.getGroupName() + .equals( + userGroup + .getGroupName()))) { if (rule.isCanWrite()) { canEdit = true; canDelete = true; @@ -649,79 +755,68 @@ private void readSecurity() { } } } - } } } - /** - * @return true if the logged user is owner of the resource and false otherwise - */ + /** @return true if the logged user is owner of the resource and false otherwise */ boolean isCanDelete() { return canDelete; } - /** - * @return true if the logged user is owner of the resource and false otherwise - */ + /** @return true if the logged user is owner of the resource and false otherwise */ boolean isCanEdit() { return canEdit; } - /** - * @return data creation - */ + /** @return data creation */ Date getCreation() { return sr != null ? sr.getCreation() : r.getCreation(); } - /** - * @return last update - */ + /** @return last update */ Date getLastUpdate() { return sr != null ? sr.getLastUpdate() : r.getLastUpdate(); } - /** - * @return resource description - */ + /** @return resource description */ String getDescription() { return sr != null ? sr.getDescription() : r.getDescription(); } - /** - * @return resource id - */ + /** @return resource id */ long getId() { return sr != null ? sr.getId() : r.getId(); } - /** - * @return resource name - */ + /** @return resource name */ String getName() { return sr != null ? sr.getName() : r.getName(); } - /** - * @return resource attributes if contains - */ + /** @return resource attributes if contains */ List getAttribute() { return r != null ? r.getAttribute() : null; } - /** - * @return true if there are an user logged - */ + /** @return true if there are a user logged */ public Boolean isCanCopy() { return authUser != null; } - /** - * @return resource owner - */ + /** @return resource owner */ public String getOwner() { return owner; } + + public String getCreator() { + String creator = sr != null ? sr.getCreator() : r.getCreator(); + return creator != null ? creator : getOwner(); + } + + public String getEditor() { + String editor = sr != null ? sr.getEditor() : r.getEditor(); + return editor != null ? editor : getOwner(); + } } } diff --git a/src/modules/rest/extjs/src/main/resources/applicationContext.xml b/src/modules/rest/extjs/src/main/resources/applicationContext.xml index aacb5ebe..174e692b 100644 --- a/src/modules/rest/extjs/src/main/resources/applicationContext.xml +++ b/src/modules/rest/extjs/src/main/resources/applicationContext.xml @@ -1,56 +1,51 @@ + - - - + + + - + - + - + - + - + - - - + + + - - + + - + diff --git a/src/modules/rest/extjs/src/test/java/it/geosolutions/geostore/services/rest/impl/RESTExtJsServiceImplTest.java b/src/modules/rest/extjs/src/test/java/it/geosolutions/geostore/services/rest/impl/RESTExtJsServiceImplTest.java index f173d6fe..7400bf7e 100644 --- a/src/modules/rest/extjs/src/test/java/it/geosolutions/geostore/services/rest/impl/RESTExtJsServiceImplTest.java +++ b/src/modules/rest/extjs/src/test/java/it/geosolutions/geostore/services/rest/impl/RESTExtJsServiceImplTest.java @@ -17,10 +17,17 @@ package it.geosolutions.geostore.services.rest.impl; +import static org.junit.Assert.*; + import com.googlecode.genericdao.search.Search; import it.geosolutions.geostore.core.model.Category; import it.geosolutions.geostore.core.model.Resource; +import it.geosolutions.geostore.core.model.SecurityRule; +import it.geosolutions.geostore.core.model.UserGroup; import it.geosolutions.geostore.core.model.enums.Role; +import it.geosolutions.geostore.services.dto.ShortResource; +import it.geosolutions.geostore.services.rest.model.SecurityRuleList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -31,46 +38,38 @@ import net.sf.json.JSONSerializer; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.*; -/** - * - * @author ETj (etj at geo-solutions.it) - */ -public class RESTExtJsServiceImplTest extends ServiceTestBase -{ +/** @author ETj (etj at geo-solutions.it) */ +public class RESTExtJsServiceImplTest extends ServiceTestBase { - private RESTExtJsServiceImpl restExtJsService; + private final RESTExtJsServiceImpl restExtJsService; - public RESTExtJsServiceImplTest() - { + public RESTExtJsServiceImplTest() { restExtJsService = ctx.getBean("restExtJsService", RESTExtJsServiceImpl.class); assertNotNull(restExtJsService); } @Before - public void setUp() throws Exception - { + public void setUp() throws Exception { assertNotNull(restExtJsService); removeAll(); } @Test - public void testGetAllResources_auth_base() throws Exception - { + public void testGetAllResources_auth_base() throws Exception { final String CAT_NAME = "CAT000"; assertEquals(0, resourceService.getAll(null, null, buildFakeAdminUser()).size()); - long u0 = restCreateUser("u0", Role.USER, "p0"); - long u1 = restCreateUser("u1", Role.USER, "p1"); + long u0 = restCreateUser("u0", Role.USER, null, "p0"); + long u1 = restCreateUser("u1", Role.USER, null, "p1"); Category cat = createCategory(CAT_NAME); - restCreateResource("r_u0_0", "x", CAT_NAME, u0); + restCreateResource("r_u0_0", "x", CAT_NAME, u0, true); - restCreateResource("r_u1_0", "x", CAT_NAME, u1); - restCreateResource("r_u1_1", "x", CAT_NAME, u1); + restCreateResource("r_u1_0", "x", CAT_NAME, u1, true); + restCreateResource("r_u1_1", "x", CAT_NAME, u1, true); { SecurityContext sc = new SimpleSecurityContext(u0); @@ -103,14 +102,13 @@ public void testGetAllResources_auth_base() throws Exception } @Test - public void testGetAllResources_auth_many() throws Exception - { + public void testGetAllResources_auth_many() throws Exception { final String CAT_NAME = "CAT009"; assertEquals(0, resourceService.getAll(null, null, buildFakeAdminUser()).size()); - long u0 = restCreateUser("u0", Role.USER, "p0"); - long u1 = restCreateUser("u1", Role.USER, "p1"); + long u0 = restCreateUser("u0", Role.USER, null, "p0"); + long u1 = restCreateUser("u1", Role.USER, null, "p1"); Category cat = createCategory(CAT_NAME); @@ -118,11 +116,10 @@ public void testGetAllResources_auth_many() throws Exception int RESNUM1 = RESNUM0 * 2; for (int i = 1000; i < 1000 + RESNUM0; i++) { + restCreateResource("r_u0_" + i, "x", CAT_NAME, u0, true); - restCreateResource("r_u0_"+i, "x", CAT_NAME, u0); - - restCreateResource("r_u1_"+i+"a", "x", CAT_NAME, u1); - restCreateResource("r_u1_"+i+"b", "x", CAT_NAME, u1); + restCreateResource("r_u1_" + i + "a", "x", CAT_NAME, u1, true); + restCreateResource("r_u1_" + i + "b", "x", CAT_NAME, u1, true); } int cnt = resourceDAO.count(new Search(Resource.class)); @@ -151,7 +148,8 @@ public void testGetAllResources_auth_many() throws Exception } { SecurityContext sc = new SimpleSecurityContext(u0); - String response = restExtJsService.getResourcesByCategory(sc, CAT_NAME, "*", 0, 10, false, false); + String response = + restExtJsService.getResourcesByCategory(sc, CAT_NAME, "*", 0, 10, false, false); System.out.println("JSON for u0 " + response); @@ -162,7 +160,8 @@ public void testGetAllResources_auth_many() throws Exception { SecurityContext sc = new SimpleSecurityContext(u1); - String response = restExtJsService.getResourcesByCategory(sc, CAT_NAME, "*", 0, 10, false, false); + String response = + restExtJsService.getResourcesByCategory(sc, CAT_NAME, "*", 0, 10, false, false); System.out.println("JSON for u1 " + response); @@ -172,29 +171,27 @@ public void testGetAllResources_auth_many() throws Exception } } - @Test - public void testGetAllResources_ilike() throws Exception - { + public void testGetAllResources_iLike() throws Exception { final String CAT0_NAME = "CAT000"; final String CAT1_NAME = "CAT111"; final String RES_NAME = "a MiXeD cAsE sTrInG"; assertEquals(0, resourceService.getAll(null, null, buildFakeAdminUser()).size()); - long u0 = restCreateUser("u0", Role.USER, "p0"); + long u0 = restCreateUser("u0", Role.USER, null, "p0"); createCategory(CAT0_NAME); createCategory(CAT1_NAME); - restCreateResource(RES_NAME, "x", CAT0_NAME, u0); - restCreateResource(RES_NAME.toLowerCase(), "x", CAT0_NAME, u0); - restCreateResource(RES_NAME.toUpperCase(), "x", CAT0_NAME, u0); - - restCreateResource(RES_NAME + " in another category", "x", CAT1_NAME, u0); + restCreateResource(RES_NAME, "x", CAT0_NAME, u0, true); + restCreateResource(RES_NAME.toLowerCase(), "x", CAT0_NAME, u0, true); + restCreateResource(RES_NAME.toUpperCase(), "x", CAT0_NAME, u0, true); - restCreateResource("just an extra resource we shouldnt care about", "x", CAT0_NAME, u0); + restCreateResource(RES_NAME + " in another category", "x", CAT1_NAME, u0, true); + restCreateResource( + "just an extra resource we shouldn't care about", "x", CAT0_NAME, u0, true); { SecurityContext sc = new SimpleSecurityContext(u0); @@ -209,7 +206,9 @@ public void testGetAllResources_ilike() throws Exception { SecurityContext sc = new SimpleSecurityContext(u0); - String response = restExtJsService.getResourcesByCategory(sc, CAT0_NAME, "*mIxEd*", null, 0, 1000, false, false);; + String response = + restExtJsService.getResourcesByCategory( + sc, CAT0_NAME, "*mIxEd*", null, 0, 1000, false, false); System.out.println("JSON " + response); @@ -219,27 +218,168 @@ public void testGetAllResources_ilike() throws Exception } } + @Test + public void testGetAllResources_editorUpdate() throws Exception { + final String CAT0_NAME = "CAT000"; + final String RES_NAME = "a MiXeD cAsE sTrInG"; + + assertEquals(0, resourceService.getAll(null, null, buildFakeAdminUser()).size()); + + long a0 = restCreateUser("a0", Role.ADMIN, new HashSet<>(), "p0"); + long u0 = restCreateUser("u0", Role.USER, new HashSet<>(), "p0"); + + createCategory(CAT0_NAME); + long r0Id = restCreateResource(RES_NAME, "x", CAT0_NAME, u0, true); + + { + SecurityContext sc = new SimpleSecurityContext(u0); + String response = restExtJsService.getAllResources(sc, "*mIxEd*", 0, 1000); + + System.out.println("JSON " + response); + + JSONResult result = parse(response); + assertEquals(1, result.total); + assertEquals(1, result.returnedCount); + + ShortResource resource = restExtJsService.getResource(sc, r0Id); + assertEquals(RES_NAME, resource.getName()); + assertEquals("u0", resource.getCreator()); + assertEquals("u0", resource.getEditor()); + } + + { + SecurityContext sc = new SimpleSecurityContext(a0); + Resource realResource = resourceService.get(r0Id); + realResource.setName("new name"); + restResourceService.update(sc, r0Id, createRESTResource(realResource)); + + ShortResource resource = restExtJsService.getResource(sc, r0Id); + assertEquals(realResource.getName(), resource.getName()); + assertEquals("u0", resource.getCreator()); + assertEquals("a0", resource.getEditor()); + } + } + + @Test + public void testGetAllResources_unadvertised() throws Exception { + final String CAT0_NAME = "CAT000"; + final String CAT1_NAME = "CAT111"; + final String RES_NAME = "a MiXeD cAsE sTrInG"; + + assertEquals(0, resourceService.getAll(null, null, buildFakeAdminUser()).size()); + + long g0Id = createGroup("g0"); + UserGroup g0 = new UserGroup(); + g0.setId(g0Id); + g0.setGroupName("g0"); + Set groups = new HashSet<>(); + groups.add(g0); + + long a0 = restCreateUser("a0", Role.ADMIN, groups, "p0"); + long u0 = restCreateUser("u0", Role.USER, groups, "p0"); + long u1 = restCreateUser("u1", Role.USER, groups, "p1"); + + createCategory(CAT0_NAME); + createCategory(CAT1_NAME); + + SecurityRule sr0 = new SecurityRule(); + sr0.setUser(userDAO.find(u0)); + sr0.setCanRead(true); + sr0.setCanWrite(true); + + SecurityRule sr1 = new SecurityRule(); + sr1.setGroup(g0); + sr1.setCanRead(true); + sr1.setCanWrite(false); - private JSONResult parse(String jsonString) - { + SecurityRuleList rules = new SecurityRuleList(Arrays.asList(sr0, sr1)); + + restCreateResource(RES_NAME, "x", CAT0_NAME, u0, rules, true); + restCreateResource(RES_NAME.toLowerCase(), "x", CAT0_NAME, u0, rules, false); + restCreateResource(RES_NAME.toUpperCase(), "x", CAT0_NAME, u0, rules, true); + + restCreateResource(RES_NAME + " in another category", "x", CAT1_NAME, u0, rules, false); + + restCreateResource( + "just an extra resource we shouldn't care about", "x", CAT0_NAME, u0, rules, true); + + { + // ADMIN + SecurityContext sc = new SimpleSecurityContext(a0); + String response = restExtJsService.getAllResources(sc, "*mIxEd*", 0, 1000); + + System.out.println("JSON " + response); + + JSONResult result = parse(response); + assertEquals(4, result.total); + assertEquals(4, result.returnedCount); + + // OWNER + sc = new SimpleSecurityContext(u0); + response = restExtJsService.getAllResources(sc, "*mIxEd*", 0, 1000); + + System.out.println("JSON " + response); + + result = parse(response); + assertEquals(4, result.total); + assertEquals(4, result.returnedCount); + + // READER + sc = new SimpleSecurityContext(u1); + response = restExtJsService.getAllResources(sc, "*mIxEd*", 0, 1000); + + System.out.println("JSON " + response); + + result = parse(response); + assertEquals(2, result.total); + assertEquals(2, result.returnedCount); + } + + { + // OWNER + SecurityContext sc = new SimpleSecurityContext(u0); + String response = + restExtJsService.getResourcesByCategory( + sc, CAT0_NAME, "*mIxEd*", null, 0, 1000, false, false); + + System.out.println("JSON " + response); + + JSONResult result = parse(response); + assertEquals(3, result.total); + assertEquals(3, result.returnedCount); + + // READER + sc = new SimpleSecurityContext(u1); + response = + restExtJsService.getResourcesByCategory( + sc, CAT0_NAME, "*mIxEd*", null, 0, 1000, false, false); + + System.out.println("JSON " + response); + + result = parse(response); + assertEquals(2, result.total); + assertEquals(2, result.returnedCount); + } + } + + private JSONResult parse(String jsonString) { JSONResult ret = new JSONResult(); JSON json = JSONSerializer.toJSON(jsonString); - JSONObject jo = (JSONObject)json; + JSONObject jo = (JSONObject) json; ret.total = jo.getInt("totalCount"); - Set names; + Set names; JSONArray arrResults = jo.optJSONArray("results"); - if(arrResults != null) { + if (arrResults != null) { names = getArray(arrResults); } else { JSONObject results = jo.optJSONObject("results"); - if(results != null) { + if (results != null) { names = Collections.singleton(getSingle(results)); - } - else { + } else { LOGGER.warn("No results found"); names = Collections.EMPTY_SET; } @@ -255,7 +395,7 @@ Set getArray(JSONArray arr) { Set ret = new HashSet<>(); for (Object object : arr) { - ret.add(getSingle((JSONObject)object)); + ret.add(getSingle((JSONObject) object)); } return ret; @@ -268,8 +408,6 @@ String getSingle(JSONObject jo) { static class JSONResult { int total; int returnedCount; - Set names; + Set names; } - - } diff --git a/src/modules/rest/extjs/src/test/java/it/geosolutions/geostore/services/rest/impl/ServiceTestBase.java b/src/modules/rest/extjs/src/test/java/it/geosolutions/geostore/services/rest/impl/ServiceTestBase.java index 6241a653..93b5fa8f 100644 --- a/src/modules/rest/extjs/src/test/java/it/geosolutions/geostore/services/rest/impl/ServiceTestBase.java +++ b/src/modules/rest/extjs/src/test/java/it/geosolutions/geostore/services/rest/impl/ServiceTestBase.java @@ -19,21 +19,13 @@ */ package it.geosolutions.geostore.services.rest.impl; +import static org.junit.Assert.*; + import it.geosolutions.geostore.core.dao.ResourceDAO; import it.geosolutions.geostore.core.dao.UserDAO; -import it.geosolutions.geostore.core.model.Category; -import it.geosolutions.geostore.core.model.Resource; -import it.geosolutions.geostore.core.model.SecurityRule; -import it.geosolutions.geostore.core.model.StoredData; -import it.geosolutions.geostore.core.model.User; -import it.geosolutions.geostore.core.model.UserAttribute; -import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.*; import it.geosolutions.geostore.core.model.enums.Role; -import it.geosolutions.geostore.services.CategoryService; -import it.geosolutions.geostore.services.ResourceService; -import it.geosolutions.geostore.services.StoredDataService; -import it.geosolutions.geostore.services.UserGroupService; -import it.geosolutions.geostore.services.UserService; +import it.geosolutions.geostore.services.*; import it.geosolutions.geostore.services.dto.ShortResource; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; @@ -41,21 +33,22 @@ import it.geosolutions.geostore.services.rest.RESTUserService; import it.geosolutions.geostore.services.rest.model.RESTCategory; import it.geosolutions.geostore.services.rest.model.RESTResource; +import it.geosolutions.geostore.services.rest.model.SecurityRuleList; +import it.geosolutions.geostore.services.rest.utils.Convert; import java.security.Principal; import java.util.Collections; - import java.util.List; +import java.util.Set; import javax.ws.rs.core.SecurityContext; - -import org.apache.log4j.Logger; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -import static org.junit.Assert.*; +import org.apache.commons.collections.CollectionUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.Before; import org.junit.Rule; import org.junit.rules.TestName; import org.springframework.aop.framework.Advised; import org.springframework.aop.support.AopUtils; +import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; /** @@ -63,8 +56,7 @@ * * @author ETj (etj at geo-solutions.it) */ -public class ServiceTestBase { - +public class ServiceTestBase { protected static RESTResourceService restResourceService; protected static RESTUserService restUserService; @@ -79,23 +71,16 @@ public class ServiceTestBase { protected static UserDAO userDAO; protected static ClassPathXmlApplicationContext ctx = null; + protected final Logger LOGGER = LogManager.getLogger(getClass()); + @Rule public TestName testName = new TestName(); - @Rule - public TestName testName = new TestName(); - - protected final Logger LOGGER = Logger.getLogger(getClass()); - - - - /** - * - */ + /** */ public ServiceTestBase() { - + synchronized (ServiceTestBase.class) { if (ctx == null) { - String[] paths = { "classpath*:applicationContext.xml" - // ,"applicationContext-test.xml" + String[] paths = {"classpath*:applicationContext.xml" + // ,"applicationContext-test.xml" }; ctx = new ClassPathXmlApplicationContext(paths); @@ -118,19 +103,19 @@ public ServiceTestBase() { protected void setUp() throws Exception { testCheckServices(); - LOGGER.info("################ Running " + getClass().getSimpleName() + "::" + testName.getMethodName()); + LOGGER.info( + "################ Running " + + getClass().getSimpleName() + + "::" + + testName.getMethodName()); removeAll(); } - /** - * - */ + /** */ public void testCheckServices() { - assertNotNull(restResourceService); assertNotNull(restUserService); - assertNotNull(storedDataService); assertNotNull(resourceService); assertNotNull(categoryService); @@ -170,10 +155,7 @@ private void removeAllUserGroup() throws BadRequestServiceEx, NotFoundServiceEx assertEquals("Group have not been properly deleted", 0, userService.getCount(null)); } - - /** - * @throws BadRequestServiceEx - */ + /** @throws BadRequestServiceEx */ private void removeAllUser() throws BadRequestServiceEx { List list = userService.getAll(null, null); for (User item : list) { @@ -186,9 +168,7 @@ private void removeAllUser() throws BadRequestServiceEx { assertEquals("User have not been properly deleted", 0, userService.getCount(null)); } - /** - * @throws BadRequestServiceEx - */ + /** @throws BadRequestServiceEx */ private void removeAllCategory() throws BadRequestServiceEx { List list = categoryService.getAll(null, null); for (Category item : list) { @@ -201,9 +181,7 @@ private void removeAllCategory() throws BadRequestServiceEx { assertEquals("Category have not been properly deleted", 0, categoryService.getCount(null)); } - /** - * @throws NotFoundServiceEx - */ + /** @throws NotFoundServiceEx */ protected void removeAllStoredData() throws NotFoundServiceEx { List list = storedDataService.getAll(); for (StoredData item : list) { @@ -214,10 +192,7 @@ protected void removeAllStoredData() throws NotFoundServiceEx { } } - /** - * @throws BadRequestServiceEx - * - */ + /** @throws BadRequestServiceEx */ private void removeAllResource() throws BadRequestServiceEx { List list = resourceService.getAll(null, null, buildFakeAdminUser()); for (ShortResource item : list) { @@ -231,8 +206,8 @@ private void removeAllResource() throws BadRequestServiceEx { } /** - * @param name * @param data + * @param resource * @return long * @throws Exception */ @@ -242,14 +217,16 @@ protected long createData(String data, Resource resource) throws Exception { /** * @param name - * @param creation * @param description - * @param storedData + * @param catName + * @param advertised + * @return long * @return long * @throws Exception + * @throws Exception */ - protected long createResource(String name, String description, String catName) throws Exception { - + protected long createResource( + String name, String description, String catName, boolean advertised) throws Exception { Category category = new Category(); category.setName(catName); @@ -259,41 +236,73 @@ protected long createResource(String name, String description, String catName) t resource.setName(name); resource.setDescription(description); resource.setCategory(category); + resource.setCreator("USER1"); + resource.setEditor("USER2"); + resource.setAdvertised(advertised); return resourceService.insert(resource); } - protected long restCreateResource(String name, String description, String catName, long userId) throws Exception { - + protected long restCreateResource( + String name, String description, String catName, long userId, boolean advertised) + throws Exception { RESTResource resource = new RESTResource(); resource.setName(name); resource.setDescription(description); resource.setCategory(new RESTCategory(catName)); + resource.setAdvertised(advertised); SecurityContext sc = new SimpleSecurityContext(userId); return restResourceService.insert(sc, resource); } - protected long createResource(String name, String description, Category category) throws Exception { + protected long restCreateResource( + String name, + String description, + String catName, + long userId, + SecurityRuleList rules, + boolean advertised) + throws Exception { + long resId = restCreateResource(name, description, catName, userId, advertised); + + SecurityContext sc = new SimpleSecurityContext(userId); + + restResourceService.updateSecurityRules(sc, resId, rules); + return resId; + } + protected long createResource( + String name, String description, Category category, boolean advertised) + throws Exception { Resource resource = new Resource(); resource.setName(name); resource.setDescription(description); resource.setCategory(category); + resource.setCreator("USER1"); + resource.setEditor("USER2"); + resource.setAdvertised(advertised); return resourceService.insert(resource); } /** * @param name - * @param creation * @param description - * @param storedData + * @param catName + * @param rules + * @param advertised * @return long * @throws Exception */ - protected long createResource(String name, String description, String catName, List rules) throws Exception { + protected long createResource( + String name, + String description, + String catName, + List rules, + boolean advertised) + throws Exception { Category category = new Category(); category.setName(catName); @@ -305,6 +314,9 @@ protected long createResource(String name, String description, String catName, L resource.setDescription(description); resource.setCategory(category); resource.setSecurity(rules); + resource.setCreator("USER1"); + resource.setEditor("USER2"); + resource.setAdvertised(advertised); return resourceService.insert(resource); } @@ -343,10 +355,12 @@ protected long createUser(String name, Role role, String password) throws Except return userService.insert(user); } - protected long restCreateUser(String name, Role role, String password) throws Exception { + protected long restCreateUser(String name, Role role, Set groups, String password) + throws Exception { User user = new User(); user.setName(name); user.setRole(role); + if (groups != null && !groups.isEmpty()) user.setGroups(groups); user.setNewPassword(password); UserAttribute attr = new UserAttribute(); @@ -354,24 +368,21 @@ protected long restCreateUser(String name, Role role, String password) throws Ex attr.setValue("RESTattvalue"); user.setAttribute(Collections.singletonList(attr)); - long id = restUserService.insert(null, user); - -// UserDAOImpl impl = getTargetObject(userDAO, UserDAOImpl.class); -// impl.flush(); - - return id; + return restUserService.insert(null, user); } - protected T getTargetObject(Object proxy, Class targetClass) throws Exception - { + protected T getTargetObject(Object proxy, Class targetClass) throws Exception { if (AopUtils.isJdkDynamicProxy(proxy)) { return (T) ((Advised) proxy).getTargetSource().getTarget(); } else { - return (T) proxy; // expected to be cglib proxy then, which is simply a specialized class + return (T) + proxy; // expected to be cglib proxy then, which is simply a specialized class } } - protected long createUser(String name, Role role, String password, List attributes) throws Exception { + protected long createUser( + String name, Role role, String password, List attributes) + throws Exception { User user = new User(); user.setName(name); user.setRole(role); @@ -394,54 +405,65 @@ protected long createGroup(String name) throws Exception { return userGroupService.insert(group); } + protected RESTResource createRESTResource(Resource resource) { + RESTResource ret = new RESTResource(); + ret.setCategory(new RESTCategory(resource.getCategory().getName())); + ret.setName(resource.getName()); + ret.setDescription(resource.getDescription()); + ret.setMetadata(resource.getMetadata()); + ret.setCreator(resource.getCreator()); + ret.setEditor(resource.getEditor()); + if (resource.getData() != null) ret.setData(resource.getData().getData()); + if (CollectionUtils.isNotEmpty(resource.getAttribute())) + ret.setAttribute(Convert.convertToShortAttributeList(resource.getAttribute())); + return ret; + } + + protected User buildFakeAdminUser() { + User user = new User(); + user.setRole(Role.ADMIN); + user.setName("ThisIsNotARealUser"); + return user; + } + class SimpleSecurityContext implements SecurityContext { private Principal userPrincipal; - public SimpleSecurityContext() - { - } + public SimpleSecurityContext() {} - public SimpleSecurityContext(long userId) - { + public SimpleSecurityContext(long userId) { userPrincipal = new UsernamePasswordAuthenticationToken(userDAO.find(userId), null); } @Override - public Principal getUserPrincipal() - { + public Principal getUserPrincipal() { return userPrincipal; } - @Override - public boolean isUserInRole(String role) - { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + public void setUserPrincipal(Principal userPrincipal) { + this.userPrincipal = userPrincipal; } @Override - public boolean isSecure() - { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + public boolean isUserInRole(String role) { + throw new UnsupportedOperationException( + "Not supported yet."); // To change body of generated methods, choose Tools | + // Templates. } @Override - public String getAuthenticationScheme() - { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + public boolean isSecure() { + throw new UnsupportedOperationException( + "Not supported yet."); // To change body of generated methods, choose Tools | + // Templates. } - public void setUserPrincipal(Principal userPrincipal) - { - this.userPrincipal = userPrincipal; + @Override + public String getAuthenticationScheme() { + throw new UnsupportedOperationException( + "Not supported yet."); // To change body of generated methods, choose Tools | + // Templates. } - - } - - protected User buildFakeAdminUser() { - User user = new User(); - user.setRole(Role.ADMIN); - user.setName("ThisIsNotARealUser"); - return user; } } diff --git a/src/modules/rest/extjs/src/test/resources/geostore-datasource-ovr.properties b/src/modules/rest/extjs/src/test/resources/geostore-datasource-ovr.properties index 1fd0ea08..270ed453 100644 --- a/src/modules/rest/extjs/src/test/resources/geostore-datasource-ovr.properties +++ b/src/modules/rest/extjs/src/test/resources/geostore-datasource-ovr.properties @@ -1,7 +1,4 @@ - #Some custom configuration for jUnit TEST ONLY - - #geostoreDataSource.url=jdbc:h2:/tmp/geostore/geostore_test geostoreDataSource.username=geostore_test geostoreDataSource.password=geostore_test diff --git a/src/modules/rest/extjs/src/test/resources/log4j.properties b/src/modules/rest/extjs/src/test/resources/log4j.properties deleted file mode 100644 index 272457a8..00000000 --- a/src/modules/rest/extjs/src/test/resources/log4j.properties +++ /dev/null @@ -1,9 +0,0 @@ -log4j.rootLogger=WARN, consoleAppender - -log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender -log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout -log4j.appender.consoleAppender.layout.ConversionPattern=%p %d{yyyy-MM-dd HH:mm:ss.SSS} %c::%M:%L - %m%n - -log4j.logger.it.geosolutions.geostore=INFO -log4j.logger.it.geosolutions.geostore.services=DEBUG - diff --git a/src/modules/rest/extjs/src/test/resources/log4j2.properties b/src/modules/rest/extjs/src/test/resources/log4j2.properties new file mode 100644 index 00000000..f9ae5245 --- /dev/null +++ b/src/modules/rest/extjs/src/test/resources/log4j2.properties @@ -0,0 +1,13 @@ +rootLogger.level=INFO +appenders=console +appender.console.type=Console +appender.console.name=LogToConsole +appender.console.layout.type=PatternLayout +appender.console.layout.pattern=%p %d{yyyy-MM-dd HH:mm:ss.SSS} %c::%M:%L - %m%n +rootLogger.appenderRef.stdout.ref=LogToConsole +rootLogger.appenderRef.console.ref=LogToConsole +logger.srv.name=it.geosolutions.geostore.services +logger.srv.level=DEBUG +logger.gstore.name=it.geosolutions.geostore +logger.gstore.level=INFO + diff --git a/src/modules/rest/impl/pom.xml b/src/modules/rest/impl/pom.xml index 4a113de7..ae1984ef 100644 --- a/src/modules/rest/impl/pom.xml +++ b/src/modules/rest/impl/pom.xml @@ -18,14 +18,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - + 4.0.0 it.geosolutions.geostore geostore-rest-root - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-rest-impl @@ -63,17 +64,17 @@ - commons-httpclient - commons-httpclient + org.apache.httpcomponents + httpclient - it.geosolutions.geostore - geostore-security - ${geostore-version} - test-jar - test + it.geosolutions.geostore + geostore-security + ${project.version} + test-jar + test @@ -81,20 +82,73 @@ javax.servlet - servlet-api + javax.servlet-api provided - - - + + org.springframework.security + spring-security-oauth2-core + ${spring-security-version} + + + org.springframework.security.oauth + spring-security-oauth2 + + + org.springframework + spring-context + + + org.springframework + spring-core + + + org.springframework + spring-webmvc + + + org.springframework + spring-beans + + + org.codehaus.jackson + jackson-core-asl + + + org.codehaus.jackson + jackson-mapper-asl + + + - org.mockito - mockito-core - test + org.keycloak + keycloak-spring-security-adapter + + + org.keycloak + keycloak-authz-client + + + + org.keycloak + keycloak-admin-client + + + + org.springframework.security + spring-security-jwt + + + + com.auth0 + java-jwt + + + com.google.guava guava @@ -104,10 +158,22 @@ org.apache.cxf cxf-rt-frontend-jaxrs - + + org.apache.cxf + cxf-rt-rs-extension-providers + + + org.apache.cxf + cxf-rt-rs-json-basic + + + org.codehaus.jettison + jettison + + javax.annotation @@ -133,9 +199,23 @@ - xom - xom - 1.1 + com.google.code.gson + gson + + + + com.fasterxml.jackson.core + jackson-core + + + + com.fasterxml.jackson.core + jackson-databind + + + + com.fasterxml.jackson.core + jackson-annotations @@ -153,49 +233,61 @@ test + + org.mockito + mockito-core + test + + + com.github.tomakehurst + wiremock-standalone + test + + + org.springframework + spring-test + test + + + + org.mockito + mockito-inline + test + + - - - src/main/resources - - **/*.xml - **/*.ldif - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - utf8 - 1.7 - 1.7 - - - + + + src/main/resources + + **/*.xml + **/*.ldif + + + + + - + diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTBackupServiceImpl.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTBackupServiceImpl.java index c49c258d..31d822da 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTBackupServiceImpl.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTBackupServiceImpl.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -48,27 +48,26 @@ import it.geosolutions.geostore.services.rest.model.RESTQuickBackup.RESTBackupResource; import it.geosolutions.geostore.services.rest.model.RESTResource; import it.geosolutions.geostore.services.rest.utils.Convert; - import java.util.List; - import javax.ws.rs.core.SecurityContext; - import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.NotImplementedException; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -/** - * - */ +/** */ public class RESTBackupServiceImpl extends RESTServiceImpl implements RESTBackupService { - private final static Logger LOGGER = Logger.getLogger(RESTBackupServiceImpl.class); - + private static final Logger LOGGER = LogManager.getLogger(RESTBackupServiceImpl.class); + private static final long MAX_RESOURCES_FOR_QUICK_BACKUP = 100L; private CategoryService categoryService; - private ResourceService resourceService; - private final static long MAX_RESOURCES_FOR_QUICK_BACKUP = 100l; + private static Category rbc2cat(RESTBackupCategory rbc) { + Category ret = new Category(); + ret.setName(rbc.getName()); + return ret; + } @Override public String backup(SecurityContext sc) { @@ -83,10 +82,9 @@ public String restore(SecurityContext sc, String token) { @Override public RESTQuickBackup quickBackup(SecurityContext sc) throws BadRequestServiceEx { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("quickBackup()"); + if (LOGGER.isDebugEnabled()) LOGGER.debug("quickBackup()"); - if (resourceService.getCount((String) null) > MAX_RESOURCES_FOR_QUICK_BACKUP) + if (resourceService.getCount(null) > MAX_RESOURCES_FOR_QUICK_BACKUP) throw new BadRequestServiceEx("Too many resources for a quick backup"); RESTQuickBackup backup = new RESTQuickBackup(); @@ -107,8 +105,7 @@ public RESTQuickBackup quickBackup(SecurityContext sc) throws BadRequestServiceE @Override public String quickRestore(SecurityContext sc, RESTQuickBackup backup) { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("quickRestore()"); + if (LOGGER.isDebugEnabled()) LOGGER.debug("quickRestore()"); try { @@ -134,8 +131,8 @@ public String quickRestore(SecurityContext sc, RESTQuickBackup backup) { } } - private void quickRestoreCategory(RESTBackupCategory rbc) throws BadRequestServiceEx, - NotFoundServiceEx, DuplicatedResourceNameServiceEx { + private void quickRestoreCategory(RESTBackupCategory rbc) + throws BadRequestServiceEx, NotFoundServiceEx, DuplicatedResourceNameServiceEx { LOGGER.info("Restoring category: " + rbc.getName()); Category cat = rbc2cat(rbc); long catId = categoryService.insert(cat); @@ -143,10 +140,18 @@ private void quickRestoreCategory(RESTBackupCategory rbc) throws BadRequestServi for (RESTBackupResource rbr : rbc.getResources()) { if (LOGGER.isInfoEnabled()) { - int attnum = (rbr != null && rbr.getResource() != null && rbr.getResource() - .getAttribute() != null) ? rbr.getResource().getAttribute().size() : -1; - LOGGER.info("Restoring resource: " + cat.getName() + ":" - + rbr.getResource().getName() + " (" + attnum + " attrs)"); + int attnum = + (rbr != null + && rbr.getResource() != null + && rbr.getResource().getAttribute() != null) + ? rbr.getResource().getAttribute().size() + : -1; + assert rbr != null; + LOGGER.info( + "Restoring resource: {}:{} ({} attrs)", + cat.getName(), + rbr.getResource().getName(), + attnum); } Resource res = rbr2res(rbr, catId); resourceService.insert(res); @@ -154,20 +159,14 @@ private void quickRestoreCategory(RESTBackupCategory rbc) throws BadRequestServi } } - private static Category rbc2cat(RESTBackupCategory rbc) { - Category ret = new Category(); - ret.setName(rbc.getName()); - return ret; - } - private Resource rbr2res(RESTBackupResource rbr, long catId) { Resource ret = Convert.convertResource(rbr.getResource()); ret.getCategory().setId(catId); // remap category return ret; } - private RESTBackupCategory createCategory(Category category) throws BadRequestServiceEx, - InternalErrorServiceEx { + private RESTBackupCategory createCategory(Category category) + throws BadRequestServiceEx, InternalErrorServiceEx { LOGGER.info("Packing category " + category.getName()); RESTBackupCategory ret = new RESTBackupCategory(); @@ -196,8 +195,11 @@ private RESTResource createRESTResource(Resource resource) { ret.setName(resource.getName()); ret.setDescription(resource.getDescription()); ret.setMetadata(resource.getMetadata()); - if (resource.getData() != null) - ret.setData(resource.getData().getData()); + ret.setCreator(resource.getCreator()); + ret.setEditor(resource.getEditor()); + ret.setAdvertised(resource.isAdvertised()); + + if (resource.getData() != null) ret.setData(resource.getData().getData()); if (CollectionUtils.isNotEmpty(resource.getAttribute())) ret.setAttribute(Convert.convertToShortAttributeList(resource.getAttribute())); return ret; @@ -220,5 +222,4 @@ public void setCategoryService(CategoryService categoryService) { protected SecurityService getSecurityService() { throw new NotImplementedException("This method is not implemented yet..."); } - } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTCategoryServiceImpl.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTCategoryServiceImpl.java index a496e89f..cbf56f98 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTCategoryServiceImpl.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTCategoryServiceImpl.java @@ -9,7 +9,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -21,7 +21,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -43,30 +43,26 @@ import it.geosolutions.geostore.services.rest.exception.ForbiddenErrorWebEx; import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; import it.geosolutions.geostore.services.rest.model.CategoryList; - import javax.ws.rs.core.SecurityContext; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * Class RESTCategoryServiceImpl. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class RESTCategoryServiceImpl extends RESTServiceImpl implements RESTCategoryService { - private final static Logger LOGGER = Logger.getLogger(RESTCategoryServiceImpl.class); + private static final Logger LOGGER = LogManager.getLogger(RESTCategoryServiceImpl.class); private CategoryService categoryService; - /** - * @param categoryService the categoryService to set - */ + /** @param categoryService the categoryService to set */ public void setCategoryService(CategoryService categoryService) { this.categoryService = categoryService; } - + /* (non-Javadoc) * @see it.geosolutions.geostore.services.rest.impl.RESTServiceImpl#getSecurityService() */ @@ -77,15 +73,13 @@ protected SecurityService getSecurityService() { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTCategoryService#insert(it.geosolutions.geostore.core.model.Category) */ @Override public long insert(SecurityContext sc, Category category) { - if (category == null) - throw new BadRequestWebEx("Category is null"); - if (category.getId() != null) - throw new BadRequestWebEx("Id should be null"); + if (category == null) throw new BadRequestWebEx("Category is null"); + if (category.getId() != null) throw new BadRequestWebEx("Id should be null"); long id = -1; @@ -119,8 +113,7 @@ public long insert(SecurityContext sc, Category category) { public long update(SecurityContext sc, long id, Category category) { try { Category old = categoryService.get(id); - if (old == null) - throw new NotFoundWebEx("Category not found"); + if (old == null) throw new NotFoundWebEx("Category not found"); // // Authorization check. @@ -144,7 +137,7 @@ public long update(SecurityContext sc, long id, Category category) { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTCategoryService#delete(long) */ @Override @@ -158,23 +151,19 @@ public void delete(SecurityContext sc, long id) throws NotFoundWebEx { if (canDelete) { boolean ret = categoryService.delete(id); - if (!ret) - throw new NotFoundWebEx("Category not found"); - } else - throw new ForbiddenErrorWebEx("This user cannot delete this category !"); - + if (!ret) throw new NotFoundWebEx("Category not found"); + } else throw new ForbiddenErrorWebEx("This user cannot delete this category !"); } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTCategoryService#get(long) */ @Override public Category get(SecurityContext sc, long id) throws NotFoundWebEx { if (id == -1) { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Retriving dummy data !"); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Retriving dummy data !"); // // return test instance @@ -185,15 +174,14 @@ public Category get(SecurityContext sc, long id) throws NotFoundWebEx { } Category ret = categoryService.get(id); - if (ret == null) - throw new NotFoundWebEx("Category not found"); + if (ret == null) throw new NotFoundWebEx("Category not found"); return ret; } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTCategoryService#getAll(java.lang.Integer, java.lang.Integer) */ @Override @@ -208,7 +196,7 @@ public CategoryList getAll(SecurityContext sc, Integer page, Integer entries) /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTCategoryService#getCount(java.lang.String) */ @Override diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTMiscServiceImpl.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTMiscServiceImpl.java index eeeb5735..0406c3c6 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTMiscServiceImpl.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTMiscServiceImpl.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -35,12 +35,7 @@ import it.geosolutions.geostore.services.SecurityService; import it.geosolutions.geostore.services.StoredDataService; import it.geosolutions.geostore.services.dto.ShortResource; -import it.geosolutions.geostore.services.dto.search.AndFilter; -import it.geosolutions.geostore.services.dto.search.BaseField; -import it.geosolutions.geostore.services.dto.search.CategoryFilter; -import it.geosolutions.geostore.services.dto.search.FieldFilter; -import it.geosolutions.geostore.services.dto.search.SearchFilter; -import it.geosolutions.geostore.services.dto.search.SearchOperator; +import it.geosolutions.geostore.services.dto.search.*; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.InternalErrorServiceEx; import it.geosolutions.geostore.services.rest.RESTMiscService; @@ -51,10 +46,9 @@ import it.geosolutions.geostore.services.rest.model.ResourceList; import it.geosolutions.geostore.services.rest.model.ShortResourceList; import java.util.List; - import javax.ws.rs.core.SecurityContext; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; @@ -62,41 +56,42 @@ /** * Class RESTMiscServiceImpl. - * + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ -public class RESTMiscServiceImpl extends RESTServiceImpl implements RESTMiscService, ApplicationContextAware { +@SuppressWarnings("PMD.UnusedPrivateField") +public class RESTMiscServiceImpl extends RESTServiceImpl + implements RESTMiscService, ApplicationContextAware { - private final static Logger LOGGER = Logger.getLogger(RESTMiscServiceImpl.class); + private static final Logger LOGGER = LogManager.getLogger(RESTMiscServiceImpl.class); private CategoryService categoryService; private ResourceService resourceService; private StoredDataService storedDataService; - + private ApplicationContext appContext; /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTMiscService#getData(javax.ws.rs.core.SecurityContext, java.lang.String, java.lang.String) */ @Override - public String getData(SecurityContext sc, String catName, String resName) throws NotFoundWebEx, - ConflictWebEx, BadRequestWebEx, InternalErrorWebEx { + public String getData(SecurityContext sc, String catName, String resName) + throws NotFoundWebEx, ConflictWebEx, BadRequestWebEx, InternalErrorWebEx { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("getData(" + catName + "," + resName + ")"); + if (LOGGER.isDebugEnabled()) LOGGER.debug("getData(" + catName + "," + resName + ")"); - if (catName == null) - throw new BadRequestWebEx("Category is null"); - if (resName == null) - throw new BadRequestWebEx("Resource is null"); + if (catName == null) throw new BadRequestWebEx("Category is null"); + if (resName == null) throw new BadRequestWebEx("Resource is null"); - SearchFilter filter = new AndFilter(new CategoryFilter(catName, SearchOperator.EQUAL_TO), - new FieldFilter(BaseField.NAME, resName, SearchOperator.EQUAL_TO)); + SearchFilter filter = + new AndFilter( + new CategoryFilter(catName, SearchOperator.EQUAL_TO), + new FieldFilter(BaseField.NAME, resName, SearchOperator.EQUAL_TO)); List resources = null; try { @@ -119,23 +114,22 @@ public String getData(SecurityContext sc, String catName, String resName) throws /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTMiscService#getResource(javax.ws.rs.core.SecurityContext, java.lang.String, java.lang.String) */ @Override public Resource getResource(SecurityContext sc, String catName, String resName) throws NotFoundWebEx, ConflictWebEx, BadRequestWebEx, InternalErrorWebEx { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("getResource(" + catName + "," + resName + ")"); + if (LOGGER.isDebugEnabled()) LOGGER.debug("getResource(" + catName + "," + resName + ")"); - if (catName == null) - throw new BadRequestWebEx("Category is null"); - if (resName == null) - throw new BadRequestWebEx("Resource is null"); + if (catName == null) throw new BadRequestWebEx("Category is null"); + if (resName == null) throw new BadRequestWebEx("Resource is null"); - SearchFilter filter = new AndFilter(new CategoryFilter(catName, SearchOperator.EQUAL_TO), - new FieldFilter(BaseField.NAME, resName, SearchOperator.EQUAL_TO)); + SearchFilter filter = + new AndFilter( + new CategoryFilter(catName, SearchOperator.EQUAL_TO), + new FieldFilter(BaseField.NAME, resName, SearchOperator.EQUAL_TO)); List resources = null; try { @@ -158,19 +152,17 @@ public Resource getResource(SecurityContext sc, String catName, String resName) /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTMiscService#getResource(javax.ws.rs.core.SecurityContext, java.lang.String, java.lang.String) */ @Override public ShortResourceList getResourcesByCategory(SecurityContext sc, String catName) throws NotFoundWebEx, ConflictWebEx, BadRequestWebEx, InternalErrorWebEx { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("getResourcesByCategory(" + catName + ")"); + if (LOGGER.isDebugEnabled()) LOGGER.debug("getResourcesByCategory(" + catName + ")"); // some checks on category - if (catName == null) - throw new BadRequestWebEx("Category is null"); + if (catName == null) throw new BadRequestWebEx("Category is null"); Category category; try { @@ -178,8 +170,7 @@ public ShortResourceList getResourcesByCategory(SecurityContext sc, String catNa } catch (BadRequestServiceEx ex) { throw new BadRequestWebEx(ex.getMessage()); } - if (category == null) - throw new NotFoundWebEx("Category not found"); + if (category == null) throw new NotFoundWebEx("Category not found"); // ok, search for the resource list SearchFilter filter = new CategoryFilter(catName, SearchOperator.EQUAL_TO); @@ -187,7 +178,7 @@ public ShortResourceList getResourcesByCategory(SecurityContext sc, String catNa List resources = null; try { User user = extractAuthUser(sc); - + resources = resourceService.getResources(filter, user); } catch (BadRequestServiceEx ex) { throw new BadRequestWebEx(ex.getMessage()); @@ -197,23 +188,21 @@ public ShortResourceList getResourcesByCategory(SecurityContext sc, String catNa return new ShortResourceList(resources); } - + /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTMiscService#getResource(javax.ws.rs.core.SecurityContext, java.lang.String, java.lang.String) */ @Override - public ResourceList getResourcesByCategory(SecurityContext sc, String catName, - boolean includeAttributes, boolean includeData) throws NotFoundWebEx, ConflictWebEx, - BadRequestWebEx, InternalErrorWebEx { + public ResourceList getResourcesByCategory( + SecurityContext sc, String catName, boolean includeAttributes, boolean includeData) + throws NotFoundWebEx, ConflictWebEx, BadRequestWebEx, InternalErrorWebEx { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("getResourcesByCategory(" + catName + ")"); + if (LOGGER.isDebugEnabled()) LOGGER.debug("getResourcesByCategory(" + catName + ")"); // some checks on category - if (catName == null) - throw new BadRequestWebEx("Category is null"); + if (catName == null) throw new BadRequestWebEx("Category is null"); Category category; try { @@ -221,8 +210,7 @@ public ResourceList getResourcesByCategory(SecurityContext sc, String catName, } catch (BadRequestServiceEx ex) { throw new BadRequestWebEx(ex.getMessage()); } - if (category == null) - throw new NotFoundWebEx("Category not found"); + if (category == null) throw new NotFoundWebEx("Category not found"); // ok, search for the resource list SearchFilter filter = new CategoryFilter(catName, SearchOperator.EQUAL_TO); @@ -230,7 +218,9 @@ public ResourceList getResourcesByCategory(SecurityContext sc, String catName, List resources = null; try { User user = extractAuthUser(sc); - resources = resourceService.getResources(filter, null, null, includeAttributes, includeData, user); + resources = + resourceService.getResources( + filter, null, null, includeAttributes, includeData, user); } catch (BadRequestServiceEx ex) { throw new BadRequestWebEx(ex.getMessage()); } catch (InternalErrorServiceEx ex) { @@ -265,15 +255,15 @@ protected SecurityService getSecurityService() { @Override public void reload(SecurityContext sc, String service) throws BadRequestWebEx { String reloadService = service; - if(appContext != null) { - if(!appContext.containsBean(reloadService)) { + if (appContext != null) { + if (!appContext.containsBean(reloadService)) { reloadService = reloadService + "Initializer"; } - if(!appContext.containsBean(reloadService)) { + if (!appContext.containsBean(reloadService)) { throw new BadRequestWebEx("No service named " + service + " to reload"); } InitializingBean bean = appContext.getBean(reloadService, InitializingBean.class); - if(bean != null) { + if (bean != null) { try { bean.afterPropertiesSet(); } catch (Exception e) { @@ -287,7 +277,4 @@ public void reload(SecurityContext sc, String service) throws BadRequestWebEx { public void setApplicationContext(ApplicationContext appContext) throws BeansException { this.appContext = appContext; } - - - } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTResourceServiceImpl.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTResourceServiceImpl.java index d1353868..005bdebd 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTResourceServiceImpl.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTResourceServiceImpl.java @@ -28,11 +28,7 @@ package it.geosolutions.geostore.services.rest.impl; -import it.geosolutions.geostore.core.model.Attribute; -import it.geosolutions.geostore.core.model.Category; -import it.geosolutions.geostore.core.model.Resource; -import it.geosolutions.geostore.core.model.SecurityRule; -import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.*; import it.geosolutions.geostore.core.model.enums.DataType; import it.geosolutions.geostore.core.model.enums.Role; import it.geosolutions.geostore.services.ResourceService; @@ -47,44 +43,30 @@ import it.geosolutions.geostore.services.exception.InternalErrorServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; import it.geosolutions.geostore.services.rest.RESTResourceService; -import it.geosolutions.geostore.services.rest.exception.BadRequestWebEx; -import it.geosolutions.geostore.services.rest.exception.ConflictWebEx; -import it.geosolutions.geostore.services.rest.exception.ForbiddenErrorWebEx; -import it.geosolutions.geostore.services.rest.exception.InternalErrorWebEx; -import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; -import it.geosolutions.geostore.services.rest.model.RESTAttribute; -import it.geosolutions.geostore.services.rest.model.RESTCategory; -import it.geosolutions.geostore.services.rest.model.RESTResource; -import it.geosolutions.geostore.services.rest.model.ResourceList; -import it.geosolutions.geostore.services.rest.model.SecurityRuleList; -import it.geosolutions.geostore.services.rest.model.ShortAttributeList; -import it.geosolutions.geostore.services.rest.model.ShortResourceList; +import it.geosolutions.geostore.services.rest.exception.*; +import it.geosolutions.geostore.services.rest.model.*; import it.geosolutions.geostore.services.rest.utils.Convert; - import java.util.ArrayList; import java.util.List; - import javax.ws.rs.core.SecurityContext; - -import org.apache.log4j.Level; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * Class RESTResourceServiceImpl. - * + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author DamianoG */ public class RESTResourceServiceImpl extends RESTServiceImpl implements RESTResourceService { - private final static Logger LOGGER = Logger.getLogger(RESTResourceServiceImpl.class); + private static final Logger LOGGER = LogManager.getLogger(RESTResourceServiceImpl.class); private ResourceService resourceService; - - /** - * @param resourceService - */ + + /** @param resourceService */ public void setResourceService(ResourceService resourceService) { this.resourceService = resourceService; } @@ -99,15 +81,13 @@ protected SecurityService getSecurityService() { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTResourceService#insert(it.geosolutions.geostore.core.model.Resource) */ @Override public long insert(SecurityContext sc, RESTResource resource) { - if (resource == null) - throw new BadRequestWebEx("Resource is null"); - if (resource.getId() != null) - throw new BadRequestWebEx("Id should be null"); + if (resource == null) throw new BadRequestWebEx("Resource is null"); + if (resource.getId() != null) throw new BadRequestWebEx("Id should be null"); if (resource.getCategory() == null) throw new BadRequestWebEx("Category should be not null"); @@ -115,11 +95,12 @@ public long insert(SecurityContext sc, RESTResource resource) { // This list holds the security rules for this resources // By default, when a resource is inserted, 2 rules are created : - // - one is related to the User that inserts the rule + // - one is related to the User that inserts the rule // - the other one is related to its group List securities = new ArrayList<>(); - // User Security rule: the user that insert the resource (the "owner") is allowed to Read and Write the resources + // User Security rule: the user that insert the resource (the "owner") is allowed to Read + // and Write the resources SecurityRule userSecurityRule = new SecurityRule(); userSecurityRule.setCanRead(true); userSecurityRule.setCanWrite(true); @@ -130,8 +111,7 @@ public long insert(SecurityContext sc, RESTResource resource) { r.setSecurity(securities); try { - long id = resourceService.insert(r); - return id; + return resourceService.insert(r); } catch (BadRequestServiceEx ex) { throw new BadRequestWebEx(ex.getMessage()); } catch (NotFoundServiceEx e) { @@ -142,34 +122,41 @@ public long insert(SecurityContext sc, RESTResource resource) { } /** - * Updates a resource. Name, Description and Metadata will be replaced if not null.
    - * Category can not be changed; category element may exist in the input resource provided it is the same as in the original resource.
    - * Attribute list will be updated only if it exists in the input resource.
    - *

    - * TODO: attribute list behaviour should be checked: read comments in source. - * - * @see it.geosolutions.geostore.services.rest.RESTResourceService#update(long, it.geosolutions.geostore.core.model.Resource) + * Updates a resource. Name, Description and Metadata will be replaced if not null.
    + * Category can not be changed; category element may exist in the input resource provided it is + * the same as in the original resource.
    + * Attribute list will be updated only if it exists in the input resource.
    + * + *

    TODO: attribute list behaviour should be checked: read comments in source. + * + * @see it.geosolutions.geostore.services.rest.RESTResourceService#update(long, + * it.geosolutions.geostore.core.model.Resource) */ @Override - public long update(SecurityContext sc, long id, RESTResource resource) throws NotFoundWebEx, - BadRequestWebEx { + public long update(SecurityContext sc, long id, RESTResource resource) + throws NotFoundWebEx, BadRequestWebEx { try { - if (resource == null) - throw new BadRequestWebEx("Resource is null"); + if (resource == null) throw new BadRequestWebEx("Resource is null"); resource.setId(id); Resource old = resourceService.get(id); - if (old == null) - throw new NotFoundWebEx("Resource not found"); + if (old == null) throw new NotFoundWebEx("Resource not found"); if (resource.getCategory() != null) { RESTCategory newrc = resource.getCategory(); Category oldrc = old.getCategory(); if ((newrc.getId() != null && !newrc.getId().equals(oldrc.getId())) || (newrc.getName() != null && !newrc.getName().equals(oldrc.getName()))) { - LOGGER.info("Trying to change category old(" + oldrc.getId() + ":" - + oldrc.getName() + ") new(" + newrc.getId() + ":" + newrc.getName() - + ")"); + LOGGER.info( + "Trying to change category old(" + + oldrc.getId() + + ":" + + oldrc.getName() + + ") new(" + + newrc.getId() + + ":" + + newrc.getName() + + ")"); throw new BadRequestWebEx("Can't change resource category"); } } @@ -184,10 +171,11 @@ public long update(SecurityContext sc, long id, RESTResource resource) throws No if (canUpdate) { if (resource.getDescription() != null) old.setDescription(resource.getDescription()); - if (resource.getName() != null) - old.setName(resource.getName()); - if (resource.getMetadata() != null) - old.setMetadata(resource.getMetadata()); + if (resource.getName() != null) old.setName(resource.getName()); + if (resource.getMetadata() != null) old.setMetadata(resource.getMetadata()); + if (resource.getCreator() != null) old.setCreator(resource.getCreator()); + old.setEditor(authUser.getName()); + old.setAdvertised(resource.isAdvertised()); try { resourceService.update(old); @@ -207,14 +195,13 @@ public long update(SecurityContext sc, long id, RESTResource resource) throws No } else { if (LOGGER.isDebugEnabled()) LOGGER.debug("Attribute list is " + resource.getAttribute().size()); - List attributes = Convert.convertAttributeList(resource - .getAttribute()); + List attributes = + Convert.convertAttributeList(resource.getAttribute()); resourceService.updateAttributes(id, attributes); } return id; - } else - throw new ForbiddenErrorWebEx("Can't update resource"); + } else throw new ForbiddenErrorWebEx("Can't update resource"); } catch (NotFoundServiceEx ex) { LOGGER.warn("Resource not found (" + id + "): " + ex.getMessage(), ex); @@ -224,7 +211,7 @@ public long update(SecurityContext sc, long id, RESTResource resource) throws No /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTResourceService#delete(long) */ @Override @@ -238,29 +225,25 @@ public void delete(SecurityContext sc, long id) throws NotFoundWebEx { if (canDelete) { boolean ret = resourceService.delete(id); - if (!ret) - throw new NotFoundWebEx("Resource not found"); - } else - throw new ForbiddenErrorWebEx("This user cannot delete this resource !"); + if (!ret) throw new NotFoundWebEx("Resource not found"); + } else throw new ForbiddenErrorWebEx("This user cannot delete this resource !"); } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTResourceService#delete(long) */ @Override - public void deleteResources(SecurityContext sc, SearchFilter filter) throws BadRequestWebEx, - InternalErrorWebEx { + public void deleteResources(SecurityContext sc, SearchFilter filter) + throws BadRequestWebEx, InternalErrorWebEx { try { resourceService.deleteResources(filter); } catch (BadRequestServiceEx e) { - if (LOGGER.isEnabledFor(Level.ERROR)) - LOGGER.error(e.getMessage()); + if (LOGGER.isEnabled(Level.ERROR)) LOGGER.error(e.getMessage()); throw new BadRequestWebEx(e.getMessage()); } catch (InternalErrorServiceEx e) { - if (LOGGER.isEnabledFor(Level.ERROR)) - LOGGER.error(e.getMessage()); + if (LOGGER.isEnabled(Level.ERROR)) LOGGER.error(e.getMessage()); throw new InternalErrorWebEx(e.getMessage()); } } @@ -274,35 +257,31 @@ public Resource get(SecurityContext sc, long id, boolean fullResource) throws No boolean canRead = false; User authUser = extractAuthUser(sc); canRead = resourceAccessRead(authUser, id); - if(!canRead){ + if (!canRead) { throw new ForbiddenErrorWebEx("This user cannot read this resource !"); } - + if (fullResource) { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Retrieving a full resource"); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Retrieving a full resource"); List resourcesFull; try { - SearchFilter filter = new FieldFilter(BaseField.ID, Long.toString(id), - SearchOperator.EQUAL_TO); + SearchFilter filter = + new FieldFilter(BaseField.ID, Long.toString(id), SearchOperator.EQUAL_TO); resourcesFull = resourceService.getResourcesFull(filter, authUser); } catch (Exception ex) { LOGGER.error(ex.getMessage(), ex); throw new InternalErrorWebEx("Internal error"); } - if (resourcesFull.isEmpty()) - throw new NotFoundWebEx("Resource not found"); + if (resourcesFull.isEmpty()) throw new NotFoundWebEx("Resource not found"); - if (LOGGER.isDebugEnabled()) - LOGGER.debug("DATA is " + resourcesFull.get(0).getData()); + if (LOGGER.isDebugEnabled()) LOGGER.debug("DATA is " + resourcesFull.get(0).getData()); return resourcesFull.get(0); } else { Resource ret = resourceService.get(id); - if (ret == null) - throw new NotFoundWebEx("Resource not found"); + if (ret == null) throw new NotFoundWebEx("Resource not found"); // CHECKME if (ret.getData() != null) { @@ -316,17 +295,19 @@ public Resource get(SecurityContext sc, long id, boolean fullResource) throws No /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTResourceService#getList(java.lang.String, java.lang.Integer, java.lang.Integer) */ @Override - public ShortResourceList getList(SecurityContext sc, String nameLike, Integer page, - Integer entries) throws BadRequestWebEx { + public ShortResourceList getList( + SecurityContext sc, String nameLike, Integer page, Integer entries) + throws BadRequestWebEx { User authUser = extractAuthUser(sc); nameLike = nameLike.replaceAll("[*]", "%"); try { - return new ShortResourceList(resourceService.getList(nameLike, page, entries, authUser)); + return new ShortResourceList( + resourceService.getList(nameLike, page, entries, authUser)); } catch (BadRequestServiceEx ex) { throw new BadRequestWebEx(ex.getMessage()); } @@ -334,7 +315,7 @@ public ShortResourceList getList(SecurityContext sc, String nameLike, Integer pa /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTResourceService#getList(java.lang.Integer, java.lang.Integer) */ @Override @@ -351,7 +332,7 @@ public ShortResourceList getAll(SecurityContext sc, Integer page, Integer entrie /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTResourceService#getCount(java.lang.String) */ @Override @@ -377,14 +358,13 @@ private long parseId(SecurityContext sc, String id) throws BadRequestWebEx { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTResourceService#getAttributes(long) */ @Override public ShortAttributeList getAttributes(SecurityContext sc, long id) throws NotFoundWebEx { Resource resource = resourceService.get(id); - if (resource == null) - throw new NotFoundWebEx("Resource not found"); + if (resource == null) throw new NotFoundWebEx("Resource not found"); // // Authorization check. @@ -399,19 +379,17 @@ public ShortAttributeList getAttributes(SecurityContext sc, long id) throws NotF throw new ForbiddenErrorWebEx( "This user cannot read this resource so neither its attributes!"); } - } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTResourceService#getAttribute(long, java.lang.String) */ @Override public String getAttribute(SecurityContext sc, long id, String name) throws NotFoundWebEx { Resource resource = resourceService.get(id); - if (resource == null) - throw new NotFoundWebEx("Resource not found"); + if (resource == null) throw new NotFoundWebEx("Resource not found"); // // Authorization check. @@ -422,83 +400,86 @@ public String getAttribute(SecurityContext sc, long id, String name) throws NotF if (canRead) { ShortAttribute shAttribute = resourceService.getAttribute(id, name); - - if (shAttribute == null) - throw new NotFoundWebEx("Resource attribute not found"); - + + if (shAttribute == null) throw new NotFoundWebEx("Resource attribute not found"); + return shAttribute.getValue(); } else { throw new ForbiddenErrorWebEx( "This user cannot read this resource so neither its attributes!"); - } + } } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTResourceService#insertAttribute(long, java.lang.String, java.lang.String,it.geosolutions.geostore.core.model.enums.DataType) */ @Override - public long updateAttribute(SecurityContext sc, long id, String name, String value, DataType type) + public long updateAttribute( + SecurityContext sc, long id, String name, String value, DataType type) throws NotFoundWebEx, InternalErrorWebEx { Resource resource = resourceService.get(id); - if (resource == null) - throw new NotFoundWebEx("Resource not found"); + if (resource == null) throw new NotFoundWebEx("Resource not found"); // // Authorization check. // + long attributeId; boolean canUpdate = false; try { User authUser = extractAuthUser(sc); canUpdate = resourceAccessWrite(authUser, resource.getId()); - if (canUpdate){ - - ShortAttribute a = resourceService.getAttribute(id, name); - //if the attribute exists, will be updated - if(a!=null){ - return resourceService.updateAttribute(id, name, value); - }else{ - //create the attribute if missing - if(type != null){ - return resourceService.insertAttribute(id, name, value, type); - }else{ - return resourceService.insertAttribute(id, name, value, DataType.STRING); - } - - } - + if (canUpdate) { + ShortAttribute a = resourceService.getAttribute(id, name); + // if the attribute exists, will be updated + if (a != null) { + attributeId = resourceService.updateAttribute(id, name, value); + } else { + // create the attribute if missing + if (type != null) { + attributeId = resourceService.insertAttribute(id, name, value, type); + } else { + attributeId = + resourceService.insertAttribute(id, name, value, DataType.STRING); + } + } } else { throw new InternalErrorServiceEx("This user cannot access this resource !"); } - } catch (InternalErrorServiceEx ex) { - + resource.setEditor(authUser.getName()); + resourceService.update(resource); + } catch (InternalErrorServiceEx ex) { throw new InternalErrorWebEx(ex.getMessage()); + } catch (DuplicatedResourceNameServiceEx | NotFoundServiceEx e) { + throw new RuntimeException(e); } + return attributeId; } - + /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTResourceService#insertAttribute(long, java.lang.String, java.lang.String) */ - public long updateAttribute(SecurityContext sc, long id,String name, String value) { - return updateAttribute(sc, id, name, value, null); - } + public long updateAttribute(SecurityContext sc, long id, String name, String value) { + return updateAttribute(sc, id, name, value, null); + } @Override - public long updateAttribute(SecurityContext sc, long id, RESTAttribute content) { - if (content != null && content.getName() != null) { - // TODO: type - return updateAttribute(sc, id, content.getName(), content.getValue(), content.getType()); - } - throw new BadRequestWebEx("missing attribute content or attribute name in request"); - } + public long updateAttribute(SecurityContext sc, long id, RESTAttribute content) { + if (content != null && content.getName() != null) { + // TODO: type + return updateAttribute( + sc, id, content.getName(), content.getValue(), content.getType()); + } + throw new BadRequestWebEx("missing attribute content or attribute name in request"); + } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTResourceService#getResourceByFilter(it.geosolutions.geostore.services.dto.SearchFilter) */ @Override @@ -508,43 +489,45 @@ public ShortResourceList getResources(SecurityContext sc, SearchFilter filter) { try { return new ShortResourceList(resourceService.getResources(filter, authUser)); } catch (BadRequestServiceEx e) { - if (LOGGER.isInfoEnabled()) - LOGGER.info(e.getMessage()); + if (LOGGER.isInfoEnabled()) LOGGER.info(e.getMessage()); throw new BadRequestWebEx(e.getMessage()); } catch (InternalErrorServiceEx e) { - if (LOGGER.isInfoEnabled()) - LOGGER.info(e.getMessage()); + if (LOGGER.isInfoEnabled()) LOGGER.info(e.getMessage()); throw new InternalErrorWebEx(e.getMessage()); } } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTResourceService#getResourcesList(javax.ws.rs.core.SecurityContext, * it.geosolutions.geostore.services.dto.search.SearchFilter, java.lang.Integer, java.lang.Integer, boolean, boolean) */ @Override - public ResourceList getResourcesList(SecurityContext sc, Integer page, Integer entries, - boolean includeAttributes, boolean includeData, SearchFilter filter) { + public ResourceList getResourcesList( + SecurityContext sc, + Integer page, + Integer entries, + boolean includeAttributes, + boolean includeData, + SearchFilter filter) { User authUser = extractAuthUser(sc); try { - return new ResourceList(resourceService.getResources(filter, page, entries, includeAttributes, includeData, authUser)); + return new ResourceList( + resourceService.getResources( + filter, page, entries, includeAttributes, includeData, authUser)); } catch (BadRequestServiceEx e) { - if (LOGGER.isInfoEnabled()) - LOGGER.info(e.getMessage()); + if (LOGGER.isInfoEnabled()) LOGGER.info(e.getMessage()); throw new BadRequestWebEx(e.getMessage()); } catch (InternalErrorServiceEx e) { - if (LOGGER.isInfoEnabled()) - LOGGER.info(e.getMessage()); + if (LOGGER.isInfoEnabled()) LOGGER.info(e.getMessage()); throw new InternalErrorWebEx(e.getMessage()); } } - @Override - public void updateSecurityRules(SecurityContext sc, long id, - SecurityRuleList securityRules) { - // + @Override + public void updateSecurityRules(SecurityContext sc, long id, SecurityRuleList securityRules) { + // // Authorization check. // boolean canWrite = false; @@ -552,37 +535,35 @@ public void updateSecurityRules(SecurityContext sc, long id, canWrite = resourceAccessWrite(authUser, id); if (canWrite) { - ShortAttribute owner = resourceService.getAttribute(id, "owner"); - if((authUser.getRole() == Role.ADMIN) || (owner == null) || (owner.getValue().equals(authUser.getName()))) { - try { - resourceService.updateSecurityRules(id, Convert - .convertSecurityRuleList(securityRules.getList(), id)); - } catch (BadRequestServiceEx e) { - if (LOGGER.isInfoEnabled()) - LOGGER.info(e.getMessage()); - throw new BadRequestWebEx(e.getMessage()); - } catch (InternalErrorServiceEx e) { - if (LOGGER.isInfoEnabled()) - LOGGER.info(e.getMessage()); - throw new InternalErrorWebEx(e.getMessage()); - } catch (NotFoundServiceEx e) { - if (LOGGER.isInfoEnabled()) - LOGGER.info(e.getMessage()); - throw new NotFoundWebEx(e.getMessage()); - } - } else { - throw new ForbiddenErrorWebEx( - "This user cannot update this resource permissions!"); - } + ShortAttribute owner = resourceService.getAttribute(id, "owner"); + if ((authUser.getRole() == Role.ADMIN) + || (owner == null) + || (owner.getValue().equals(authUser.getName()))) { + try { + resourceService.updateSecurityRules( + id, Convert.convertSecurityRuleList(securityRules.getList(), id)); + } catch (BadRequestServiceEx e) { + if (LOGGER.isInfoEnabled()) LOGGER.info(e.getMessage()); + throw new BadRequestWebEx(e.getMessage()); + } catch (InternalErrorServiceEx e) { + if (LOGGER.isInfoEnabled()) LOGGER.info(e.getMessage()); + throw new InternalErrorWebEx(e.getMessage()); + } catch (NotFoundServiceEx e) { + if (LOGGER.isInfoEnabled()) LOGGER.info(e.getMessage()); + throw new NotFoundWebEx(e.getMessage()); + } + } else { + throw new ForbiddenErrorWebEx("This user cannot update this resource permissions!"); + } } else { throw new ForbiddenErrorWebEx( "This user cannot write this resource so neither its permissions!"); - } - } + } + } - @Override - public SecurityRuleList getSecurityRules(SecurityContext sc, long id) { - // + @Override + public SecurityRuleList getSecurityRules(SecurityContext sc, long id) { + // // Authorization check. // boolean canRead = false; @@ -590,31 +571,25 @@ public SecurityRuleList getSecurityRules(SecurityContext sc, long id) { canRead = resourceAccessRead(authUser, id); if (canRead) { - ShortAttribute owner = resourceService.getAttribute(id, "owner"); - if((authUser.getRole() == Role.ADMIN) || (owner == null) || (owner.getValue().equals(authUser.getName()))) { - try { - return new SecurityRuleList(resourceService.getSecurityRules(id)); - } catch (BadRequestServiceEx e) { - if (LOGGER.isInfoEnabled()) - LOGGER.info(e.getMessage()); - throw new BadRequestWebEx(e.getMessage()); - } catch (InternalErrorServiceEx e) { - if (LOGGER.isInfoEnabled()) - LOGGER.info(e.getMessage()); - throw new InternalErrorWebEx(e.getMessage()); - } - } else { - throw new ForbiddenErrorWebEx( - "This user cannot read this resource permissions!"); - } + ShortAttribute owner = resourceService.getAttribute(id, "owner"); + if ((authUser.getRole() == Role.ADMIN) + || (owner == null) + || (owner.getValue().equals(authUser.getName()))) { + try { + return new SecurityRuleList(resourceService.getSecurityRules(id)); + } catch (BadRequestServiceEx e) { + if (LOGGER.isInfoEnabled()) LOGGER.info(e.getMessage()); + throw new BadRequestWebEx(e.getMessage()); + } catch (InternalErrorServiceEx e) { + if (LOGGER.isInfoEnabled()) LOGGER.info(e.getMessage()); + throw new InternalErrorWebEx(e.getMessage()); + } + } else { + throw new ForbiddenErrorWebEx("This user cannot read this resource permissions!"); + } } else { throw new ForbiddenErrorWebEx( "This user cannot read this resource so neither its permissions!"); - } - - - } - - - + } + } } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTServiceImpl.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTServiceImpl.java index f88375ea..2aadb79c 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTServiceImpl.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTServiceImpl.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -37,140 +37,161 @@ import it.geosolutions.geostore.services.UserService; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; import it.geosolutions.geostore.services.rest.exception.InternalErrorWebEx; - import java.security.Principal; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; - import javax.ws.rs.core.SecurityContext; import org.apache.commons.collections.CollectionUtils; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.GrantedAuthorityImpl; +import org.springframework.security.core.authority.SimpleGrantedAuthority; /** * Class RESTServiceImpl. - * - * This is the super class for each RESTServices implementation - * + * + *

    This is the superclass for each RESTServices implementation + * * @author ETj (etj at geo-solutions.it) * @author DamianoG */ -public abstract class RESTServiceImpl{ +public abstract class RESTServiceImpl { + + private static final Logger LOGGER = LogManager.getLogger(RESTServiceImpl.class); + + @Autowired UserService userService; - private final static Logger LOGGER = Logger.getLogger(RESTServiceImpl.class); + /** + * Given a Group Set returns a List that contains all the group names + * + * @param groups + * @return + */ + public static List extratcGroupNames(Set groups) { + List groupNames = new ArrayList<>(groups.size() + 1); + for (UserGroup ug : groups) { + groupNames.add(ug.getGroupName()); + } + return groupNames; + } - @Autowired - UserService userService; - protected abstract SecurityService getSecurityService(); - - - + public void setUserService(UserService userService) { this.userService = userService; } - - /** - * @return User - The authenticated user that is accessing this service, or null if guest access. + * @return User - The authenticated user that is accessing this service, or null if guest + * access. */ protected User extractAuthUser(SecurityContext sc) throws InternalErrorWebEx { - if (sc == null) - throw new InternalErrorWebEx("Missing auth info"); + if (sc == null) throw new InternalErrorWebEx("Missing auth info"); else { Principal principal = sc.getUserPrincipal(); if (principal == null) { - // If I'm here I'm sure that the service is running is allowed for the unauthenticated users - // due to service-based authorization step that uses annotations on services declaration (seee module geostore-rest-api). - // So I'm going to create a Principal to be used during resources-based authorization. + // If I'm here, I'm sure that the service is running is allowed for the + // unauthenticated users + // due to a service-based authorization step that uses annotations on services + // declaration (see module geostore-rest-api). + // So I'm going to create a Principal to be used during resources-based + // authorization. principal = createGuestPrincipal(); } if (!(principal instanceof Authentication)) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Mismatching auth principal"); - } - throw new InternalErrorWebEx("Mismatching auth principal (" + principal.getClass() - + ")"); + logMismatchedPrincipal(); + throw new InternalErrorWebEx( + "Mismatching auth principal (" + principal.getClass() + ")"); } Authentication usrToken = (Authentication) principal; - //DamianoG 06/03/2014 Why create a new Instance when we can deal with the object taken from the DB? Being the instance taken from DB Transient we avoid problems saving security rules... -// User user = new User(); -// user.setName(usrToken.getName()); -// for (GrantedAuthority authority : usrToken.getAuthorities()) { -// if (authority != null) { -// if (authority.getAuthority() != null -// && authority.getAuthority().contains("ADMIN")) -// user.setRole(Role.ADMIN); -// -// if (authority.getAuthority() != null -// && authority.getAuthority().contains("USER") && user.getRole() == null) -// user.setRole(Role.USER); -// -// if (user.getRole() == null) -// user.setRole(Role.GUEST); -// } -// } + // DamianoG 06/03/2014 Why create a new Instance when we can deal with the object taken + // from the DB? Being the instance taken from DB Transient we avoid problems saving + // security rules... + // User user = new User(); + // user.setName(usrToken.getName()); + // for (GrantedAuthority authority : usrToken.getAuthorities()) { + // if (authority != null) { + // if (authority.getAuthority() != null + // && authority.getAuthority().contains("ADMIN")) + // user.setRole(Role.ADMIN); + // + // if (authority.getAuthority() != null + // && authority.getAuthority().contains("USER") && + // user.getRole() == null) + // user.setRole(Role.USER); + // + // if (user.getRole() == null) + // user.setRole(Role.GUEST); + // } + // } if (usrToken.getPrincipal() instanceof User) { - User user = (User)usrToken.getPrincipal(); - - LOGGER.info("Accessing service with user " + user.getName() + " and role " - + user.getRole()); + User user = (User) usrToken.getPrincipal(); + + LOGGER.info( + "Accessing service with user {} and role {}", + user.getName(), + user.getRole()); return user; } - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Mismatching auth principal"); - } + logMismatchedPrincipal(); throw new InternalErrorWebEx("Mismatching auth principal (not a GeoStore User)"); } } - + + private static void logMismatchedPrincipal() { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Mismatching auth principal"); + } + } + /** - * This operation is responsible for check if a resource is accessible to an user to perform WRITE operations (update/delete). - * this operation must checks first if the user has the right permissions then, if not, check if its group is allowed. - * - * @param resource + * This operation is responsible for check if a resource is accessible to a user to perform + * WRITE operations (update/delete). This operation must check first if the user has the right + * permissions then, if not, check if its group is allowed. + * + * @param resourceId * @return boolean */ public boolean resourceAccessWrite(User authUser, long resourceId) { if (authUser.getRole().equals(Role.ADMIN)) { return true; - } -// else if(belongTo(authUser, GroupReservedNames.ALLRESOURCES.toString())){ -// return true; -// } + } + // else if(belongTo(authUser, GroupReservedNames.ALLRESOURCES.toString())){ + // return true; + // } else { - List userSecurityRules = getSecurityService().getUserSecurityRule( - authUser.getName(), resourceId); - - if (userSecurityRules != null && userSecurityRules.size() > 0){ - for(SecurityRule sr : userSecurityRules){ - // the getUserSecurityRules returns all rules instead of user rules. So the user name check is necessary until problem with DAO is solved - if (sr.isCanWrite() && sr.getUser() != null && sr.getUser().getName().equals(authUser.getName())){ - return true; - } - } + List userSecurityRules = + getSecurityService().getUserSecurityRule(authUser.getName(), resourceId); + + if (userSecurityRules != null && !userSecurityRules.isEmpty()) { + for (SecurityRule sr : userSecurityRules) { + // The getUserSecurityRules returns all rules instead of user rules. + // So the username check is necessary until a problem with DAO is solved + if (sr.isCanWrite() + && sr.getUser() != null + && sr.getUser().getName().equals(authUser.getName())) { + return true; + } + } } - + List groupNames = extratcGroupNames(authUser.getGroups()); - if(groupNames != null && groupNames.size() > 0){ - List groupSecurityRules = getSecurityService().getGroupSecurityRule( - groupNames, resourceId); - - if (groupSecurityRules != null && groupSecurityRules.size() > 0){ + if (!groupNames.isEmpty()) { + List groupSecurityRules = + getSecurityService().getGroupSecurityRule(groupNames, resourceId); + + if (groupSecurityRules != null && !groupSecurityRules.isEmpty()) { // Check if at least one user group has write permission - for(SecurityRule sr : groupSecurityRules){ - if (sr.isCanWrite()){ + for (SecurityRule sr : groupSecurityRules) { + if (sr.isCanWrite()) { return true; } } @@ -179,89 +200,92 @@ public boolean resourceAccessWrite(User authUser, long resourceId) { } return false; } - + /** - * This operation is responsible for check if a resource is accessible to an user to perform READ operations. - * this operation must checks first if the user has the right permissions then, if not, check if its group is allowed. - * - * @param resource + * This operation is responsible for check if a resource is accessible to an user to perform + * READ operations. This operation must check first if the user has the right permissions then, + * if not, check if its group is allowed. + * + * @param resourceId * @return boolean */ public boolean resourceAccessRead(User authUser, long resourceId) { if (authUser.getRole().equals(Role.ADMIN)) { return true; - } -// else if(belongTo(authUser, GroupReservedNames.ALLRESOURCES.toString())){ -// return true; -// } + } + // else if(belongTo(authUser, GroupReservedNames.ALLRESOURCES.toString())){ + // return true; + // } else { - List userSecurityRules = getSecurityService().getUserSecurityRule( - authUser.getName(), resourceId); - - if (userSecurityRules != null && userSecurityRules.size() > 0){ - // the getUserSecurityRules returns all rules instead of user rules. So the user name check is necessary until problem with DAO is solved - for(SecurityRule sr : userSecurityRules){ - if (sr.isCanRead() && sr.getUser() != null && sr.getUser().getName().equals(authUser.getName())){ - return true; - } + List userSecurityRules = + getSecurityService().getUserSecurityRule(authUser.getName(), resourceId); + + if (userSecurityRules != null && !userSecurityRules.isEmpty()) { + // The getUserSecurityRules returns all rules instead of user rules. + // So the username check is necessary until a problem with DAO is solved + for (SecurityRule sr : userSecurityRules) { + if (sr.isCanRead() + && sr.getUser() != null + && sr.getUser().getName().equals(authUser.getName())) { + return true; + } } } - + List groupNames = extratcGroupNames(authUser.getGroups()); - if(groupNames != null && groupNames.size() > 0){ - List groupSecurityRules = getSecurityService().getGroupSecurityRule( - groupNames, resourceId); - - if (groupSecurityRules != null && groupSecurityRules.size() > 0){ + if (!groupNames.isEmpty()) { + List groupSecurityRules = + getSecurityService().getGroupSecurityRule(groupNames, resourceId); + + if (groupSecurityRules != null && !groupSecurityRules.isEmpty()) { // Check if at least one user group has read permission - for(SecurityRule sr : groupSecurityRules){ - if (sr.isCanRead()){ + for (SecurityRule sr : groupSecurityRules) { + if (sr.isCanRead()) { return true; } } } } } - return false; + return false; } - - public ResourceAuth getResourceAuth(User authUser, long resourceId) - { + public ResourceAuth getResourceAuth(User authUser, long resourceId) { if (authUser.getRole().equals(Role.ADMIN)) { return new ResourceAuth(true, true); } - List userSecurityRules = getSecurityService().getUserSecurityRule(authUser.getName(), resourceId); + List userSecurityRules = + getSecurityService().getUserSecurityRule(authUser.getName(), resourceId); ResourceAuth ret = new ResourceAuth(); - if (CollectionUtils.isNotEmpty(userSecurityRules)){ + if (CollectionUtils.isNotEmpty(userSecurityRules)) { // take the more permissive grants for (SecurityRule rule : userSecurityRules) { ret.canRead |= rule.isCanRead(); ret.canWrite |= rule.isCanWrite(); - if(ret.canRead && ret.canWrite) { // short circuit + if (ret.canRead && ret.canWrite) { // short circuit return ret; } } } List groupNames = extratcGroupNames(authUser.getGroups()); - if(groupNames != null && groupNames.size() > 0){ - List groupSecurityRules = getSecurityService().getGroupSecurityRule(groupNames, resourceId); + if (!groupNames.isEmpty()) { + List groupSecurityRules = + getSecurityService().getGroupSecurityRule(groupNames, resourceId); - if (CollectionUtils.isNotEmpty(groupSecurityRules)){ + if (CollectionUtils.isNotEmpty(groupSecurityRules)) { // take the more permissive grants - for(SecurityRule rule : groupSecurityRules){ + for (SecurityRule rule : groupSecurityRules) { ret.canRead |= rule.isCanRead(); ret.canWrite |= rule.isCanWrite(); - if(ret.canRead && ret.canWrite) { // short circuit + if (ret.canRead && ret.canWrite) { // short circuit return ret; } - } } } @@ -270,19 +294,19 @@ public ResourceAuth getResourceAuth(User authUser, long resourceId) } /** - * Creates a Guest principal with Username="guest" password="" and role ROLE_GUEST. - * The guest principal should be used with unauthenticated users. - * + * Creates a Guest principal with Username="guest" password="" and role ROLE_GUEST. The guest + * principal should be used with unauthenticated users. + * * @return the Principal instance */ - public Principal createGuestPrincipal(){ + public Principal createGuestPrincipal() { List authorities = new ArrayList<>(); - authorities.add(new GrantedAuthorityImpl("ROLE_GUEST")); + authorities.add(new SimpleGrantedAuthority("ROLE_GUEST")); try { User u = userService.get(UserReservedNames.GUEST.userName()); - return new UsernamePasswordAuthenticationToken(u,"", authorities); + return new UsernamePasswordAuthenticationToken(u, "", authorities); } catch (NotFoundServiceEx e) { - if(LOGGER.isDebugEnabled()){ + if (LOGGER.isDebugEnabled()) { LOGGER.debug("User GUEST is not configured, creating on-the-fly a default one"); } } @@ -296,41 +320,21 @@ public Principal createGuestPrincipal(){ everyoneGroup.setGroupName(GroupReservedNames.EVERYONE.groupName()); groups.add(everyoneGroup); guest.setGroups(groups); - Principal principal = new UsernamePasswordAuthenticationToken(guest,"", authorities); - return principal; + return new UsernamePasswordAuthenticationToken(guest, "", authorities); } - - /** - * Given a Group Set returns a List that contains all the group names - * - * @param groups - * @return - */ - public static List extratcGroupNames(Set groups){ - List groupNames = new ArrayList<>(groups.size() + 1); - for(UserGroup ug : groups){ - groupNames.add(ug.getGroupName()); - } - return groupNames; - } - protected static class ResourceAuth { - public ResourceAuth() - { + boolean canRead; + boolean canWrite; + + public ResourceAuth() { this(false, false); } - public ResourceAuth(boolean canRead, boolean canWrite) - { + public ResourceAuth(boolean canRead, boolean canWrite) { this.canRead = canRead; this.canWrite = canWrite; } - - - boolean canRead; - boolean canWrite; } - } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTSessionServiceImpl.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTSessionServiceImpl.java index 3d93eb9a..99f04981 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTSessionServiceImpl.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTSessionServiceImpl.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -27,16 +27,8 @@ */ package it.geosolutions.geostore.services.rest.impl; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; - -import javax.ws.rs.core.SecurityContext; - -import org.springframework.beans.factory.annotation.Autowired; - +import static it.geosolutions.geostore.services.rest.SessionServiceDelegate.PROVIDER_KEY; +import static it.geosolutions.geostore.services.rest.impl.SessionServiceDelegateImpl.DEFAULT_NAME; import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.services.SecurityService; @@ -44,180 +36,226 @@ import it.geosolutions.geostore.services.dto.UserSession; import it.geosolutions.geostore.services.dto.UserSessionImpl; import it.geosolutions.geostore.services.rest.RESTSessionService; +import it.geosolutions.geostore.services.rest.SessionServiceDelegate; import it.geosolutions.geostore.services.rest.model.SessionToken; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.SecurityContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.provider.authentication.BearerTokenExtractor; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +public class RESTSessionServiceImpl extends RESTServiceImpl implements RESTSessionService { + static final String BEARER_TYPE = "bearer"; + private static final String expireParser = "yyyy-MM-dd'T'HH:mm:ssZ"; + @Autowired UserSessionService userSessionService; + private Map delegates; + private boolean autorefresh = false; + private long sessionTimeout = 86400; // 1 day + + public RESTSessionServiceImpl() { + registerDelegate(DEFAULT_NAME, new SessionServiceDelegateImpl()); + } + + /** Transform Calendar to ISO 8601 string. */ + public static String fromCalendar(final Calendar calendar) { + Date date = calendar.getTime(); + String formatted = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(date); + return formatted.substring(0, 22) + ":" + formatted.substring(22); + } + + /** Transform ISO 8601 string to Calendar. */ + public static Calendar toCalendar(final String iso8601string) throws ParseException { + Calendar calendar = GregorianCalendar.getInstance(); + String s = iso8601string.replace("Z", "+00:00"); + try { + s = s.substring(0, 22) + s.substring(23); // to get rid of the ":" + } catch (IndexOutOfBoundsException e) { + throw new ParseException("Invalid length", 0); + } + Date date = new SimpleDateFormat(expireParser).parse(s); + calendar.setTime(date); + return calendar; + } + + public boolean isAutorefresh() { + return autorefresh; + } + + public void setAutorefresh(boolean autorefresh) { + this.autorefresh = autorefresh; + } + + public UserSessionService getUserSessionService() { + return userSessionService; + } + + public void setUserSessionService(UserSessionService userSessionService) { + this.userSessionService = userSessionService; + } + + /** + * Gets the User object associated to the given sessionId (if it exists). + * + * @param sessionId + * @return + */ + public User getUser(String sessionId, boolean refresh) { + User user = null; + Collection list = delegates.values(); + for (SessionServiceDelegate del : list) { + user = del.getUser(sessionId, refresh, autorefresh); + if (user != null) break; + } + return user; + } + + /** + * Gets the username associated to the given sessionId (if it exists). + * + * @param sessionId + * @return + */ + public String getUserName(String sessionId, boolean refresh) { + String userName = null; + Collection list = delegates.values(); + for (SessionServiceDelegate del : list) { + userName = del.getUserName(sessionId, refresh, autorefresh); + if (userName != null) break; + } + return userName; + } + + private Calendar getExpiration(String expires) throws ParseException { + if (!"".equals(expires)) { + return toCalendar(expires); + } + return null; + } + + /** + * Creates a new session for the User in SecurityContext. + * + * @return + * @throws ParseException + */ + public String createSession(String expires, SecurityContext sc) throws ParseException { + User user = extractAuthUser(sc); + if (user != null) { + Calendar expiration = getExpiration(expires); + UserSession session = null; + if (user instanceof User) { + session = new UserSessionImpl(null, user, expiration); + } + return userSessionService.registerNewSession(session); + } + + return null; + } + + @Override + public SessionToken login(SecurityContext sc) throws ParseException { + Calendar expires = new GregorianCalendar(); + expires.add(Calendar.SECOND, (int) getSessionTimeout()); + User user = extractAuthUser(sc); + if (user != null) { + + UserSession session = null; + if (user instanceof User) { + session = new UserSessionImpl(null, user, expires); + session.setExpirationInterval(getSessionTimeout()); + } + return toSessionToken(userSessionService.registerNewSession(session), session); + } + return null; + } + + private SessionToken toSessionToken(String accessToken, UserSession sessionToken) { + if (sessionToken == null) { + return null; + } + SessionToken token = new SessionToken(); + token.setAccessToken(accessToken); + token.setRefreshToken(sessionToken.getRefreshToken()); + token.setExpires(sessionToken.getExpirationInterval()); + token.setTokenType(BEARER_TYPE); + return token; + } + + @Override + public SessionToken refresh(SecurityContext sc, String sessionId, String refreshToken) { + String provider = + (String) RequestContextHolder.getRequestAttributes().getAttribute(PROVIDER_KEY, 0); + SessionServiceDelegate delegate = getDelegate(provider); + return delegate.refresh(refreshToken, sessionId); + } + + private SessionServiceDelegate getDelegate(String key) { + SessionServiceDelegate result; + if (key == null) result = delegates.get(DEFAULT_NAME); + else result = delegates.get(key); + + if (result == null) result = delegates.get(DEFAULT_NAME); + + return result; + } + + /** + * Removes the given session. + * + * @return + */ + public void removeSession(String sessionId) { + String provider = + (String) RequestContextHolder.getRequestAttributes().getAttribute(PROVIDER_KEY, 0); + SessionServiceDelegate delegate = getDelegate(provider); + delegate.doLogout(sessionId); + } + + @Override + public SessionToken refresh(SessionToken sessionToken) throws ParseException { + return refresh(null, sessionToken.getAccessToken(), sessionToken.getRefreshToken()); + } + + @Override + public void removeSession() { + HttpServletRequest request = + ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) + .getRequest(); + Authentication authentication = new BearerTokenExtractor().extract(request); + if (authentication != null && authentication.getPrincipal() != null) + removeSession(authentication.getPrincipal().toString()); + } + + /** + * Removes all sessions. + * + * @return + */ + public void clear() { + userSessionService.removeAllSessions(); + } + + @Override + protected SecurityService getSecurityService() { + return null; + } + + public long getSessionTimeout() { + return sessionTimeout; + } -public class RESTSessionServiceImpl extends RESTServiceImpl implements RESTSessionService{ - private static final String BEARER_TYPE = "bearer"; - @Autowired - UserSessionService userSessionService; - private boolean autorefresh = false; - - public boolean isAutorefresh() { - return autorefresh; - } - - public void setAutorefresh(boolean autorefresh) { - this.autorefresh = autorefresh; - } - - private long sessionTimeout = 86400; // 1 day - - public UserSessionService getUserSessionService() { - return userSessionService; - } - - public void setUserSessionService(UserSessionService userSessionService) { - this.userSessionService = userSessionService; - } - - private static SimpleDateFormat expireParser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); - - /** - * Gets the User object associated to the given sessionId (if it exists). - * - * @param sessionId - * @return - */ - public User getUser(String sessionId, boolean refresh) { - User details = userSessionService.getUserData(sessionId); - if (details != null && refresh && autorefresh) { - userSessionService.refreshSession(sessionId, userSessionService.getRefreshToken(sessionId)); - } - return details; - } - - /** - * Gets the username associated to the given sessionId (if it exists). - * - * @param sessionId - * @return - */ - public String getUserName(String sessionId, boolean refresh) { - User userData = userSessionService.getUserData(sessionId); - if (userData != null) { - if (refresh && autorefresh) { - userSessionService.refreshSession(sessionId, userSessionService.getRefreshToken(sessionId)); - } - return userData.getName(); - } - return null; - } - - private Calendar getExpiration(String expires) throws ParseException { - if (!"".equals(expires)) { - return toCalendar(expires); - } - return null; - } - - /** - * Creates a new session for the User in SecurityContext. - * - * @return - * @throws ParseException - */ - public String createSession(String expires,SecurityContext sc) throws ParseException { - User user = extractAuthUser(sc); - if (user != null) { - Calendar expiration = getExpiration(expires); - UserSession session = null; - if (user instanceof User) { - session = new UserSessionImpl(null, user, expiration); - } - return userSessionService.registerNewSession(session); - } - - return null; - } - - @Override - public SessionToken login(SecurityContext sc) throws ParseException { - Calendar expires = new GregorianCalendar(); - expires.add(Calendar.SECOND, (int) getSessionTimeout()); - User user = extractAuthUser(sc); - if (user != null) { - - UserSession session = null; - if (user instanceof User) { - session = new UserSessionImpl(null, user, expires); - session.setExpirationInterval(getSessionTimeout()); - } - return toSessionToken(userSessionService.registerNewSession(session), session); - } - return null; - } - - private SessionToken toSessionToken(String accessToken, UserSession sessionToken) { - if(sessionToken == null) { - return null; - } - SessionToken token = new SessionToken(); - token.setAccessToken(accessToken); - token.setRefreshToken(sessionToken.getRefreshToken()); - token.setExpires(sessionToken.getExpirationInterval()); - token.setTokenType(BEARER_TYPE); - return token; - } - - @Override - public SessionToken refresh(SecurityContext sc, String sessionId, String refreshToken) { - return toSessionToken(sessionId, userSessionService.refreshSession(sessionId, refreshToken)); - - } - - /** - * Removes the given session. - * - * @return - */ - public void removeSession(String sessionId) { - userSessionService.removeSession(sessionId); - } - - /** - * Removes all sessions. - * - * @return - */ - public void clear() { - userSessionService.removeAllSessions(); - } - - /** Transform Calendar to ISO 8601 string. */ - public static String fromCalendar(final Calendar calendar) { - Date date = calendar.getTime(); - String formatted = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(date); - return formatted.substring(0, 22) + ":" + formatted.substring(22); - } - - /** Transform ISO 8601 string to Calendar. */ - public static Calendar toCalendar(final String iso8601string) throws ParseException { - Calendar calendar = GregorianCalendar.getInstance(); - String s = iso8601string.replace("Z", "+00:00"); - try { - s = s.substring(0, 22) + s.substring(23); // to get rid of the ":" - } catch (IndexOutOfBoundsException e) { - throw new ParseException("Invalid length", 0); - } - Date date = expireParser.parse(s); - calendar.setTime(date); - return calendar; - } - - - @Override - protected SecurityService getSecurityService() { - return null; - } - - public long getSessionTimeout() { - return sessionTimeout; - } - - public void setSessionTimeout(long sessionTimeout) { - this.sessionTimeout = sessionTimeout; - } - - + public void setSessionTimeout(long sessionTimeout) { + this.sessionTimeout = sessionTimeout; + } + @Override + public void registerDelegate(String key, SessionServiceDelegate delegate) { + if (delegates == null) this.delegates = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + this.delegates.put(key, delegate); + } } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTStoredDataServiceImpl.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTStoredDataServiceImpl.java index 17d844b8..67cc29a0 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTStoredDataServiceImpl.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTStoredDataServiceImpl.java @@ -31,26 +31,22 @@ import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; import it.geosolutions.geostore.services.rest.model.enums.RawFormat; import it.geosolutions.geostore.services.rest.utils.DataURIDecoder; - - import java.io.StringReader; import java.util.Arrays; import java.util.Collection; import java.util.Collections; - import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; - import net.sf.json.JSON; import net.sf.json.JSONException; import net.sf.json.JSONObject; import net.sf.json.JSONSerializer; import net.sf.json.xml.XMLSerializer; import org.apache.commons.codec.binary.Base64; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.jdom.Element; import org.jdom.input.SAXBuilder; import org.jdom.output.Format; @@ -58,42 +54,51 @@ /** * Class RESTStoredDataServiceImpl. - * + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class RESTStoredDataServiceImpl extends RESTServiceImpl implements RESTStoredDataService { - private final static Logger LOGGER = Logger.getLogger(RESTStoredDataServiceImpl.class); - + private static final Logger LOGGER = LogManager.getLogger(RESTStoredDataServiceImpl.class); + private static final Collection GET_XML_MEDIA_TYPES = + Arrays.asList(MediaType.TEXT_XML_TYPE, MediaType.APPLICATION_XML_TYPE); + private static final Collection GET_JSON_MEDIA_TYPES = + Collections.singletonList(MediaType.APPLICATION_JSON_TYPE); + private static final Collection GET_TEXT_MEDIA_TYPES = + Collections.singletonList(MediaType.TEXT_PLAIN_TYPE); private StoredDataService storedDataService; - /** - * @param storedDataService - */ + // /* (non-Javadoc) + // * @see it.geosolutions.geostore.services.rest.RESTStoredDataService#getAll() + // */ + // @Override + // public StoredDataList getAll(SecurityContext sc) { + // return new StoredDataList(storedDataService.getAll()); + // } + + /** @param storedDataService */ public void setStoredDataService(StoredDataService storedDataService) { this.storedDataService = storedDataService; } - + /* (non-Javadoc) * @see it.geosolutions.geostore.services.rest.impl.RESTServiceImpl#getSecurityService() */ @Override protected SecurityService getSecurityService() { - return storedDataService; + return storedDataService; } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTStoredDataService#update(long, java.lang.String) */ @Override public long update(SecurityContext sc, long id, String data) throws NotFoundWebEx { try { - if (data == null) - throw new BadRequestWebEx("Data is null"); + if (data == null) throw new BadRequestWebEx("Data is null"); // // Authorization check. @@ -115,17 +120,9 @@ public long update(SecurityContext sc, long id, String data) throws NotFoundWebE } } - // /* (non-Javadoc) - // * @see it.geosolutions.geostore.services.rest.RESTStoredDataService#getAll() - // */ - // @Override - // public StoredDataList getAll(SecurityContext sc) { - // return new StoredDataList(storedDataService.getAll()); - // } - /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTStoredDataService#delete(long) */ @Override @@ -139,30 +136,18 @@ public void delete(SecurityContext sc, long id) throws NotFoundWebEx { if (canDelete) { boolean ret = storedDataService.delete(id); - if (!ret) - throw new NotFoundWebEx("Data not found"); - } else - throw new ForbiddenErrorWebEx("This user cannot delete this store !"); + if (!ret) throw new NotFoundWebEx("Data not found"); + } else throw new ForbiddenErrorWebEx("This user cannot delete this store !"); } - private final static Collection GET_XML_MEDIA_TYPES = Arrays.asList( - MediaType.TEXT_XML_TYPE, MediaType.APPLICATION_XML_TYPE); - - private final static Collection GET_JSON_MEDIA_TYPES = Arrays - .asList(MediaType.APPLICATION_JSON_TYPE); - - private final static Collection GET_TEXT_MEDIA_TYPES = Arrays - .asList(MediaType.TEXT_PLAIN_TYPE); - /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTStoredDataService#get(long) */ @Override public String get(SecurityContext sc, HttpHeaders headers, long id) throws NotFoundWebEx { - if (id == -1) - return "dummy payload"; + if (id == -1) return "dummy payload"; // // Authorization check. @@ -170,10 +155,10 @@ public String get(SecurityContext sc, HttpHeaders headers, long id) throws NotFo boolean canRead = false; User authUser = extractAuthUser(sc); canRead = resourceAccessRead(authUser, id); // The ID is also the resource ID - if(!canRead){ + if (!canRead) { throw new ForbiddenErrorWebEx("This user cannot read this stored data !"); } - + StoredData storedData; try { storedData = storedDataService.get(id); @@ -193,8 +178,8 @@ public String get(SecurityContext sc, HttpHeaders headers, long id) throws NotFo } else if (!Collections.disjoint(GET_XML_MEDIA_TYPES, headers.getAcceptableMediaTypes())) { return toXML(data); } else - throw new InternalErrorWebEx("Illegal state (" + headers.getAcceptableMediaTypes() - + ")"); + throw new InternalErrorWebEx( + "Illegal state (" + headers.getAcceptableMediaTypes() + ")"); } private String toJSON(String data) { @@ -206,12 +191,10 @@ private String toJSON(String data) { XMLSerializer xmlSerializer = new XMLSerializer(); JSON json = xmlSerializer.read(data); String ret = json.toString(); - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Transformed XML -> JSON"); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Transformed XML -> JSON"); return ret; } catch (JSONException exc) { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Data is not in native XML format."); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Data is not in native XML format."); } try { @@ -219,38 +202,32 @@ private String toJSON(String data) { // data To JSON conversion // /////////////////////// JSONSerializer.toJSON(data); - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Data is in native JSON format."); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Data is in native JSON format."); return data; } catch (JSONException e) { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Data is not in native JSON format."); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Data is not in native JSON format."); } JSONObject jsonObj = new JSONObject(); jsonObj.put("data", data); String ret = jsonObj.toString(); - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Transformed plaintext -> JSON"); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Transformed plaintext -> JSON"); return ret; } private String toXML(String data) { - // Try XML source - try { - StringReader reader = new StringReader(data); + // Try an XML source + try (StringReader reader = new StringReader(data)) { SAXBuilder builder = new SAXBuilder(); builder.build(reader); // no errors: return the original data - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Data is in native XML format."); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Data is in native XML format."); return data; } catch (Exception e) { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Data is not in native XML format."); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Data is not in native XML format.", e); } // Try JSON source @@ -261,13 +238,11 @@ private String toXML(String data) { JSON json = JSONSerializer.toJSON(data); XMLSerializer xmlSerializer = new XMLSerializer(); String ret = xmlSerializer.write(json); - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Transformed JSON -> XML"); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Transformed JSON -> XML"); return ret; } catch (JSONException exc) { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Data is not in native JSON format."); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Data is not in native JSON format.", exc); } // Force XML format @@ -276,74 +251,69 @@ private String toXML(String data) { XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat()); String ret = outputter.outputString(element); - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Transformed plaintext -> XML"); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Transformed plaintext -> XML"); return ret; } @Override public Response getRaw(SecurityContext sc, HttpHeaders headers, long id, String decodeFormat) - throws NotFoundWebEx - { - if(id == -1) - return Response.ok().entity("dummy payload").build(); + throws NotFoundWebEx { + if (id == -1) return Response.ok().entity("dummy payload").build(); StoredData storedData; try { storedData = storedDataService.get(id); - } catch(NotFoundServiceEx e){ + } catch (NotFoundServiceEx e) { return Response.status(Response.Status.NOT_FOUND).build(); } - if(storedData == null) { + if (storedData == null) { return Response.noContent().build(); } String data = storedData.getData(); // prefer no transformation - if( decodeFormat == null) { + if (decodeFormat == null) { return Response.ok().entity(data).build(); - } - else if(decodeFormat.equalsIgnoreCase(RawFormat.BASE64.name())) { + } else if (decodeFormat.equalsIgnoreCase(RawFormat.BASE64.name())) { byte[] decoded = Base64.decodeBase64(data); return Response.ok().entity(decoded).build(); - } - else if(decodeFormat.equalsIgnoreCase(RawFormat.DATAURI.name())) { + } else if (decodeFormat.equalsIgnoreCase(RawFormat.DATAURI.name())) { return decodeDataURI(data); - } - else { - LOGGER.warn("Unknown decode format '"+decodeFormat+"'"); + } else { + LOGGER.warn("Unknown decode format '" + decodeFormat + "'"); return Response.ok().entity(data).build(); } } - private Response decodeDataURI(String data) - { - if(! data.startsWith("data:")) { + private Response decodeDataURI(String data) { + if (!data.startsWith("data:")) { return Response.status(Response.Status.BAD_REQUEST).entity("Not a data URI").build(); } String[] split = data.split(",", 2); - if(split.length < 2) { - return Response.status(Response.Status.BAD_REQUEST).entity("Bad data, comma is missing").build(); + if (split.length < 2) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("Bad data, comma is missing") + .build(); } DataURIDecoder dud = new DataURIDecoder(split[0]); - if(! dud.isValid()) { - LOGGER.warn("Could not parse data URI '"+split[0]+"'"); + if (!dud.isValid()) { + LOGGER.warn("Could not parse data URI '" + split[0] + "'"); return Response.status(Response.Status.BAD_REQUEST).entity("Bad data URI").build(); } - if(dud.getCharset() != null) { - LOGGER.warn("TODO: Charset '"+dud.getCharset()+"' should be handled."); + if (dud.getCharset() != null) { + LOGGER.warn("TODO: Charset '" + dud.getCharset() + "' should be handled."); } - if(dud.getEncoding() != null && ! dud.isBase64Encoded()) { - LOGGER.warn("TODO: Encoding '"+dud.getEncoding()+"' should be handled."); + if (dud.getEncoding() != null && !dud.isBase64Encoded()) { + LOGGER.warn("TODO: Encoding '" + dud.getEncoding() + "' should be handled."); } Object entity = dud.isBase64Encoded() ? Base64.decodeBase64(split[1]) : split[1]; diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTUserGroupServiceImpl.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTUserGroupServiceImpl.java index de431c52..6bc2216c 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTUserGroupServiceImpl.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTUserGroupServiceImpl.java @@ -21,6 +21,7 @@ import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.UserGroupAttribute; import it.geosolutions.geostore.core.model.enums.GroupReservedNames; import it.geosolutions.geostore.services.UserGroupService; import it.geosolutions.geostore.services.UserService; @@ -33,31 +34,23 @@ import it.geosolutions.geostore.services.rest.model.RESTUserGroup; import it.geosolutions.geostore.services.rest.model.ShortResourceList; import it.geosolutions.geostore.services.rest.model.UserGroupList; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; - +import it.geosolutions.geostore.services.rest.model.UserList; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.ws.rs.core.SecurityContext; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -import org.apache.log4j.Logger; - -/** - * @author DamianoG - * - */ +/** @author DamianoG */ public class RESTUserGroupServiceImpl implements RESTUserGroupService { - private final static Logger LOGGER = Logger.getLogger(RESTUserGroupServiceImpl.class); + private static final Logger LOGGER = LogManager.getLogger(RESTUserGroupServiceImpl.class); private UserGroupService userGroupService; private UserService userService; - /** - * - * @param userGroupService - */ + /** @param userGroupService */ public void setUserGroupService(UserGroupService userGroupService) { this.userGroupService = userGroupService; } @@ -66,29 +59,41 @@ public void setUserService(UserService userService) { this.userService = userService; } - /* + /* * (non-Javadoc) @see it.geosolutions.geostore.services.rest.RESTUserGroupService#insert(javax.ws.rs.core.SecurityContext, it.geosolutions.geostore.core.model.UserGroup) */ @Override - public long insert(SecurityContext sc, UserGroup userGroup){ + public long insert(SecurityContext sc, RESTUserGroup userGroup) { if (userGroup == null) { throw new BadRequestWebEx("User is null"); } if (userGroup.getId() != null) { throw new BadRequestWebEx("Id should be null"); } - + long id = -1; try { - id = userGroupService.insert(userGroup); - } catch (BadRequestServiceEx e) { + UserGroup group = new UserGroup(); + group.setGroupName(userGroup.getGroupName()); + group.setDescription(userGroup.getDescription()); + group.setEnabled(true); + List ugAttrs = userGroup.getAttributes(); + // persist the user first + if (ugAttrs != null) { + userGroup.setAttributes(null); + } + id = userGroupService.insert(group); + // insert attributes after user creation + if (ugAttrs != null) { + userGroupService.updateAttributes(id, ugAttrs); + } + } catch (BadRequestServiceEx | NotFoundServiceEx e) { throw new BadRequestWebEx(e.getMessage()); } return id; - } - /* + /* * (non-Javadoc) @see it.geosolutions.geostore.services.rest.RESTUserGroupService#delete(javax.ws.rs.core.SecurityContext, long) */ @Override @@ -105,28 +110,34 @@ public void delete(SecurityContext sc, long id) throws NotFoundWebEx { } } - /* + /* * (non-Javadoc) @see it.geosolutions.geostore.services.rest.RESTUserGroupService#get(javax.ws.rs.core.SecurityContext, long) */ @Override - public RESTUserGroup get(SecurityContext sc, long id) + public RESTUserGroup get(SecurityContext sc, long id, boolean includeAttributes) throws NotFoundWebEx { try { UserGroup g = userGroupService.get(id); Collection users = userService.getByGroup(g); - - return new RESTUserGroup(g.getId(), g.getGroupName(), new HashSet<>(users), g.getDescription()); + RESTUserGroup group = + new RESTUserGroup( + g.getId(), g.getGroupName(), new HashSet<>(users), g.getDescription()); + if (includeAttributes) group.setAttributes(g.getAttributes()); + return group; } catch (BadRequestServiceEx e) { throw new BadRequestWebEx("UserGroup Not found"); } } - /* + + /* * (non-Javadoc) @see it.geosolutions.geostore.services.rest.RESTUserGroupService#assignUserGroup(javax.ws.rs.core.SecurityContext, long, long) */ @Override - public void assignUserGroup(SecurityContext sc, long userId, long groupId) throws NotFoundWebEx { + public void assignUserGroup(SecurityContext sc, long userId, long groupId) + throws NotFoundWebEx { if (userId < 0 || groupId < 0) { - throw new BadRequestWebEx("The user group or user id you provide is < 0... not good..."); + throw new BadRequestWebEx( + "The user group or user id you provide is < 0... not good..."); } try { userGroupService.assignUserGroup(userId, groupId); @@ -134,11 +145,13 @@ public void assignUserGroup(SecurityContext sc, long userId, long groupId) throw throw new NotFoundWebEx(e.getMessage()); } } - + @Override - public void deassignUserGroup(SecurityContext sc, long userId, long groupId) throws NotFoundWebEx { + public void deassignUserGroup(SecurityContext sc, long userId, long groupId) + throws NotFoundWebEx { if (userId < 0 || groupId < 0) { - throw new BadRequestWebEx("The user group or user id you provide is < 0... not good..."); + throw new BadRequestWebEx( + "The user group or user id you provide is < 0... not good..."); } try { userGroupService.deassignUserGroup(userId, groupId); @@ -151,15 +164,22 @@ public void deassignUserGroup(SecurityContext sc, long userId, long groupId) thr * @see it.geosolutions.geostore.services.rest.RESTUserGroupService#getAll(javax.ws.rs.core.SecurityContext, java.lang.Integer, java.lang.Integer) */ @Override - public UserGroupList getAll(SecurityContext sc, Integer page, Integer entries, boolean all, boolean includeUsers) + public UserGroupList getAll( + SecurityContext sc, Integer page, Integer entries, boolean all, boolean includeUsers) throws BadRequestWebEx { try { List returnList = userGroupService.getAll(page, entries); List ugl = new ArrayList<>(returnList.size()); - for(UserGroup ug : returnList){ - if(all || GroupReservedNames.isAllowedName(ug.getGroupName())) { - Collection users = includeUsers ? userService.getByGroup(ug) : new HashSet(); - RESTUserGroup rug = new RESTUserGroup(ug.getId(), ug.getGroupName(), new HashSet<>(users), ug.getDescription()); + for (UserGroup ug : returnList) { + if (all || GroupReservedNames.isAllowedName(ug.getGroupName())) { + Collection users = + includeUsers ? userService.getByGroup(ug) : new HashSet(); + RESTUserGroup rug = + new RESTUserGroup( + ug.getId(), + ug.getGroupName(), + new HashSet<>(users), + ug.getDescription()); ugl.add(rug); } } @@ -174,20 +194,26 @@ public UserGroupList getAll(SecurityContext sc, Integer page, Integer entries, b * @see it.geosolutions.geostore.services.rest.RESTUserGroupService#updateSecurityRules(it.geosolutions.geostore.core.model.UserGroup, java.util.List, boolean, boolean) */ @Override - public ShortResourceList updateSecurityRules(SecurityContext sc, ShortResourceList resourcesToSet, Long groupId, - Boolean canRead, Boolean canWrite) throws BadRequestWebEx, NotFoundWebEx { + public ShortResourceList updateSecurityRules( + SecurityContext sc, + ShortResourceList resourcesToSet, + Long groupId, + Boolean canRead, + Boolean canWrite) + throws BadRequestWebEx, NotFoundWebEx { List srll = new ArrayList(); - if(groupId == null || groupId < 0){ + if (groupId == null || groupId < 0) { throw new BadRequestWebEx("The groupId is null or less than 0..."); } - if(resourcesToSet == null || resourcesToSet.isEmpty()){ + if (resourcesToSet == null || resourcesToSet.isEmpty()) { throw new BadRequestWebEx("The resources set provided is null or empty..."); } List sl = resourcesToSet.getList(); List slOnlyIds = new ArrayList(); - for(ShortResource sr : sl){ - if(sr.getId() < 0){ - throw new BadRequestWebEx("One or more ids in resource set is less than 0... check the resources list."); + for (ShortResource sr : sl) { + if (sr.getId() < 0) { + throw new BadRequestWebEx( + "One or more ids in resource set is less than 0... check the resources list."); } slOnlyIds.add(sr.getId()); } @@ -205,13 +231,107 @@ public ShortResourceList updateSecurityRules(SecurityContext sc, ShortResourceLi } @Override - public RESTUserGroup get(SecurityContext sc, String name) + public long update(SecurityContext sc, long id, RESTUserGroup newGroup) throws NotFoundWebEx { + try { + + UserGroup old = userGroupService.get(id); + if (old == null) { + throw new NotFoundWebEx("UserGroup not found"); + } + old = updateGroupObject(newGroup, old); + updateAttributes(newGroup, old); + old.setAttributes(null); + id = userGroupService.update(old); + return id; + + } catch (NotFoundServiceEx e) { + throw new NotFoundWebEx(e.getMessage()); + } catch (BadRequestServiceEx e) { + throw new BadRequestWebEx(e.getMessage()); + } + } + + private UserGroup updateGroupObject(RESTUserGroup newGroup, UserGroup old) { + String name = newGroup.getGroupName(); + if (name != null && !name.trim().isEmpty()) old.setGroupName(name); + + String description = newGroup.getDescription(); + if (description != null && !description.trim().isEmpty()) old.setDescription(description); + + UserList users = newGroup.getRestUsers(); + if (users != null && users.getList() != null && !users.getList().isEmpty()) { + old.setUsers( + users.getList().stream() + .map( + u -> { + User user = new User(); + user.setId(u.getId()); + return user; + }) + .collect(Collectors.toList())); + } + return old; + } + + private void updateAttributes(RESTUserGroup newGroup, UserGroup oldGroup) + throws NotFoundServiceEx { + List attributes = newGroup.getAttributes(); + List newList = Collections.emptyList(); + if (attributes != null && !attributes.isEmpty()) { + newList = new ArrayList<>(attributes.size()); + for (UserGroupAttribute attr : attributes) { + UserGroupAttribute attribute = new UserGroupAttribute(); + attribute.setName(attr.getName()); + attribute.setValue(attr.getValue()); + newList.add(attribute); + } + } + userGroupService.updateAttributes(oldGroup.getId(), newList); + } + + @Override + public RESTUserGroup get(SecurityContext sc, String name, boolean includeAttributes) throws NotFoundWebEx { - UserGroup ug = userGroupService.get(name); + UserGroup ug; + if (name != null && name.equalsIgnoreCase(GroupReservedNames.EVERYONE.groupName())) + ug = userGroupService.get(null); + else ug = userGroupService.get(name); + RESTUserGroup result = null; if (ug != null) { Collection users = userService.getByGroup(ug); - return new RESTUserGroup(ug.getId(), ug.getGroupName(), new HashSet(users), ug.getDescription()); + result = + new RESTUserGroup( + ug.getId(), ug.getGroupName(), new HashSet(users), ug.getDescription()); + if (includeAttributes) result.setAttributes(ug.getAttributes()); + } + return result; + } + + @Override + public UserGroupList getByAttribute( + SecurityContext sc, String name, String value, boolean ignoreCase) { + return getGroups(name, Collections.singletonList(value), ignoreCase); + } + + @Override + public UserGroupList getByAttribute( + SecurityContext sc, String name, List values, boolean ignoreCase) { + return getGroups(name, values, ignoreCase); + } + + private UserGroupList getGroups(String name, List values, boolean ignoreCase) { + Collection groups = userGroupService.findByAttribute(name, values, ignoreCase); + UserGroupList groupList; + if (groups != null && !groups.isEmpty()) { + Stream groupStream = groups.stream(); + List restGroups = + groupStream + .map(g -> new RESTUserGroup(g, Collections.emptySet())) + .collect(Collectors.toList()); + groupList = new UserGroupList(restGroups); + } else { + groupList = new UserGroupList(); } - return null; + return groupList; } } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTUserServiceImpl.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTUserServiceImpl.java index cfb9554b..a74e6427 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTUserServiceImpl.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTUserServiceImpl.java @@ -4,7 +4,7 @@ * http://www.geo-solutions.it * * GPLv3 + Classpath exception - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. + * along with this program. * * ==================================================================== * @@ -27,16 +27,6 @@ */ package it.geosolutions.geostore.services.rest.impl; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import javax.ws.rs.core.SecurityContext; - -import org.apache.commons.lang.NotImplementedException; -import org.apache.log4j.Logger; - import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserAttribute; import it.geosolutions.geostore.core.model.UserGroup; @@ -51,23 +41,28 @@ import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; import it.geosolutions.geostore.services.rest.model.RESTUser; import it.geosolutions.geostore.services.rest.model.UserList; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import javax.ws.rs.core.SecurityContext; +import org.apache.commons.lang.NotImplementedException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * Class RESTUserServiceImpl. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author Emanuele Tajariol (etj at geo-solutions.it) - * */ public class RESTUserServiceImpl extends RESTServiceImpl implements RESTUserService { - private final static Logger LOGGER = Logger.getLogger(RESTUserServiceImpl.class); + private static final Logger LOGGER = LogManager.getLogger(RESTUserServiceImpl.class); private UserService userService; - /** - * @param userService the userService to set - */ + /** @param userService the userService to set */ public void setUserService(UserService userService) { this.userService = userService; } @@ -90,17 +85,16 @@ public long insert(SecurityContext sc, User user) { // Parsing UserAttributes list // List usAttribute = user.getAttribute(); - //persist the user first + // persist the user first if (usAttribute != null) { - user.setAttribute(null); + user.setAttribute(null); } id = userService.insert(user); - //insert attributes after user creation + // insert attributes after user creation if (usAttribute != null) { - userService.updateAttributes(id, usAttribute); + userService.updateAttributes(id, usAttribute); } - - + } catch (NotFoundServiceEx e) { throw new NotFoundWebEx(e.getMessage()); } catch (BadRequestServiceEx e) { @@ -138,9 +132,9 @@ public long update(SecurityContext sc, long id, User user) { old.setRole(nr); userUpdated = true; } - if(old.isEnabled() != user.isEnabled()){ - old.setEnabled(user.isEnabled()); - userUpdated = true; + if (old.isEnabled() != user.isEnabled()) { + old.setEnabled(user.isEnabled()); + userUpdated = true; } Set groups = user.getGroups(); if (groups != null) { @@ -179,8 +173,8 @@ public long update(SecurityContext sc, long id, User user) { } } if (userUpdated) { - //attributes where updated before - old.setAttribute(null); + // attributes where updated before + old.setAttribute(null); id = userService.update(old); return id; } else { @@ -231,7 +225,8 @@ public User get(SecurityContext sc, long id, boolean includeAttributes) throws N User ret = new User(); ret.setId(authUser.getId()); ret.setName(authUser.getName()); - // ret.setPassword(authUser.getPassword()); // NO! password should not be sent out of the server! + // ret.setPassword(authUser.getPassword()); // NO! password should not be sent out of the + // server! ret.setRole(authUser.getRole()); ret.setEnabled(authUser.isEnabled()); ret.setGroups(removeReservedGroups(authUser.getGroups())); @@ -284,7 +279,13 @@ public UserList getAll(SecurityContext sc, Integer page, Integer entries) while (iterator.hasNext()) { User user = iterator.next(); - RESTUser restUser = new RESTUser(user.getId(), user.getName(), user.getRole(), user.getGroups(), false); + RESTUser restUser = + new RESTUser( + user.getId(), + user.getName(), + user.getRole(), + user.getGroups(), + false); restUSERList.add(restUser); } @@ -305,7 +306,7 @@ public long getCount(SecurityContext sc, String nameLike) { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.rest.RESTUserService#getAuthUserDetails (javax.ws.rs.core.SecurityContext) */ @Override @@ -319,13 +320,14 @@ public User getAuthUserDetails(SecurityContext sc, boolean includeAttributes) { } if (authUser != null) { - if(authUser.getRole().equals(Role.GUEST)){ - throw new NotFoundWebEx("User not found"); - } + if (authUser.getRole().equals(Role.GUEST)) { + throw new NotFoundWebEx("User not found"); + } ret = new User(); ret.setId(authUser.getId()); ret.setName(authUser.getName()); - // ret.setPassword(authUser.getPassword()); // NO! password should not be sent out of the server! + // ret.setPassword(authUser.getPassword()); // NO! password should not be sent out + // of the server! ret.setRole(authUser.getRole()); ret.setGroups(authUser.getGroups()); if (includeAttributes) { @@ -341,8 +343,13 @@ public User getAuthUserDetails(SecurityContext sc, boolean includeAttributes) { } @Override - public UserList getUserList(SecurityContext sc, String nameLike, Integer page, Integer entries, - boolean includeAttributes) throws BadRequestWebEx { + public UserList getUserList( + SecurityContext sc, + String nameLike, + Integer page, + Integer entries, + boolean includeAttributes) + throws BadRequestWebEx { nameLike = nameLike.replaceAll("[*]", "%"); @@ -354,7 +361,13 @@ public UserList getUserList(SecurityContext sc, String nameLike, Integer page, I while (iterator.hasNext()) { User user = iterator.next(); - RESTUser restUser = new RESTUser(user.getId(), user.getName(), user.getRole(), user.getGroups(), false); + RESTUser restUser = + new RESTUser( + user.getId(), + user.getName(), + user.getRole(), + user.getGroups(), + false); restUSERList.add(restUser); } @@ -366,23 +379,23 @@ public UserList getUserList(SecurityContext sc, String nameLike, Integer page, I /** * Utility method to remove Reserved group (for example EVERYONE) from a group list - * + * * @param groups * @return */ - private Set removeReservedGroups(Set groups){ + private Set removeReservedGroups(Set groups) { List reserved = new ArrayList(); - for(UserGroup ug : groups){ - if(!GroupReservedNames.isAllowedName(ug.getGroupName())){ + for (UserGroup ug : groups) { + if (!GroupReservedNames.isAllowedName(ug.getGroupName())) { reserved.add(ug); } } - for(UserGroup ug : reserved){ + for (UserGroup ug : reserved) { groups.remove(ug); } return groups; } - + /* (non-Javadoc) * @see it.geosolutions.geostore.services.rest.impl.RESTServiceImpl#getSecurityService() */ diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/SessionServiceDelegateImpl.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/SessionServiceDelegateImpl.java new file mode 100644 index 00000000..471a5de4 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/SessionServiceDelegateImpl.java @@ -0,0 +1,71 @@ +package it.geosolutions.geostore.services.rest.impl; + +import static it.geosolutions.geostore.services.rest.impl.RESTSessionServiceImpl.BEARER_TYPE; + +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.services.UserSessionService; +import it.geosolutions.geostore.services.dto.UserSession; +import it.geosolutions.geostore.services.rest.RESTSessionService; +import it.geosolutions.geostore.services.rest.SessionServiceDelegate; +import it.geosolutions.geostore.services.rest.exception.ForbiddenErrorWebEx; +import it.geosolutions.geostore.services.rest.model.SessionToken; +import org.springframework.beans.factory.annotation.Autowired; + +public class SessionServiceDelegateImpl implements SessionServiceDelegate { + + public static final String DEFAULT_NAME = "DEFAULT"; + @Autowired private UserSessionService userSessionService; + + public SessionServiceDelegateImpl(RESTSessionService restSessionService) { + restSessionService.registerDelegate(DEFAULT_NAME, this); + } + + public SessionServiceDelegateImpl() {} + + @Override + public SessionToken refresh(String refreshToken, String accessToken) { + UserSession sessionToken = userSessionService.refreshSession(accessToken, refreshToken); + if (sessionToken == null) { + throw new ForbiddenErrorWebEx( + "Refresh token was not provided or session is already expired."); + } + SessionToken token = new SessionToken(); + token.setAccessToken(accessToken); + token.setRefreshToken(sessionToken.getRefreshToken()); + token.setExpires(sessionToken.getExpirationInterval()); + token.setTokenType(BEARER_TYPE); + return token; + } + + @Override + public void doLogout(String sessionId) { + userSessionService.removeSession(sessionId); + } + + /** + * Set the user session service. + * + * @param userSessionService the user session service. + */ + public void setUserSessionService(UserSessionService userSessionService) { + this.userSessionService = userSessionService; + } + + public User getUser(String sessionId, boolean refresh, boolean autorefresh) { + User details = null; + if (userSessionService != null) { + details = userSessionService.getUserData(sessionId); + if (details != null && refresh && autorefresh) { + userSessionService.refreshSession( + sessionId, userSessionService.getRefreshToken(sessionId)); + } + } + return details; + } + + public String getUserName(String sessionId, boolean refresh, boolean autorefresh) { + User userData = getUser(sessionId, refresh, autorefresh); + if (userData != null) return userData.getName(); + return null; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GeoStoreAuthenticationFilter.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GeoStoreAuthenticationFilter.java index c88ddb00..3b9a5ebd 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GeoStoreAuthenticationFilter.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GeoStoreAuthenticationFilter.java @@ -33,80 +33,72 @@ import it.geosolutions.geostore.services.UserService; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; - import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; - import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.GrantedAuthorityImpl; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.web.filter.GenericFilterBean; /** - * Base class for GeoStore authentication filters (based on - * Spring Security filters). - * Includes basic functionalities for authentication based on - * external services, like: - * - automatic user creation / enabling - * - mapping of attributes on user creation - * - * @author Mauro Bartolomeoli + * Base class for GeoStore authentication filters (based on Spring Security filters). Includes basic + * functionalities for authentication based on external services, like: - automatic user creation / + * enabling - mapping of attributes on user creation * + * @author Mauro Bartolomeoli */ public abstract class GeoStoreAuthenticationFilter extends GenericFilterBean { - private final static Logger LOGGER = Logger.getLogger(GeoStoreAuthenticationFilter.class); public static final String USER_NOT_FOUND_MSG = "User not found. Please check your credentials"; - - @Autowired - protected UserService userService; - + private static final Logger LOGGER = LogManager.getLogger(GeoStoreAuthenticationFilter.class); + @Autowired protected UserService userService; + private boolean autoCreateUser = false; private boolean enableAutoCreatedUsers = true; - + private UserMapper userMapper; - + @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { - if(req instanceof HttpServletRequest) { + if (req instanceof HttpServletRequest) { authenticate((HttpServletRequest) req); } chain.doFilter(req, resp); } - protected abstract void authenticate(HttpServletRequest req); /** - * Helper method that creates an Authentication object for the given - * userName and raw (service retrieved) user details object. - * - * If autoCreateUser is true, creates unexisting users, before returning the - * authentication object. - * + * Helper method that creates an Authentication object for the given userName and raw (service + * retrieved) user details object. + * + *

    If autoCreateUser is true, creates unexisting users, before returning the authentication + * object. + * * @param userName * @param rawUser * @return */ - protected Authentication createAuthenticationForUser(String userName, String credentials, Object rawUser) { + protected Authentication createAuthenticationForUser( + String userName, String credentials, Object rawUser) { User user = null; try { user = userService.get(userName); } catch (NotFoundServiceEx e) { - if(autoCreateUser) { + if (autoCreateUser) { try { user = createUser(userName, credentials, rawUser); } catch (BadRequestServiceEx e1) { @@ -117,27 +109,26 @@ protected Authentication createAuthenticationForUser(String userName, String cre } else { LOGGER.error("User not found: " + userName, e); } - } - + return createAuthenticationForUser(user); } - + /** - * Creates a new user with the given - * userName and raw (service retrieved) user details object. - * - * It uses the configured UserMapper to populate user attributes. - * - * The user is assigned the USER role and no groups. - * + * Creates a new user with the given userName and raw (service retrieved) user details object. + * + *

    It uses the configured UserMapper to populate user attributes. + * + *

    The user is assigned the USER role and no groups. + * * @param userName * @param rawUser * @return * @throws BadRequestServiceEx * @throws NotFoundServiceEx */ - protected User createUser(String userName, String credentials, Object rawUser) throws BadRequestServiceEx, NotFoundServiceEx { + protected User createUser(String userName, String credentials, Object rawUser) + throws BadRequestServiceEx, NotFoundServiceEx { User user = new User(); user.setName(userName); @@ -147,7 +138,7 @@ protected User createUser(String userName, String credentials, Object rawUser) t Role role = Role.USER; user.setRole(role); user.setGroups(Collections.EMPTY_SET); - if(userMapper != null) { + if (userMapper != null) { userMapper.mapUser(rawUser, user); } if (userService != null) { @@ -155,11 +146,11 @@ protected User createUser(String userName, String credentials, Object rawUser) t } return user; } - + /** - * Helper method that creates an Authentication object for the given user, - * populating GrantedAuthority instances. - * + * Helper method that creates an Authentication object for the given user, populating + * GrantedAuthority instances. + * * @param user * @return */ @@ -168,14 +159,14 @@ protected Authentication createAuthenticationForUser(User user) { String role = user.getRole().toString(); List authorities = new ArrayList(); - authorities.add(new GrantedAuthorityImpl("ROLE_" + role)); + authorities.add(new SimpleGrantedAuthority("ROLE_" + role)); return new UsernamePasswordAuthenticationToken(user, user.getPassword(), authorities); } else { LOGGER.error(USER_NOT_FOUND_MSG); return null; } } - + public void setUserService(UserService userService) { this.userService = userService; } @@ -183,7 +174,7 @@ public void setUserService(UserService userService) { public void setUserMapper(UserMapper userMapper) { this.userMapper = userMapper; } - + public void setAutoCreateUser(boolean autoCreateUser) { this.autoCreateUser = autoCreateUser; } @@ -191,5 +182,4 @@ public void setAutoCreateUser(boolean autoCreateUser) { public void setEnableAutoCreatedUsers(boolean enableAutoCreatedUsers) { this.enableAutoCreatedUsers = enableAutoCreatedUsers; } - } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GeoStoreLdapAuthoritiesPopulator.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GeoStoreLdapAuthoritiesPopulator.java index 17ea870e..ca14f1da 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GeoStoreLdapAuthoritiesPopulator.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GeoStoreLdapAuthoritiesPopulator.java @@ -27,115 +27,71 @@ */ package it.geosolutions.geostore.services.rest.security; +import it.geosolutions.geostore.core.security.GrantedAuthoritiesMapper; import java.text.MessageFormat; import java.util.HashSet; import java.util.List; import java.util.Set; - import javax.naming.directory.SearchControls; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.ldap.core.ContextSource; import org.springframework.ldap.core.DirContextOperations; import org.springframework.ldap.core.support.AbstractContextMapper; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.GrantedAuthorityImpl; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.ldap.SpringSecurityLdapTemplate; import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator; import org.springframework.util.Assert; -import it.geosolutions.geostore.core.security.GrantedAuthoritiesMapper; - -/** - * @author alessio.fabiani - * - */ -public class GeoStoreLdapAuthoritiesPopulator extends - DefaultLdapAuthoritiesPopulator implements GroupsRolesService { - - private static class Authority { - private String name; - - private String dn; - - public String getName() { - return name; - } - - public String getDn() { - return dn; - } - - public Authority(String name, String dn) { - super(); - this.name = name; - this.dn = dn; - } - - } +/** @author alessio.fabiani */ +@SuppressWarnings("PMD.UnusedPrivateField") +public class GeoStoreLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopulator + implements GroupsRolesService { private static final Log logger = LogFactory.getLog(GeoStoreLdapAuthoritiesPopulator.class); - - /** - * Template that will be used for searching - */ + /** Template that will be used for searching */ private final SpringSecurityLdapTemplate ldapTemplate; - /** - * Controls used to determine whether group searches should be performed over the full sub-tree from the - * base DN. Modified by searchSubTree property + * Controls used to determine whether group searches should be performed over the full sub-tree + * from the base DN. Modified by searchSubTree property */ private final SearchControls searchControls = new SearchControls(); + /** The base DN from which the search for group membership should be performed */ + private final String groupSearchBase; - /** - * The ID of the attribute which contains the role name for a group - */ + private final String roleSearchBase; + private final String allGroupsSearchFilter = "(objectClass=group)"; + private final String allRolesSearchFilter = "(objectClass=group)"; + /** The ID of the attribute which contains the role name for a group */ private String groupRoleAttribute = "cn"; - - /** - * The base DN from which the search for group membership should be performed - */ - private String groupSearchBase; - private String roleSearchBase; - - /** - * The pattern to be used for the user search. {0} is the user's DN - */ + /** The pattern to be used for the user search. {0} is the user's DN */ private String groupSearchFilter = "(member={0})"; + private String roleSearchFilter = "(member={0})"; - - private String allGroupsSearchFilter = "(objectClass=group)"; - private String allRolesSearchFilter = "(objectClass=group)"; - /** - * The role prefix that will be prepended to each role name - */ + /** The role prefix that will be prepended to each role name */ private String rolePrefix = "ROLE_"; private boolean searchSubtree = false; - private boolean enableHierarchicalGroups = false; - private String groupInGroupSearchFilter = "(member={0})"; - private int maxLevelGroupsSearch = Integer.MAX_VALUE; - /** - * Should we convert the role name to uppercase - */ + /** Should we convert the role name to uppercase */ private boolean convertToUpperCase = true; - - private GrantedAuthoritiesMapper authoritiesMapper = null; + + private GrantedAuthoritiesMapper roleMapper = null; + private GrantedAuthoritiesMapper groupMapper = null; /** * @param contextSource * @param groupSearchBase */ - public GeoStoreLdapAuthoritiesPopulator(ContextSource contextSource, String groupSearchBase, - String roleSearchBase) { + public GeoStoreLdapAuthoritiesPopulator( + ContextSource contextSource, String groupSearchBase, String roleSearchBase) { super(contextSource, groupSearchBase); Assert.notNull(contextSource, "contextSource must not be null"); - + ldapTemplate = new SpringSecurityLdapTemplate(contextSource); ldapTemplate.setSearchControls(searchControls); @@ -144,7 +100,8 @@ public GeoStoreLdapAuthoritiesPopulator(ContextSource contextSource, String grou if (groupSearchBase == null) { logger.info("groupSearchBase is null. No group search will be performed."); } else if (groupSearchBase.length() == 0) { - logger.info("groupSearchBase is empty. Searches will be performed from the context source base"); + logger.info( + "groupSearchBase is empty. Searches will be performed from the context source base"); } this.roleSearchBase = roleSearchBase; @@ -152,164 +109,269 @@ public GeoStoreLdapAuthoritiesPopulator(ContextSource contextSource, String grou if (roleSearchBase == null) { logger.info("roleSearchBase is null. No group search will be performed."); } else if (roleSearchBase.length() == 0) { - logger.info("roleSearchBase is empty. Searches will be performed from the context source base"); + logger.info( + "roleSearchBase is empty. Searches will be performed from the context source base"); } } + @Deprecated public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) { - this.authoritiesMapper = authoritiesMapper; + logger.error( + "AuthoritiesMapper is deprecated, please set roleMapper and groupMapper separately"); + this.roleMapper = authoritiesMapper; + this.groupMapper = authoritiesMapper; } + public void setRoleMapper(GrantedAuthoritiesMapper roleMapper) { + this.roleMapper = roleMapper; + } + public void setGroupMapper(GrantedAuthoritiesMapper groupMapper) { + this.groupMapper = groupMapper; + } @Override public Set getGroupMembershipRoles(String userDn, String username) { - return getGroupsOrRoles(userDn, username, true, true); + // TODO: double check if we really want to return groups+roles + Set ret = new HashSet<>(); + ret.addAll(getGroups(userDn, username)); + ret.addAll(getRoles(userDn, username)); + return ret; } - private Set getGroupsOrRoles(String userDn, String username, boolean groups, boolean roles) { - if (roleSearchBase == null && groupSearchBase == null) { - return new HashSet(); + private Set getRoles(String userDn, String username) { + if (roleSearchBase == null) { + return new HashSet<>(); } - Set authorities = new HashSet(); - String[] searchParams = username == null ? new String[] {} : new String[] {userDn, username}; - if(roles) { - // Searching for ROLES - if (logger.isDebugEnabled()) { - logger.debug("Searching for roles for user '" + username + "', DN = " + "'" + userDn + "', with filter " - + roleSearchFilter + " in search base '" + roleSearchBase + "'"); - } - - String[] rolesRoots = roleSearchBase.split(";"); - String filter = username == null ? allRolesSearchFilter : roleSearchFilter; - - for(String rolesRoot : rolesRoots) { - addAuthorities(searchParams, authorities, rolesRoot, filter, rolePrefix, false); - } + Set authorities = new HashSet<>(); + + String[] searchParams = + username == null ? new String[] {} : new String[] {userDn, username}; + + // Searching for ROLES + if (logger.isDebugEnabled()) { + logger.debug( + "Searching for roles for user '" + + username + + "', DN = " + + "'" + + userDn + + "', with filter " + + roleSearchFilter + + " in search base '" + + roleSearchBase + + "'"); } - - if(groups) { - // Searching for Groups - if (logger.isDebugEnabled()) { - logger.debug("Searching for groups for user '" + username + "', DN = " + "'" + userDn + "', with filter " - + groupSearchFilter + " in search base '" + groupSearchBase + "'"); - } - String[] groupsRoots = groupSearchBase.split(";"); - String filter = username == null ? allGroupsSearchFilter : groupSearchFilter; - for(String groupsRoot : groupsRoots) { - addAuthorities(searchParams, authorities, groupsRoot, filter, null, enableHierarchicalGroups); - } + + String[] rolesRoots = roleSearchBase.split(";"); + String filter = username == null ? allRolesSearchFilter : roleSearchFilter; + + for (String rolesRoot : rolesRoots) { + addAuthorities(searchParams, authorities, rolesRoot, filter, rolePrefix, false); + } + + if (roleMapper != null) { + authorities = new HashSet<>(roleMapper.mapAuthorities(authorities)); + } + return authorities; + } + + private Set getGroups(String userDn, String username) { + if (groupSearchBase == null) { + return new HashSet<>(); + } + + Set authorities = new HashSet<>(); + String[] searchParams = + username == null ? new String[] {} : new String[] {userDn, username}; + + // Searching for Groups + if (logger.isDebugEnabled()) { + logger.debug( + "Searching for groups for user '" + + username + + "', DN = " + + "'" + + userDn + + "', with filter " + + groupSearchFilter + + " in search base '" + + groupSearchBase + + "'"); } - - if(authoritiesMapper != null) { - return new HashSet(authoritiesMapper.mapAuthorities(authorities)); + String[] groupsRoots = groupSearchBase.split(";"); + String filter = username == null ? allGroupsSearchFilter : groupSearchFilter; + for (String groupsRoot : groupsRoots) { + addAuthorities( + searchParams, authorities, groupsRoot, filter, null, enableHierarchicalGroups); + } + + if (groupMapper != null) { + authorities = new HashSet<>(groupMapper.mapAuthorities(authorities)); } return authorities; } public Set getAllGroups() { - return getGroupsOrRoles(null, null, true, false); + return getGroups(null, null); } - + public Set getAllRoles() { - return getGroupsOrRoles(null, null, false, true); + return getRoles(null, null); } - - private void addAuthorities(String[] params, Set authorities, - String root, String filter, String authorityPrefix, boolean hierarchical) { + + private void addAuthorities( + String[] params, + Set authorities, + String root, + String filter, + String authorityPrefix, + boolean hierarchical) { addAuthorities(params, authorities, root, filter, authorityPrefix, hierarchical, 0); } - - private void addAuthorities(String[] params, Set authorities, - String root, String filter, String authorityPrefix, boolean hierarchical, int level) { + + private void addAuthorities( + String[] params, + Set authorities, + String root, + String filter, + String authorityPrefix, + boolean hierarchical, + int level) { String formattedFilter = MessageFormat.format(filter, params); - - List ldapAuthorities = ldapTemplate.search(root, formattedFilter, new AbstractContextMapper() { - @Override - protected Object doMapFromContext(DirContextOperations ctx) { - return new Authority(ctx.getStringAttribute(groupRoleAttribute), ctx.getNameInNamespace()); - } - }); + + List ldapAuthorities = + ldapTemplate.search( + root, + formattedFilter, + new AbstractContextMapper() { + @Override + protected Object doMapFromContext(DirContextOperations ctx) { + return new Authority( + ctx.getStringAttribute(groupRoleAttribute), + ctx.getNameInNamespace()); + } + }); if (logger.isDebugEnabled()) { - logger.debug("Authorities from search: " + ldapAuthorities); + logger.debug("Found " + ldapAuthorities.size() + " authorities from search"); } for (Object authority : ldapAuthorities) { - Authority ldapAuthority = (Authority)authority; - + Authority ldapAuthority = (Authority) authority; + boolean added = addAuthority(authorities, authorityPrefix, ldapAuthority.getName()); if (added && hierarchical && level < maxLevelGroupsSearch) { - String[] searchParams = new String[] {ldapAuthority.getDn(), ldapAuthority.getName()}; - addAuthorities(searchParams, authorities, root, groupInGroupSearchFilter, authorityPrefix, hierarchical, level + 1); + String[] searchParams = + new String[] {ldapAuthority.getDn(), ldapAuthority.getName()}; + addAuthorities( + searchParams, + authorities, + root, + groupInGroupSearchFilter, + authorityPrefix, + hierarchical, + level + 1); } } } - private boolean addAuthority(Set authorities, String authorityPrefix, - String authority) { + private boolean addAuthority( + Set authorities, String authorityPrefix, String authority) { + + if (logger.isDebugEnabled()) { + logger.debug("Adding authority: " + authorityPrefix + "::" + authority); + } + if (convertToUpperCase) { authority = authority.toUpperCase(); } - String prefix = (authorityPrefix != null && !authority.startsWith(authorityPrefix) ? authorityPrefix : ""); - - GrantedAuthorityImpl role = new GrantedAuthorityImpl(prefix + authority); + String prefix = + (authorityPrefix != null && !authority.startsWith(authorityPrefix) + ? authorityPrefix + : ""); + + String rolename = prefix + authority; + SimpleGrantedAuthority role = new SimpleGrantedAuthority(rolename); if (!authorities.contains(role)) { authorities.add(role); + if (logger.isDebugEnabled()) { + logger.debug("Authority added: " + rolename); + } return true; } + logger.debug("Authority not added: " + rolename); return false; } - @Override - public void setConvertToUpperCase(boolean convertToUpperCase) { - super.setConvertToUpperCase(convertToUpperCase); - this.convertToUpperCase = convertToUpperCase; - } - - @Override - public void setGroupRoleAttribute(String groupRoleAttribute) { - super.setGroupRoleAttribute(groupRoleAttribute); - this.groupRoleAttribute = groupRoleAttribute; - } - - @Override - public void setGroupSearchFilter(String groupSearchFilter) { - super.setGroupSearchFilter(groupSearchFilter); - this.groupSearchFilter = groupSearchFilter; - } - - public void setRoleSearchFilter(String roleSearchFilter) { - this.roleSearchFilter = roleSearchFilter; - } - - @Override - public void setRolePrefix(String rolePrefix) { - super.setRolePrefix(rolePrefix); - this.rolePrefix = rolePrefix; - } - - public void setSearchSubtree(boolean searchSubtree) { - if (searchSubtree) { - searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); + @Override + public void setConvertToUpperCase(boolean convertToUpperCase) { + super.setConvertToUpperCase(convertToUpperCase); + this.convertToUpperCase = convertToUpperCase; + } + + @Override + public void setGroupRoleAttribute(String groupRoleAttribute) { + super.setGroupRoleAttribute(groupRoleAttribute); + this.groupRoleAttribute = groupRoleAttribute; + } + + @Override + public void setGroupSearchFilter(String groupSearchFilter) { + super.setGroupSearchFilter(groupSearchFilter); + this.groupSearchFilter = groupSearchFilter; + } + + public void setRoleSearchFilter(String roleSearchFilter) { + this.roleSearchFilter = roleSearchFilter; + } + + @Override + public void setRolePrefix(String rolePrefix) { + super.setRolePrefix(rolePrefix); + this.rolePrefix = rolePrefix; + } + + public void setSearchSubtree(boolean searchSubtree) { + if (searchSubtree) { + searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); } else { - searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE); + searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE); } - - this.searchSubtree = searchSubtree; - } - - public void setEnableHierarchicalGroups(boolean enableHierarchicalGroups) { - this.enableHierarchicalGroups = enableHierarchicalGroups; - } - - public void setGroupInGroupSearchFilter(String groupInGroupSearchFilter) { - this.groupInGroupSearchFilter = groupInGroupSearchFilter; - } - - public void setMaxLevelGroupsSearch(int maxLevelGroupsSearch) { - this.maxLevelGroupsSearch = maxLevelGroupsSearch; - } + this.searchSubtree = searchSubtree; + } + + public void setEnableHierarchicalGroups(boolean enableHierarchicalGroups) { + this.enableHierarchicalGroups = enableHierarchicalGroups; + } + + public void setGroupInGroupSearchFilter(String groupInGroupSearchFilter) { + this.groupInGroupSearchFilter = groupInGroupSearchFilter; + } + + public void setMaxLevelGroupsSearch(int maxLevelGroupsSearch) { + this.maxLevelGroupsSearch = maxLevelGroupsSearch; + } + + private static class Authority { + private final String name; + + private final String dn; + + public Authority(String name, String dn) { + super(); + this.name = name; + this.dn = dn; + } + + public String getName() { + return name; + } + + public String getDn() { + return dn; + } + } } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GeoStoreRequestHeadersAuthenticationFilter.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GeoStoreRequestHeadersAuthenticationFilter.java index 25b74e99..6ca13f0f 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GeoStoreRequestHeadersAuthenticationFilter.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GeoStoreRequestHeadersAuthenticationFilter.java @@ -30,24 +30,18 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.Map; - import javax.servlet.http.HttpServletRequest; - import org.springframework.security.core.context.SecurityContextHolder; /** - * Authentication filter for preauthentication through - * request headers. - * - * An header for username and one for credentials/password (optional) - * are supported. - * - * Automatic new user creation is supported, and in the case of user creation, - * attributes mapping from headers is supported through a userMapper of type - * MapExpressionUserMapper. - * - * @author Mauro Bartolomeoli + * Authentication filter for preauthentication through request headers. + * + *

    An header for username and one for credentials/password (optional) are supported. + * + *

    Automatic new user creation is supported, and in the case of user creation, attributes mapping + * from headers is supported through a userMapper of type MapExpressionUserMapper. * + * @author Mauro Bartolomeoli */ public class GeoStoreRequestHeadersAuthenticationFilter extends GeoStoreAuthenticationFilter { private String userNameHeader; @@ -64,31 +58,31 @@ public void setCredentialsHeader(String credentialsHeader) { @Override protected void authenticate(HttpServletRequest req) { String userName = req.getHeader(userNameHeader); - if(userName != null) { + if (userName != null) { String credentials = null; - if(credentialsHeader != null) { + if (credentialsHeader != null) { credentials = req.getHeader(credentialsHeader); - if(credentials.trim().isEmpty()) { + if (credentials.trim().isEmpty()) { credentials = null; } } // create auth object with given user / credentials / attributes - SecurityContextHolder.getContext().setAuthentication( - createAuthenticationForUser(userName, credentials, getHeadersMap(req)) - ); + SecurityContextHolder.getContext() + .setAuthentication( + createAuthenticationForUser(userName, credentials, getHeadersMap(req))); } } /** * Transform headers into a map. - * + * * @param req * @return */ private Object getHeadersMap(HttpServletRequest req) { Map headers = new HashMap(); Enumeration headerNames = req.getHeaderNames(); - while(headerNames.hasMoreElements()) { + while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement().toString(); headers.put(cleanHeaderName(headerName), req.getHeader(headerName)); } @@ -99,7 +93,4 @@ private String cleanHeaderName(String headerName) { // create a good SpEL identifier return headerName.replaceAll("[^a-zA-Z0-9_$]", "_"); } - - - } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GroupsRolesService.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GroupsRolesService.java index 4276e3ec..12982490 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GroupsRolesService.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/GroupsRolesService.java @@ -28,29 +28,27 @@ package it.geosolutions.geostore.services.rest.security; import java.util.Set; - import org.springframework.security.core.GrantedAuthority; /** * Service to extract groups and/or roles list from an external service. - * - * @author mauro.bartolomeoli@geo-solutions.it * + * @author mauro.bartolomeoli@geo-solutions.it */ public interface GroupsRolesService { /** * Get all groups from the external service. - * + * * @return */ - public Set getAllGroups(); - + Set getAllGroups(); + /** * Get all roles from the external service. - * - * (currently not used, it will be useful when roles will not be - * fixed, finally). + * + *

    (currently not used, it will be useful when roles will not be fixed, finally). + * * @return */ - public Set getAllRoles(); + Set getAllRoles(); } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/HeadersAuthenticationFilter.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/HeadersAuthenticationFilter.java index 0f5b228c..e9b49325 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/HeadersAuthenticationFilter.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/HeadersAuthenticationFilter.java @@ -27,44 +27,41 @@ */ package it.geosolutions.geostore.services.rest.security; +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.enums.GroupReservedNames; +import it.geosolutions.geostore.core.model.enums.Role; +import it.geosolutions.geostore.core.security.GrantedAuthoritiesMapper; +import it.geosolutions.geostore.services.rest.utils.GroupMapper; import java.util.HashSet; import java.util.Set; - import javax.servlet.http.HttpServletRequest; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.GrantedAuthorityImpl; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; -import it.geosolutions.geostore.core.model.User; -import it.geosolutions.geostore.core.model.UserGroup; -import it.geosolutions.geostore.core.model.enums.GroupReservedNames; -import it.geosolutions.geostore.core.model.enums.Role; -import it.geosolutions.geostore.core.security.GrantedAuthoritiesMapper; -import it.geosolutions.geostore.services.rest.utils.GroupMapper; public class HeadersAuthenticationFilter extends GeoStoreAuthenticationFilter { public static final String DEFAULT_USERNAME_HEADER = "x-geostore-user"; public static final String DEFAULT_GROUPS_HEADER = "x-geostore-groups"; public static final String DEFAULT_ROLE_HEADER = "x-geostore-role"; - + private String usernameHeader = DEFAULT_USERNAME_HEADER; private String groupsHeader = DEFAULT_GROUPS_HEADER; private String roleHeader = DEFAULT_ROLE_HEADER; - private String listDelimiter=","; + private String listDelimiter = ","; private String defaultRole = "USER"; private boolean addEveryOneGroup = false; - + private GrantedAuthoritiesMapper authoritiesMapper; - /** - * remove this prefix from groups header - */ + /** remove this prefix from groups header */ private GroupMapper groupMapper = null; - @Override + @Override protected void authenticate(HttpServletRequest req) { String username = req.getHeader(usernameHeader); - + if (username != null) { User user = new User(); user.setId(-1L); @@ -78,22 +75,21 @@ protected void authenticate(HttpServletRequest req) { long groupCounter = 1; if (groups != null) { String[] groupsList = groups.split(listDelimiter); - + for (String groupName : groupsList) { if (groupName.equals(GroupReservedNames.EVERYONE.groupName())) { everyoneFound = true; } - if(groupMapper != null) { - groupName = groupMapper.transform(groupName); + if (groupMapper != null) { + groupName = groupMapper.transform(groupName); } UserGroup group = new UserGroup(); group.setGroupName(groupName); group.setId(groupCounter++); group.setEnabled(true); user.getGroups().add(group); - groupAuthorities.add(new GrantedAuthorityImpl(groupName)); + groupAuthorities.add(new SimpleGrantedAuthority(groupName)); } - } if (!everyoneFound && addEveryOneGroup) { UserGroup group = new UserGroup(); @@ -101,7 +97,8 @@ protected void authenticate(HttpServletRequest req) { group.setId(groupCounter++); group.setEnabled(true); user.getGroups().add(group); - groupAuthorities.add(new GrantedAuthorityImpl(GroupReservedNames.EVERYONE.groupName())); + groupAuthorities.add( + new SimpleGrantedAuthority(GroupReservedNames.EVERYONE.groupName())); } Set authorities = new HashSet(); String role = req.getHeader(roleHeader); @@ -110,7 +107,8 @@ protected void authenticate(HttpServletRequest req) { authorities.add(createRole(role)); } else if (authoritiesMapper != null) { Role chosenRole = user.getRole(); - for (GrantedAuthority authority : authoritiesMapper.mapAuthorities(groupAuthorities)) { + for (GrantedAuthority authority : + authoritiesMapper.mapAuthorities(groupAuthorities)) { authorities.add(createRole(authority.getAuthority())); Role userRole = getUserRole(authority.getAuthority()); chosenRole = morePrivileged(userRole, chosenRole); @@ -120,10 +118,9 @@ protected void authenticate(HttpServletRequest req) { } else { authorities.add(createRole(user.getRole().name())); } - Authentication auth = new PreAuthenticatedAuthenticationToken(user, "", authorities); + Authentication auth = new PreAuthenticatedAuthenticationToken(user, "", authorities); SecurityContextHolder.getContext().setAuthentication(auth); } - } private Role morePrivileged(Role role1, Role role2) { @@ -136,8 +133,8 @@ private Role morePrivileged(Role role1, Role role2) { return Role.GUEST; } - private GrantedAuthorityImpl createRole(String role) { - return new GrantedAuthorityImpl("ROLE_" + role); + private SimpleGrantedAuthority createRole(String role) { + return new SimpleGrantedAuthority("ROLE_" + role); } private Role getUserRole(String role) { @@ -209,12 +206,11 @@ public void setAddEveryOneGroup(boolean addEveryOneGroup) { this.addEveryOneGroup = addEveryOneGroup; } - public GroupMapper getGroupMapper() { - return groupMapper; - } + public GroupMapper getGroupMapper() { + return groupMapper; + } - public void setGroupMapper(GroupMapper groupMapper) { - this.groupMapper = groupMapper; - } - + public void setGroupMapper(GroupMapper groupMapper) { + this.groupMapper = groupMapper; + } } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/IdPConfiguration.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/IdPConfiguration.java new file mode 100644 index 00000000..f7e76dd9 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/IdPConfiguration.java @@ -0,0 +1,116 @@ +package it.geosolutions.geostore.services.rest.security; + +import it.geosolutions.geostore.core.model.enums.Role; +import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.BeanNameAware; + +/** + * Base configuration class for authentication mechanisms that rely on external Identity providers. + */ +public abstract class IdPConfiguration implements BeanNameAware { + + protected String beanName; + + protected boolean enabled = false; + + protected boolean autoCreateUser; + + protected String internalRedirectUri; + + protected String redirectUri; + + protected Role authenticatedDefaultRole; + + /** + * @return true if the filter to which this configuration object refers is enabled. False + * otherwise. + */ + public boolean isEnabled() { + return enabled; + } + + /** + * Set the enabled flag. + * + * @param enabled true to enable the filter to which this configuration refers. + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + /** + * @return true if the logged-in user should be created on the db if not present. False + * otherwise. + */ + public boolean isAutoCreateUser() { + return autoCreateUser; + } + + /** + * Set the autocreate user flag. + * + * @param autoCreateUser the autoCreateUser flag. + */ + public void setAutoCreateUser(Boolean autoCreateUser) { + this.autoCreateUser = autoCreateUser; + } + + public void setAutoCreateUser(boolean autoCreateUser) { + this.autoCreateUser = autoCreateUser; + } + + /** + * Return the bean name of this configuration object. + * + * @return the bean name. + */ + public String getBeanName() { + return beanName; + } + + @Override + public void setBeanName(String s) { + this.beanName = s; + } + + /** + * @return The internal redirect uri: the endpoint to which the client is redirected after the + * callback endpoint is invoked. + */ + public String getInternalRedirectUri() { + return internalRedirectUri; + } + + /** + * Set the internalRedirectUri. + * + * @param internalRedirectUri the internal redirect URI. + */ + public void setInternalRedirectUri(String internalRedirectUri) { + this.internalRedirectUri = internalRedirectUri; + } + + /** @return the redirect URI. */ + public String getRedirectUri() { + return redirectUri; + } + + /** + * Set the redirect URI. + * + * @param redirectUri the redirect URI. + */ + public void setRedirectUri(String redirectUri) { + this.redirectUri = redirectUri; + } + + public Role getAuthenticatedDefaultRole() { + if (authenticatedDefaultRole == null) return Role.USER; + return authenticatedDefaultRole; + } + + public void setAuthenticatedDefaultRole(String authenticatedDefaultRole) { + if (StringUtils.isNotBlank(authenticatedDefaultRole)) + this.authenticatedDefaultRole = Role.valueOf(authenticatedDefaultRole); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/PreAuthenticatedAuthenticationProvider.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/PreAuthenticatedAuthenticationProvider.java index 08f05801..933e3af7 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/PreAuthenticatedAuthenticationProvider.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/PreAuthenticatedAuthenticationProvider.java @@ -1,17 +1,21 @@ package it.geosolutions.geostore.services.rest.security; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; public class PreAuthenticatedAuthenticationProvider implements AuthenticationProvider { - private final static Logger LOGGER = Logger.getLogger(PreAuthenticatedAuthenticationProvider.class); + private static final Logger LOGGER = + LogManager.getLogger(PreAuthenticatedAuthenticationProvider.class); @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - PreAuthenticatedAuthenticationToken token =(PreAuthenticatedAuthenticationToken) authentication; + public Authentication authenticate(Authentication authentication) + throws AuthenticationException { + PreAuthenticatedAuthenticationToken token = + (PreAuthenticatedAuthenticationToken) authentication; LOGGER.debug("Pre Authentication for " + authentication.getName()); return token; } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/RestAuthenticationEntryPoint.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/RestAuthenticationEntryPoint.java index a3c88d9f..605b9d79 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/RestAuthenticationEntryPoint.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/RestAuthenticationEntryPoint.java @@ -30,50 +30,48 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; - -import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; + /** - * This Class wrap the AuthenticationEntryPoint to reply with forbidden for the - * /users/user/details path. - * It is used to emulate the login without showing a WWW-Authenticate window in the browser - * @author Lorenzo Natali (lorenzo.natali at geo-solutions.it) + * This Class wrap the AuthenticationEntryPoint to reply with forbidden for the /users/user/details + * path. It is used to emulate the login without showing a WWW-Authenticate window in the browser * + * @author Lorenzo Natali (lorenzo.natali at geo-solutions.it) */ -public class RestAuthenticationEntryPoint extends BasicAuthenticationEntryPoint { - private static final String LOGIN_PATH="users/user/details"; - private static final String SESSION_LOGIN_PATH= "session/"; - private static final Logger LOGGER = Logger.getLogger(RestAuthenticationEntryPoint.class); - @Override - public void commence(HttpServletRequest request, - HttpServletResponse response, AuthenticationException authException) - throws IOException, ServletException { - URI url=null; - try { - url = new URI(request.getRequestURI()); - } catch (URISyntaxException e) { - // TODO Auto-generated catch block - LOGGER.error("Invalid URI:"+ request.getRequestURI()); - super.commence(request, response, authException); - return; - } - if(url == null){ - super.commence(request, response, authException); - return; - } - if( url.getPath().contains(LOGIN_PATH) || url.getPath().contains(SESSION_LOGIN_PATH)){ - response.setHeader("WWW-Authenticate", "FormBased"); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - } - else{ - super.commence(request, response, authException); - - } - - } -} \ No newline at end of file +public class RestAuthenticationEntryPoint extends BasicAuthenticationEntryPoint { + private static final String LOGIN_PATH = "users/user/details"; + private static final String SESSION_LOGIN_PATH = "session/"; + private static final Logger LOGGER = LogManager.getLogger(RestAuthenticationEntryPoint.class); + + @Override + public void commence( + HttpServletRequest request, + HttpServletResponse response, + AuthenticationException authException) + throws IOException { + URI url = null; + try { + url = new URI(request.getRequestURI()); + } catch (URISyntaxException e) { + // TODO Auto-generated catch block + LOGGER.error("Invalid URI:" + request.getRequestURI()); + super.commence(request, response, authException); + return; + } + if (url == null) { + super.commence(request, response, authException); + return; + } + if (url.getPath().contains(LOGIN_PATH) || url.getPath().contains(SESSION_LOGIN_PATH)) { + response.setHeader("WWW-Authenticate", "FormBased"); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + } else { + super.commence(request, response, authException); + } + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/SecurityExceptionMapper.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/SecurityExceptionMapper.java index b72f1959..d8c7d1d2 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/SecurityExceptionMapper.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/SecurityExceptionMapper.java @@ -1,27 +1,21 @@ /** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + *

    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. + *

    Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. */ - package it.geosolutions.geostore.services.rest.security; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; - import org.springframework.security.access.AccessDeniedException; public class SecurityExceptionMapper implements ExceptionMapper { @@ -29,5 +23,4 @@ public class SecurityExceptionMapper implements ExceptionMapperThe attribute name is configurable (defaults to UUID). + * + * @author Lorenzo Natali */ public class SessionTokenAuthenticationFilter extends TokenAuthenticationFilter { - - private final static Logger LOGGER = Logger.getLogger(SessionTokenAuthenticationFilter.class); - - private boolean validateUserFromService = true; - - @Autowired - UserSessionService userSessionService; - @Autowired - UserService userService; + private static final Logger LOGGER = + LogManager.getLogger(SessionTokenAuthenticationFilter.class); + @Autowired UserSessionService userSessionService; + @Autowired UserService userService; + private boolean validateUserFromService = true; @Override protected Authentication checkToken(String token) { - if (userSessionService == null) { - return null; - } - User ud = userSessionService.getUserData(token); - if(ud != null) { - User user = null; - if (validateUserFromService) { - // we search user by id first, if available - if(ud.getId() != null) { - user = userService.get((Long) ud.getId()); - } - // then by name if no id is available or the service cannot search by id (e.g. LDAP) - if (user == null && ud.getName() != null){ - try { - user = userService.get(ud.getName()); - } catch (NotFoundServiceEx e) { - LOGGER.error("User " + ud.getName() + " not found on the database because of an exception", e); - } - } - } else { - user = ud; - } - if (user != null) { + if (userSessionService == null) { + return null; + } + User ud = userSessionService.getUserData(token); + if (ud != null) { + User user = null; + if (validateUserFromService) { + // we search user by id first, if available + if (ud.getId() != null) { + user = userService.get(ud.getId()); + } + // then by name if no id is available or the service cannot search by id (e.g. LDAP) + if (user == null && ud.getName() != null) { + try { + user = userService.get(ud.getName()); + } catch (NotFoundServiceEx e) { + LOGGER.error( + "User " + + ud.getName() + + " not found on the database because of an exception", + e); + } + } + } else { + user = ud; + } + if (user != null) { return createAuthenticationForUser(user); } else { - LOGGER.error("User login success, but couldn't retrieve a session. Probably auth user and and userService are out of sync."); + LOGGER.error( + "User login success, but couldn't retrieve a session. Probably auth user and and userService are out of sync."); } - } + } return null; } + public UserSessionService getUserSessionService() { + return userSessionService; + } - public UserSessionService getUserSessionService() { - return userSessionService; - } - - public void setUserSessionService(UserSessionService userSessionService) { - this.userSessionService = userSessionService; - } - - public UserService getUserService() { - return userService; - } + public void setUserSessionService(UserSessionService userSessionService) { + this.userSessionService = userSessionService; + } - public void setUserService(UserService userService) { - this.userService = userService; - } + public UserService getUserService() { + return userService; + } + public void setUserService(UserService userService) { + this.userService = userService; + } public boolean isValidateUserFromService() { return validateUserFromService; } - public void setValidateUserFromService(boolean validateUserFromService) { this.validateUserFromService = validateUserFromService; } - - } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/TokenAuthenticationCache.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/TokenAuthenticationCache.java new file mode 100644 index 00000000..f82d0ea3 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/TokenAuthenticationCache.java @@ -0,0 +1,168 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.RemovalCause; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Configuration; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils; +import it.geosolutions.geostore.services.rest.security.oauth2.TokenDetails; +import java.util.Date; +import java.util.concurrent.TimeUnit; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; +import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.OAuth2RefreshToken; +import org.springframework.web.client.RestTemplate; + +/** + * A cache for OAuth2 Authentication object. Authentication instances are identified by the + * corresponding accessToken. + */ +public class TokenAuthenticationCache implements ApplicationContextAware { + + private static final Logger LOGGER = LogManager.getLogger(TokenAuthenticationCache.class); + private final Cache cache; + private final int cacheSize = 1000; + private final int cacheExpirationMinutes = 8; + private ApplicationContext context; + + public TokenAuthenticationCache() { + CacheBuilder cacheBuilder = + CacheBuilder.newBuilder() + .maximumSize(cacheSize) + .expireAfterWrite(cacheExpirationMinutes, TimeUnit.HOURS) + .removalListener( + notification -> { + if (notification.getCause().equals(RemovalCause.EXPIRED)) { + Authentication authentication = notification.getValue(); + revokeAuthIfRefreshExpired(authentication); + } + }); + this.cache = cacheBuilder.build(); + } + + /** + * Perform a revoke authorization when the cache entry expires. + * + * @param authentication the authentication object. + */ + protected void revokeAuthIfRefreshExpired(Authentication authentication) { + TokenDetails tokenDetails = OAuth2Utils.getTokenDetails(authentication); + if (tokenDetails != null && tokenDetails.getAccessToken() != null) { + OAuth2AccessToken accessToken = tokenDetails.getAccessToken(); + OAuth2RefreshToken refreshToken = accessToken.getRefreshToken(); + if (refreshToken instanceof ExpiringOAuth2RefreshToken) { + ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken; + OAuth2Configuration configuration = + (OAuth2Configuration) context.getBean(tokenDetails.getProvider()); + if (configuration != null && configuration.isEnabled()) { + if (expiring.getExpiration().after(new Date())) { + OAuth2Configuration.Endpoint revokeEndpoint = + configuration.buildRevokeEndpoint( + expiring.getValue(), accessToken.getValue(), configuration); + if (revokeEndpoint != null) { + RestTemplate template = new RestTemplate(); + ResponseEntity responseEntity = + template.exchange( + revokeEndpoint.getUrl(), + revokeEndpoint.getMethod(), + null, + String.class); + if (responseEntity.getStatusCode().value() != 200) { + LOGGER.error( + "Error while revoking authorization. Error is: {}", + responseEntity.getBody()); + } + } + } + } + } + } + } + + /** + * Retrieve the authentication by its accessToken value. + * + * @param accessToken the accessToken. + * @return the Authentication identified by the token if present. Null otherwise. + */ + public Authentication get(String accessToken) { + return cache.asMap().get(accessToken); + } + + /** + * Put an Authentication instance identified by an accessToken value. If the passed + * Authentication instance does not have a refresh token, and we have an old one that has, the + * refresh Token is set to the new instance. + * + * @param accessToken the access token identifying the instance to update + * @param authentication the Authentication to cache. + * @return the Authentication cached. + */ + public Authentication putCacheEntry(String accessToken, Authentication authentication) { + Authentication old = get(accessToken); + TokenDetails oldDetails = OAuth2Utils.getTokenDetails(old); + if (oldDetails != null) { + TokenDetails newDetails = OAuth2Utils.getTokenDetails(authentication); + OAuth2AccessToken newToken = newDetails.getAccessToken(); + OAuth2AccessToken oldToken = oldDetails.getAccessToken(); + if (newToken.getRefreshToken() == null && oldToken != null) { + DefaultOAuth2AccessToken defaultOAuth2AccessToken = + new DefaultOAuth2AccessToken(newToken.getValue()); + defaultOAuth2AccessToken.setRefreshToken(oldToken.getRefreshToken()); + newDetails.setAccessToken(defaultOAuth2AccessToken); + } + } + + this.cache.put(accessToken, authentication); + return authentication; + } + + /** + * Remove an authentication from the cache. + * + * @param accessToken the accessToken identifying the authentication to remove. + */ + public void removeEntry(String accessToken) { + this.cache.invalidate(accessToken); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.context = applicationContext; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/TokenAuthenticationFilter.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/TokenAuthenticationFilter.java index 123a4182..a1aad395 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/TokenAuthenticationFilter.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/TokenAuthenticationFilter.java @@ -27,55 +27,51 @@ */ package it.geosolutions.geostore.services.rest.security; +import com.google.common.base.Optional; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; - import javax.servlet.http.HttpServletRequest; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import com.google.common.base.Optional; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; - /** * Base Token based authentication filter. - * - * Authenticates a user reading an authentication token from a configured header - * (defaults to Authorization). - * - * The token can have a prefix that needs to be present in the header value (defaults to - * Bearer, to be compatible with OAuth 2.0 tokens). - * - * Each implementation can verify the validity of a token (and the user bounded to it) - * using a different methodology. - * - * A cache is internally used to avoid continuous token testing. - * - * Cache expiration time and size can be configured. - * - * @author Mauro Bartolomeoli * + *

    Authenticates a user reading an authentication token from a configured header (defaults to + * Authorization). + * + *

    The token can have a prefix that needs to be present in the header value (defaults to Bearer, + * to be compatible with OAuth 2.0 tokens). + * + *

    Each implementation can verify the validity of a token (and the user bounded to it) using a + * different methodology. + * + *

    A cache is internally used to avoid continuous token testing. + * + *

    Cache expiration time and size can be configured. + * + * @author Mauro Bartolomeoli */ public abstract class TokenAuthenticationFilter extends GeoStoreAuthenticationFilter { - private final static Logger LOGGER = Logger.getLogger(TokenAuthenticationFilter.class); - + private static final Logger LOGGER = LogManager.getLogger(TokenAuthenticationFilter.class); + protected LoadingCache> cache; - - private String tokenHeader = "Authorization"; + + private String tokenHeader = "Authorization"; private String tokenPrefix = "Bearer "; - + private int cacheSize = 1000; private int cacheExpiration = 60; - - + /** * Header to check for token (defaults to Authorization). - * + * * @param tokenHeader */ public void setTokenHeader(String tokenHeader) { @@ -83,23 +79,20 @@ public void setTokenHeader(String tokenHeader) { } /** - * Static prefix to look for in the header value. - * Only if the prefix is found, the rest of the header is checked as a Token. - * - * Defaults to Bearer (OAuth 2.0 compatible). + * Static prefix to look for in the header value. Only if the prefix is found, the rest of the + * header is checked as a Token. + * + *

    Defaults to Bearer (OAuth 2.0 compatible). + * * @param tokenPrefix */ public void setTokenPrefix(String tokenPrefix) { this.tokenPrefix = tokenPrefix; } - - public void setCache(LoadingCache> cache) { - this.cache = cache; - } - + /** * Max number of cached entries (defaults to 1000). - * + * * @param cacheSize */ public void setCacheSize(int cacheSize) { @@ -108,7 +101,7 @@ public void setCacheSize(int cacheSize) { /** * Cached entries expiration time, in seconds (defaults to 60s). - * + * * @param cacheExpiration */ public void setCacheExpiration(int cacheExpiration) { @@ -116,24 +109,29 @@ public void setCacheExpiration(int cacheExpiration) { } protected LoadingCache> getCache() { - if(cache == null) { - - cache = CacheBuilder.newBuilder() - .maximumSize(cacheSize) - .refreshAfterWrite(cacheExpiration, TimeUnit.SECONDS) - .build(new CacheLoader>() { - public Optional load(String token) { - return Optional.fromNullable(checkToken(token)); - } - }); + if (cache == null) { + + cache = + CacheBuilder.newBuilder() + .maximumSize(cacheSize) + .refreshAfterWrite(cacheExpiration, TimeUnit.SECONDS) + .build( + new CacheLoader>() { + public Optional load(String token) { + return Optional.fromNullable(checkToken(token)); + } + }); } return cache; } - - + + public void setCache(LoadingCache> cache) { + this.cache = cache; + } + protected void authenticate(HttpServletRequest req) { String authHeader = req.getHeader(tokenHeader); - + if (authHeader != null && authHeader.trim().toUpperCase().startsWith(tokenPrefix.toUpperCase())) { String token = authHeader.substring(tokenPrefix.length()).trim(); @@ -148,18 +146,14 @@ protected void authenticate(HttpServletRequest req) { LOGGER.error("Error authenticating token", e); } } - } - - - + /** - * Phisically checks the validity of the given token and - * returns an Authentication object for the corresponding principal. - * + * Phisically checks the validity of the given token and returns an Authentication object for + * the corresponding principal. + * * @param token * @return */ protected abstract Authentication checkToken(String token); - } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/UserAttributeTokenAuthenticationFilter.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/UserAttributeTokenAuthenticationFilter.java index 8440e3a5..9ef60eb1 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/UserAttributeTokenAuthenticationFilter.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/UserAttributeTokenAuthenticationFilter.java @@ -29,26 +29,25 @@ import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserAttribute; - import java.util.Collection; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.security.core.Authentication; /** * Token based authentication filter that looks for the token in a user attribute. - * - * The attribute name is configurable (defaults to UUID). - * - * @author Mauro Bartolomeoli * + *

    The attribute name is configurable (defaults to UUID). + * + * @author Mauro Bartolomeoli */ public class UserAttributeTokenAuthenticationFilter extends TokenAuthenticationFilter { - - private final static Logger LOGGER = Logger.getLogger(UserAttributeTokenAuthenticationFilter.class); - + + private static final Logger LOGGER = + LogManager.getLogger(UserAttributeTokenAuthenticationFilter.class); + private String attributeName = "UUID"; - + public void setAttributeName(String attributeName) { this.attributeName = attributeName; } @@ -62,16 +61,15 @@ protected Authentication checkToken(String token) { // token value Collection users = userService.getByAttribute(attribute); // the token is considered valid if only 1 user matches - if(users.size() == 1) { + if (users.size() == 1) { User user = users.iterator().next(); return createAuthenticationForUser(user); - } else if(users.size() > 1) { - LOGGER.error("Too many users matching the given token. Only one is allowed for a token to be valid!"); + } else if (users.size() > 1) { + LOGGER.error( + "Too many users matching the given token. Only one is allowed for a token to be valid!"); } else { LOGGER.error("No users matching the given token."); } return null; } - - } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/UserLdapAuthenticationProvider.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/UserLdapAuthenticationProvider.java index 2c054d83..7107ce72 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/UserLdapAuthenticationProvider.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/UserLdapAuthenticationProvider.java @@ -1,6 +1,4 @@ -/** - * - */ +/** */ package it.geosolutions.geostore.services.rest.security; import it.geosolutions.geostore.core.model.User; @@ -12,15 +10,10 @@ import it.geosolutions.geostore.services.UserService; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.apache.log4j.Level; -import org.apache.log4j.Logger; +import java.util.*; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.DisabledException; @@ -28,65 +21,45 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.GrantedAuthorityImpl; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; import org.springframework.security.ldap.authentication.LdapAuthenticator; import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; import org.springframework.security.ldap.userdetails.LdapUserDetails; -/** - * @author alessio.fabiani - * - */ +/** @author alessio.fabiani */ public class UserLdapAuthenticationProvider extends LdapAuthenticationProvider { -private final static Logger LOGGER = Logger.getLogger(UserLdapAuthenticationProvider.class); - - @Autowired - UserService userService; - - @Autowired - UserGroupService userGroupService; - - private UserMapper userMapper; - - /** - * Message shown if the user credentials are wrong. TODO: Localize it - */ - private static final String UNAUTHORIZED_MSG = "Bad credentials"; - - /** - * Message shown if the user it's not found. TODO: Localize it - */ + /** Message shown if the user it's not found. TODO: Localize it */ public static final String USER_NOT_FOUND_MSG = "User not found. Please check your credentials"; + public static final String USER_NOT_ENABLED = "The user present but not enabled"; - - public UserLdapAuthenticationProvider(LdapAuthenticator authenticator, - LdapAuthoritiesPopulator authoritiesPopulator) { + private static final Logger LOGGER = LogManager.getLogger(UserLdapAuthenticationProvider.class); + /** Message shown if the user credentials are wrong. TODO: Localize it */ + private static final String UNAUTHORIZED_MSG = "Bad credentials"; + + @Autowired UserService userService; + @Autowired UserGroupService userGroupService; + private UserMapper userMapper; + + public UserLdapAuthenticationProvider( + LdapAuthenticator authenticator, LdapAuthoritiesPopulator authoritiesPopulator) { super(authenticator, authoritiesPopulator); } - - public void setUserService(UserService userService) { this.userService = userService; } - - public void setUserGroupService(UserGroupService userGroupService) { this.userGroupService = userGroupService; } - - public void setUserMapper(UserMapper userMapper) { this.userMapper = userMapper; } - - @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { @@ -99,12 +72,14 @@ public Authentication authenticate(Authentication authentication) LdapUserDetails ldapUser = null; if (authentication.isAuthenticated()) { - Collection authorities = null; + Collection authorities = null; ldapUser = (LdapUserDetails) authentication.getPrincipal(); - if (!(ldapUser.isAccountNonExpired() && ldapUser.isAccountNonLocked() - && ldapUser.isCredentialsNonExpired() && ldapUser.isEnabled())) { + if (!(ldapUser.isAccountNonExpired() + && ldapUser.isAccountNonLocked() + && ldapUser.isCredentialsNonExpired() + && ldapUser.isEnabled())) { throw new DisabledException(USER_NOT_FOUND_MSG); } @@ -117,8 +92,9 @@ public Authentication authenticate(Authentication authentication) User user = null; try { user = userService.get(us); - LOGGER.info("US: " + us);// + " PW: " + PwEncoder.encode(pw) + " -- " + user.getPassword()); - + LOGGER.info("US: " + us); // + " PW: " + PwEncoder.encode(pw) + " -- " + + // user.getPassword()); + if (!user.isEnabled()) { throw new DisabledException(USER_NOT_FOUND_MSG); } @@ -135,8 +111,7 @@ public Authentication authenticate(Authentication authentication) user.setRole(role); user.setGroups(GroupReservedNames.checkReservedGroups(groups)); - if (userService != null) - userService.update(user); + if (userService != null) userService.update(user); Authentication a = prepareAuthentication(pw, user, role); return a; @@ -160,11 +135,10 @@ public Authentication authenticate(Authentication authentication) Role role = extractUserRoleAndGroups(null, authorities, groups); user.setRole(role); user.setGroups(GroupReservedNames.checkReservedGroups(groups)); - if(userMapper != null) { + if (userMapper != null) { userMapper.mapUser(ldapUser, user); } - if (userService != null) - userService.insert(user); + if (userService != null) userService.insert(user); Authentication a = prepareAuthentication(pw, user, role); return a; @@ -189,7 +163,7 @@ public Authentication authenticate(Authentication authentication) */ protected Authentication prepareAuthentication(String pw, User user, Role role) { List grantedAuthorities = new ArrayList(); - grantedAuthorities.add(new GrantedAuthorityImpl("ROLE_" + role)); + grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + role)); Authentication a = new UsernamePasswordAuthenticationToken(user, pw, grantedAuthorities); // a.setAuthenticated(true); return a; @@ -202,8 +176,10 @@ protected Authentication prepareAuthentication(String pw, User user, Role role) * @return * @throws BadRequestServiceEx */ - protected Role extractUserRoleAndGroups(Role userRole, - Collection authorities, Set groups) + protected Role extractUserRoleAndGroups( + Role userRole, + Collection authorities, + Set groups) throws BadRequestServiceEx { Role role = (userRole != null ? userRole : Role.USER); for (GrantedAuthority a : authorities) { @@ -222,16 +198,15 @@ protected Role extractUserRoleAndGroups(Role userRole, } public void synchronizeGroups() throws BadRequestServiceEx { - if(getAuthoritiesPopulator() instanceof GroupsRolesService) { + if (getAuthoritiesPopulator() instanceof GroupsRolesService) { GroupsRolesService groupsService = (GroupsRolesService) getAuthoritiesPopulator(); - for(GrantedAuthority authority : groupsService.getAllGroups()) { + for (GrantedAuthority authority : groupsService.getAllGroups()) { synchronizeGroup(authority); } } } - private UserGroup synchronizeGroup(GrantedAuthority a) - throws BadRequestServiceEx { + private UserGroup synchronizeGroup(GrantedAuthority a) throws BadRequestServiceEx { UserGroup group = new UserGroup(); group.setGroupName(a.getAuthority()); diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/UserServiceAuthenticationProvider.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/UserServiceAuthenticationProvider.java index 9d049a83..72e6d46c 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/UserServiceAuthenticationProvider.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/UserServiceAuthenticationProvider.java @@ -3,11 +3,10 @@ import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.security.password.PwEncoder; import it.geosolutions.geostore.services.UserService; - import java.util.ArrayList; import java.util.List; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; @@ -15,32 +14,26 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.GrantedAuthorityImpl; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UsernameNotFoundException; /** * Wrap geostore Rest Services to allow Authentication using Geostore Users - * + * * @author Lorenzo Natali - * */ public class UserServiceAuthenticationProvider implements AuthenticationProvider { - private final static Logger LOGGER = Logger.getLogger(UserServiceAuthenticationProvider.class); - - @Autowired - UserService userService; + /** Message shown if the user it's not found. TODO: Localize it */ + public static final String USER_NOT_FOUND_MSG = "User not found. Please check your credentials"; - /** - * Message shown if the user credentials are wrong. TODO: Localize it - */ + public static final String USER_NOT_ENABLED = "The user present but not enabled"; + private static final Logger LOGGER = + LogManager.getLogger(UserServiceAuthenticationProvider.class); + /** Message shown if the user credentials are wrong. TODO: Localize it */ private static final String UNAUTHORIZED_MSG = "Bad credentials"; - /** - * Message shown if the user it's not found. TODO: Localize it - */ - public static final String USER_NOT_FOUND_MSG = "User not found. Please check your credentials"; - public static final String USER_NOT_ENABLED = "The user present but not enabled"; + @Autowired UserService userService; @Override public boolean supports(Class authentication) { @@ -56,12 +49,13 @@ public Authentication authenticate(Authentication authentication) { User user = null; try { user = userService.get(us); - LOGGER.info("US: " + us );//+ " PW: " + PwEncoder.encode(pw) + " -- " + user.getPassword()); - if (user.getPassword() == null || !PwEncoder.isPasswordValid(user.getPassword(),pw)) { + LOGGER.info("US: " + us); // + " PW: " + PwEncoder.encode(pw) + " -- " + + // user.getPassword()); + if (user.getPassword() == null || !PwEncoder.isPasswordValid(user.getPassword(), pw)) { throw new BadCredentialsException(UNAUTHORIZED_MSG); } - if(!user.isEnabled()){ - throw new DisabledException(USER_NOT_FOUND_MSG); + if (!user.isEnabled()) { + throw new DisabledException(USER_NOT_FOUND_MSG); } } catch (Exception e) { LOGGER.info(USER_NOT_FOUND_MSG); @@ -72,14 +66,13 @@ public Authentication authenticate(Authentication authentication) { String role = user.getRole().toString(); // return null; List authorities = new ArrayList(); - authorities.add(new GrantedAuthorityImpl("ROLE_" + role)); + authorities.add(new SimpleGrantedAuthority("ROLE_" + role)); Authentication a = new UsernamePasswordAuthenticationToken(user, pw, authorities); // a.setAuthenticated(true); return a; } else { throw new UsernameNotFoundException(USER_NOT_FOUND_MSG); } - } // GETTERS AND SETTERS diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/WebServiceTokenAuthenticationFilter.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/WebServiceTokenAuthenticationFilter.java index 8e365917..f292dde9 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/WebServiceTokenAuthenticationFilter.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/WebServiceTokenAuthenticationFilter.java @@ -30,110 +30,111 @@ import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpException; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.HttpStatus; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.commons.httpclient.params.HttpClientParams; -import org.apache.log4j.Logger; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.security.core.Authentication; /** * Token based authentication filter that looks for the token calling an external webservice. - * - * The url of the service needs to be configured. A placeholder in the url will be replaced by + * + *

    The url of the service needs to be configured. A placeholder in the url will be replaced by * the actual token. - * - * The result of the web service call will be parsed using given regular expression to: - * - check if the token is valid - * - extract the user name from the result - * - * @author Mauro Bartolomeoli * + *

    The result of the web service call will be parsed using given regular expression to: - check + * if the token is valid - extract the user name from the result + * + * @author Mauro Bartolomeoli */ public class WebServiceTokenAuthenticationFilter extends TokenAuthenticationFilter { - - private final static Logger LOGGER = Logger.getLogger(WebServiceTokenAuthenticationFilter.class); - - private String url; - + + private static final Logger LOGGER = + LogManager.getLogger(WebServiceTokenAuthenticationFilter.class); + private final String url; // compiled user search regex Pattern searchUserRegex = null; - // connection timeout to the mapper web service (in seconds) - long connectTimeout = 5; - + int connectTimeout = 5; // read timeout to the mapper web service (in seconds) int readTimeout = 10; - + HttpClientBuilder clientBuilder = HttpClientBuilder.create(); // optional external httpClient for web service connection (used mainly for tests) private HttpClient httpClient = null; + private RequestConfig connectionConfig; public WebServiceTokenAuthenticationFilter(String url) { super(); this.url = url; } - + /** - * Regular expression to extract the username from the - * webservice response. - * - * The first group in the expression will be used for extraction. - * + * Regular expression to extract the username from the webservice response. + * + *

    The first group in the expression will be used for extraction. + * * @param searchUser */ public void setSearchUser(String searchUser) { searchUserRegex = Pattern.compile(searchUser); } - - - public void setConnectTimeout(long connectTimeout) { + public void setConnectTimeout(int connectTimeout) { this.connectTimeout = connectTimeout; } - - public void setReadTimeout(int readTimeout) { this.readTimeout = readTimeout; } + private HttpClient getHttpClient() { + if (httpClient == null) { + httpClient = clientBuilder.useSystemProperties().build(); + } + return httpClient; + } + /** * Configures the HTTPClient implementation to be used to connect to the web service. - * + * * @param httpClient */ public void setHttpClient(HttpClient httpClient) { this.httpClient = httpClient; } - - private HttpClient getHttpClient() { - if (httpClient == null) { - httpClient = new HttpClient(); - } - return httpClient; - } - + @Override protected Authentication checkToken(String token) { String webServiceUrl = url.replace("{token}", token); HttpClient client = getHttpClient(); - client.getParams().setParameter(HttpClientParams.CONNECTION_MANAGER_TIMEOUT, connectTimeout * 1000l); - client.getParams().setParameter(HttpClientParams.SO_TIMEOUT, readTimeout * 1000); - HttpMethod method = null; + connectionConfig = + RequestConfig.custom() + .setConnectionRequestTimeout(connectTimeout * 1000) + .setSocketTimeout(readTimeout * 1000) + .build(); + + HttpRequestBase method = null; try { LOGGER.debug("Issuing request to webservice: " + url); - method = new GetMethod(webServiceUrl); - int statusCode = client.executeMethod(method); - - if (statusCode == HttpStatus.SC_OK) { + method = new HttpGet(webServiceUrl); + method.setConfig(connectionConfig); + HttpResponse httpResponse = client.execute(method); + + if (getStatusCode(httpResponse) == HttpStatus.SC_OK) { // get response content as a single string, without new lines // so that is simpler to apply an extraction regular expression - String response = method.getResponseBodyAsString() - .replace("\r", "").replace("\n", ""); - if(response != null) { + String response = + EntityUtils.toString(httpResponse.getEntity(), "UTF-8") + .replace("\r", "") + .replace("\n", ""); + if (response != null) { if (searchUserRegex == null) { return createAuthenticationForUser(response, null, ""); } else { @@ -141,23 +142,30 @@ protected Authentication checkToken(String token) { if (matcher.find()) { return createAuthenticationForUser(matcher.group(1), null, response); } else { - LOGGER.warn("Error in getting username from webservice response cannot find userName in response"); + LOGGER.warn( + "Error in getting username from webservice response cannot find userName in response"); } } } else { LOGGER.error("No response received from webservice: " + url); } } - } catch (HttpException e) { - LOGGER.error("Error contacting webservice: " + url, e); } catch (IOException e) { LOGGER.error("Error reading data from webservice: " + url, e); } finally { - if(method != null) { + if (method != null) { method.releaseConnection(); } } return null; - - } + } + + public int getStatusCode(HttpResponse response) { + if (response != null) { + StatusLine statusLine = response.getStatusLine(); + return statusLine.getStatusCode(); + } else { + return -1; + } + } } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/AuthoritiesMappings.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/AuthoritiesMappings.java new file mode 100644 index 00000000..6e6ea14e --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/AuthoritiesMappings.java @@ -0,0 +1,57 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import java.util.HashMap; + +/** Case insensitive map for RoleMappings used by Keycloak classes. */ +class AuthoritiesMappings extends HashMap { + + AuthoritiesMappings(int initialCapacity) { + super(initialCapacity); + } + + AuthoritiesMappings() {} + + @Override + public String get(Object key) { + if (!(key instanceof String)) return null; + return super.get(key.toString().toUpperCase()); + } + + @Override + public boolean containsKey(Object key) { + if (!(key instanceof String)) return false; + return super.containsKey(key.toString().toUpperCase()); + } + + @Override + public String put(String key, String value) { + return super.put(key.toUpperCase(), value); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/BaseKeycloakDAO.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/BaseKeycloakDAO.java new file mode 100644 index 00000000..14f98aa1 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/BaseKeycloakDAO.java @@ -0,0 +1,107 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import com.googlecode.genericdao.search.ISearch; +import it.geosolutions.geostore.services.rest.utils.GeoStoreContext; +import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.resource.RolesResource; +import org.keycloak.admin.client.resource.UsersResource; + +/** Base DAO class for keycloak based repository. */ +public abstract class BaseKeycloakDAO { + + protected KeycloakAdminClientConfiguration adminClientConfiguration; + + public BaseKeycloakDAO(KeycloakAdminClientConfiguration adminClientConfiguration) { + this.adminClientConfiguration = adminClientConfiguration; + } + + /** + * Converts the ISearch to a {@link KeycloakQuery}. + * + * @param search the search with filters to convert. + * @return a {@link KeycloakQuery} representation of the search. + */ + protected KeycloakQuery toKeycloakQuery(ISearch search) { + return new KeycloakSearchMapper().keycloackQuery(search); + } + + /** + * Get the Keycloak client instance. + * + * @return the {@link Keycloak} REST client. + */ + protected Keycloak keycloak() { + return adminClientConfiguration.getKeycloak(); + } + + /** + * Get the UsersResource client instance. + * + * @param keycloak REST client instance. + * @return the {@link UsersResource} REST client. + */ + protected UsersResource getUsersResource(Keycloak keycloak) { + return keycloak.realm(adminClientConfiguration.getRealm()).users(); + } + + /** + * Get the RolesResource client instance. + * + * @param keycloak the {@link Keycloak} REST client instance. + * @return the {@link RolesResource} REST client instance. + */ + protected RolesResource getRolesResource(Keycloak keycloak) { + return keycloak.realm(adminClientConfiguration.getRealm()).roles(); + } + + /** + * Close the REST client. + * + * @param keycloak the {@link Keycloak} REST client instance. + */ + protected void close(Keycloak keycloak) { + if (keycloak.isClosed()) keycloak.close(); + } + + /** + * Get an authorities mapper instance. + * + * @return the authorities mapper. + */ + protected GeoStoreKeycloakAuthoritiesMapper getAuthoritiesMapper() { + KeyCloakConfiguration configuration = GeoStoreContext.bean(KeyCloakConfiguration.class); + if (configuration != null) + return new GeoStoreKeycloakAuthoritiesMapper( + configuration.getRoleMappings(), + configuration.getGroupMappings(), + configuration.isDropUnmapped()); + else return new GeoStoreKeycloakAuthoritiesMapper(null, null, false); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/GeoStoreKeycloakAuthProvider.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/GeoStoreKeycloakAuthProvider.java new file mode 100644 index 00000000..90aae1d9 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/GeoStoreKeycloakAuthProvider.java @@ -0,0 +1,322 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ + +package it.geosolutions.geostore.services.rest.security.keycloak; + +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.*; + +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.enums.GroupReservedNames; +import it.geosolutions.geostore.core.model.enums.Role; +import it.geosolutions.geostore.core.security.password.SecurityUtils; +import it.geosolutions.geostore.services.UserGroupService; +import it.geosolutions.geostore.services.UserService; +import it.geosolutions.geostore.services.exception.BadRequestServiceEx; +import it.geosolutions.geostore.services.exception.NotFoundServiceEx; +import java.util.*; +import javax.servlet.http.HttpServletRequest; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.keycloak.KeycloakSecurityContext; +import org.keycloak.adapters.OidcKeycloakAccount; +import org.keycloak.adapters.RefreshableKeycloakSecurityContext; +import org.keycloak.adapters.springsecurity.account.KeycloakRole; +import org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount; +import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; +import org.keycloak.representations.AccessToken; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; + +/** + * GeoStore custom Authentication provider. It is used to map a Keycloak Authentication to a + * GeoStore Authentication where the principal is of type {@link User}. + */ +public class GeoStoreKeycloakAuthProvider implements AuthenticationProvider { + + private static final Logger LOGGER = LogManager.getLogger(GeoStoreKeycloakAuthProvider.class); + private final KeyCloakConfiguration configuration; + @Autowired private UserService userService; + @Autowired private UserGroupService groupService; + + public GeoStoreKeycloakAuthProvider(KeyCloakConfiguration configuration) { + this.configuration = configuration; + } + + @Override + public Authentication authenticate(Authentication authentication) + throws AuthenticationException { + KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) authentication; + OidcKeycloakAccount account = token.getAccount(); + KeycloakSecurityContext context = account.getKeycloakSecurityContext(); + AccessToken accessToken = context.getToken(); + String accessTokenStr = context.getTokenString(); + String refreshToken = null; + Long expiration = null; + HttpServletRequest request = getRequest(); + // set tokens as request attributes so that can made available in a cookie for the frontend + // on the callback url. + if (accessToken != null) { + expiration = accessToken.getExp(); + if (request != null) request.setAttribute(ACCESS_TOKEN_PARAM, accessToken); + } + if (context instanceof RefreshableKeycloakSecurityContext) { + refreshToken = ((RefreshableKeycloakSecurityContext) context).getRefreshToken(); + if (request != null) request.setAttribute(REFRESH_TOKEN_PARAM, refreshToken); + } + + List grantedAuthorities = new ArrayList<>(); + GeoStoreKeycloakAuthoritiesMapper grantedAuthoritiesMapper = + new GeoStoreKeycloakAuthoritiesMapper( + configuration.getRoleMappings(), + configuration.getGroupMappings(), + configuration.isDropUnmapped()); + for (String role : token.getAccount().getRoles()) { + grantedAuthorities.add(new KeycloakRole(role)); + } + + // maps authorities to GeoStore Role and UserGroup + Collection mapped = + mapAuthorities(grantedAuthoritiesMapper, grantedAuthorities); + + KeycloakTokenDetails details = + new KeycloakTokenDetails(accessTokenStr, refreshToken, expiration); + details.setIdToken(context.getIdTokenString()); + String username = getUsername(authentication); + Set keycloakGroups = + grantedAuthoritiesMapper != null + ? grantedAuthoritiesMapper.getGroups() + : new HashSet<>(); + + // if the auto creation of user is set to true from keycloak properties we add the groups as + // well. + if (configuration.isAutoCreateUser()) + keycloakGroups = importGroups(keycloakGroups, grantedAuthorities); + + User user = retrieveUser(username, "", grantedAuthoritiesMapper, keycloakGroups); + addEveryOne(user.getGroups()); + if (user.getRole() == null) { + // no role gets the one configured to be default for authenticated users. + Role defRole = configuration.getAuthenticatedDefaultRole(); + user.setRole(defRole); + } + if (user.getGroups() == null) user.setGroups(new HashSet<>()); + PreAuthenticatedAuthenticationToken result = + new PreAuthenticatedAuthenticationToken(user, "", mapped); + result.setDetails(details); + return result; + } + + private Collection mapAuthorities( + GeoStoreKeycloakAuthoritiesMapper grantedAuthoritiesMapper, + Collection authorities) { + return grantedAuthoritiesMapper != null + ? grantedAuthoritiesMapper.mapAuthorities(authorities) + : authorities; + } + + @Override + public boolean supports(Class aClass) { + return KeycloakAuthenticationToken.class.isAssignableFrom(aClass); + } + + /** + * Retrieve the user from db or create a new instance. If {@link + * KeyCloakConfiguration#isAutoCreateUser()} returns true, will insert the user in the db. + * + * @param userName + * @param credentials + * @return + */ + protected User retrieveUser( + String userName, + String credentials, + GeoStoreKeycloakAuthoritiesMapper mapper, + Set groups) { + User user = null; + if (userService != null) { + try { + user = userService.get(userName); + } catch (NotFoundServiceEx e) { + if (LOGGER.isDebugEnabled()) { + LOGGER.warn("Keycloak user not found in DB.", e); + } + } + } + if (user == null) { + user = new User(); + user.setName(userName); + user.setNewPassword(credentials); + user.setEnabled(true); + Role role = mappedRole(mapper); + user.setRole(role); + if (groups == null) groups = new HashSet<>(); + user.setGroups(groups); + // user not found in db, if configured to insert will insert it. + if (userService != null && configuration.isAutoCreateUser()) { + try { + long id = userService.insert(user); + user = userService.get(id); + } catch (NotFoundServiceEx | BadRequestServiceEx e) { + LOGGER.error("Exception while inserting the user.", e); + } + } else { + user.setTrusted(true); + } + } else { + Role role = mappedRole(mapper); + // might need to update the role / groups if on keycloak side roles changed. + if (isUpdateUser(user, groups, role)) { + updateRoleAndGroups(role, groups, user); + } + } + return user; + } + + // update user groups adding the one not already present and added on keycloak side + private User updateRoleAndGroups(Role role, Set groups, User user) { + user.setRole(role); + try { + for (UserGroup g : user.getGroups()) { + if (!groups.stream() + .anyMatch(group -> group.getGroupName().equals(g.getGroupName()))) { + UserGroup newGroup = new UserGroup(); + newGroup.setGroupName(g.getGroupName()); + newGroup.setId(g.getId()); + groups.add(g); + } + } + user.setGroups(groups); + userService.update(new User(user)); + user = userService.get(user.getName()); + } catch (NotFoundServiceEx | BadRequestServiceEx ex) { + LOGGER.error("Error while updating user role...", ex); + } + return user; + } + + // we only update if new roles were added on keycloak or the role changed + private boolean isUpdateUser(User user, Set groups, Role mappedRole) { + Set incoming = new HashSet<>(groups); + incoming.removeAll(user.getGroups()); + if (!incoming.stream() + .allMatch(g -> g.getGroupName().equals(GroupReservedNames.EVERYONE.groupName()))) + return true; + + return configuration.isAutoCreateUser() + && (user.getRole() == null || !user.getRole().equals(mappedRole)); + } + + private Role mappedRole(GeoStoreKeycloakAuthoritiesMapper mapper) { + Role role = null; + if (mapper != null && mapper.getRole() != null) role = mapper.getRole(); + if (role == null) role = configuration.getAuthenticatedDefaultRole(); + if (role == null) role = Role.USER; + return role; + } + + private String getUsername(Authentication authentication) { + String username = null; + if (authentication != null + && authentication.getDetails() instanceof SimpleKeycloakAccount) { + SimpleKeycloakAccount account = (SimpleKeycloakAccount) authentication.getDetails(); + AccessToken token = account.getKeycloakSecurityContext().getToken(); + if (token != null) username = token.getPreferredUsername(); + } + if (username == null) username = SecurityUtils.getUsername(authentication); + return username; + } + + private Set importGroups( + Set mappedGroups, Collection authorities) { + Set returnSet = new HashSet<>(mappedGroups.size()); + try { + if (mappedGroups == null || mappedGroups.isEmpty()) { + for (GrantedAuthority auth : authorities) { + UserGroup res = importGroup(auth); + returnSet.add(res); + } + } else { + for (UserGroup g : mappedGroups) { + UserGroup res = importGroup(g.getGroupName()); + returnSet.add(res); + } + } + } catch (BadRequestServiceEx e) { + LOGGER.error("Error while synchronizing groups.... Error is: ", e); + } + return returnSet; + } + + private UserGroup importGroup(GrantedAuthority a) throws BadRequestServiceEx { + return importGroup(a.getAuthority()); + } + + private UserGroup importGroup(String groupName) throws BadRequestServiceEx { + UserGroup group; + if (groupService != null) { + group = groupService.get(groupName); + + if (group == null) { + LOGGER.log(Level.INFO, "Creating new group from Keycloak: " + groupName); + group = new UserGroup(); + group.setGroupName(groupName); + long id = groupService.insert(group); + group = groupService.get(id); + } + } else { + group = new UserGroup(); + group.setGroupName(groupName); + } + return group; + } + + public void setUserService(UserService userService) { + this.userService = userService; + } + + public void setGroupService(UserGroupService groupService) { + this.groupService = groupService; + } + + private void addEveryOne(Set groups) { + String everyone = GroupReservedNames.EVERYONE.groupName(); + if (!groups.stream().anyMatch(g -> g.getGroupName().equals(everyone))) { + UserGroup everyoneGroup = new UserGroup(); + everyoneGroup.setEnabled(true); + everyoneGroup.setId(-1L); + everyoneGroup.setGroupName(GroupReservedNames.EVERYONE.groupName()); + groups.add(everyoneGroup); + } + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/GeoStoreKeycloakAuthenticator.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/GeoStoreKeycloakAuthenticator.java new file mode 100644 index 00000000..1aaa8e0e --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/GeoStoreKeycloakAuthenticator.java @@ -0,0 +1,65 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import javax.servlet.http.HttpServletRequest; +import org.keycloak.adapters.AdapterTokenStore; +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.adapters.OAuthRequestAuthenticator; +import org.keycloak.adapters.spi.HttpFacade; +import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticator; + +/** + * Custom {@link SpringSecurityRequestAuthenticator}. Takes care of performing the various + * authentication step against Keycloak. + */ +public class GeoStoreKeycloakAuthenticator extends SpringSecurityRequestAuthenticator { + /** + * Creates a new Spring Security request authenticator. + * + * @param facade the current HttpFacade (required) + * @param request the current HttpServletRequest (required) + * @param deployment the KeycloakDeployment (required) + * @param tokenStore the AdapterTokenStore (required) + * @param sslRedirectPort the SSL redirect port (required) + */ + public GeoStoreKeycloakAuthenticator( + HttpFacade facade, + HttpServletRequest request, + KeycloakDeployment deployment, + AdapterTokenStore tokenStore, + int sslRedirectPort) { + super(facade, request, deployment, tokenStore, sslRedirectPort); + } + + @Override + protected OAuthRequestAuthenticator createOAuthAuthenticator() { + return new GeoStoreOAuthAuthenticator( + this, facade, deployment, sslRedirectPort, tokenStore); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/GeoStoreKeycloakAuthoritiesMapper.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/GeoStoreKeycloakAuthoritiesMapper.java new file mode 100644 index 00000000..d9109371 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/GeoStoreKeycloakAuthoritiesMapper.java @@ -0,0 +1,197 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.enums.Role; +import it.geosolutions.geostore.core.security.GrantedAuthoritiesMapper; +import it.geosolutions.geostore.services.rest.utils.GeoStoreContext; +import java.util.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +/** + * A granted authorities mapper aware of the difference that there is in GeoStore between Role and + * Groups. + */ +public class GeoStoreKeycloakAuthoritiesMapper implements GrantedAuthoritiesMapper { + + private static final String ROLE_PREFIX = "ROLE_"; + private static final Logger LOGGER = + LogManager.getLogger(GeoStoreKeycloakAuthoritiesMapper.class); + private final Map roleMappings; + private final Map groupMappings; + private Set groups; + private Role role; + private boolean dropUnmapped = false; + private int idCounter; + + GeoStoreKeycloakAuthoritiesMapper( + Map roleMappings, + Map groupMappings, + boolean dropUnmapped) { + this.roleMappings = roleMappings; + this.groupMappings = groupMappings; + if (LOGGER.isDebugEnabled() && roleMappings != null) + LOGGER.debug("Role mappings have been configured...."); + this.idCounter = 1; + this.dropUnmapped = dropUnmapped; + } + + @Override + public Collection mapAuthorities( + Collection authorities) { + HashSet mapped = new HashSet<>(authorities.size()); + for (GrantedAuthority authority : authorities) { + GrantedAuthority newAuthority = mapAuthority(authority.getAuthority()); + if (newAuthority != null) mapped.add(newAuthority); + } + KeyCloakConfiguration configuration = GeoStoreContext.bean(KeyCloakConfiguration.class); + String defRoleStr = null; + if (configuration != null) { + Role def = configuration.getAuthenticatedDefaultRole(); + if (def == null) def = Role.USER; + defRoleStr = "ROLE_" + def.name(); + } + String finalDefRoleStr = defRoleStr; + if (defRoleStr != null + && !mapped.stream() + .anyMatch(ga -> ga.getAuthority().equalsIgnoreCase(finalDefRoleStr))) { + GrantedAuthority ga = new SimpleGrantedAuthority(defRoleStr); + mapped.add(ga); + } + if (getRole() == null) setDefaultRole(); + return mapped; + } + + void mapAuthorities(List authorities) { + if (authorities != null) authorities.forEach(r -> mapStringAuthority(r)); + if (getRole() == null) setDefaultRole(); + } + + private void setDefaultRole() { + KeyCloakConfiguration configuration = GeoStoreContext.bean(KeyCloakConfiguration.class); + Role role = configuration != null ? configuration.getAuthenticatedDefaultRole() : null; + setRole(role); + } + + private GrantedAuthority mapAuthority(String name) { + String authName = mapStringAuthority(name); + if (authName == null) return null; + return new SimpleGrantedAuthority(authName); + } + + private String mapStringAuthority(String name) { + name = name.toUpperCase(); + String authName; + if (roleMappings == null) authName = defaultRoleMatch(name); + else authName = userMappingsMatch(name); + if (authName == null) { + // if not a role then is a group. + authName = name; + if (groupMappings != null) authName = groupMappings.get(authName); + if (authName == null && !dropUnmapped) authName = name; + if (authName != null) addGroup(authName); + } + return authName; + } + + private String userMappingsMatch(String name) { + if (LOGGER.isDebugEnabled()) { + LOGGER.info("Using the configured role mappings.."); + } + String result = roleMappings.get(name); + if (result != null) { + try { + Role role = Role.valueOf(result); + if (getRole() == null || role.ordinal() < getRole().ordinal()) setRole(role); + result = ROLE_PREFIX + result; + } catch (Exception e) { + String message = + "The value " + + result + + " set to match role " + + name + + " is not a valid Role. You should use one of ADMIN,USER,GUEST"; + LOGGER.error(message, e); + throw new RuntimeException(message); + } + } + return result; + } + + private String defaultRoleMatch(String name) { + if (LOGGER.isDebugEnabled()) { + LOGGER.info("Matching the keycloak role to geostore roles based on name equality..."); + } + String result = null; + if (name.equalsIgnoreCase(Role.ADMIN.name())) { + result = ROLE_PREFIX + Role.ADMIN.name(); + setRole(Role.ADMIN); + } else if (name.equalsIgnoreCase(Role.USER.name())) { + result = ROLE_PREFIX + Role.USER.name(); + setRole(Role.USER); + } else if (name.equalsIgnoreCase(Role.GUEST.name())) { + result = ROLE_PREFIX + Role.GUEST.name(); + setRole(Role.GUEST); + } + return result; + } + + /** @return the found UserGroups. */ + public Set getGroups() { + if (groups == null) return new HashSet<>(); + return groups; + } + + /** @return the found role. */ + Role getRole() { + return role; + } + + private void setRole(Role role) { + if (this.role == null) this.role = role; + else if (this.role.ordinal() > role.ordinal()) this.role = role; + } + + private void addGroup(String groupName) { + if (this.groups == null) this.groups = new HashSet<>(); + UserGroup userGroup = new UserGroup(); + userGroup.setGroupName(groupName); + userGroup.setId(Long.valueOf(idCounter)); + idCounter++; + userGroup.setEnabled(true); + this.groups.add(userGroup); + } + + int getIdCounter() { + return idCounter; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/GeoStoreOAuthAuthenticator.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/GeoStoreOAuthAuthenticator.java new file mode 100644 index 00000000..db3e98a1 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/GeoStoreOAuthAuthenticator.java @@ -0,0 +1,107 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ + +package it.geosolutions.geostore.services.rest.security.keycloak; + +import it.geosolutions.geostore.services.rest.utils.GeoStoreContext; +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.adapters.OAuthRequestAuthenticator; +import org.keycloak.adapters.OIDCAuthenticationError; +import org.keycloak.adapters.RequestAuthenticator; +import org.keycloak.adapters.spi.AdapterSessionStore; +import org.keycloak.adapters.spi.AuthChallenge; +import org.keycloak.adapters.spi.HttpFacade; + +/** Custom OAuthRequestAuthenticator. Used to force the redirect URI to the configured one. */ +public class GeoStoreOAuthAuthenticator extends OAuthRequestAuthenticator { + + public GeoStoreOAuthAuthenticator( + RequestAuthenticator requestAuthenticator, + HttpFacade facade, + KeycloakDeployment deployment, + int sslRedirectPort, + AdapterSessionStore tokenStore) { + super(requestAuthenticator, facade, deployment, sslRedirectPort, tokenStore); + } + + @Override + protected AuthChallenge loginRedirect() { + final String state = getStateCode(); + final String redirect = getRedirectUri(state); + if (redirect == null) { + return challenge(403, OIDCAuthenticationError.Reason.NO_REDIRECT_URI, null); + } + return new AuthChallenge() { + + @Override + public int getResponseCode() { + return 0; + } + + @Override + public boolean challenge(HttpFacade exchange) { + tokenStore.saveRequest(); + exchange.getResponse().setStatus(302); + // the default keycloak authenticator set the path to / + // but this causes a bug for which the state cookie is overrided all the times by + // the keycloak + // server. Here we set it to null. + exchange.getResponse() + .setCookie( + deployment.getStateCookieName(), + state, + null, + null, + -1, + deployment + .getSslRequired() + .isRequired(facade.getRequest().getRemoteAddr()), + true); + exchange.getResponse().setHeader("Location", redirect); + return true; + } + }; + } + + @Override + protected String stripOauthParametersFromRedirect() { + String redirectUrl = super.stripOauthParametersFromRedirect(); + KeyCloakConfiguration configuration = GeoStoreContext.bean(KeyCloakConfiguration.class); + Boolean forceRedirectURI = configuration.getForceConfiguredRedirectURI(); + if (forceRedirectURI) return configuration.getRedirectUri(); + return redirectUrl; + } + + @Override + protected String getRequestUrl() { + KeyCloakConfiguration configuration = GeoStoreContext.bean(KeyCloakConfiguration.class); + String redirectUri = configuration.getRedirectUri(); + if (redirectUri != null && !"".equals(redirectUri)) return redirectUri; + return super.getRequestUrl(); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakConfiguration.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakConfiguration.java new file mode 100644 index 00000000..0f9bdd07 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakConfiguration.java @@ -0,0 +1,131 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import it.geosolutions.geostore.services.rest.security.IdPConfiguration; +import java.util.Map; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.keycloak.adapters.KeycloakDeploymentBuilder; +import org.keycloak.enums.TokenStore; +import org.keycloak.representations.adapters.config.AdapterConfig; + +/** KeyCloak Configuration. */ +public class KeyCloakConfiguration extends IdPConfiguration { + + private String jsonConfig; + + private AdapterConfig config; + + private Boolean forceConfiguredRedirectURI; + + private Map roleMappings; + + private Map groupMappings; + + private boolean dropUnmapped = false; + + /** @return the JSON config, obtained at client configuration time from Keycloak. */ + public String getJsonConfig() { + return jsonConfig; + } + + /** + * Set the JsonConfig. + * + * @param jsonConfig the jsonConfig as a string. + */ + public void setJsonConfig(String jsonConfig) { + this.jsonConfig = jsonConfig; + if (config != null && StringUtils.isNotBlank(this.jsonConfig)) { + config = + KeycloakDeploymentBuilder.loadAdapterConfig( + IOUtils.toInputStream(getJsonConfig())); + } + } + + private Map toMap(String mappings) { + if (mappings != null) { + String[] keyValues = mappings.split(","); + Map map = new AuthoritiesMappings(keyValues.length); + for (String keyValue : keyValues) { + String[] keyValueAr = keyValue.split(":"); + map.put(keyValueAr[0], keyValueAr[1]); + } + return map; + } + return null; + } + + public Map getRoleMappings() { + return roleMappings; + } + + public void setRoleMappings(String roleMappings) { + this.roleMappings = toMap(roleMappings); + } + + public Map getGroupMappings() { + return groupMappings; + } + + public void setGroupMappings(String groupMappings) { + this.groupMappings = toMap(groupMappings); + } + + public boolean isDropUnmapped() { + return dropUnmapped; + } + + public void setDropUnmapped(boolean dropUnmapped) { + this.dropUnmapped = dropUnmapped; + } + + /** + * Read the adapter config from the json. + * + * @return an {@link AdapterConfig} instance. + */ + public AdapterConfig readAdapterConfig() { + String jsonConfig = getJsonConfig(); + if (config == null && StringUtils.isNotBlank(jsonConfig)) { + config = KeycloakDeploymentBuilder.loadAdapterConfig(IOUtils.toInputStream(jsonConfig)); + config.setTokenStore(TokenStore.COOKIE.name()); + } + return config; + } + + public Boolean getForceConfiguredRedirectURI() { + if (forceConfiguredRedirectURI == null) return false; + return forceConfiguredRedirectURI; + } + + public void setForceConfiguredRedirectURI(Boolean forceConfiguredRedirectURI) { + this.forceConfiguredRedirectURI = forceConfiguredRedirectURI; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakFilter.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakFilter.java new file mode 100644 index 00000000..d89fd354 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakFilter.java @@ -0,0 +1,215 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import static it.geosolutions.geostore.services.rest.SessionServiceDelegate.PROVIDER_KEY; +import static it.geosolutions.geostore.services.rest.security.keycloak.KeyCloakLoginService.KEYCLOAK_REDIRECT; +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.*; +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.getResponse; + +import it.geosolutions.geostore.services.UserService; +import it.geosolutions.geostore.services.rest.security.TokenAuthenticationCache; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils; +import it.geosolutions.geostore.services.rest.utils.GeoStoreContext; +import java.io.IOException; +import java.util.Date; +import java.util.Objects; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.adapters.RequestAuthenticator; +import org.keycloak.adapters.spi.AuthOutcome; +import org.keycloak.adapters.spi.HttpFacade; +import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.filter.GenericFilterBean; + +/** + * Keycloak Authentication Filter. Manage the logic to authenticate a user against a keycloak + * server. + */ +@SuppressWarnings("PMD.UnusedLocalVariable") +public class KeyCloakFilter extends GenericFilterBean { + + private static final Logger LOGGER = LogManager.getLogger(KeyCloakFilter.class); + // used to map keycloak roles to spring-security roles + private final GeoStoreKeycloakAuthProvider authenticationProvider; + // creates token stores capable of generating spring-security tokens from keycloak auth + // the context of the keycloak environment (realm, URL, client-secrets etc.) + private final KeyCloakHelper helper; + private final KeyCloakConfiguration configuration; + private final TokenAuthenticationCache cache; + @Autowired protected UserService userService; + + /** + * @param helper a {@link KeyCloakHelper} instance. + * @param cache an instance of {@link TokenAuthenticationCache} to cache authentication objects. + * @param configuration the {@link KeyCloakConfiguration} for this geostore instance. + * @param authenticationProvider the authentication provider to map the Keycloak Authentication + * to the GeoStore one. + */ + public KeyCloakFilter( + KeyCloakHelper helper, + TokenAuthenticationCache cache, + KeyCloakConfiguration configuration, + GeoStoreKeycloakAuthProvider authenticationProvider) { + this.helper = helper; + this.authenticationProvider = authenticationProvider; + this.cache = cache; + this.configuration = configuration; + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + if (enabledAndValid() && SecurityContextHolder.getContext().getAuthentication() == null) { + Authentication authentication = + authenticate((HttpServletRequest) request, (HttpServletResponse) response); + if (authentication != null) { + SecurityContextHolder.getContext().setAuthentication(authentication); + if (authentication.getDetails() instanceof KeycloakTokenDetails) { + KeycloakTokenDetails details = + (KeycloakTokenDetails) authentication.getDetails(); + if (details.getAccessToken() != null) + RequestContextHolder.getRequestAttributes() + .setAttribute(ACCESS_TOKEN_PARAM, details.getAccessToken(), 0); + if (details.getRefreshToken() != null) + RequestContextHolder.getRequestAttributes() + .setAttribute(REFRESH_TOKEN_PARAM, details.getRefreshToken(), 0); + } + } + RequestContextHolder.getRequestAttributes().setAttribute(PROVIDER_KEY, "keycloak", 0); + } + chain.doFilter(request, response); + } + + private boolean enabledAndValid() { + return configuration.isEnabled() && configuration.getJsonConfig() != null; + } + + /** + * Perform the authentication and updates the cache. + * + * @param request the request. + * @param response the response. + * @return the authentication object. Can be null if the user is not authenticated. + */ + protected Authentication authenticateAndUpdateCache( + HttpServletRequest request, HttpServletResponse response) { + // do some setup and create the authenticator + KeycloakDeployment deployment = helper.getDeployment(request, response); + RequestAuthenticator authenticator = helper.getAuthenticator(request, response, deployment); + // perform the authentication operation + AuthOutcome result = authenticator.authenticate(); + Authentication auth = null; + if (result.equals(AuthOutcome.AUTHENTICATED)) { + auth = SecurityContextHolder.getContext().getAuthentication(); + auth = authenticationProvider.authenticate(auth); + updateCache(auth); + } else if (result.equals(AuthOutcome.NOT_ATTEMPTED)) { + AuthenticationEntryPoint entryPoint; + if (deployment.isBearerOnly()) { + // if bearer-only, then missing auth means you are forbidden + entryPoint = new KeycloakAuthenticationEntryPoint(null); + } else { + entryPoint = new KeycloakAuthenticationEntryPoint(authenticator.getChallenge()); + } + Objects.requireNonNull(RequestContextHolder.getRequestAttributes()) + .setAttribute(KEYCLOAK_REDIRECT, entryPoint, RequestAttributes.SCOPE_REQUEST); + } else { + LOGGER.warn("Failed to authentication and to redirect the user."); + } + return auth; + } + + /** + * Updates the cache with the new Authentication entry. + * + * @param authentication the new Authentication entry. + */ + protected void updateCache(Authentication authentication) { + Object details = authentication.getDetails(); + if (details instanceof KeycloakTokenDetails) { + KeyCloakHelper helper = GeoStoreContext.bean(KeyCloakHelper.class); + KeycloakTokenDetails keycloakDetails = (KeycloakTokenDetails) details; + String accessToken = keycloakDetails.getAccessToken(); + if (accessToken != null) { + cache.putCacheEntry(accessToken, authentication); + if (helper != null) { + HttpFacade facade = new SimpleHttpFacade(getRequest(), getResponse()); + KeycloakDeployment deployment = helper.getDeployment(facade); + KeycloakCookieUtils.setTokenCookie(deployment, facade, keycloakDetails); + } + } + } + } + + /** + * Performs the authentication. The method will check the cache before calling keycloak. If the + * token is expired, a new authentication is anyway issued and the cache updated. + * + * @param request the request. + * @param response the response. + * @return the authentication. + */ + protected Authentication authenticate( + HttpServletRequest request, HttpServletResponse response) { + Authentication authentication = null; + String token = OAuth2Utils.tokenFromParamsOrBearer(ACCESS_TOKEN_PARAM, request); + if (token != null) { + authentication = cache.get(token); + if (authentication != null + && authentication.getDetails() instanceof KeycloakTokenDetails) { + KeycloakTokenDetails details = (KeycloakTokenDetails) authentication.getDetails(); + if (details.getExpiration().before(new Date())) { + LOGGER.warn( + "Token has expired and the refresh token endpoint has not been called. The request will not be authorized by the keycloak filter"); + cache.removeEntry(details.getAccessToken()); + authentication = null; + } + } + if (authentication == null) { + authentication = authenticateAndUpdateCache(request, response); + } + } else { + authentication = authenticateAndUpdateCache(request, response); + } + return authentication; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakHelper.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakHelper.java new file mode 100644 index 00000000..759bbf0a --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakHelper.java @@ -0,0 +1,194 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import it.geosolutions.geostore.services.rest.security.TokenAuthenticationCache; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.keycloak.adapters.AdapterDeploymentContext; +import org.keycloak.adapters.AdapterTokenStore; +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.adapters.RequestAuthenticator; +import org.keycloak.adapters.spi.HttpFacade; +import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade; +import org.keycloak.adapters.springsecurity.token.SpringSecurityAdapterTokenStoreFactory; +import org.keycloak.authorization.client.Configuration; +import org.keycloak.authorization.client.util.Http; +import org.keycloak.enums.TokenStore; +import org.keycloak.representations.AccessTokenResponse; +import org.keycloak.representations.adapters.config.AdapterConfig; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; + +/** This class provides some utility methods to deal with Keycloak Authentication. */ +public class KeyCloakHelper { + + private static final Logger LOGGER = LogManager.getLogger(KeycloakSessionServiceDelegate.class); + + protected final SpringSecurityAdapterTokenStoreFactory adapterTokenStoreFactory; + protected AdapterDeploymentContext keycloakContext; + + public KeyCloakHelper(AdapterDeploymentContext keycloakContext) { + this.adapterTokenStoreFactory = new SpringSecurityAdapterTokenStoreFactory(); + this.keycloakContext = keycloakContext; + } + + /** + * @param request request. + * @param response response. + * @return return a KeycloakDeployment instance. + */ + public KeycloakDeployment getDeployment( + HttpServletRequest request, HttpServletResponse response) { + HttpFacade exchange = new SimpleHttpFacade(request, response); + return getDeployment(exchange); + } + + /** + * @param exchange the httpFacade. + * @return return a KeycloakDeployment instance. + */ + public KeycloakDeployment getDeployment(HttpFacade exchange) { + KeycloakDeployment deployment = keycloakContext.resolveDeployment(exchange); + deployment.setTokenStore(TokenStore.COOKIE); + deployment.setDelegateBearerErrorResponseSending(true); + return deployment; + } + + /** + * Return the request authenticator that will be used by the filter to perform the various + * authentication steps. + * + * @param request the request. + * @param response the response. + * @param deployment the deployment instance. + * @return the request authenticator. + */ + public RequestAuthenticator getAuthenticator( + HttpServletRequest request, + HttpServletResponse response, + KeycloakDeployment deployment) { + request = new KeyCloakRequestWrapper(request); + AdapterTokenStore tokenStore = + adapterTokenStoreFactory.createAdapterTokenStore(deployment, request, response); + SimpleHttpFacade simpleHttpFacade = new SimpleHttpFacade(request, response); + return new GeoStoreKeycloakAuthenticator( + simpleHttpFacade, request, deployment, tokenStore, -1); + } + + /** + * Issue a refresh token operation. + * + * @return the new Authentication instance with new tokens if they were expired. + */ + public AccessTokenResponse refreshToken(AdapterConfig adapter, String refreshToken) { + Configuration clientConf = getClientConfiguration(adapter); + String url = + adapter.getAuthServerUrl() + + "/realms/" + + adapter.getRealm() + + "/protocol/openid-connect/token"; + String clientId = adapter.getResource(); + String secret = (String) adapter.getCredentials().get("secret"); + Http http = new Http(clientConf, (params, headers) -> {}); + + return http.post(url) + .authentication() + .client() + .form() + .param("grant_type", "refresh_token") + .param("refresh_token", refreshToken) + .param("client_id", clientId) + .param("client_secret", secret) + .response() + .json(AccessTokenResponse.class) + .execute(); + } + + /** + * Build a Configuration instance out of a specific AdapterConfig. + * + * @param config the AdapterConfig. + * @return the Configuration instance. + */ + public Configuration getClientConfiguration(AdapterConfig config) { + String serverUrl = config.getAuthServerUrl(); + String realm = config.getRealm(); + String resource = config.getResource(); + Map credentials = config.getCredentials(); + return new Configuration(serverUrl, realm, resource, credentials, null); + } + + // + + /** + * Builds an authentication instance out of the passed values. Sets it to the cache and to the + * SecurityContext to be sure the new token is updates. + * + * @param cache the auth cache. + * @param oldToken the old token. + * @param newToken the new token. + * @param expiresIn the expiration of the new token. + * @return the new Authentication object. + */ + public Authentication updateAuthentication( + TokenAuthenticationCache cache, + String oldToken, + String newToken, + String refreshToken, + long expiresIn) { + Authentication authentication = cache.get(oldToken); + if (authentication == null) + authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (authentication instanceof PreAuthenticatedAuthenticationToken) { + if (LOGGER.isDebugEnabled()) + LOGGER.info("Updating the cache and the SecurityContext with new Auth details"); + cache.removeEntry(oldToken); + + PreAuthenticatedAuthenticationToken updated = + new PreAuthenticatedAuthenticationToken( + authentication.getPrincipal(), + authentication.getCredentials(), + authentication.getAuthorities()); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Updating keycloak details."); + KeycloakTokenDetails details = + new KeycloakTokenDetails(newToken, refreshToken, expiresIn); + updated.setDetails(details); + + cache.putCacheEntry(newToken, updated); + SecurityContextHolder.getContext().setAuthentication(updated); + authentication = updated; + } + return authentication; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakLoginService.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakLoginService.java new file mode 100644 index 00000000..77f9d976 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakLoginService.java @@ -0,0 +1,114 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.getAccessToken; +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.getRefreshAccessToken; + +import it.geosolutions.geostore.services.rest.IdPLoginRest; +import it.geosolutions.geostore.services.rest.security.oauth2.Oauth2LoginService; +import java.io.IOException; +import java.util.Objects; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.Response; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +/** + * Keycloak implementation for a LoginService. Since keycloak redirects to the url from which the + * call to the authorization page was issued, no internal redirect is really performed here. + */ +public class KeyCloakLoginService extends Oauth2LoginService { + + private static final Logger LOGGER = LogManager.getLogger(KeyCloakLoginService.class); + + static String KEYCLOAK_REDIRECT = "KEYCLOAK_REDIRECT"; + + public KeyCloakLoginService(IdPLoginRest loginRest) { + loginRest.registerService("keycloak", this); + } + + @Override + public void doLogin(HttpServletRequest request, HttpServletResponse response, String provider) { + KeycloakTokenDetails details = getDetails(); + boolean attempInternalRedirect = (details != null && details.getAccessToken() != null); + + AuthenticationEntryPoint challenge = + (AuthenticationEntryPoint) + Objects.requireNonNull(RequestContextHolder.getRequestAttributes()) + .getAttribute(KEYCLOAK_REDIRECT, RequestAttributes.SCOPE_REQUEST); + if (challenge == null) attempInternalRedirect = true; + else if (!attempInternalRedirect) { + try { + challenge.commence(request, response, null); + attempInternalRedirect = false; + } catch (Exception e) { + LOGGER.error("Error while redirecting to Keycloak authorization.", e); + throw new RuntimeException(e); + } + } + + if (attempInternalRedirect) { + try { + response.sendRedirect(configuration(provider).getInternalRedirectUri()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public Response doInternalRedirect( + HttpServletRequest request, HttpServletResponse response, String provider) { + String token; + String refreshToken; + KeycloakTokenDetails details = getDetails(); + if (details != null) { + token = details.getAccessToken(); + refreshToken = details.getRefreshToken(); + } else { + token = getAccessToken(); + refreshToken = getRefreshAccessToken(); + } + return buildCallbackResponse(token, refreshToken, provider); + } + + private KeycloakTokenDetails getDetails() { + KeycloakTokenDetails result = null; + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth != null && auth.getDetails() instanceof KeycloakTokenDetails) + result = (KeycloakTokenDetails) auth.getDetails(); + return result; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakRequestWrapper.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakRequestWrapper.java new file mode 100644 index 00000000..2bcbe6db --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakRequestWrapper.java @@ -0,0 +1,55 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +/** A request wrapper to fix the url when using nginx. */ +public class KeyCloakRequestWrapper extends HttpServletRequestWrapper { + /** + * Constructs a request object wrapping the given request. + * + * @param request + * @throws IllegalArgumentException if the request is null + */ + public KeyCloakRequestWrapper(HttpServletRequest request) { + super(request); + } + + @Override + public StringBuffer getRequestURL() { + String url = super.getRequestURL().toString(); + String proto = super.getHeader("x-forwarded-proto"); + + if (proto != null && url.startsWith("http://") && proto.equals("https")) { + url = url.replaceAll("^http", "https"); + } + return new StringBuffer(url); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakSecurityConfiguration.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakSecurityConfiguration.java new file mode 100644 index 00000000..dd0a1513 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeyCloakSecurityConfiguration.java @@ -0,0 +1,92 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Configuration.CONFIG_NAME_SUFFIX; + +import it.geosolutions.geostore.services.rest.security.TokenAuthenticationCache; +import org.keycloak.adapters.AdapterDeploymentContext; +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.adapters.KeycloakDeploymentBuilder; +import org.keycloak.representations.adapters.config.AdapterConfig; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration("keycloakConfig") +public class KeyCloakSecurityConfiguration { + + public static final String CACHE_BEAN_NAME = "keycloakCache"; + static final String CONF_BEAN_NAME = "keycloak" + CONFIG_NAME_SUFFIX; + + @Bean(value = CONF_BEAN_NAME) + public KeyCloakConfiguration keycloakConfiguration() { + return new KeyCloakConfiguration(); + } + + @Bean + public KeycloakAdminClientConfiguration keycloakRESTClient() { + return new KeycloakAdminClientConfiguration(); + } + + @Bean + public KeyCloakFilter keycloakFilter() { + return new KeyCloakFilter( + keyCloakHelper(), + keycloakCache(), + keycloakConfiguration(), + keycloakAuthenticationProvider()); + } + + @Bean + public GeoStoreKeycloakAuthProvider keycloakAuthenticationProvider() { + return new GeoStoreKeycloakAuthProvider(keycloakConfiguration()); + } + + @Bean(value = CACHE_BEAN_NAME) + public TokenAuthenticationCache keycloakCache() { + return new TokenAuthenticationCache(); + } + + @Bean + public AdapterDeploymentContext keycloackContext() { + AdapterConfig config = keycloakConfiguration().readAdapterConfig(); + AdapterDeploymentContext context; + if (config != null) { + KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config); + context = new AdapterDeploymentContext(deployment); + } else { + context = new AdapterDeploymentContext(); + } + return context; + } + + @Bean + public KeyCloakHelper keyCloakHelper() { + return new KeyCloakHelper(keycloackContext()); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakAdminClientConfiguration.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakAdminClientConfiguration.java new file mode 100644 index 00000000..b468e3bc --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakAdminClientConfiguration.java @@ -0,0 +1,119 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; +import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.KeycloakBuilder; + +/** + * Keycloak Admin REST client configuration class. Used to configure the {@link KeycloakUserDAO} and + * {@link KeycloakUserGroupDAO}. + */ +public class KeycloakAdminClientConfiguration { + + private Keycloak keycloak; + + private String serverUrl; + + private String realm; + + private String username; + + private String password; + + private String clientId; + + /** @return the keycloak server url. */ + public String getServerUrl() { + return serverUrl; + } + + /** @param serverUrl the keycloak server url. */ + public void setServerUrl(String serverUrl) { + this.serverUrl = serverUrl; + } + + /** @return the realm from which retrieve users and groups. */ + public String getRealm() { + return realm; + } + + /** @param realm the realm from which retrieve users and groups. */ + public void setRealm(String realm) { + this.realm = realm; + } + + /** @return the username of a keycloak admin. */ + public String getUsername() { + return username; + } + + /** @param username the username of a keycloak admin. */ + public void setUsername(String username) { + this.username = username; + } + + /** @return the pwd of a keycloak admin. */ + public String getPassword() { + return password; + } + + /** @param password the pwd of a keycloak admin. */ + public void setPassword(String password) { + this.password = password; + } + + /** @return The client id of the client web app configured. */ + public String getClientId() { + return clientId; + } + + /** @param clientId The client id of the client web app configured. */ + public void setClientId(String clientId) { + this.clientId = clientId; + } + + /** @return the {@link Keycloak} REST client. */ + public Keycloak getKeycloak() { + if (keycloak == null) this.keycloak = buildKeycloak(); + return keycloak; + } + + private Keycloak buildKeycloak() { + KeycloakBuilder keycloakBuilder = KeycloakBuilder.builder(); + return keycloakBuilder + .serverUrl(getServerUrl()) + .realm(getRealm()) + .clientId(getClientId()) + .username(getUsername()) + .password(getPassword()) + .resteasyClient(ResteasyClientBuilder.newClient()) + .build(); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakAuthenticationEntryPoint.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakAuthenticationEntryPoint.java new file mode 100644 index 00000000..9ce88d91 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakAuthenticationEntryPoint.java @@ -0,0 +1,63 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.keycloak.adapters.spi.AuthChallenge; +import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; + +/** + * AuthenticationEntryPoint that execute the Keycloak challenge and redirect to the Keycloak login + * page. + */ +class KeycloakAuthenticationEntryPoint implements AuthenticationEntryPoint { + + private final AuthChallenge challenge; + + KeycloakAuthenticationEntryPoint(AuthChallenge challenge) { + this.challenge = challenge; + } + + @Override + public void commence( + HttpServletRequest request, + HttpServletResponse response, + AuthenticationException authException) + throws IOException, ServletException { + if (challenge == null) + throw new RuntimeException( + "Keycloak config is bearer only. No redirect to authorization page can be performed."); + challenge.challenge(new SimpleHttpFacade(request, response)); + response.sendRedirect(response.getHeader("Location")); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakCookieUtils.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakCookieUtils.java new file mode 100644 index 00000000..9eae50f2 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakCookieUtils.java @@ -0,0 +1,86 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.adapters.spi.HttpFacade; +import org.keycloak.common.util.KeycloakUriBuilder; +import org.keycloak.constants.AdapterConstants; + +/** Utility class that provides method to update the Keycloak Adapter cookie. */ +class KeycloakCookieUtils { + + private static final String SEPARATOR = "___"; + + static void setTokenCookie( + KeycloakDeployment deployment, HttpFacade facade, KeycloakTokenDetails tokenDetails) { + String accessToken = tokenDetails.getAccessToken(); + String idToken = tokenDetails.getIdToken(); + String refreshToken = tokenDetails.getRefreshToken(); + String cookie = accessToken + SEPARATOR + idToken + SEPARATOR + refreshToken; + + String cookiePath = getCookiePath(deployment, facade); + // Forces the expiration of the old keycloak cookie after refresh token. Keycloak doesn't do + // it for us. + facade.getResponse() + .setCookie( + AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE, + cookie, + cookiePath, + null, + 0, + deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr()), + true); + } + + static String getCookiePath(KeycloakDeployment deployment, HttpFacade facade) { + String path = + deployment.getAdapterStateCookiePath() == null + ? "" + : deployment.getAdapterStateCookiePath().trim(); + if (path.startsWith("/")) { + return path; + } + String contextPath = getContextPath(facade); + StringBuilder cookiePath = new StringBuilder(contextPath); + if (!contextPath.endsWith("/") && !path.isEmpty()) { + cookiePath.append("/"); + } + return cookiePath.append(path).toString(); + } + + static String getContextPath(HttpFacade facade) { + String uri = facade.getRequest().getURI(); + String path = KeycloakUriBuilder.fromUri(uri).getPath(); + if (path == null || path.isEmpty()) { + return "/"; + } + int index = path.indexOf("/", 1); + return index == -1 ? path : path.substring(0, index); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakQuery.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakQuery.java new file mode 100644 index 00000000..665c237b --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakQuery.java @@ -0,0 +1,141 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +/** A convenience class to be used to represent a query to the keycloak REST api. */ +public class KeycloakQuery { + + private Boolean exact; + + private Integer startIndex; + + private Integer maxResults; + + private String groupName; + + private String userName; + + private Boolean enabled; + + private Boolean skipEveryBodyGroup; + + /** @return the group name if any, null otherwise. */ + public String getGroupName() { + return groupName; + } + + /** @param groupName the group name value to filter by. */ + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + /** @return the user name if any, null otherwise. */ + public String getUserName() { + return userName; + } + + /** @param userName the user name value to filter by. */ + public void setUserName(String userName) { + this.userName = userName; + } + + /** @return the enabled flag of a user if any, null otherwise. */ + public Boolean getEnabled() { + return enabled; + } + + /** @param enabled the enabled value to filter by. */ + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + /** @return true if the query is an = query false or null if it is a LIKE one. */ + public Boolean getExact() { + return exact; + } + + /** @return the start index if any, null otherwise. */ + public Integer getStartIndex() { + return startIndex; + } + + /** @param startIndex the start index of a page. */ + public void setStartIndex(Integer startIndex) { + this.startIndex = startIndex; + } + + /** @return the max result per page value if any, null otherwise. */ + public Integer getMaxResults() { + return maxResults; + } + + /** @param maxResults the max result number per page. */ + public void setMaxResults(Integer maxResults) { + this.maxResults = maxResults; + } + + /** @return true if the query is a = query, false if it is a LIKE one. */ + public boolean isExact() { + return exact != null && exact.booleanValue(); + } + + /** @param exact the exact flag. */ + public void setExact(Boolean exact) { + this.exact = exact; + } + + public Boolean getSkipEveryBodyGroup() { + return skipEveryBodyGroup != null && skipEveryBodyGroup.booleanValue(); + } + + public void setSkipEveryBodyGroup(Boolean skipEveryBodyGroup) { + this.skipEveryBodyGroup = skipEveryBodyGroup; + } + + @Override + public String toString() { + return "KeycloakQuery{" + + "exact=" + + exact + + ", startIndex=" + + startIndex + + ", maxResults=" + + maxResults + + ", groupName='" + + groupName + + '\'' + + ", userName='" + + userName + + '\'' + + ", enabled=" + + enabled + + ", skipEveryBodyGroup=" + + skipEveryBodyGroup + + '}'; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakSearchMapper.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakSearchMapper.java new file mode 100644 index 00000000..110823f3 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakSearchMapper.java @@ -0,0 +1,150 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import static it.geosolutions.geostore.core.model.enums.GroupReservedNames.EVERYONE; + +import com.googlecode.genericdao.search.Filter; +import com.googlecode.genericdao.search.ISearch; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** Class responsible to map a Search to a KeycloakQuery. */ +class KeycloakSearchMapper { + + private static final List SUPPORTED_OPERATORS = + Arrays.asList(Filter.OP_AND, Filter.OP_EQUAL, Filter.OP_ILIKE, Filter.OP_NOT_EQUAL); + + /** + * Convert the ISearch to a keycloak query. + * + * @param search the search instance. + * @return the keycloak query. + */ + KeycloakQuery keycloackQuery(ISearch search) { + KeycloakQuery query = new KeycloakQuery(); + search = getNestedSearch(search); + List filters = search.getFilters(); + boolean allSupported = + filters.stream().allMatch(f -> SUPPORTED_OPERATORS.contains(f.getOperator())); + if (!allSupported) { + throw new UnsupportedOperationException( + "Keycloak DAO cannot filter for more then one value"); + } + if (!filters.isEmpty()) { + for (Filter filter : filters) processFilter(query, filter); + } + Integer page = search.getPage(); + Integer maxRes = search.getMaxResults(); + if (page > -1) { + int startIndex = maxRes != null ? page * maxRes : page; + if (startIndex > 0) startIndex++; + if (maxRes != null) query.setStartIndex(startIndex); + } + if (maxRes > -1) query.setMaxResults(maxRes); + return query; + } + + private void processFilter(KeycloakQuery query, Filter filter) { + if (filter.getOperator() == Filter.OP_AND) return; + else if (filter.getOperator() == Filter.OP_EQUAL) query.setExact(true); + else if (filter.getOperator() != Filter.OP_ILIKE + && filter.getOperator() != Filter.OP_NOT_EQUAL) + throw new UnsupportedOperationException( + "Keycloak DAO supports only EQUAL and LIKE operators"); + + if (filter.getOperator() == Filter.OP_NOT_EQUAL + && filter.getValue().toString().equalsIgnoreCase(EVERYONE.groupName())) + query.setSkipEveryBodyGroup(true); + else { + String property = filter.getProperty().toUpperCase(); + FilterType type; + try { + type = FilterType.valueOf(property); + } catch (UnsupportedOperationException e) { + type = FilterType.NOT_FOUND; + } + switch (type) { + case NAME: + query.setUserName(filter.getValue().toString()); + break; + case GROUPNAME: + query.setGroupName(filter.getValue().toString()); + break; + case ENABLED: + query.setEnabled(Boolean.valueOf(filter.getValue().toString())); + break; + default: + break; + } + } + } + + /** + * If the given search has filters working on nested objects, replaces them with the nested + * filter. + * + * @param search + * @return + */ + protected ISearch getNestedSearch(ISearch search) { + List newFilters = new ArrayList<>(); + for (Filter filter : search.getFilters()) { + Filter nestedFilter = getNestedFilter(filter); + if (nestedFilter != null) { + newFilters.add(nestedFilter); + } else { + newFilters.add(filter); + } + } + search.getFilters().clear(); + search.getFilters().addAll(newFilters); + return search; + } + + /** + * Returns the internal filter of a nested filter, null otherwise. + * + * @param filter the filter. + * @return the nested filter if any null otherwise. + */ + protected Filter getNestedFilter(Filter filter) { + if (filter.getOperator() == Filter.OP_SOME || filter.getOperator() == Filter.OP_ALL) { + return (Filter) filter.getValue(); + } + return null; + } + + enum FilterType { + NAME, + GROUPNAME, + ENABLED, + NOT_FOUND + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakSessionServiceDelegate.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakSessionServiceDelegate.java new file mode 100644 index 00000000..bb3582bc --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakSessionServiceDelegate.java @@ -0,0 +1,228 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import static it.geosolutions.geostore.services.rest.security.keycloak.KeyCloakSecurityConfiguration.CACHE_BEAN_NAME; +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.*; + +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.security.password.SecurityUtils; +import it.geosolutions.geostore.services.UserService; +import it.geosolutions.geostore.services.rest.RESTSessionService; +import it.geosolutions.geostore.services.rest.SessionServiceDelegate; +import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; +import it.geosolutions.geostore.services.rest.model.SessionToken; +import it.geosolutions.geostore.services.rest.security.TokenAuthenticationCache; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils; +import it.geosolutions.geostore.services.rest.utils.GeoStoreContext; +import java.util.Date; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.keycloak.adapters.AdapterTokenStore; +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.adapters.spi.HttpFacade; +import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade; +import org.keycloak.adapters.springsecurity.token.SpringSecurityAdapterTokenStoreFactory; +import org.keycloak.authorization.client.Configuration; +import org.keycloak.authorization.client.util.Http; +import org.keycloak.representations.AccessTokenResponse; +import org.keycloak.representations.adapters.config.AdapterConfig; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * Keycloak implementation of SessionService delegate to provide method of refreshing the token and + * logging out. + */ +public class KeycloakSessionServiceDelegate implements SessionServiceDelegate { + + private static final Logger LOGGER = LogManager.getLogger(KeycloakSessionServiceDelegate.class); + + private final UserService userService; + + public KeycloakSessionServiceDelegate( + RESTSessionService restSessionService, UserService userService) { + restSessionService.registerDelegate("keycloak", this); + this.userService = userService; + } + + @Override + public SessionToken refresh(String refreshToken, String accessToken) { + HttpServletRequest request = getRequest(); + if (accessToken == null) accessToken = tokenFromParamsOrBearer(ACCESS_TOKEN_PARAM, request); + if (accessToken == null) throw new NotFoundWebEx("The accessToken is missing"); + if (refreshToken == null) refreshToken = getParameterValue(REFRESH_TOKEN_PARAM, request); + TokenAuthenticationCache cache = cache(); + Date tokenExpiration = tokenExpirationTime(accessToken, cache); + Date fiveMinutesFromNow = OAuth2Utils.fiveMinutesFromNow(); + SessionToken sessionToken; + if (refreshToken != null + && (tokenExpiration == null || fiveMinutesFromNow.after(tokenExpiration))) + sessionToken = doRefresh(accessToken, refreshToken, cache); + else sessionToken = sessionToken(accessToken, refreshToken, null); + return sessionToken; + } + + private SessionToken doRefresh( + String accessToken, String refreshToken, TokenAuthenticationCache cache) { + KeyCloakConfiguration configuration = GeoStoreContext.bean(KeyCloakConfiguration.class); + AdapterConfig adapter = configuration.readAdapterConfig(); + KeyCloakHelper helper = GeoStoreContext.bean(KeyCloakHelper.class); + AccessTokenResponse response = helper.refreshToken(adapter, refreshToken); + String newAccessToken = response.getToken(); + long exp = response.getExpiresIn(); + String newRefreshToken = response.getRefreshToken(); + Authentication updated = + helper.updateAuthentication( + cache, accessToken, newAccessToken, newRefreshToken, exp); + HttpFacade facade = new SimpleHttpFacade(getRequest(), getResponse()); + KeycloakDeployment deployment = helper.getDeployment(facade); + KeycloakCookieUtils.setTokenCookie( + deployment, facade, (KeycloakTokenDetails) updated.getDetails()); + return sessionToken(newAccessToken, newRefreshToken); + } + + private Date tokenExpirationTime(String accessToken, TokenAuthenticationCache cache) { + Date result = null; + Authentication authentication = cache.get(accessToken); + if (authentication != null && authentication.getDetails() instanceof KeycloakTokenDetails) { + KeycloakTokenDetails details = (KeycloakTokenDetails) authentication.getDetails(); + result = details.getExpiration(); + } + return result; + } + + private SessionToken sessionToken(String accessToken, String refreshToken) { + return sessionToken(accessToken, refreshToken, null); + } + + private SessionToken sessionToken(String accessToken, String refreshToken, Date expires) { + SessionToken sessionToken = new SessionToken(); + sessionToken.setAccessToken(accessToken); + sessionToken.setRefreshToken(refreshToken); + if (expires != null) sessionToken.setExpires(expires.getTime()); + sessionToken.setTokenType("bearer"); + return sessionToken; + } + + @Override + public void doLogout(String sessionId) { + HttpServletRequest request = OAuth2Utils.getRequest(); + HttpServletResponse response = OAuth2Utils.getResponse(); + KeyCloakHelper helper = GeoStoreContext.bean(KeyCloakHelper.class); + KeycloakDeployment deployment = helper.getDeployment(request, response); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String refreshToken = null; + if (authentication.getDetails() instanceof KeycloakTokenDetails) { + refreshToken = ((KeycloakTokenDetails) authentication.getDetails()).getRefreshToken(); + } + String logoutUrl = deployment.getLogoutUrl().build().toString(); + AdapterConfig adapterConfig = + GeoStoreContext.bean(KeyCloakConfiguration.class).readAdapterConfig(); + Configuration clientConfiguration = helper.getClientConfiguration(adapterConfig); + Http http = new Http(clientConfiguration, (params, headers) -> {}); + String clientId = adapterConfig.getResource(); + String secret = (String) adapterConfig.getCredentials().get("secret"); + try { + http.post(logoutUrl) + .form() + .param("client_id", clientId) + .param("client_secret", secret) + .param("refresh_token", refreshToken) + .execute(); + } catch (Exception e) { + LOGGER.error("Error while performing global logout.", e); + } + SpringSecurityAdapterTokenStoreFactory factory = + new SpringSecurityAdapterTokenStoreFactory(); + AdapterTokenStore tokenStore = + factory.createAdapterTokenStore(deployment, getRequest(), getResponse()); + if (tokenStore != null) tokenStore.logout(); + internalLogout(sessionId, request, response); + } + + private void internalLogout( + String accessToken, HttpServletRequest request, HttpServletResponse response) { + TokenAuthenticationCache cache = + GeoStoreContext.bean(CACHE_BEAN_NAME, TokenAuthenticationCache.class); + if (cache.get(accessToken) != null) cache.removeEntry(accessToken); + SecurityContextHolder.clearContext(); + try { + HttpSession session = request.getSession(false); + if (session != null) session.invalidate(); + request.logout(); + } catch (ServletException e) { + if (LOGGER.isDebugEnabled()) { + LOGGER.warn("Error while logging out from servlet request.", e); + } + } + } + + protected TokenAuthenticationCache cache() { + return GeoStoreContext.bean(CACHE_BEAN_NAME, TokenAuthenticationCache.class); + } + + @Override + public User getUser(String sessionId, boolean refresh, boolean autorefresh) { + String username = getUserName(sessionId, refresh, autorefresh); + if (username != null) { + User user; + try { + user = userService.get(username); + } catch (Exception e) { + LOGGER.warn("Issue while retrieving user. Will return just the username.", e); + user = new User(); + user.setName(username); + } + return user; + } + return null; + } + + @Override + public String getUserName(String sessionId, boolean refresh, boolean autorefresh) { + TokenAuthenticationCache cache = cache(); + Authentication authentication = cache.get(sessionId); + if (authentication != null) { + if (refresh && autorefresh) { + KeycloakTokenDetails tokenDetails = + (KeycloakTokenDetails) authentication.getDetails(); + String refreshToken = tokenDetails.getRefreshToken(); + String accessToken = tokenDetails.getAccessToken(); + doRefresh(accessToken, refreshToken, cache); + } + Object o = authentication.getPrincipal(); + if (o != null) return SecurityUtils.getUsername(o); + } + return null; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakTokenDetails.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakTokenDetails.java new file mode 100644 index 00000000..d463d08c --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakTokenDetails.java @@ -0,0 +1,105 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import java.util.Calendar; +import java.util.Date; + +/** + * Data class meant to be set as details to an Authentication object. It holds information about the + * access and the refresh tokens. + */ +public class KeycloakTokenDetails { + + private final Date expiration; + private String accessToken; + private String idToken; + private String refreshToken; + + public KeycloakTokenDetails(String accessToken, String refreshToken, Long exp) { + this.accessToken = accessToken; + this.refreshToken = refreshToken; + Date epoch = new Date(0); + this.expiration = expirationDate(epoch, exp.intValue()); + } + + public KeycloakTokenDetails(String accessToken, String refreshToken, long expIn) { + this.accessToken = accessToken; + this.refreshToken = refreshToken; + Date start = new Date(); + this.expiration = expirationDate(start, Long.valueOf(expIn).intValue()); + } + + private Date expirationDate(Date start, int toAdd) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(start); + calendar.add(Calendar.SECOND, toAdd); + return calendar.getTime(); + } + + /** @return the access_token. */ + public String getAccessToken() { + return accessToken; + } + + /** + * Set the access_token. + * + * @param accessToken the access_token. + */ + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + /** @return the refresh_token. */ + public String getRefreshToken() { + return refreshToken; + } + + /** + * Set the refresh_token. + * + * @param refreshToken the refresh_token. + */ + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + + /** @return the access_token expiration date. */ + public Date getExpiration() { + return expiration; + } + + public String getIdToken() { + return idToken; + } + + public void setIdToken(String idToken) { + this.idToken = idToken; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakUserDAO.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakUserDAO.java new file mode 100644 index 00000000..a20e7602 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakUserDAO.java @@ -0,0 +1,296 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import static it.geosolutions.geostore.core.model.enums.GroupReservedNames.EVERYONE; + +import com.googlecode.genericdao.search.ISearch; +import it.geosolutions.geostore.core.dao.UserDAO; +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.enums.Role; +import it.geosolutions.geostore.core.model.enums.UserReservedNames; +import java.util.*; +import java.util.stream.Collectors; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.core.Response; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.admin.client.resource.UsersResource; +import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.springframework.http.HttpStatus; + +/** + * Keycloak implementation for a {@link UserDAO}. Supports basic search operations. Currently it + * doesn't support querying and updating by id. + */ +public class KeycloakUserDAO extends BaseKeycloakDAO implements UserDAO { + + private static final Logger LOGGER = LogManager.getLogger(KeycloakUserDAO.class); + + public KeycloakUserDAO(KeycloakAdminClientConfiguration adminClientConfiguration) { + super(adminClientConfiguration); + } + + @Override + public List findAll() { + Keycloak keycloak = keycloak(); + try { + UsersResource ur = getUsersResource(keycloak); + List userRepresentations = ur.list(); + return toUsers(userRepresentations, ur, false); + } catch (NotFoundException e) { + LOGGER.warn("No users were found.", e); + return null; + } finally { + close(keycloak); + } + } + + @Override + public User find(Long id) { + return null; + } + + @Override + public void persist(User... users) { + Keycloak keycloak = keycloak(); + try { + List representations = toUserRepresentation(users); + UsersResource usersResource = getUsersResource(keycloak); + representations.forEach(r -> usersResource.create(r)); + for (User u : users) u.setId(-1L); + } finally { + close(keycloak); + } + } + + @Override + public User[] save(User... users) { + Keycloak keycloak = keycloak(); + try { + List representations = toUserRepresentation(users); + UsersResource usersResource = getUsersResource(keycloak); + representations.forEach(r -> usersResource.create(r)); + for (User u : users) u.setId(-1L); + } finally { + close(keycloak); + } + return users; + } + + @Override + public User merge(User user) { + return null; + } + + @Override + public boolean remove(User user) { + Keycloak keycloak = keycloak(); + try { + UsersResource ur = getUsersResource(keycloak); + List userRep = ur.search(user.getName(), true); + if (userRep.isEmpty()) return false; + try (Response response = ur.delete(userRep.get(0).getId())) { + if (response.getStatus() == HttpStatus.NO_CONTENT.value()) return true; + LOGGER.debug("Delete failed with response status {}", response.getStatus()); + return false; + } + } catch (NotFoundException e) { + LOGGER.warn("No user found with name {}", user.getName(), e); + return false; + } finally { + close(keycloak); + } + } + + @Override + public boolean removeById(Long id) { + return false; + } + + @Override + public List search(ISearch search) { + Keycloak keycloak = keycloak(); + KeycloakQuery query = toKeycloakQuery(search); + try { + if (LOGGER.isDebugEnabled()) LOGGER.debug("Executing the query " + query.toString()); + + UsersResource ur = getUsersResource(keycloak); + Collection userRepresentations; + if (query.isExact()) + userRepresentations = ur.search(query.getUserName(), query.getExact()); + else if (query.getGroupName() != null) + userRepresentations = + getRolesResource(keycloak) + .get(query.getGroupName()) + .getRoleUserMembers(query.getStartIndex(), query.getMaxResults()); + else + userRepresentations = + ur.search( + query.getUserName(), + null, + null, + null, + null, + null, + null, + search.getPage(), + search.getMaxResults(), + query.getEnabled(), + false); + if (userRepresentations == null) userRepresentations = new ArrayList<>(); + if ((userRepresentations.isEmpty()) && isByUsername(query)) { + UserResource resource = ur.get(query.getUserName()); + if (resource != null) userRepresentations.add(resource.toRepresentation()); + } + List users = toUsers(userRepresentations, ur, isGuest(query)); + return users; + } catch (NotFoundException e) { + LOGGER.warn("No users were found", e); + List list = new ArrayList<>(); + list.add(createGUESTUser(1)); + return list; + } finally { + close(keycloak); + } + } + + private boolean isGuest(KeycloakQuery query) { + return query.getUserName() != null + && UserReservedNames.GUEST.name().equals(query.getUserName()); + } + + private User createGUESTUser(long id) { + User user = new User(); + user.setName(UserReservedNames.GUEST.userName()); + user.setPassword(""); + user.setEnabled(true); + user.setRole(Role.GUEST); + user.setId(id); + UserGroup groupEveryone = new UserGroup(); + groupEveryone.setGroupName(EVERYONE.groupName()); + groupEveryone.setEnabled(true); + Set groups = new HashSet<>(); + groups.add(groupEveryone); + user.setGroups(groups); + return user; + } + + private boolean isByUsername(KeycloakQuery query) { + return query.isExact() && query.getUserName() != null; + } + + @Override + public int count(ISearch search) { + Keycloak keycloak = keycloak(); + try { + KeycloakQuery query = toKeycloakQuery(search); + UsersResource ur = getUsersResource(keycloak); + if (LOGGER.isDebugEnabled()) LOGGER.debug("Executing the query " + query.toString()); + + Integer count; + if (query.getExact() != null && query.getExact().booleanValue()) + count = ur.count(null, null, null, null, query.getUserName()); + else + count = + ur.search( + query.getUserName(), + null, + null, + null, + null, + null, + null, + search.getPage(), + search.getMaxResults(), + query.getEnabled(), + true) + .size(); + return count.intValue(); + } catch (NotFoundException e) { + LOGGER.warn("No users were found", e); + return 0; + } finally { + close(keycloak); + } + } + + private List toUserRepresentation(User... users) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Converting User to UserRepresentation"); + } + List userList = new ArrayList<>(); + for (User user : users) { + UserRepresentation representation = new UserRepresentation(); + if (user.getNewPassword() != null) { + CredentialRepresentation credentialRepresentation = new CredentialRepresentation(); + credentialRepresentation.setType(CredentialRepresentation.PASSWORD); + credentialRepresentation.setValue(user.getNewPassword()); + representation.setCredentials(Collections.singletonList(credentialRepresentation)); + } + representation.setUsername(user.getName()); + representation.setEnabled(user.isEnabled()); + userList.add(representation); + } + return userList; + } + + private List toUsers( + Collection userRepresentations, UsersResource ur, boolean isGuest) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Converting UserRepresentation to User"); + } + List users = new ArrayList<>(); + int id = 1; + for (UserRepresentation representation : userRepresentations) { + User user = new User(); + user.setId(Long.valueOf(id)); + id++; + user.setName(representation.getUsername()); + user.setEnabled(representation.isEnabled()); + user.setTrusted(true); + GeoStoreKeycloakAuthoritiesMapper mapper = getAuthoritiesMapper(); + List roles = + ur.get(representation.getId()).roles().realmLevel().listEffective().stream() + .map(m -> m.getName()) + .collect(Collectors.toList()); + mapper.mapAuthorities(roles); + user.setRole(mapper.getRole()); + Set groups = mapper.getGroups(); + groups.add(KeycloakUserGroupDAO.everyoneGroup(mapper.getIdCounter())); + user.setGroups(groups); + users.add(user); + } + if (isGuest && users.isEmpty()) users.add(createGUESTUser(Long.valueOf(id))); + return users; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakUserGroupDAO.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakUserGroupDAO.java new file mode 100644 index 00000000..9d8a1b9e --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/keycloak/KeycloakUserGroupDAO.java @@ -0,0 +1,275 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.keycloak; + +import static it.geosolutions.geostore.core.model.enums.GroupReservedNames.EVERYONE; + +import com.googlecode.genericdao.search.ISearch; +import com.googlecode.genericdao.search.Search; +import it.geosolutions.geostore.core.dao.UserGroupDAO; +import it.geosolutions.geostore.core.model.UserGroup; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.ws.rs.NotFoundException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.resource.RolesResource; +import org.keycloak.representations.idm.RoleRepresentation; + +/** + * Keycloak implementation for a {@link UserGroupDAO}. Supports basic search operations. Currently + * it doesn't support querying and updating by id. + */ +public class KeycloakUserGroupDAO extends BaseKeycloakDAO implements UserGroupDAO { + + private static final Logger LOGGER = LogManager.getLogger(KeycloakUserGroupDAO.class); + + private boolean addEveryOneGroup = false; + + public KeycloakUserGroupDAO(KeycloakAdminClientConfiguration clientConfiguration) { + super(clientConfiguration); + } + + static UserGroup everyoneGroup(int id) { + UserGroup everyoneGroup = new UserGroup(); + everyoneGroup.setGroupName(EVERYONE.groupName()); + everyoneGroup.setEnabled(true); + everyoneGroup.setId(Long.valueOf(id)); + return everyoneGroup; + } + + @Override + public List findAll() { + Keycloak keycloak = keycloak(); + try { + List roleRepresentations = getRolesResource(keycloak).list(); + return toUserGroups(roleRepresentations, true); + } catch (NotFoundException e) { + LOGGER.warn("No users were found", e); + return null; + } finally { + close(keycloak); + } + } + + @Override + public UserGroup find(Long id) { + return null; + } + + @Override + public void persist(UserGroup... userGroups) { + Keycloak keycloak = keycloak(); + try { + for (UserGroup group : userGroups) { + RoleRepresentation roleRepresentation = toRoleRepresentation(group); + getRolesResource(keycloak).create(roleRepresentation); + group.setId(-1L); + } + } finally { + close(keycloak); + } + } + + @Override + public UserGroup[] save(UserGroup... userGroups) { + Keycloak keycloak = keycloak(); + try { + for (UserGroup group : userGroups) { + RoleRepresentation roleRepresentation = toRoleRepresentation(group); + getRolesResource(keycloak).create(roleRepresentation); + group.setId(-1L); + } + return userGroups; + } finally { + close(keycloak); + } + } + + @Override + public UserGroup merge(UserGroup userGroup) { + Keycloak keycloak = keycloak(); + try { + RoleRepresentation roleRepresentation = new RoleRepresentation(); + roleRepresentation.setName(userGroup.getGroupName()); + roleRepresentation.setDescription(userGroup.getDescription()); + getRolesResource(keycloak).get(userGroup.getGroupName()).update(roleRepresentation); + userGroup.setId(-1L); + return userGroup; + } finally { + close(keycloak); + } + } + + @Override + public boolean remove(UserGroup userGroup) { + Keycloak keycloak = keycloak(); + try { + getRolesResource(keycloak).get(userGroup.getGroupName()).remove(); + return true; + } catch (NotFoundException e) { + LOGGER.warn("No user found with name " + userGroup.getGroupName(), e); + return false; + } finally { + close(keycloak); + } + } + + @Override + public boolean removeById(Long id) { + return false; + } + + @Override + public List search(ISearch search) { + Keycloak keycloak = keycloak(); + try { + KeycloakQuery query = toKeycloakQuery(search); + if (LOGGER.isDebugEnabled()) + LOGGER.debug("Executing the following Keycloak query " + query.toString()); + RolesResource rr = getRolesResource(keycloak); + List roles; + String groupName = query.getGroupName(); + if (groupName != null && !query.isExact()) + roles = rr.list(groupName, query.getStartIndex(), query.getMaxResults()); + else if (groupName != null && query.isExact()) { + if (LOGGER.isDebugEnabled()) LOGGER.debug("Executing exact query"); + RoleRepresentation representation; + if (EVERYONE.groupName().equals(groupName) + && !query.getSkipEveryBodyGroup().booleanValue()) + representation = everyoneRoleRep(); + else representation = rr.get(groupName).toRepresentation(); + roles = Collections.singletonList(representation); + } else roles = rr.list(query.getStartIndex(), query.getMaxResults()); + return toUserGroups(roles, !query.getSkipEveryBodyGroup().booleanValue()); + } catch (NotFoundException e) { + LOGGER.warn("No groups were found", e); + return null; + } finally { + close(keycloak); + } + } + + @Override + public int count(ISearch search) { + Keycloak keycloak = keycloak(); + try { + KeycloakQuery query = toKeycloakQuery(search); + int count; + RolesResource rr = getRolesResource(keycloak); + if (query.getUserName() != null && query.isExact()) count = 1; + else if (query.getUserName() != null) count = rr.list(query.getUserName(), true).size(); + else count = rr.list().size(); + if (isAddEveryOneGroup()) count++; + return count; + } catch (NotFoundException e) { + LOGGER.warn("No groups were found", e); + return 0; + } finally { + close(keycloak); + } + } + + private List toUserGroups( + List roleRepresentations, boolean isEveryoneRequested) { + List groups = new ArrayList<>(roleRepresentations.size()); + int counter = 1; + for (RoleRepresentation role : roleRepresentations) { + GeoStoreKeycloakAuthoritiesMapper mapper = getAuthoritiesMapper(); + mapper.mapAuthorities(Collections.singletonList(role.getName())); + if (mapper.getGroups() != null && !mapper.getGroups().isEmpty()) { + UserGroup group = new UserGroup(); + group.setGroupName(role.getName()); + group.setDescription(role.getDescription()); + group.setEnabled(true); + group.setId(Long.valueOf(counter)); + groups.add(group); + counter++; + } + } + addEveryOne(groups, isEveryoneRequested, counter); + return groups; + } + + private RoleRepresentation toRoleRepresentation(UserGroup group) { + RoleRepresentation roleRepresentation = new RoleRepresentation(); + roleRepresentation.setName(group.getGroupName()); + roleRepresentation.setDescription(group.getDescription()); + return roleRepresentation; + } + + /** @return True if everyone group should be added, false otherwise. */ + public boolean isAddEveryOneGroup() { + return addEveryOneGroup; + } + + /** + * Sets the addEveryoneGroup flag. + * + * @param addEveryOneGroup + */ + public void setAddEveryOneGroup(boolean addEveryOneGroup) { + this.addEveryOneGroup = addEveryOneGroup; + } + + /** + * Add the everyOne group to the LDAP returned list. + * + * @param groups + * @return + */ + private List addEveryOne(List groups, boolean addEveryOneGroup, int id) { + boolean found = + groups.stream().anyMatch(g -> g.getGroupName().equals(EVERYONE.groupName())); + if (!found && addEveryOneGroup && isAddEveryOneGroup()) { + UserGroup everyoneGroup = everyoneGroup(id); + groups.add(everyoneGroup); + } + return groups; + } + + private RoleRepresentation everyoneRoleRep() { + RoleRepresentation roleRepresentation = new RoleRepresentation(); + roleRepresentation.setName(EVERYONE.groupName()); + return roleRepresentation; + } + + @Override + public UserGroup findByName(String name) { + Search searchCriteria = new Search(UserGroup.class); + searchCriteria.addFilterEqual("groupName", name); + UserGroup result = null; + List existingGroups = search(searchCriteria); + if (existingGroups.size() > 0) { + result = existingGroups.get(0); + } + return result; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/AccessCookie.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/AccessCookie.java new file mode 100644 index 00000000..da3450c6 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/AccessCookie.java @@ -0,0 +1,63 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2; + +import java.util.Date; +import javax.ws.rs.core.Cookie; +import javax.ws.rs.core.NewCookie; + +/** Cookie class to allow an easy setting of the SameSite cookie part. */ +public class AccessCookie extends NewCookie { + + private final String sameSite; + + public AccessCookie( + Cookie cookie, String comment, int maxAge, boolean secure, String sameSite) { + super(cookie, comment, maxAge, secure); + this.sameSite = sameSite; + } + + public AccessCookie( + Cookie cookie, + String comment, + int maxAge, + Date expiry, + boolean secure, + boolean httpOnly, + String sameSite) { + super(cookie, comment, maxAge, expiry, secure, httpOnly); + this.sameSite = sameSite; + } + + @Override + public String toString() { + String cookie = super.toString(); + if (sameSite != null) cookie = cookie.concat(";").concat("SameSite=").concat(sameSite); + return cookie; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/DiscoveryClient.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/DiscoveryClient.java new file mode 100644 index 00000000..93989edd --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/DiscoveryClient.java @@ -0,0 +1,153 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import org.springframework.web.client.RestTemplate; + +/** + * DiscoveryClient to perform a discovery request and set the value to the OAuth2Configuration + * instance. + */ +public class DiscoveryClient { + private static final String PROVIDER_END_PATH = "/.well-known/openid-configuration"; + private static final String AUTHORIZATION_ENDPOINT_ATTR_NAME = "authorization_endpoint"; + private static final String TOKEN_ENDPOINT_ATTR_NAME = "token_endpoint"; + private static final String USERINFO_ENDPOINT_ATTR_NAME = "userinfo_endpoint"; + private static final String END_SESSION_ENDPOINT = "end_session_endpoint"; + private static final String JWK_SET_URI_ATTR_NAME = "jwks_uri"; + private static final String SCOPES_SUPPORTED = "scopes_supported"; + private static final String REVOCATION_ENDPOINT = "revocation_endpoint"; + + private final RestTemplate restTemplate; + private String location; + + public DiscoveryClient(String location) { + setLocation(location); + this.restTemplate = new RestTemplate(); + } + + public DiscoveryClient(String location, RestTemplate restTemplate) { + setLocation(location); + this.restTemplate = restTemplate; + } + + private static String appendPath(String... pathComponents) { + StringBuilder result = new StringBuilder(pathComponents[0]); + for (int i = 1; i < pathComponents.length; i++) { + String component = pathComponents[i]; + boolean endsWithSlash = result.charAt(result.length() - 1) == '/'; + boolean startsWithSlash = component.startsWith("/"); + if (endsWithSlash && startsWithSlash) { + result.setLength(result.length() - 1); + } else if (!endsWithSlash && !startsWithSlash) { + result.append("/"); + } + result.append(component); + } + + return result.toString(); + } + + private void setLocation(String location) { + if (!location.endsWith(PROVIDER_END_PATH)) { + location = appendPath(location, PROVIDER_END_PATH); + } + this.location = location; + } + + /** + * Fill the OAuth2Configuration instance with the values found in the discovery response. + * + * @param conf the OAuth2Configuration. + */ + public void autofill(OAuth2Configuration conf) { + if (location != null) { + Map response = restTemplate.getForObject(this.location, Map.class); + assert response != null; + Optional.ofNullable(response.get(getAuthorizationEndpointAttrName())) + .ifPresent(uri -> conf.setAuthorizationUri((String) uri)); + Optional.ofNullable(response.get(getTokenEndpointAttrName())) + .ifPresent(uri -> conf.setAccessTokenUri((String) uri)); + Optional.ofNullable(response.get(getUserinfoEndpointAttrName())) + .ifPresent(uri -> conf.setCheckTokenEndpointUrl((String) uri)); + Optional.ofNullable(response.get(getJwkSetUriAttrName())) + .ifPresent(uri -> conf.setIdTokenUri((String) uri)); + Optional.ofNullable(response.get(getEndSessionEndpoint())) + .ifPresent(uri -> conf.setLogoutUri((String) uri)); + Optional.ofNullable(response.get(getScopesSupported())) + .ifPresent( + s -> { + @SuppressWarnings("unchecked") + List scopes = (List) s; + conf.setScopes(collectScopes(scopes)); + }); + Optional.ofNullable(response.get(getRevocationEndpoint())) + .ifPresent(s -> conf.setRevokeEndpoint((String) s)); + } + } + + private String collectScopes(List scopes) { + return scopes.stream().collect(Collectors.joining(",")); + } + + protected String getUserinfoEndpointAttrName() { + return USERINFO_ENDPOINT_ATTR_NAME; + } + + protected String getEndSessionEndpoint() { + return END_SESSION_ENDPOINT; + } + + protected String getJwkSetUriAttrName() { + return JWK_SET_URI_ATTR_NAME; + } + + protected String getProviderEndPath() { + return PROVIDER_END_PATH; + } + + protected String getAuthorizationEndpointAttrName() { + return AUTHORIZATION_ENDPOINT_ATTR_NAME; + } + + protected String getTokenEndpointAttrName() { + return TOKEN_ENDPOINT_ATTR_NAME; + } + + protected String getScopesSupported() { + return SCOPES_SUPPORTED; + } + + protected String getRevocationEndpoint() { + return REVOCATION_ENDPOINT; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/GeoStoreAccessTokenConverter.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/GeoStoreAccessTokenConverter.java new file mode 100644 index 00000000..ca045e7b --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/GeoStoreAccessTokenConverter.java @@ -0,0 +1,134 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2; + +import java.io.Serializable; +import java.util.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; +import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; +import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter; +import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter; + +/** + * Extends the spring security DefaultAccessTokenConverter to customize the creation of the + * Authentication object. + */ +public class GeoStoreAccessTokenConverter extends DefaultAccessTokenConverter { + + // For oidc, this will be the userinfo. + // For oauth2, the result of the "token check" endpoint. + // This is attached to the request's extensions once it's been retrieved. + public static String ACCESS_TOKEN_CHECK_KEY = "oauth2.AccessTokenCheckResponse"; + protected static Logger LOGGER = LogManager.getLogger(GeoStoreAccessTokenConverter.class); + protected UserAuthenticationConverter userTokenConverter; + + public GeoStoreAccessTokenConverter() { + this("email"); + } + + public GeoStoreAccessTokenConverter(String usernameKey) { + final DefaultUserAuthenticationConverter defaultUserAuthConverter = + new GeoStoreAuthenticationConverter(usernameKey); + setUserTokenConverter(defaultUserAuthConverter); + } + + /** + * Converter for the part of the data in the token representing a user. + * + * @param userTokenConverter the userTokenConverter to set + */ + @Override + public final void setUserTokenConverter(UserAuthenticationConverter userTokenConverter) { + this.userTokenConverter = userTokenConverter; + super.setUserTokenConverter(userTokenConverter); + } + + @Override + public OAuth2Authentication extractAuthentication(Map map) { + Map parameters = new HashMap<>(); + Set scope = parseScopes(map); + Authentication user = userTokenConverter.extractAuthentication(map); + LOGGER.debug("User: " + user); + String clientId = (String) map.get(CLIENT_ID); + parameters.put(CLIENT_ID, clientId); + + Map extensionParameters = new HashMap<>(); + try { + extensionParameters.put(ACCESS_TOKEN_CHECK_KEY, (Serializable) map); + } catch (Exception e) { + // + LOGGER.debug("Exception while trying to record the access token check info", e); + } + + Set resourceIds = new LinkedHashSet<>(getAud(map)); + LOGGER.debug("ResourceIds: " + resourceIds); + OAuth2Request request = + new OAuth2Request( + parameters, + clientId, + null, + true, + scope, + resourceIds, + null, + null, + extensionParameters); + return new OAuth2Authentication(request, user); + } + + @SuppressWarnings("unchecked") + private Collection getAud(Map map) { + if (!map.containsKey(AUD)) { + return Collections.emptySet(); + } + + Object aud = map.get(AUD); + if (aud instanceof Collection) return (Collection) aud; + else return Collections.singletonList(String.valueOf(aud)); + } + + private Set parseScopes(Map map) { + // Parsing of scopes coming back from GeoNode is slightly different from + // the default implementation. Instead of it being a collection, it is a + // String where multiple scopes are separated by a space. + Object scopeAsObject = map.containsKey(SCOPE) ? map.get(SCOPE) : ""; + Set scope = new LinkedHashSet<>(); + if (String.class.isAssignableFrom(scopeAsObject.getClass())) { + String scopeAsString = (String) scopeAsObject; + Collections.addAll(scope, scopeAsString.split(" ")); + } else if (Collection.class.isAssignableFrom(scopeAsObject.getClass())) { + Collection scopes = (Collection) scopeAsObject; + scope.addAll(scopes); + } + return scope; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/GeoStoreAuthenticationConverter.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/GeoStoreAuthenticationConverter.java new file mode 100644 index 00000000..467fb947 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/GeoStoreAuthenticationConverter.java @@ -0,0 +1,68 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ + +package it.geosolutions.geostore.services.rest.security.oauth2; + +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter; + +/** GeoStore specific AuthenticationConverter. */ +public class GeoStoreAuthenticationConverter extends DefaultUserAuthenticationConverter { + protected static Logger LOGGER = LogManager.getLogger(GeoStoreAuthenticationConverter.class); + private Object usernameKey = USERNAME; + + /** Default Constructor. */ + public GeoStoreAuthenticationConverter() { + super(); + } + + /** Default Constructor. */ + public GeoStoreAuthenticationConverter(final String username_key) { + super(); + + usernameKey = username_key; + } + + @Override + public Authentication extractAuthentication(Map map) { + if (LOGGER.isDebugEnabled()) { + LOGGER.info( + "Extracting authentication from a map with following keys: " + + map.keySet().stream().collect(Collectors.joining(","))); + } + if (map.containsKey(usernameKey)) { + return new UsernamePasswordAuthenticationToken(map.get(usernameKey), "N/A", null); + } + return null; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/GeoStoreOAuthRestTemplate.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/GeoStoreOAuthRestTemplate.java new file mode 100644 index 00000000..41035519 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/GeoStoreOAuthRestTemplate.java @@ -0,0 +1,177 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2; + +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.*; + +import java.util.Optional; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.security.oauth2.client.OAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException; +import org.springframework.security.oauth2.client.token.AccessTokenRequest; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.store.jwk.JwkTokenStore; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +/** Custom OAuth2RestTemplate. Allows the extraction of the id token from the response. */ +@SuppressWarnings("PMD.UnusedPrivateField") +public class GeoStoreOAuthRestTemplate extends OAuth2RestTemplate { + + public static final String ID_TOKEN_VALUE = "OpenIdConnect-IdTokenValue"; + private static final Logger LOGGER = LogManager.getLogger(GeoStoreOAuthRestTemplate.class); + private final String idTokenParam; + private JwkTokenStore store; + + public GeoStoreOAuthRestTemplate( + OAuth2ProtectedResourceDetails resource, + OAuth2ClientContext context, + OAuth2Configuration configuration) { + this(resource, context, configuration, ID_TOKEN_PARAM); + } + + public GeoStoreOAuthRestTemplate( + OAuth2ProtectedResourceDetails resource, + OAuth2ClientContext context, + OAuth2Configuration configuration, + String idTokenParam) { + super(resource, context); + if (configuration.getIdTokenUri() != null) + this.store = new JwkTokenStore(configuration.getIdTokenUri()); + this.idTokenParam = idTokenParam; + } + + @Override + protected OAuth2AccessToken acquireAccessToken(OAuth2ClientContext oauth2Context) + throws UserRedirectRequiredException { + + OAuth2AccessToken result = null; + try { + result = super.acquireAccessToken(oauth2Context); + return result; + } finally { + // CODE shouldn't typically be displayed since it can be "handed in" for an access/id + // token So, we don't log the CODE until AFTER it has been handed in. + // CODE is one-time-use. + if ((oauth2Context != null) && (oauth2Context.getAccessTokenRequest() != null)) { + AccessTokenRequest accessTokenRequest = oauth2Context.getAccessTokenRequest(); + if ((accessTokenRequest.getAuthorizationCode() != null) + && (!accessTokenRequest.getAuthorizationCode().isEmpty())) { + LOGGER.debug( + "OIDC: received a CODE from Identity Provider - handing it in for ID/Access Token"); + LOGGER.debug("OIDC: CODE=" + accessTokenRequest.getAuthorizationCode()); + if (result != null) { + LOGGER.debug( + "OIDC: Identity Provider returned Token, type=" + + result.getTokenType()); + LOGGER.debug("OIDC: SCOPES=" + String.join(" ", result.getScope())); + final String accessToken = saferJWT(result.getValue()); + LOGGER.debug("OIDC: ACCESS TOKEN:" + accessToken); + RequestContextHolder.getRequestAttributes() + .setAttribute(ACCESS_TOKEN_PARAM, accessToken, 0); + if (result.getAdditionalInformation().containsKey("refresh_token")) { + final String refreshToken = + saferJWT( + (String) + result.getAdditionalInformation() + .get("refresh_token")); + LOGGER.debug("OIDC: REFRESH TOKEN:" + refreshToken); + RequestContextHolder.getRequestAttributes() + .setAttribute(REFRESH_TOKEN_PARAM, accessToken, 0); + } + if (result.getAdditionalInformation().containsKey("id_token")) { + final String idToken = + saferJWT( + (String) + result.getAdditionalInformation() + .get("id_token")); + LOGGER.debug("OIDC: ID TOKEN:" + idToken); + RequestContextHolder.getRequestAttributes() + .setAttribute(ID_TOKEN_PARAM, accessToken, 0); + } + } + } + } + } + } + + /** + * Logs the string value of a token if it's a JWT token - it should be in 3 parts, separated by + * a "." These 3 sections are: header, claims, signature We only log the 2nd (claims) part. This + * is safer because without the signature, the token will not validate. + * + *

    We don't log the token directly because it can be used to access protected resources. + * + * @param jwt + * @return + */ + String saferJWT(String jwt) { + String[] JWTParts = jwt.split("\\."); + if (JWTParts.length > 1) return JWTParts[1]; // this is the claims part + return "NOT A JWT"; // not a JWT + } + + @Override + public OAuth2AccessToken getAccessToken() throws UserRedirectRequiredException { + OAuth2AccessToken token = super.getAccessToken(); + if (token != null) validate(token); + return token; + } + + private void validate(OAuth2AccessToken token) { + Object maybeIdToken = token.getAdditionalInformation().get("id_token"); + if (maybeIdToken instanceof String) { + String idToken = (String) maybeIdToken; + setAsRequestAttribute(GeoStoreOAuthRestTemplate.ID_TOKEN_VALUE, idToken); + // among other things, this verifies the token + if (store != null) store.readAuthentication(idToken); + // TODO: the authentication just read could contain roles, could be treated as + // another role source... but needs to be made available to role computation + } + } + + private void setAsRequestAttribute(String key, String value) { + Optional.ofNullable(RequestContextHolder.getRequestAttributes()) + .filter(ra -> ra instanceof ServletRequestAttributes) + .map(ra -> ((ServletRequestAttributes) ra)) + .map(ServletRequestAttributes::getRequest) + .ifPresent(r -> r.setAttribute(key, value)); + } + + public OAuth2Authentication readAuthentication(String idToken) { + return store.readAuthentication(idToken); + } + + public void setTokenStore(JwkTokenStore jwkTokenStore) { + this.store = jwkTokenStore; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/GeoStoreRemoteTokenServices.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/GeoStoreRemoteTokenServices.java new file mode 100644 index 00000000..743caeb8 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/GeoStoreRemoteTokenServices.java @@ -0,0 +1,172 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2; + +import java.io.IOException; +import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.AccessTokenConverter; +import org.springframework.security.oauth2.provider.token.RemoteTokenServices; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.DefaultResponseErrorHandler; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +/** + * Extends the SpringSecurity class to provide an additional method to be able to deal with not + * fully standardized/check_token endpoint responses. + */ +public class GeoStoreRemoteTokenServices extends RemoteTokenServices { + + protected static Logger LOGGER = + LogManager.getLogger(GeoStoreRemoteTokenServices.class.getName()); + + protected RestOperations restTemplate; + + protected String checkTokenEndpointUrl; + + protected String clientId; + + protected String clientSecret; + + protected AccessTokenConverter tokenConverter; + + protected GeoStoreRemoteTokenServices() { + // constructor for subclasses that want to configure everything + } + + protected GeoStoreRemoteTokenServices(AccessTokenConverter tokenConverter) { + this.tokenConverter = tokenConverter; + this.restTemplate = new RestTemplate(); + ((RestTemplate) restTemplate) + .setErrorHandler( + new DefaultResponseErrorHandler() { + @Override + // Ignore 400 + public void handleError(ClientHttpResponse response) + throws IOException { + if (response.getRawStatusCode() != 400) { + super.handleError(response); + } + } + }); + } + + @Override + public void setRestTemplate(RestOperations restTemplate) { + this.restTemplate = restTemplate; + } + + @Override + public void setCheckTokenEndpointUrl(String checkTokenEndpointUrl) { + this.checkTokenEndpointUrl = checkTokenEndpointUrl; + } + + @Override + public void setClientId(String clientId) { + this.clientId = clientId; + } + + @Override + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + @Override + public void setAccessTokenConverter(AccessTokenConverter accessTokenConverter) { + this.tokenConverter = accessTokenConverter; + } + + @Override + public OAuth2Authentication loadAuthentication(String accessToken) + throws AuthenticationException, InvalidTokenException { + Map checkTokenResponse = checkToken(accessToken); + verifyTokenResponse(accessToken, checkTokenResponse); + transformNonStandardValuesToStandardValues(checkTokenResponse); + return tokenConverter.extractAuthentication(checkTokenResponse); + } + + protected void verifyTokenResponse(String accessToken, Map checkTokenResponse) { + if (checkTokenResponse.containsKey("error")) { + logger.debug("check_token returned error: " + checkTokenResponse.get("error")); + throw new InvalidTokenException(accessToken); + } + } + + /** + * Subclass must override this method if values are not standardized. + * + * @param map the map of values to be transformed. + */ + protected void transformNonStandardValuesToStandardValues(Map map) { + // nothing to do if everything is standardized + } + + protected Map checkToken(String accessToken) { + MultiValueMap formData = new LinkedMultiValueMap<>(); + formData.add("token", accessToken); + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", getAuthorizationHeader(accessToken)); + String accessTokenUrl = checkTokenEndpointUrl + "?access_token=" + accessToken; + return sendRequestForMap(accessTokenUrl, formData, headers, HttpMethod.POST); + } + + protected String getAuthorizationHeader(String accessToken) { + return "Bearer " + accessToken; + } + + protected Map sendRequestForMap( + String path, + MultiValueMap formData, + HttpHeaders headers, + HttpMethod method) { + if (headers.getContentType() == null) { + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + } + ParameterizedTypeReference> map = + new ParameterizedTypeReference>() {}; + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Executing request " + path + " form data are " + formData); + LOGGER.debug("Headers are " + headers); + } + return restTemplate + .exchange(path, method, new HttpEntity<>(formData, headers), map) + .getBody(); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/IdPLoginRestImpl.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/IdPLoginRestImpl.java new file mode 100644 index 00000000..a08734a7 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/IdPLoginRestImpl.java @@ -0,0 +1,71 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ + +package it.geosolutions.geostore.services.rest.security.oauth2; + +import it.geosolutions.geostore.services.rest.IdPLoginRest; +import it.geosolutions.geostore.services.rest.IdPLoginService; +import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; +import it.geosolutions.geostore.services.rest.model.SessionToken; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.Response; + +/** This class provides authentication entry point to login using an OAuth2 provider. */ +public class IdPLoginRestImpl implements IdPLoginRest { + + private final Map services = new HashMap<>(); + + @Override + public void login(String provider) { + HttpServletRequest request = OAuth2Utils.getRequest(); + HttpServletResponse resp = OAuth2Utils.getResponse(); + IdPLoginService service = services.get(provider); + service.doLogin(request, resp, provider); + } + + @Override + public Response callback(String provider) throws NotFoundWebEx { + IdPLoginService service = services.get(provider); + return service.doInternalRedirect( + OAuth2Utils.getRequest(), OAuth2Utils.getResponse(), provider); + } + + @Override + public SessionToken getTokensByTokenIdentifier(String provider, String tokenIdentifier) + throws NotFoundWebEx { + return services.get(provider).getTokenByIdentifier(provider, tokenIdentifier); + } + + @Override + public void registerService(String providerName, IdPLoginService service) { + this.services.put(providerName, service); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/InMemoryTokenStorage.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/InMemoryTokenStorage.java new file mode 100644 index 00000000..a5e77992 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/InMemoryTokenStorage.java @@ -0,0 +1,52 @@ +package it.geosolutions.geostore.services.rest.security.oauth2; + +import it.geosolutions.geostore.services.rest.model.SessionToken; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class InMemoryTokenStorage implements TokenStorage { + + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + private final Map sessions = new ConcurrentHashMap<>(); + private final int cleanUpSeconds = 120; + private final Runnable evictionTask = + new Runnable() { + @Override + public void run() { + for (String sessionId : sessions.keySet()) { + removeTokenByIdentifier(sessionId); + } + } + }; + + public InMemoryTokenStorage() { + super(); + // schedule eviction thread + scheduler.scheduleAtFixedRate( + evictionTask, cleanUpSeconds, cleanUpSeconds, TimeUnit.SECONDS); + } + + @Override + public SessionToken getTokenByIdentifier(String identifier) { + return sessions.get(identifier); + } + + @Override + public void removeTokenByIdentifier(String identifier) { + sessions.remove(identifier); + } + + @Override + public void saveToken(String identifier, SessionToken token) { + sessions.put(identifier, token); + } + + @Override + public String buildTokenKey() { + return UUID.randomUUID().toString(); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/JWTHelper.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/JWTHelper.java new file mode 100644 index 00000000..474ba90b --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/JWTHelper.java @@ -0,0 +1,90 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.impl.NullClaim; +import com.auth0.jwt.interfaces.Claim; +import com.auth0.jwt.interfaces.DecodedJWT; +import java.util.ArrayList; +import java.util.List; + +/** A class holding utilities method for handling JWT tokens. */ +public class JWTHelper { + + private final DecodedJWT decodedJWT; + + public JWTHelper(String jwtToken) { + this.decodedJWT = JWT.decode(jwtToken); + } + + /** + * Get a claim by name from the idToken. + * + * @param claimName the name of the claim to retrieve. + * @param binding the Class to which convert the claim value. + * @param the type of the claim value. + * @return the claim value. + */ + public T getClaim(String claimName, Class binding) { + T result = null; + if (decodedJWT != null && claimName != null) { + Claim claim = decodedJWT.getClaim(claimName); + if (nonNullClaim(claim)) result = claim.as(binding); + } + return result; + } + + /** + * Get a claim values as List by its name. + * + * @param claimName the name of the claim to retrieve. + * @param binding the Class to which convert the claim value. + * @param the type of the claim value. + * @return the claim value. + */ + public List getClaimAsList(String claimName, Class binding) { + List result = null; + if (decodedJWT != null && claimName != null) { + Claim claim = decodedJWT.getClaim(claimName); + if (nonNullClaim(claim)) { + result = claim.asList(binding); + if (result == null) { + result = new ArrayList<>(); + T singleValue = claim.as(binding); + if (singleValue != null) result.add(singleValue); + } + } + } + return result; + } + + private boolean nonNullClaim(Claim claim) { + return claim != null && !(claim instanceof NullClaim); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/OAuth2Configuration.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/OAuth2Configuration.java new file mode 100644 index 00000000..aeee55fa --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/OAuth2Configuration.java @@ -0,0 +1,548 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ + +package it.geosolutions.geostore.services.rest.security.oauth2; + +import it.geosolutions.geostore.services.rest.security.IdPConfiguration; +import java.util.Collections; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * This class represents the geostore configuration for an OAuth2/OpenId provider. An + * OAuth2Configuration bean should be provided for each OAuth2 provider. The bean id has to be + * {providerName}OAuth2Config. + */ +public class OAuth2Configuration extends IdPConfiguration { + + public static final String CONFIG_NAME_SUFFIX = "OAuth2Config"; + public static final String CONFIGURATION_NAME = "CONFIGURATION_NAME"; + private static final Logger LOGGER = + LogManager.getLogger(OAuth2GeoStoreAuthenticationFilter.class); + protected String clientId; + protected String clientSecret; + protected String accessTokenUri; + protected String authorizationUri; + protected String checkTokenEndpointUrl; + protected String logoutUri; + protected boolean globalLogoutEnabled = false; + protected String scopes; + protected String idTokenUri; + protected String discoveryUrl; + protected String revokeEndpoint; + protected boolean enableRedirectEntryPoint = false; + protected String principalKey; + protected String rolesClaim; + protected String groupsClaim; + + /** + * Get an authentication entry point instance meant to handle redirect to the authorization + * page. + * + * @return the authentication entry point. + */ + public AuthenticationEntryPoint getAuthenticationEntryPoint() { + return (request, response, authException) -> { + String loginUri = buildLoginUri(); + response.sendRedirect(loginUri); + }; + } + + /** + * Build the authorization uri to the OAuth2 provider. + * + * @return the authorization uri completed with the various query strings. + */ + public String buildLoginUri() { + return buildLoginUri(null, new String[] {}); + } + + /** + * Build the authorization uri to the OAuth2 provider. + * + * @param accessType the access type request param value. Can be null. + * @return the authorization uri completed with the various query strings. + */ + public String buildLoginUri(String accessType) { + return buildLoginUri(accessType, new String[] {}); + } + + /** + * @param accessType the access type request param value. Can be null. + * @param additionalScopes additional scopes aren't set at from geostore-ovr.properties. Can be + * null. + * @return the + */ + public String buildLoginUri(String accessType, String... additionalScopes) { + final StringBuilder loginUri = new StringBuilder(getAuthorizationUri()); + loginUri.append("?") + .append("response_type=code") + .append("&") + .append("client_id=") + .append(getClientId()) + .append("&") + .append("scope=") + .append(getScopes().replace(",", "%20")); + for (String s : additionalScopes) { + loginUri.append("%20").append(s); + } + loginUri.append("&").append("redirect_uri=").append(getRedirectUri()); + if (accessType != null) loginUri.append("&").append("access_type=").append(accessType); + String finalUrl = loginUri.toString(); + if (LOGGER.isDebugEnabled()) + LOGGER.info("Going to request authorization to this endpoint {}", finalUrl); + return finalUrl; + } + + /** + * Builds the refresh token URI. + * + * @return the complete refresh token uri. + */ + public String buildRefreshTokenURI() { + return buildRefreshTokenURI(null); + } + + /** + * Builds the refresh token URI. + * + * @param accessType the access type request param. + * @return the complete refresh token uri. + */ + public String buildRefreshTokenURI(String accessType) { + final StringBuilder refreshUri = new StringBuilder(getAccessTokenUri()); + refreshUri + .append("?") + .append("&") + .append("client_id=") + .append(getClientId()) + .append("&") + .append("scope=") + .append(getScopes().replace(",", "%20")); + if (accessType != null) refreshUri.append("&").append("access_type=").append(accessType); + return refreshUri.toString(); + } + + /** @return the clientId. */ + public String getClientId() { + return clientId; + } + + /** + * Set the client id. + * + * @param cliendId the client id. + */ + public void setClientId(String cliendId) { + this.clientId = cliendId; + } + + /** @return the client secret. */ + public String getClientSecret() { + return clientSecret; + } + + /** + * Set the client secret. + * + * @param clientSecret the client secret. + */ + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + /** @return the access token uri */ + public String getAccessTokenUri() { + return accessTokenUri; + } + + /** + * Set the access token uri + * + * @param accessTokenUri the access token uri. + */ + public void setAccessTokenUri(String accessTokenUri) { + this.accessTokenUri = accessTokenUri; + } + + /** @return the authorization URI. */ + public String getAuthorizationUri() { + return authorizationUri; + } + + /** + * Set the authorization URI. + * + * @param authorizationUri the authorization URI. + */ + public void setAuthorizationUri(String authorizationUri) { + this.authorizationUri = authorizationUri; + } + + /** @return the check token endpoint URL. */ + public String getCheckTokenEndpointUrl() { + return checkTokenEndpointUrl; + } + + /** + * Set the check token endpoint URL. + * + * @param checkTokenEndpointUrl the check token endpoint URL. + */ + public void setCheckTokenEndpointUrl(String checkTokenEndpointUrl) { + this.checkTokenEndpointUrl = checkTokenEndpointUrl; + } + + /** @return the logout URI. */ + public String getLogoutUri() { + return logoutUri; + } + + /** + * Set the logout URI. + * + * @param logoutUri the logout URI. + */ + public void setLogoutUri(String logoutUri) { + this.logoutUri = logoutUri; + } + + /** @return */ + public boolean isGlobalLogoutEnabled() { + return globalLogoutEnabled; + } + + /** + * Set th + * + * @param globalLogoutEnabled + */ + public void setGlobalLogoutEnabled(boolean globalLogoutEnabled) { + this.globalLogoutEnabled = globalLogoutEnabled; + } + + /** + * Get the configured scopes as a String. + * + * @return the scopes. + */ + public String getScopes() { + return scopes; + } + + /** + * Set the scopes. + * + * @param scopes the scopes. + */ + public void setScopes(String scopes) { + this.scopes = scopes; + } + + /** @return the id Token URI. */ + public String getIdTokenUri() { + return idTokenUri; + } + + /** + * Set the id token URI. + * + * @param idTokenUri the id Token URI. + */ + public void setIdTokenUri(String idTokenUri) { + this.idTokenUri = idTokenUri; + } + + /** @return the Discovery URL. */ + public String getDiscoveryUrl() { + return discoveryUrl; + } + + /** + * Set the discovery URL. + * + * @param discoveryUrl the discovery URL. + */ + public void setDiscoveryUrl(String discoveryUrl) { + this.discoveryUrl = discoveryUrl; + } + + /** + * Check if the configuration is valid or not. Is considered invalid if either one of client id, + * secret, authorization, accessToke Uri was not provided. + * + * @return + */ + public boolean isInvalid() { + return clientId == null + || clientSecret == null + || authorizationUri == null + || accessTokenUri == null; + } + + /** @return the revoke endpoint. */ + public String getRevokeEndpoint() { + return revokeEndpoint; + } + + /** + * Set the revoke endpoint. + * + * @param revokeEndpoint the revoke endpoint. + */ + public void setRevokeEndpoint(String revokeEndpoint) { + this.revokeEndpoint = revokeEndpoint; + } + + /** + * Get the string identifier the provider associated with the configuration. + * + * @return the provider name. + */ + public String getProvider() { + return getBeanName().replaceAll(CONFIG_NAME_SUFFIX, ""); + } + + /** + * Append the request params to the URL. + * + * @param params the request params. + * @param url the url. + * @return the complete url. + */ + protected String appendParameters(MultiValueMap params, String url) { + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url); + builder.queryParams(params); + return builder.build().toUriString(); + } + + protected static void getLogoutRequestParams( + String token, String clientId, MultiValueMap params) { + params.put("token", Collections.singletonList(token)); + if (clientId != null && !clientId.isEmpty()) { + params.put("client_id", Collections.singletonList(clientId)); + } + } + + /** + * Build the revoke endpoint. + * + * @param token the access_token to revoke. + * @return the revoke endpoint. + */ + public Endpoint buildRevokeEndpoint( + String token, String accessToken, OAuth2Configuration configuration) { + Endpoint result = null; + if (revokeEndpoint != null) { + HttpHeaders headers = getHttpHeaders(accessToken, configuration); + + MultiValueMap bodyParams = new LinkedMultiValueMap<>(); + bodyParams.add("token", token); + bodyParams.add("client_id", clientId); + + HttpEntity> requestEntity = + new HttpEntity<>(bodyParams, headers); + + result = new Endpoint(HttpMethod.POST, revokeEndpoint); + result.setRequestEntity(requestEntity); + } + return result; + } + + private static HttpHeaders getHttpHeaders( + String accessToken, OAuth2Configuration configuration) { + HttpHeaders headers = getHeaders(accessToken, configuration); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + return headers; + } + + /** + * Build the logout endpoint. + * + * @param token the current access_token. + * @return the logout endpoint. + */ + public Endpoint buildLogoutEndpoint( + String token, String accessToken, OAuth2Configuration configuration) { + Endpoint result = null; + if (logoutUri != null) { + HttpHeaders headers = getHeaders(accessToken, configuration); + + MultiValueMap params = new LinkedMultiValueMap<>(); + getLogoutRequestParams(token, clientId, params); + + HttpEntity> requestEntity = + new HttpEntity<>(null, headers); + + result = new Endpoint(HttpMethod.GET, appendParameters(params, logoutUri)); + result.setRequestEntity(requestEntity); + } + return result; + } + + private static HttpHeaders getHeaders(String accessToken, OAuth2Configuration configuration) { + HttpHeaders headers = new HttpHeaders(); + if (configuration != null + && configuration.clientId != null + && configuration.clientSecret != null) + headers.setBasicAuth( + configuration.clientId, + configuration + .clientSecret); // Set client ID and client secret for authentication + else if (accessToken != null) { + headers.set("Authorization", "Bearer " + accessToken); + } + return headers; + } + + /** @return true if redirect to authorization is active always. False otherwise. */ + public boolean isEnableRedirectEntryPoint() { + return enableRedirectEntryPoint; + } + + /** + * Set the enableRedirectEntryPoint flag. + * + * @param enableRedirectEntryPoint true to enable always redirection, false otherwise. + */ + public void setEnableRedirectEntryPoint(boolean enableRedirectEntryPoint) { + this.enableRedirectEntryPoint = enableRedirectEntryPoint; + } + + /** + * Get the principal key. Default is email. + * + * @return the principal key. + */ + public String getPrincipalKey() { + if (principalKey == null || principalKey.isEmpty()) return "email"; + return principalKey; + } + + /** + * Set the principal key. + * + * @param principalKey the principal key. + */ + public void setPrincipalKey(String principalKey) { + this.principalKey = principalKey; + } + + /** + * The roles claim name. + * + * @return the roles claim name. + */ + public String getRolesClaim() { + return rolesClaim; + } + + /** + * Set the roles claim name. + * + * @param rolesClaim the roles claim name. + */ + public void setRolesClaim(String rolesClaim) { + this.rolesClaim = rolesClaim; + } + + /** @return the groups claim name. */ + public String getGroupsClaim() { + return groupsClaim; + } + + /** + * Set the groups claim name. + * + * @param groupsClaim the groups claim name. + */ + public void setGroupsClaim(String groupsClaim) { + this.groupsClaim = groupsClaim; + } + + /** Class the representing and endpoint with a HTTP method. */ + public static class Endpoint { + + private String url; + + private HttpMethod method; + + private HttpEntity requestEntity; + + public Endpoint(HttpMethod method, String url) { + this.method = method; + this.url = url; + } + + /** @return the url. */ + public String getUrl() { + return url; + } + + /** + * Set the url. + * + * @param url the url. + */ + public void setUrl(String url) { + this.url = url; + } + + /** @return the HttpMethod. */ + public HttpMethod getMethod() { + return method; + } + + /** + * Set the HttpMethod. + * + * @param method the HttpMethod. + */ + public void setMethod(HttpMethod method) { + this.method = method; + } + + /** @return */ + public HttpEntity getRequestEntity() { + return requestEntity; + } + + /** @param requestEntity */ + public void setRequestEntity(HttpEntity requestEntity) { + this.requestEntity = requestEntity; + } + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/OAuth2GeoStoreAuthenticationFilter.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/OAuth2GeoStoreAuthenticationFilter.java new file mode 100644 index 00000000..27ab982c --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/OAuth2GeoStoreAuthenticationFilter.java @@ -0,0 +1,613 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ + +package it.geosolutions.geostore.services.rest.security.oauth2; + +import static com.google.common.collect.Lists.newArrayList; +import static it.geosolutions.geostore.core.security.password.SecurityUtils.getUsername; +import static it.geosolutions.geostore.services.rest.SessionServiceDelegate.PROVIDER_KEY; +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.*; + +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.UserAttribute; +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.enums.Role; +import it.geosolutions.geostore.services.UserGroupService; +import it.geosolutions.geostore.services.UserService; +import it.geosolutions.geostore.services.exception.BadRequestServiceEx; +import it.geosolutions.geostore.services.exception.NotFoundServiceEx; +import it.geosolutions.geostore.services.rest.security.TokenAuthenticationCache; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.apache.commons.lang.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.client.OAuth2ClientContext; +import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter; +import org.springframework.security.oauth2.client.http.AccessTokenRequiredException; +import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException; +import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException; +import org.springframework.security.oauth2.client.token.AccessTokenRequest; +import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; +import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.RemoteTokenServices; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import org.springframework.web.client.ResourceAccessException; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +/** Base filter class for an OAuth2 authentication filter. Authentication instances are cached. */ +public abstract class OAuth2GeoStoreAuthenticationFilter + extends OAuth2ClientAuthenticationProcessingFilter { + + public static final String OAUTH2_AUTHENTICATION_KEY = "oauth2.authentication"; + public static final String OAUTH2_AUTHENTICATION_TYPE_KEY = "oauth2.authenticationType"; + public static final String OAUTH2_ACCESS_TOKEN_CHECK_KEY = "oauth2.AccessTokenCheckResponse"; + private static final Logger LOGGER = + LogManager.getLogger(OAuth2GeoStoreAuthenticationFilter.class); + private final AuthenticationEntryPoint authEntryPoint; + private final TokenAuthenticationCache cache; + @Autowired protected UserService userService; + @Autowired protected UserGroupService userGroupService; + protected RemoteTokenServices tokenServices; + protected OAuth2Configuration configuration; + + /** + * @param tokenServices a RemoteTokenServices instance. + * @param oAuth2RestTemplate the rest template to use for OAuth2 requests. + * @param configuration the OAuth2 configuration. + * @param tokenAuthenticationCache the cache. + */ + public OAuth2GeoStoreAuthenticationFilter( + RemoteTokenServices tokenServices, + GeoStoreOAuthRestTemplate oAuth2RestTemplate, + OAuth2Configuration configuration, + TokenAuthenticationCache tokenAuthenticationCache) { + super("/**"); + super.setTokenServices(tokenServices); + this.tokenServices = tokenServices; + super.restTemplate = oAuth2RestTemplate; + this.configuration = configuration; + this.authEntryPoint = configuration.getAuthenticationEntryPoint(); + this.cache = tokenAuthenticationCache; + } + + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) + throws IOException, ServletException { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + // do we need to authenticate? + if (configuration.isEnabled() && !configuration.isInvalid() && authentication == null) + super.doFilter(req, res, chain); + else if (req instanceof HttpServletRequest) + // ok no need to authenticate, but in case the security context + // holds a Token authentication, we set the access token to request's attributes. + addRequestAttributes((HttpServletRequest) req, authentication); + if (configuration.isEnabled() && configuration.isInvalid()) + if (LOGGER.isDebugEnabled()) + LOGGER.info( + "Skipping configured OAuth2 authentication. One or more mandatory properties are missing (clientId, clientSecret, authorizationUri, tokenUri"); + chain.doFilter(req, res); + } + + @Override + public Authentication attemptAuthentication( + HttpServletRequest request, HttpServletResponse response) + throws AuthenticationException, IOException, ServletException { + Authentication authentication; + String token = OAuth2Utils.tokenFromParamsOrBearer(ACCESS_TOKEN_PARAM, request); + + if (token != null) { + request.setAttribute(OAUTH2_AUTHENTICATION_TYPE_KEY, OAuth2AuthenticationType.BEARER); + } else { + request.setAttribute(OAUTH2_AUTHENTICATION_TYPE_KEY, OAuth2AuthenticationType.USER); + } + + if (token != null) { + authentication = cache.get(token); + if (authentication == null) { + authentication = + authenticateAndUpdateCache( + request, response, token, new DefaultOAuth2AccessToken(token)); + } else { + TokenDetails details = tokenDetails(authentication); + if (details != null) { + OAuth2AccessToken accessToken = details.getAccessToken(); + if (accessToken.isExpired()) + authentication = + authenticateAndUpdateCache(request, response, token, accessToken); + } + } + } else { + clearState(); + authentication = authenticateAndUpdateCache(request, response, null, null); + token = + (String) + RequestContextHolder.getRequestAttributes() + .getAttribute(ACCESS_TOKEN_PARAM, 0); + if (token != null) { + request.setAttribute(ACCESS_TOKEN_PARAM, token); + request.setAttribute( + OAUTH2_AUTHENTICATION_TYPE_KEY, OAuth2AuthenticationType.BEARER); + request.setAttribute( + ID_TOKEN_PARAM, + RequestContextHolder.getRequestAttributes() + .getAttribute(ID_TOKEN_PARAM, 0)); + request.setAttribute( + REFRESH_TOKEN_PARAM, + RequestContextHolder.getRequestAttributes() + .getAttribute(REFRESH_TOKEN_PARAM, 0)); + } + } + + return authentication; + } + + private TokenDetails tokenDetails(Authentication authentication) { + TokenDetails tokenDetails = null; + Object details = authentication.getDetails(); + if (details instanceof TokenDetails) { + tokenDetails = ((TokenDetails) details); + } + return tokenDetails; + } + + private Authentication authenticateAndUpdateCache( + HttpServletRequest request, + HttpServletResponse response, + String token, + OAuth2AccessToken accessToken) { + Authentication authentication = performOAuthAuthentication(request, response, accessToken); + if (authentication != null) { + SecurityContextHolder.getContext().setAuthentication(authentication); + TokenDetails tokenDetails = tokenDetails(authentication); + if (tokenDetails != null) { + OAuth2AccessToken accessTokenDetails = tokenDetails.getAccessToken(); + if (accessTokenDetails != null) { + token = accessTokenDetails.getValue(); + RequestContextHolder.getRequestAttributes() + .setAttribute(ACCESS_TOKEN_PARAM, accessTokenDetails.getValue(), 0); + if (accessTokenDetails != null + && accessTokenDetails.getRefreshToken() != null + && accessTokenDetails.getRefreshToken().getValue() != null) { + RequestContextHolder.getRequestAttributes() + .setAttribute( + REFRESH_TOKEN_PARAM, + accessTokenDetails.getRefreshToken().getValue(), + 0); + } + } + if (tokenDetails.getIdToken() != null) + RequestContextHolder.getRequestAttributes() + .setAttribute(ID_TOKEN_PARAM, tokenDetails.getIdToken(), 0); + } + cache.putCacheEntry(token, authentication); + } + RequestContextHolder.getRequestAttributes() + .setAttribute(PROVIDER_KEY, configuration.getProvider(), 0); + return authentication; + } + + private void clearState() { + OAuth2ClientContext clientContext = restTemplate.getOAuth2ClientContext(); + final AccessTokenRequest accessTokenRequest = clientContext.getAccessTokenRequest(); + if (accessTokenRequest != null && accessTokenRequest.getStateKey() != null) { + clientContext.removePreservedState(accessTokenRequest.getStateKey()); + } + if (accessTokenRequest != null) { + try { + accessTokenRequest.remove(ACCESS_TOKEN_PARAM); + } finally { + SecurityContextHolder.clearContext(); + HttpServletRequest request = + ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) + .getRequest(); + HttpSession session = request.getSession(false); + if (session != null) session.invalidate(); + LOGGER.debug("Cleaned out Session Access Token Request!"); + } + } + } + + /** + * Perform the authentication. + * + * @param request the httpServletRequest. + * @param response the httpServletResponse. + * @param accessToken the accessToken. + * @return the Authentication object. Null if not authenticated. + */ + protected Authentication performOAuthAuthentication( + HttpServletRequest request, + HttpServletResponse response, + OAuth2AccessToken accessToken) { + LOGGER.debug("About to perform remote authentication."); + LOGGER.debug("Access Token: " + accessToken); + String principal = null; + PreAuthenticatedAuthenticationToken result = null; + try { + LOGGER.debug("Trying to get the preauthenticated principal."); + principal = getPreAuthenticatedPrincipal(request, response, accessToken); + } catch (IOException e1) { + LOGGER.error(e1.getMessage(), e1); + principal = null; + } catch (ServletException e1) { + LOGGER.error(e1.getMessage(), e1); + principal = null; + } + + LOGGER.debug("preAuthenticatedPrincipal = " + principal + ", trying to authenticate"); + + if (principal != null && principal.trim().length() > 0) + result = createPreAuthentication(principal, request, response); + return result; + } + + /** + * Get the PreAuthenticatedPrincipal. + * + * @param req the request. + * @param resp the response. + * @param accessToken the access token. + * @return the principal as a string. + * @throws IOException + * @throws ServletException + */ + protected String getPreAuthenticatedPrincipal( + HttpServletRequest req, HttpServletResponse resp, OAuth2AccessToken accessToken) + throws IOException, ServletException { + + // Make sure the REST Resource Template has been correctly configured + LOGGER.debug("About to configure the REST Resource Template"); + configureRestTemplate(); + + if (accessToken != null) { + LOGGER.debug("Setting the access token on the OAuth2ClientContext"); + restTemplate.getOAuth2ClientContext().setAccessToken(accessToken); + } + + // Setting up OAuth2 Filter services and resource template + LOGGER.debug("Setting up OAuth2 Filter services and resource template"); + setRestTemplate(restTemplate); + setTokenServices(tokenServices); + + // Validating the access_token + Authentication authentication = null; + try { + authentication = super.attemptAuthentication(req, resp); + req.setAttribute(OAUTH2_AUTHENTICATION_KEY, authentication); + + // The authentication (in the extensions) should contain a Map which is the result of + // the Access Token Check Request (which will be the json result from the oidc + // "userinfo" + // endpoint). + // We move it from inside the authentication to directly to a request attributes. + // This will make it a "peer" with the Access Token (which spring puts on the request as + // an attribute). + if (authentication instanceof OAuth2Authentication) { + OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication; + Object map = + oAuth2Authentication + .getOAuth2Request() + .getExtensions() + .get(OAUTH2_ACCESS_TOKEN_CHECK_KEY); + if (map instanceof Map) { + req.setAttribute(OAUTH2_ACCESS_TOKEN_CHECK_KEY, map); + } + } + + if (authentication != null && LOGGER.isDebugEnabled()) + LOGGER.debug( + "Authenticated OAuth request for principal " + + authentication.getPrincipal()); + } catch (Exception e) { + handleOAuthException(e, req, resp); + } + + String username = + (authentication != null ? getUsername(authentication.getPrincipal()) : null); + if (username != null && username.trim().length() == 0) username = null; + return username; + } + + private void handleOAuthException(Exception e, HttpServletRequest req, HttpServletResponse resp) + throws IOException, ServletException { + if (e instanceof UserRedirectRequiredException + && configuration.isEnableRedirectEntryPoint()) { + handleUserRedirection(req, resp); + } else if (e instanceof BadCredentialsException || e instanceof ResourceAccessException) { + if (e.getCause() instanceof OAuth2AccessDeniedException) { + LOGGER.warn( + "Error while trying to authenticate to OAuth2 Provider with the following Exception cause:", + e.getCause()); + } else if (e instanceof ResourceAccessException) { + LOGGER.error( + "Could not Authorize OAuth2 Resource due to the following exception:", e); + } else if (e instanceof ResourceAccessException + || e.getCause() instanceof OAuth2AccessDeniedException) { + LOGGER.warn( + "It is worth notice that if you try to validate credentials against an SSH protected Endpoint, you need either your server exposed on a secure SSL channel or OAuth2 Provider Certificate to be trusted on your JVM!"); + LOGGER.info( + "Please refer to the GeoServer OAuth2 Plugin Documentation in order to find the steps for importing the SSH certificates."); + } else { + if (LOGGER.isDebugEnabled()) { + LOGGER.error( + "Could not Authorize OAuth2 Resource due to the following exception:", + e); + } + } + } + } + + private void handleUserRedirection(HttpServletRequest req, HttpServletResponse resp) + throws IOException, ServletException { + if (req.getRequestURI().contains(configuration.getProvider() + "/login")) { + authEntryPoint.commence(req, resp, null); + } else { + if (resp.getStatus() != 302) { + // AEP redirection failed + final AccessTokenRequest accessTokenRequest = + restTemplate.getOAuth2ClientContext().getAccessTokenRequest(); + if (accessTokenRequest.getPreservedState() != null + && accessTokenRequest.getStateKey() != null) { + accessTokenRequest.remove("state"); + accessTokenRequest.remove(accessTokenRequest.getStateKey()); + accessTokenRequest.setPreservedState(null); + } + } + } + } + + protected void configureRestTemplate() { + AuthorizationCodeResourceDetails details = + (AuthorizationCodeResourceDetails) restTemplate.getResource(); + + details.setClientId(configuration.getClientId()); + details.setClientSecret(configuration.getClientSecret()); + this.tokenServices.setClientId(configuration.getClientId()); + this.tokenServices.setClientSecret(configuration.getClientSecret()); + details.setAccessTokenUri(configuration.getAccessTokenUri()); + details.setUserAuthorizationUri(configuration.getAuthorizationUri()); + details.setPreEstablishedRedirectUri(configuration.getRedirectUri()); + this.tokenServices.setCheckTokenEndpointUrl(configuration.getCheckTokenEndpointUrl()); + details.setScope( + parseScopes(Stream.of(configuration.getScopes()).collect(Collectors.joining(",")))); + } + + /** + * Parse the scopes from a comma separated string to a list. + * + * @param commaSeparatedScopes the scopes as a string. + * @return the scopes as a list. + */ + protected List parseScopes(String commaSeparatedScopes) { + List scopes = newArrayList(); + Collections.addAll(scopes, commaSeparatedScopes.split(",")); + return scopes; + } + + /** + * Create the preauthentication token instance from the User name. + * + * @param username the username. + * @param request the HttpServletRequest. + * @param response the HttpServletResponse. + * @return the PreAuthenticatedAuthenticationToken instance. Null if no user was found for the + * username. + */ + protected PreAuthenticatedAuthenticationToken createPreAuthentication( + String username, HttpServletRequest request, HttpServletResponse response) { + User user = retrieveUserWithAuthorities(username, request, response); + if (user == null) return null; + SimpleGrantedAuthority authority = + new SimpleGrantedAuthority("ROLE_" + user.getRole().toString()); + PreAuthenticatedAuthenticationToken authenticationToken = + new PreAuthenticatedAuthenticationToken( + user, null, Collections.singletonList(authority)); + String idToken = OAuth2Utils.getIdToken(); + if (user != null + && (StringUtils.isNotBlank(configuration.getGroupsClaim()) + || StringUtils.isNotBlank(configuration.getRolesClaim()))) { + addAuthoritiesFromToken(user, idToken); + } + OAuth2AccessToken accessToken = restTemplate.getOAuth2ClientContext().getAccessToken(); + authenticationToken.setDetails( + new TokenDetails(accessToken, idToken, configuration.getBeanName())); + return authenticationToken; + } + + /** + * Add authorities from the idToken claims if found. + * + * @param user the user instance. + * @param idToken the id token. + */ + protected void addAuthoritiesFromToken(User user, String idToken) { + JWTHelper helper = new JWTHelper(idToken); + List roles = null; + List groups = null; + if (configuration.getRolesClaim() != null) + roles = helper.getClaimAsList(configuration.getRolesClaim(), String.class); + else roles = Collections.emptyList(); + + if (configuration.getGroupsClaim() != null) + groups = helper.getClaimAsList(configuration.getGroupsClaim(), String.class); + if (groups == null) groups = Collections.emptyList(); + for (String r : roles) { + if (r.equals(Role.ADMIN.name())) user.setRole(Role.ADMIN); + } + for (String g : groups) { + UserGroup group = null; + if (userGroupService != null) group = userGroupService.get(g); + if (group == null) { + group = new UserGroup(); + group.setGroupName(g); + } + user.getGroups().add(group); + } + } + + /** + * Retrieves a user by username. Will create the user when not found, if the auto create flag + * was set to true. + * + * @param username the username. + * @param request the HttpServletRequest. + * @param response the HttpServletResponse. + * @return a {@link User} instance if the user was found/created. Null otherwise. + */ + protected User retrieveUserWithAuthorities( + String username, HttpServletRequest request, HttpServletResponse response) { + User user = null; + if (username != null && userService != null) { + try { + user = userService.get(username); + } catch (NotFoundServiceEx notFoundServiceEx) { + LOGGER.debug("User with username " + username + " not found."); + } + } + if (user == null) { + try { + user = createUser(username, null, ""); + } catch (BadRequestServiceEx | NotFoundServiceEx e) { + LOGGER.error("Error while autocreating the user: " + username, e); + } + } + return user; + } + + /** + * Create a User instance. + * + * @param userName the username. + * @param credentials the password. + * @param rawUser user object. + * @return a User instance. + * @throws BadRequestServiceEx + * @throws NotFoundServiceEx + */ + protected User createUser(String userName, String credentials, Object rawUser) + throws BadRequestServiceEx, NotFoundServiceEx { + User user = new User(); + + user.setName(userName); + user.setNewPassword(credentials); + user.setEnabled(true); + UserAttribute userAttribute = new UserAttribute(); + userAttribute.setName(OAuth2Configuration.CONFIGURATION_NAME); + userAttribute.setValue(configuration.getBeanName()); + user.setAttribute(Collections.singletonList(userAttribute)); + Set groups = new HashSet(); + user.setGroups(groups); + user.setRole(Role.USER); + if (userService != null && configuration.isAutoCreateUser()) { + long id = userService.insert(user); + user = new User(user); + user.setId(id); + } + return user; + } + + @Override + public void afterPropertiesSet() { + // do nothing: avoid filter instantiation failing due RestTemplate bean having creation + // scope=session + } + + @Override + protected void successfulAuthentication( + HttpServletRequest request, + HttpServletResponse response, + FilterChain chain, + Authentication authResult) + throws IOException, ServletException { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug( + "Authentication success. Updating SecurityContextHolder to contain: " + + authResult); + } + + SecurityContextHolder.getContext().setAuthentication(authResult); + addRequestAttributes(request, authResult); + request.setAttribute(PROVIDER_KEY, configuration.getProvider()); + } + + private void addRequestAttributes(HttpServletRequest request, Authentication authentication) { + if (authentication != null) { + TokenDetails tokenDetails = tokenDetails(authentication); + if (tokenDetails != null && tokenDetails.getAccessToken() != null) { + OAuth2AccessToken accessToken = tokenDetails.getAccessToken(); + request.setAttribute(ACCESS_TOKEN_PARAM, accessToken.getValue()); + if (tokenDetails.getIdToken() != null) + request.setAttribute(ID_TOKEN_PARAM, tokenDetails.getIdToken()); + if (accessToken.getRefreshToken() != null) + request.setAttribute( + REFRESH_TOKEN_PARAM, accessToken.getRefreshToken().getValue()); + request.setAttribute(PROVIDER_KEY, configuration.getProvider()); + } + } + } + + @Override + protected void unsuccessfulAuthentication( + HttpServletRequest request, + HttpServletResponse response, + AuthenticationException failed) + throws IOException, ServletException { + if (failed instanceof AccessTokenRequiredException) { + SecurityContextHolder.clearContext(); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Authentication request failed: " + failed, failed); + LOGGER.debug("Updated SecurityContextHolder to contain null Authentication"); + } + } + } + + public enum OAuth2AuthenticationType { + BEARER, // this is a bearer token (meaning existing access token is in the request headers) + USER // this is a "normal" oauth2 login (i.e. interactive user login) + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/OAuth2GeoStoreSecurityConfiguration.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/OAuth2GeoStoreSecurityConfiguration.java new file mode 100644 index 00000000..87166205 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/OAuth2GeoStoreSecurityConfiguration.java @@ -0,0 +1,145 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import javax.annotation.Resource; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.client.token.AccessTokenProvider; +import org.springframework.security.oauth2.client.token.AccessTokenProviderChain; +import org.springframework.security.oauth2.client.token.AccessTokenRequest; +import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; +import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider; +import org.springframework.security.oauth2.common.AuthenticationScheme; + +/** + * Base abstract class for @Configuration classes providing needed beans from the Spring OAuth2 + * mechanism. + */ +@Configuration +public abstract class OAuth2GeoStoreSecurityConfiguration implements ApplicationContextAware { + + static final String DETAILS_ID = "oauth2-client"; + + protected ApplicationContext context; + + @Resource + @Qualifier("accessTokenRequest") + private AccessTokenRequest accessTokenRequest; + + /** + * Returns the resource bean containing the Access Token Request info. + * + * @return the accessTokenRequest + */ + public AccessTokenRequest getAccessTokenRequest() { + return accessTokenRequest; + } + + protected OAuth2ProtectedResourceDetails resourceDetails() { + AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails(); + details.setId(getDetailsId()); + + details.setGrantType("authorization_code"); + details.setAuthenticationScheme(AuthenticationScheme.header); + details.setClientAuthenticationScheme(AuthenticationScheme.form); + + return details; + } + + protected String getDetailsId() { + return DETAILS_ID; + } + + protected GeoStoreOAuthRestTemplate restTemplate() { + return new GeoStoreOAuthRestTemplate( + resourceDetails(), + new DefaultOAuth2ClientContext(getAccessTokenRequest()), + configuration()); + } + + public GeoStoreOAuthRestTemplate oauth2RestTemplate() { + GeoStoreOAuthRestTemplate oAuth2RestTemplate = restTemplate(); + setJacksonConverter(oAuth2RestTemplate); + AuthorizationCodeAccessTokenProvider authorizationCodeAccessTokenProvider = + new AuthorizationCodeAccessTokenProvider(); + authorizationCodeAccessTokenProvider.setStateMandatory(false); + + AccessTokenProvider accessTokenProviderChain = + new AccessTokenProviderChain( + Arrays.asList( + authorizationCodeAccessTokenProvider, + new ImplicitAccessTokenProvider(), + new ResourceOwnerPasswordAccessTokenProvider(), + new ClientCredentialsAccessTokenProvider())); + + oAuth2RestTemplate.setAccessTokenProvider(accessTokenProviderChain); + return oAuth2RestTemplate; + } + + protected void setJacksonConverter(OAuth2RestTemplate oAuth2RestTemplate) { + List> converterList = oAuth2RestTemplate.getMessageConverters(); + MappingJackson2HttpMessageConverter jacksonConverter = null; + for (HttpMessageConverter converter : converterList) { + if (converter instanceof MappingJackson2HttpMessageConverter) { + jacksonConverter = (MappingJackson2HttpMessageConverter) converter; + break; + } + } + if (jacksonConverter == null) { + jacksonConverter = new MappingJackson2HttpMessageConverter(); + oAuth2RestTemplate.getMessageConverters().add(jacksonConverter); + } + jacksonConverter.setSupportedMediaTypes( + Collections.singletonList( + new MediaType("application", "json", StandardCharsets.UTF_8))); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.context = applicationContext; + } + + public abstract OAuth2Configuration configuration(); +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/OAuth2SessionServiceDelegate.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/OAuth2SessionServiceDelegate.java new file mode 100644 index 00000000..2e83f821 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/OAuth2SessionServiceDelegate.java @@ -0,0 +1,511 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2; + +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.*; + +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.security.password.SecurityUtils; +import it.geosolutions.geostore.services.UserService; +import it.geosolutions.geostore.services.rest.RESTSessionService; +import it.geosolutions.geostore.services.rest.SessionServiceDelegate; +import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx; +import it.geosolutions.geostore.services.rest.model.SessionToken; +import it.geosolutions.geostore.services.rest.security.TokenAuthenticationCache; +import it.geosolutions.geostore.services.rest.utils.GeoStoreContext; +import java.io.IOException; +import java.util.Date; +import java.util.Map; +import java.util.Optional; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.http.*; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.client.OAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.token.AccessTokenRequest; +import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; +import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpMessageConverterExtractor; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.context.request.RequestContextHolder; + +/** Abstract implementation of an OAuth2 SessionServiceDelegate. */ +public abstract class OAuth2SessionServiceDelegate implements SessionServiceDelegate { + + private static final Logger LOGGER = LogManager.getLogger(OAuth2SessionServiceDelegate.class); + + protected UserService userService; + + /** + * @param restSessionService the session service to which register this delegate. + * @param delegateName this delegate name eg. google or GitHub etc... + */ + public OAuth2SessionServiceDelegate( + RESTSessionService restSessionService, String delegateName, UserService userService) { + restSessionService.registerDelegate(delegateName, this); + this.userService = userService; + } + + @Override + public SessionToken refresh(String refreshToken, String accessToken) { + HttpServletRequest request = getRequest(); + if (accessToken == null) + accessToken = OAuth2Utils.tokenFromParamsOrBearer(ACCESS_TOKEN_PARAM, request); + if (accessToken == null) + throw new NotFoundWebEx("Either the accessToken or the refresh token are missing"); + + OAuth2AccessToken currentToken = retrieveAccessToken(accessToken); + Date expiresIn = currentToken.getExpiration(); + if (refreshToken == null) refreshToken = getParameterValue(REFRESH_TOKEN_PARAM, request); + Date fiveMinutesFromNow = fiveMinutesFromNow(); + SessionToken sessionToken = null; + OAuth2Configuration configuration = configuration(); + if (configuration != null && configuration.isEnabled()) { + if ((expiresIn == null || fiveMinutesFromNow.after(expiresIn)) + && refreshToken != null) { + if (LOGGER.isDebugEnabled()) LOGGER.info("Going to refresh the token."); + try { + sessionToken = doRefresh(refreshToken, accessToken, configuration); + if (sessionToken == null) + sessionToken = + sessionToken( + accessToken, refreshToken, currentToken.getExpiration()); + } catch (NullPointerException npe) { + LOGGER.error("Current configuration wasn't correctly initialized."); + } + } + } + return sessionToken; + } + + /** + * Invokes the refresh endpoint and return a session token holding the updated tokens details. + * + * @param refreshToken the refresh token. + * @param accessToken the access token. + * @param configuration the OAuth2Configuration. + * @return the SessionToken. + */ + protected SessionToken doRefresh( + String refreshToken, String accessToken, OAuth2Configuration configuration) { + SessionToken sessionToken = null; + + RestTemplate restTemplate = new RestTemplate(); + HttpHeaders headers = getHttpHeaders(accessToken, configuration); + + MultiValueMap requestBody = new LinkedMultiValueMap<>(); + requestBody.add("grant_type", "refresh_token"); + requestBody.add("refresh_token", refreshToken); + requestBody.add("client_secret", configuration.getClientSecret()); + + HttpEntity> requestEntity = + new HttpEntity<>(requestBody, headers); + + OAuth2AccessToken newToken = null; + try { + newToken = + restTemplate + .exchange( + configuration + .buildRefreshTokenURI(), // Use exchange method for POST + // request + HttpMethod.POST, + requestEntity, // Include request body + OAuth2AccessToken.class) + .getBody(); + } catch (Exception ex) { + LOGGER.error("Error trying to obtain a refresh token.", ex); + } + + if (newToken != null && newToken.getValue() != null) { + // update the Authentication + updateAuthToken(accessToken, newToken, refreshToken, configuration); + sessionToken = + sessionToken(newToken.getValue(), refreshToken, newToken.getExpiration()); + } else if (accessToken != null) { + // update the Authentication + sessionToken = sessionToken(accessToken, refreshToken, null); + } else { + // the refresh token was invalid. let's clear the session and send a remote logout. + // then redirect to the login entry point. + LOGGER.info( + "Unable to refresh the token. The following request was performed: {}. Redirecting to login.", + configuration.buildRefreshTokenURI("offline")); + doLogout(null); + try { + getResponse() + .sendRedirect( + "../../openid/" + + configuration.getProvider().toLowerCase() + + "/login"); + } catch (IOException e) { + LOGGER.error("Error while sending redirect to login service. ", e); + throw new RuntimeException(e); + } + } + return sessionToken; + } + + private static HttpHeaders getHttpHeaders( + String accessToken, OAuth2Configuration configuration) { + HttpHeaders headers = new HttpHeaders(); + if (configuration != null + && configuration.clientId != null + && configuration.clientSecret != null) + headers.setBasicAuth( + configuration.clientId, + configuration + .clientSecret); // Set client ID and client secret for authentication + else if (accessToken != null) { + headers.set("Authorization", "Bearer " + accessToken); + } + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); // Set content type + return headers; + } + + private SessionToken sessionToken(String accessToken, String refreshToken, Date expires) { + SessionToken sessionToken = new SessionToken(); + if (expires != null) sessionToken.setExpires(expires.getTime()); + sessionToken.setAccessToken(accessToken); + sessionToken.setRefreshToken(refreshToken); + sessionToken.setTokenType("bearer"); + return sessionToken; + } + + // Builds an authentication instance out of the passed values. + // Sets it to the cache and to the SecurityContext to be sure the new token is updates. + private Authentication updateAuthToken( + String oldToken, + OAuth2AccessToken newToken, + String refreshToken, + OAuth2Configuration conf) { + Authentication authentication = cache().get(oldToken); + if (authentication == null) + authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (authentication instanceof PreAuthenticatedAuthenticationToken) { + if (LOGGER.isDebugEnabled()) + LOGGER.info("Updating the cache and the SecurityContext with new Auth details"); + String idToken = null; + TokenDetails details = getTokenDetails(authentication); + idToken = details.getIdToken(); + cache().removeEntry(oldToken); + PreAuthenticatedAuthenticationToken updated = + new PreAuthenticatedAuthenticationToken( + authentication.getPrincipal(), + authentication.getCredentials(), + authentication.getAuthorities()); + DefaultOAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(newToken); + if (refreshToken != null) { + accessToken.setRefreshToken(new DefaultOAuth2RefreshToken(refreshToken)); + } + if (LOGGER.isDebugEnabled()) + LOGGER.debug( + "Creating new details. AccessToken: " + + accessToken + + " IdToken: " + + idToken); + updated.setDetails(new TokenDetails(accessToken, idToken, conf.getBeanName())); + cache().putCacheEntry(newToken.getValue(), updated); + SecurityContextHolder.getContext().setAuthentication(updated); + authentication = updated; + } + return authentication; + } + + private OAuth2AccessToken retrieveAccessToken(String accessToken) { + Authentication authentication = cache().get(accessToken); + OAuth2AccessToken result = null; + if (authentication != null) { + TokenDetails details = OAuth2Utils.getTokenDetails(authentication); + result = details.getAccessToken(); + } + if (result == null) { + OAuth2RestTemplate oAuth2RestTemplate = restTemplate(); + if (oAuth2RestTemplate != null) { + OAuth2ClientContext context = oAuth2RestTemplate.getOAuth2ClientContext(); + if (context != null) result = context.getAccessToken(); + } + } + if (result == null) result = new DefaultOAuth2AccessToken(accessToken); + return result; + } + + @Override + public void doLogout(String sessionId) { + HttpServletRequest request = getRequest(); + HttpServletResponse response = getResponse(); + OAuth2RestTemplate restTemplate = restTemplate(); + + String token = null; + String accessToken = null; + if (sessionId != null) { + TokenAuthenticationCache cache = cache(); + Authentication authentication = cache.get(sessionId); + TokenDetails tokenDetails = getTokenDetails(authentication); + if (tokenDetails != null) { + token = tokenDetails.getIdToken(); + accessToken = tokenDetails.getAccessToken().getValue(); + } + cache.removeEntry(sessionId); + } + + if (token == null) { + if (restTemplate.getOAuth2ClientContext().getAccessToken() != null) { + token = + restTemplate + .getOAuth2ClientContext() + .getAccessToken() + .getRefreshToken() + .getValue(); + } + if (token == null) { + token = OAuth2Utils.getParameterValue(REFRESH_TOKEN_PARAM, request); + } + if (token == null) { + token = + (String) + RequestContextHolder.getRequestAttributes() + .getAttribute(REFRESH_TOKEN_PARAM, 0); + } + } + + if (accessToken == null) { + if (restTemplate.getOAuth2ClientContext().getAccessToken() != null) { + accessToken = restTemplate.getOAuth2ClientContext().getAccessToken().getValue(); + } + if (accessToken == null) { + accessToken = OAuth2Utils.getParameterValue(ACCESS_TOKEN_PARAM, request); + } + if (accessToken == null) { + accessToken = + (String) + RequestContextHolder.getRequestAttributes() + .getAttribute(ACCESS_TOKEN_PARAM, 0); + } + } + + OAuth2Configuration configuration = configuration(); + if (configuration != null && configuration.isEnabled()) { + if (token != null && accessToken != null) { + if (configuration.isGlobalLogoutEnabled()) + doLogoutInternal(token, configuration, accessToken); + if (configuration.getRevokeEndpoint() != null) clearSession(restTemplate, request); + } else { + if (LOGGER.isDebugEnabled()) + LOGGER.info("Unable to retrieve access token. Remote logout was not executed."); + } + if (response != null) clearCookies(request, response); + } + } + + // clears any state a Spring OAuth2 object might preserve. + private void clearSession(OAuth2RestTemplate restTemplate, HttpServletRequest request) { + final AccessTokenRequest accessTokenRequest = + restTemplate.getOAuth2ClientContext().getAccessTokenRequest(); + if (accessTokenRequest != null && accessTokenRequest.getStateKey() != null) { + restTemplate + .getOAuth2ClientContext() + .removePreservedState(accessTokenRequest.getStateKey()); + } + try { + accessTokenRequest.remove("access_token"); + accessTokenRequest.remove("refresh_token"); + request.logout(); + } catch (ServletException e) { + LOGGER.error("Error happened while doing request logout: ", e); + } finally { + SecurityContextHolder.clearContext(); + } + } + + /** + * Call the revoke/logout endpoint of the OAuth2 provider. + * + * @param token the access token. + * @param configuration the OAuth2Configuration + */ + protected void doLogoutInternal( + Object token, OAuth2Configuration configuration, String accessToken) { + String tokenValue = null; + if (token instanceof OAuth2AccessToken) { + tokenValue = + ((OAuth2AccessToken) token).getRefreshToken() != null + ? ((OAuth2AccessToken) token).getRefreshToken().getValue() + : ((OAuth2AccessToken) token).getValue(); + } else if (token instanceof String) { + tokenValue = (String) token; + } + if (configuration.getRevokeEndpoint() != null && tokenValue != null) { + if (LOGGER.isDebugEnabled()) LOGGER.info("Performing remote logout"); + callRevokeEndpoint(tokenValue, accessToken); + callRemoteLogout(tokenValue, accessToken); + } + } + + protected void callRevokeEndpoint(String token, String accessToken) { + OAuth2Configuration configuration = configuration(); + if (configuration != null && configuration.isEnabled()) { + OAuth2Configuration.Endpoint revokeEndpoint = + configuration.buildRevokeEndpoint(token, accessToken, configuration); + if (revokeEndpoint != null) { + RestTemplate template = new RestTemplate(); + try { + ResponseEntity responseEntity = + template.exchange( + revokeEndpoint.getUrl(), + revokeEndpoint.getMethod(), + revokeEndpoint.getRequestEntity(), + String.class); + if (responseEntity.getStatusCode().value() != 200) { + logRevokeErrors(responseEntity.getBody()); + } + } catch (Exception e) { + logRevokeErrors(e); + } + } + } + } + + protected void callRemoteLogout(String token, String accessToken) { + OAuth2Configuration configuration = configuration(); + if (configuration != null && configuration.isEnabled()) { + OAuth2Configuration.Endpoint logoutEndpoint = + configuration.buildLogoutEndpoint(token, accessToken, configuration); + if (logoutEndpoint != null) { + RestTemplate template = new RestTemplate(); + ResponseEntity responseEntity = + template.exchange( + logoutEndpoint.getUrl(), + logoutEndpoint.getMethod(), + logoutEndpoint.getRequestEntity(), + String.class); + if (responseEntity.getStatusCode().value() != 200) { + logRevokeErrors(responseEntity.getBody()); + } + } + } + } + + protected void clearCookies(HttpServletRequest request, HttpServletResponse response) { + javax.servlet.http.Cookie[] allCookies = request.getCookies(); + if (allCookies != null && allCookies.length > 0) + for (int i = 0; i < allCookies.length; i++) { + javax.servlet.http.Cookie toDelete = allCookies[i]; + if (deleteCookie(toDelete)) { + toDelete.setMaxAge(-1); + toDelete.setPath("/"); + toDelete.setComment("EXPIRING COOKIE at " + System.currentTimeMillis()); + response.addCookie(toDelete); + } + } + } + + protected boolean deleteCookie(javax.servlet.http.Cookie c) { + return c.getName().equalsIgnoreCase("JSESSIONID") + || c.getName().equalsIgnoreCase(ACCESS_TOKEN_PARAM) + || c.getName().equalsIgnoreCase(REFRESH_TOKEN_PARAM); + } + + private TokenAuthenticationCache cache() { + return GeoStoreContext.bean("oAuth2Cache", TokenAuthenticationCache.class); + } + + /** + * Get the OAuth2Configuration. + * + * @return the OAuth2Configuration. + */ + protected OAuth2Configuration configuration() { + Map configurations = + GeoStoreContext.beans(OAuth2Configuration.class); + if (configurations != null) { + Optional enabledConfig = + configurations.values().stream() + .filter(OAuth2Configuration::isEnabled) + .findFirst(); + + if (enabledConfig.isPresent()) { + return enabledConfig.get(); + } + } + return null; + } + + protected HttpMessageConverterExtractor tokenExtractor() { + return new HttpMessageConverterExtractor<>( + OAuth2AccessToken.class, restTemplate().getMessageConverters()); + } + + protected abstract OAuth2RestTemplate restTemplate(); + + @Override + public User getUser(String sessionId, boolean refresh, boolean autorefresh) { + String username = getUserName(sessionId, refresh, autorefresh); + if (username != null) { + User user; + try { + user = userService.get(username); + } catch (Exception e) { + LOGGER.warn("Issue while retrieving user. Will return just the username.", e); + user = new User(); + user.setName(username); + } + return user; + } + return null; + } + + @Override + public String getUserName(String sessionId, boolean refresh, boolean autorefresh) { + TokenAuthenticationCache cache = cache(); + Authentication authentication = cache.get(sessionId); + if (refresh) + LOGGER.warn( + "Refresh was set to true but this delegate is " + + "not supporting refreshing token when retrieving the user..."); + if (authentication != null) { + Object o = authentication.getPrincipal(); + if (o != null) return SecurityUtils.getUsername(o); + } + return null; + } + + private static void logRevokeErrors(Object cause) { + LOGGER.error("Error while revoking authorization. Error is: {}", cause); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/OAuth2Utils.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/OAuth2Utils.java new file mode 100644 index 00000000..986655ad --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/OAuth2Utils.java @@ -0,0 +1,193 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2; + +import it.geosolutions.geostore.core.security.password.SecurityUtils; +import java.util.Calendar; +import java.util.Date; +import java.util.Enumeration; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.provider.authentication.BearerTokenExtractor; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +/** + * A class that groups some constants and utility methods used to handle OAuth2 related tasks. + * Provides functionality like retrieving tokens from the request, or retrieving the {@link + * TokenDetails} from an Authentication instance. + */ +public class OAuth2Utils { + + public static final String ID_TOKEN_PARAM = "id_token"; + + public static final String ACCESS_TOKEN_PARAM = "access_token"; + + public static final String REFRESH_TOKEN_PARAM = "refresh_token"; + + public static final String TOKENS_KEY = "tokens_key"; + + public static final String AUTH_PROVIDER = "authProvider"; + + /** + * Retrieve a token either from a request param or from the Bearer. + * + * @param paramName the name of the request param. + * @param request the request. + * @return the token if found, null otherwise. + */ + public static String tokenFromParamsOrBearer(String paramName, HttpServletRequest request) { + String token = getParameterValue(paramName, request); + if (token == null) { + token = getBearerToken(request); + } + return token; + } + + public static Date fiveMinutesFromNow() { + Calendar currentTimeNow = Calendar.getInstance(); + currentTimeNow.add(Calendar.MINUTE, 5); + return currentTimeNow.getTime(); + } + + /** + * Retrieve a value from a request param. + * + * @param paramName the name of the request param. + * @param request the request. + * @return the value if found, null otherwise. + */ + public static String getParameterValue(String paramName, HttpServletRequest request) { + for (Enumeration iterator = request.getParameterNames(); + iterator.hasMoreElements(); ) { + final String param = iterator.nextElement(); + if (paramName.equalsIgnoreCase(param)) { + return request.getParameter(param); + } + } + + return null; + } + + /** + * Get the bearer token from the header. + * + * @param request the request. + * @return the token if found null otherwise. + */ + public static String getBearerToken(HttpServletRequest request) { + Authentication auth = new BearerTokenExtractor().extract(request); + if (auth != null) return SecurityUtils.getUsername(auth.getPrincipal()); + + return null; + } + + /** + * Get a request attribute using first a request scope then the session scope. + * + * @param name the name of the attribute. + * @return the token attribute value if found. + */ + public static String getRequestAttribute(String name) { + String token = (String) RequestContextHolder.getRequestAttributes().getAttribute(name, 0); + if (token == null) + token = (String) RequestContextHolder.getRequestAttributes().getAttribute(name, 1); + return token; + } + + /** + * Get the id token from the request attributes. + * + * @return the id token value if found, null otherwise. + */ + public static String getIdToken() { + return getRequestAttribute(GeoStoreOAuthRestTemplate.ID_TOKEN_VALUE); + } + + /** + * Get the Access Token from the request attributes if present. + * + * @return the access token if found, null otherwise. + */ + public static String getAccessToken() { + String token = getRequestAttribute(ACCESS_TOKEN_PARAM); + if (token == null) token = tokenFromParamsOrBearer(ACCESS_TOKEN_PARAM, getRequest()); + + return token; + } + + /** + * Get the Refresh Toke from request attributes if present. + * + * @return the refresh token if found, null otherwise. + */ + public static String getRefreshAccessToken() { + String refreshToken = getRequestAttribute(REFRESH_TOKEN_PARAM); + if (refreshToken == null) + refreshToken = getParameterValue(REFRESH_TOKEN_PARAM, getRequest()); + return refreshToken; + } + + /** + * Return the {@link TokenDetails} stored in the Authentication instance. + * + * @param authentication the authentication eventually holding the TokenDetails. + * @return the token details if found. Null otherwise. + */ + public static TokenDetails getTokenDetails(Authentication authentication) { + TokenDetails tokenDetails = null; + if (authentication != null) { + Object details = authentication.getDetails(); + if (details instanceof TokenDetails) { + tokenDetails = ((TokenDetails) details); + } + } + return tokenDetails; + } + + /** + * Get the HttpServletRequest from the RequestContext. + * + * @return the current HttpServletRequest. + */ + public static HttpServletRequest getRequest() { + return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) + .getRequest(); + } + + /** + * Get the HttpServletResponse from the RequestContext. + * + * @return the current HttpServletResponse. + */ + public static HttpServletResponse getResponse() { + return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) + .getResponse(); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/Oauth2LoginService.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/Oauth2LoginService.java new file mode 100644 index 00000000..f610ce70 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/Oauth2LoginService.java @@ -0,0 +1,129 @@ +package it.geosolutions.geostore.services.rest.security.oauth2; + +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Configuration.CONFIG_NAME_SUFFIX; +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.*; +import static org.springframework.security.oauth2.common.OAuth2AccessToken.BEARER_TYPE; + +import it.geosolutions.geostore.services.rest.IdPLoginService; +import it.geosolutions.geostore.services.rest.model.SessionToken; +import it.geosolutions.geostore.services.rest.security.IdPConfiguration; +import it.geosolutions.geostore.services.rest.utils.GeoStoreContext; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Date; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.Cookie; +import javax.ws.rs.core.NewCookie; +import javax.ws.rs.core.Response; +import org.apache.commons.lang.time.DateUtils; +import org.apache.cxf.jaxrs.impl.ResponseBuilderImpl; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public abstract class Oauth2LoginService implements IdPLoginService { + + private static final Logger LOGGER = LogManager.getLogger(Oauth2LoginService.class); + + @Override + public void doLogin(HttpServletRequest request, HttpServletResponse response, String provider) { + HttpServletResponse resp = OAuth2Utils.getResponse(); + OAuth2Configuration configuration = oauth2Configuration(provider); + String login = configuration.buildLoginUri(); + try { + resp.sendRedirect(login); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public Response doInternalRedirect( + HttpServletRequest request, HttpServletResponse response, String provider) { + String token = getAccessToken(); + String refreshToken = getRefreshAccessToken(); + return buildCallbackResponse(token, refreshToken, provider); + } + + protected Response.ResponseBuilder getCallbackResponseBuilder( + String token, String refreshToken, String provider) { + Response.ResponseBuilder result = new ResponseBuilderImpl(); + IdPConfiguration configuration = configuration(provider); + LOGGER.info("Callback Provider: {}", provider); + LOGGER.debug("Token: {}", token); + LOGGER.debug("Redirect uri: {}", configuration.getRedirectUri()); + LOGGER.debug("Internal redirect uri: {}", configuration.getInternalRedirectUri()); + if (token != null) { + LOGGER.info("AccessToken found"); + SessionToken sessionToken = new SessionToken(); + try { + result = + result.status(302) + .location(new URI(configuration.getInternalRedirectUri())); + LOGGER.debug("AccessToken: {}", token); + sessionToken.setAccessToken(token); + if (refreshToken != null) { + LOGGER.debug("RefreshToken: {}", refreshToken); + sessionToken.setRefreshToken(refreshToken); + } + sessionToken.setTokenType(BEARER_TYPE); + TokenStorage tokenStorage = tokenStorage(); + Object key = tokenStorage.buildTokenKey(); + tokenStorage.saveToken(key, sessionToken); + Cookie cookie = cookie(TOKENS_KEY, key.toString()); + result.header("Set-Cookie", cookie.toString()); + cookie = cookie(AUTH_PROVIDER, provider); + result.header("Set-Cookie", cookie.toString()); + } catch (URISyntaxException e) { + LOGGER.error(e); + result = + result.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity( + "Exception while parsing the internal redirect url: " + + e.getMessage()); + } + } else { + LOGGER.error("No access token found on callback request."); + result = + Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("No access token found."); + } + return result; + } + + @Override + public SessionToken getTokenByIdentifier(String provider, String tokenIdentifier) { + TokenStorage storage = tokenStorage(); + SessionToken sessionToken = storage.getTokenByIdentifier(tokenIdentifier); + if (sessionToken != null) storage.removeTokenByIdentifier(tokenIdentifier); + return sessionToken; + } + + protected TokenStorage tokenStorage() { + return GeoStoreContext.bean(TokenStorage.class); + } + + protected Response buildCallbackResponse(String token, String refreshToken, String provider) { + Response.ResponseBuilder result = getCallbackResponseBuilder(token, refreshToken, provider); + return result.build(); + } + + protected OAuth2Configuration oauth2Configuration(String provider) { + return GeoStoreContext.bean(provider + CONFIG_NAME_SUFFIX, OAuth2Configuration.class); + } + + protected IdPConfiguration configuration(String provider) { + return GeoStoreContext.bean(provider + CONFIG_NAME_SUFFIX, IdPConfiguration.class); + } + + protected NewCookie cookie(String name, String value) { + return cookie(name, value, DateUtils.addMinutes(new Date(), 2)); + } + + protected NewCookie cookie(String name, String value, Date expires) { + Cookie cookie = new Cookie(name, value, "/", null); + return new AccessCookie( + cookie, "", 120, DateUtils.addMinutes(new Date(), 2), false, false, "lax"); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/TokenDetails.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/TokenDetails.java new file mode 100644 index 00000000..fcbfb369 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/TokenDetails.java @@ -0,0 +1,98 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.interfaces.Claim; +import com.auth0.jwt.interfaces.DecodedJWT; +import java.io.Serializable; +import org.springframework.security.oauth2.common.OAuth2AccessToken; + +/** + * Holds the token details. Instances of this class are meant to be stored into the SecurityContext + * along with the corresponding Authentication instance. + */ +public class TokenDetails implements Serializable { + private final String idToken; + private final String provider; + private OAuth2AccessToken accessToken; + private DecodedJWT decodedJWT; + + /** + * @param accessToken the accessToken instance. + * @param idToken the JWT idToken + */ + public TokenDetails(OAuth2AccessToken accessToken, String idToken, String provider) { + this.idToken = idToken; + this.accessToken = accessToken; + if (idToken != null) { + decodedJWT = JWT.decode(idToken); + } + this.provider = provider; + } + + /** + * Get a claim by name from the idToken. + * + * @param claimName the name of the claim to retrieve. + * @param binding the Class to which convert the claim value. + * @param the type of the claim value. + * @return the claim value. + */ + public T getClaim(String claimName, Class binding) { + T result = null; + if (decodedJWT != null) { + Claim claim = decodedJWT.getClaim(claimName); + if (claim != null) result = claim.as(binding); + } + return result; + } + + /** @return the JWT idToken. */ + public String getIdToken() { + return idToken; + } + + /** @return the OAuth2AccessToken instance. */ + public OAuth2AccessToken getAccessToken() { + return accessToken; + } + + /** + * Set the OAuth2AccessToken. + * + * @param accessToken the OAuth2AccessToken. + */ + public void setAccessToken(OAuth2AccessToken accessToken) { + this.accessToken = accessToken; + } + + public String getProvider() { + return provider; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/TokenStorage.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/TokenStorage.java new file mode 100644 index 00000000..c5c4229f --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/TokenStorage.java @@ -0,0 +1,14 @@ +package it.geosolutions.geostore.services.rest.security.oauth2; + +import it.geosolutions.geostore.services.rest.model.SessionToken; + +public interface TokenStorage { + + SessionToken getTokenByIdentifier(T identifier); + + void removeTokenByIdentifier(T identifier); + + void saveToken(T identifier, SessionToken token); + + T buildTokenKey(); +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/GoogleAccessTokenConverter.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/GoogleAccessTokenConverter.java new file mode 100644 index 00000000..b178e665 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/GoogleAccessTokenConverter.java @@ -0,0 +1,78 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.google; + +import it.geosolutions.geostore.services.rest.security.oauth2.GeoStoreAccessTokenConverter; +import java.util.*; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; + +/** Google AccessTokenConverter. Retrieves Authentication information from the AccessToken. */ +public class GoogleAccessTokenConverter extends GeoStoreAccessTokenConverter { + + public GoogleAccessTokenConverter(String principalKey) { + super(principalKey); + } + + @Override + public OAuth2Authentication extractAuthentication(Map map) { + Map parameters = new HashMap<>(); + Set scope = parseScopes(map); + Authentication user = userTokenConverter.extractAuthentication(map); + String clientId = (String) map.get(CLIENT_ID); + parameters.put(CLIENT_ID, clientId); + Object aud = map.get(AUD); + Set resourceIds = new LinkedHashSet<>(); + if (aud instanceof Collection) { + ((Collection) aud).stream().forEach(a -> resourceIds.add(a.toString())); + } else if (aud instanceof String) { + resourceIds.add(aud.toString()); + } + OAuth2Request request = + new OAuth2Request( + parameters, clientId, null, true, scope, resourceIds, null, null, null); + return new OAuth2Authentication(request, user); + } + + private Set parseScopes(Map map) { + // Parsing of scopes coming back from Google are slightly different from + // the default implementation. Instead of it being a collection it is a + // String where multiple scopes are separated by a space. + Object scopeAsObject = map.containsKey(SCOPE) ? map.get(SCOPE) : ""; + Set scope = new LinkedHashSet<>(); + if (String.class.isAssignableFrom(scopeAsObject.getClass())) { + String scopeAsString = (String) scopeAsObject; + Collections.addAll(scope, scopeAsString.split(" ")); + } else if (Collection.class.isAssignableFrom(scopeAsObject.getClass())) { + Collection scopes = (Collection) scopeAsObject; + scope.addAll(scopes); + } + return scope; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/GoogleLoginService.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/GoogleLoginService.java new file mode 100644 index 00000000..b9d57930 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/GoogleLoginService.java @@ -0,0 +1,38 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.google; + +import it.geosolutions.geostore.services.rest.IdPLoginRest; +import it.geosolutions.geostore.services.rest.security.oauth2.Oauth2LoginService; + +public class GoogleLoginService extends Oauth2LoginService { + + public GoogleLoginService(IdPLoginRest loginRest) { + loginRest.registerService("google", this); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/GoogleOAuth2Configuration.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/GoogleOAuth2Configuration.java new file mode 100644 index 00000000..b3664e0e --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/GoogleOAuth2Configuration.java @@ -0,0 +1,44 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.google; + +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Configuration; + +/** Google specific OAuth2Configuration object. */ +public class GoogleOAuth2Configuration extends OAuth2Configuration { + + @Override + public String buildLoginUri() { + return super.buildLoginUri("offline"); + } + + @Override + public String buildRefreshTokenURI() { + return super.buildRefreshTokenURI("offline"); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/GoogleSessionServiceDelegate.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/GoogleSessionServiceDelegate.java new file mode 100644 index 00000000..63e2e238 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/GoogleSessionServiceDelegate.java @@ -0,0 +1,48 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.google; + +import it.geosolutions.geostore.services.UserService; +import it.geosolutions.geostore.services.rest.RESTSessionService; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2SessionServiceDelegate; +import it.geosolutions.geostore.services.rest.utils.GeoStoreContext; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; + +/** Google implementation of the {@link OAuth2SessionServiceDelegate}. */ +public class GoogleSessionServiceDelegate extends OAuth2SessionServiceDelegate { + + public GoogleSessionServiceDelegate( + RESTSessionService restSessionService, UserService userService) { + super(restSessionService, "google", userService); + } + + @Override + protected OAuth2RestTemplate restTemplate() { + return GeoStoreContext.bean("googleOpenIdRestTemplate", OAuth2RestTemplate.class); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/GoogleTokenServices.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/GoogleTokenServices.java new file mode 100644 index 00000000..dae5a578 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/GoogleTokenServices.java @@ -0,0 +1,70 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.google; + +import it.geosolutions.geostore.services.rest.security.oauth2.GeoStoreRemoteTokenServices; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +/** RemoteTokenServices that handles specifically the GoogleResponse. */ +public class GoogleTokenServices extends GeoStoreRemoteTokenServices { + + public GoogleTokenServices(String principalKey) { + super(new GoogleAccessTokenConverter(principalKey)); + } + + @Override + protected Map checkToken(String accessToken) { + MultiValueMap formData = new LinkedMultiValueMap<>(); + formData.add("token", accessToken); + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", getAuthorizationHeader(accessToken)); + String accessTokenUrl = checkTokenEndpointUrl + "?access_token=" + accessToken; + return sendRequestForMap(accessTokenUrl, formData, headers, HttpMethod.POST); + } + + @Override + protected void transformNonStandardValuesToStandardValues(Map map) { + LOGGER.debug("Original map = " + map); + map.put("client_id", map.get("issued_to")); // Google sends 'client_id' as 'issued_to' + map.put("user_name", map.get("user_id")); // Google sends 'user_name' as 'user_id' + LOGGER.debug("Transformed = " + map); + } + + @Override + protected String getAuthorizationHeader(String accessToken) { + String creds = String.format("%s:%s", clientId, clientSecret); + return "Basic " + + new String(Base64.getEncoder().encode(creds.getBytes(StandardCharsets.UTF_8))); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/OAuthGoogleSecurityConfiguration.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/OAuthGoogleSecurityConfiguration.java new file mode 100644 index 00000000..f96ed784 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/OAuthGoogleSecurityConfiguration.java @@ -0,0 +1,88 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.google; + +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Configuration.CONFIG_NAME_SUFFIX; + +import it.geosolutions.geostore.services.rest.security.TokenAuthenticationCache; +import it.geosolutions.geostore.services.rest.security.oauth2.GeoStoreOAuthRestTemplate; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Configuration; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2GeoStoreSecurityConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; + +/** Configuration class for OAuth2 Google client. */ +@Configuration("googleSecConfig") +@EnableOAuth2Client +public class OAuthGoogleSecurityConfiguration extends OAuth2GeoStoreSecurityConfiguration { + + static final String CONF_BEAN_NAME = "google" + CONFIG_NAME_SUFFIX; + + @Override + public OAuth2ProtectedResourceDetails resourceDetails() { + AuthorizationCodeResourceDetails details = + (AuthorizationCodeResourceDetails) super.resourceDetails(); + details.setTokenName("authorization_code"); + return details; + } + + @Override + @Bean(value = CONF_BEAN_NAME) + public OAuth2Configuration configuration() { + return new GoogleOAuth2Configuration(); + } + + /** Must have "session" scope */ + @Override + @Bean(value = "googleOpenIdRestTemplate") + @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) + public GeoStoreOAuthRestTemplate oauth2RestTemplate() { + return super.oauth2RestTemplate(); + } + + @Bean + public OpenIdFilter googleOpenIdFilter() { + return new OpenIdFilter( + googleTokenServices(), oauth2RestTemplate(), configuration(), oAuth2Cache()); + } + + @Bean + public GoogleTokenServices googleTokenServices() { + return new GoogleTokenServices(configuration().getPrincipalKey()); + } + + @Bean + public TokenAuthenticationCache oAuth2Cache() { + return new TokenAuthenticationCache(); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/OpenIdFilter.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/OpenIdFilter.java new file mode 100644 index 00000000..36742a2d --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/google/OpenIdFilter.java @@ -0,0 +1,45 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.google; + +import it.geosolutions.geostore.services.rest.security.TokenAuthenticationCache; +import it.geosolutions.geostore.services.rest.security.oauth2.*; + +/** Google OAuth2 filter implementation. */ +public class OpenIdFilter extends OAuth2GeoStoreAuthenticationFilter { + + public OpenIdFilter( + GeoStoreRemoteTokenServices tokenServices, + GeoStoreOAuthRestTemplate oAuth2RestOperations, + OAuth2Configuration configuration, + TokenAuthenticationCache cache) { + super(tokenServices, oAuth2RestOperations, configuration, cache); + if (configuration.getDiscoveryUrl() != null && !"".equals(configuration.getDiscoveryUrl())) + new DiscoveryClient(configuration.getDiscoveryUrl()).autofill(configuration); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectAccessTokenConverter.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectAccessTokenConverter.java new file mode 100644 index 00000000..da39ebab --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectAccessTokenConverter.java @@ -0,0 +1,39 @@ +/* ==================================================================== + * + * Copyright (C) 2024 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.openid_connect; + +import it.geosolutions.geostore.services.rest.security.oauth2.GeoStoreAccessTokenConverter; + +/** + * OpenID Connect AccessTokenConverter. Retrieves Authentication information from the AccessToken. + */ +public class OpenIdConnectAccessTokenConverter extends GeoStoreAccessTokenConverter { + public OpenIdConnectAccessTokenConverter(String principalKey) { + super(principalKey); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectConfiguration.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectConfiguration.java new file mode 100644 index 00000000..e86a4cd2 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectConfiguration.java @@ -0,0 +1,136 @@ +/* ==================================================================== + * + * Copyright (C) 2024 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.openid_connect; + +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Configuration; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils; +import java.util.Collections; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; + +public class OpenIdConnectConfiguration extends OAuth2Configuration { + String jwkURI; + String postLogoutRedirectUri; + boolean sendClientSecret = false; + boolean allowBearerTokens = true; + boolean usePKCE = false; + + public String getJwkURI() { + return jwkURI; + } + + public void setJwkURI(String jwkURI) { + this.jwkURI = jwkURI; + } + + public String getPostLogoutRedirectUri() { + return postLogoutRedirectUri; + } + + public void setPostLogoutRedirectUri(String postLogoutRedirectUri) { + this.postLogoutRedirectUri = postLogoutRedirectUri; + } + + /** + * If true, the client secret will be sent to the token endpoint. This is useful for clients + * that are not capable of keeping the client secret confidential. ref.: + * https://tools.ietf.org/html/rfc6749#section-2.3.1 + * + * @return boolean + */ + public boolean isSendClientSecret() { + return sendClientSecret; + } + + public void setSendClientSecret(boolean sendClientSecret) { + this.sendClientSecret = sendClientSecret; + } + + public boolean isAllowBearerTokens() { + return allowBearerTokens; + } + + public void setAllowBearerTokens(boolean allowBearerTokens) { + this.allowBearerTokens = allowBearerTokens; + } + + /** + * Enables the use of Authorization Code Flow with Proof Key for Code Exchange (PKCE) for the + * authorization endpoint. ref.: + * https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow-with-pkce + * + * @return the authorization endpoint. + * @return boolean + */ + public boolean isUsePKCE() { + return usePKCE; + } + + public void setUsePKCE(boolean usePKCE) { + this.usePKCE = usePKCE; + } + + /** + * Build the logout endpoint. + * + * @param token the current access_token. + * @return the logout endpoint. + */ + @Override + public Endpoint buildLogoutEndpoint( + String token, String accessToken, OAuth2Configuration configuration) { + Endpoint result = null; + String uri = getLogoutUri(); + String idToken = OAuth2Utils.getIdToken() != null ? OAuth2Utils.getIdToken() : accessToken; + if (uri != null) { + HttpHeaders headers = new HttpHeaders(); + + MultiValueMap params = new LinkedMultiValueMap<>(); + if (idToken != null) { + params.put("token_type_hint", Collections.singletonList("id_token")); + headers.set("Authorization", "Bearer " + idToken); + } + if (StringUtils.hasText(getPostLogoutRedirectUri())) + params.put( + "post_logout_redirect_uri", + Collections.singletonList(getPostLogoutRedirectUri())); + getLogoutRequestParams(token, clientId, params); + + HttpEntity> requestEntity = + new HttpEntity<>(null, headers); + + result = new Endpoint(HttpMethod.GET, appendParameters(params, uri)); + result.setRequestEntity(requestEntity); + } + return result; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectFilter.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectFilter.java new file mode 100644 index 00000000..ed101082 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectFilter.java @@ -0,0 +1,112 @@ +/* ==================================================================== + * + * Copyright (C) 2024 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.openid_connect; + +import it.geosolutions.geostore.services.rest.security.TokenAuthenticationCache; +import it.geosolutions.geostore.services.rest.security.oauth2.DiscoveryClient; +import it.geosolutions.geostore.services.rest.security.oauth2.GeoStoreOAuthRestTemplate; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Configuration; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2GeoStoreAuthenticationFilter; +import it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.bearer.OpenIdTokenValidator; +import java.io.IOException; +import java.util.Map; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import net.sf.json.JSONObject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.security.jwt.Jwt; +import org.springframework.security.jwt.JwtHelper; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; +import org.springframework.security.oauth2.provider.token.RemoteTokenServices; + +/** OpenId Connect filter implementation. */ +public class OpenIdConnectFilter extends OAuth2GeoStoreAuthenticationFilter { + + private static final Logger LOGGER = LogManager.getLogger(OpenIdConnectFilter.class); + + private final OpenIdTokenValidator bearerTokenValidator; + + /** + * @param tokenServices a RemoteTokenServices instance. + * @param oAuth2RestTemplate the rest template to use for OAuth2 requests. + * @param configuration the OAuth2 configuration. + * @param tokenAuthenticationCache the cache. + */ + public OpenIdConnectFilter( + RemoteTokenServices tokenServices, + GeoStoreOAuthRestTemplate oAuth2RestTemplate, + OAuth2Configuration configuration, + TokenAuthenticationCache tokenAuthenticationCache, + OpenIdTokenValidator bearerTokenValidator) { + super(tokenServices, oAuth2RestTemplate, configuration, tokenAuthenticationCache); + if (configuration.getDiscoveryUrl() != null && !"".equals(configuration.getDiscoveryUrl())) + new DiscoveryClient(configuration.getDiscoveryUrl()).autofill(configuration); + this.bearerTokenValidator = bearerTokenValidator; + } + + @Override + protected String getPreAuthenticatedPrincipal( + HttpServletRequest req, HttpServletResponse resp, OAuth2AccessToken accessToken) + throws IOException, ServletException { + String result = super.getPreAuthenticatedPrincipal(req, resp, accessToken); + + OAuth2AuthenticationType type = + (OAuth2AuthenticationType) req.getAttribute(OAUTH2_AUTHENTICATION_TYPE_KEY); + if ((type != null) + && (type.equals(OAuth2AuthenticationType.BEARER)) + && (bearerTokenValidator != null)) { + if (!((OpenIdConnectConfiguration) configuration).isAllowBearerTokens()) { + LOGGER.warn( + "OIDC: received an attached Bearer token, but Bearer tokens aren't allowed!"); + throw new IOException( + "OIDC: received an attached Bearer token, but Bearer tokens aren't allowed!"); + } + // we must validate + String token = null; + if (accessToken != null) { + token = accessToken.getValue(); + } else { + token = (String) req.getAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE); + } + Map userinfoMap = (Map) req.getAttribute(OAUTH2_ACCESS_TOKEN_CHECK_KEY); + Jwt decodedAccessToken = JwtHelper.decode(token); + Map accessTokenClaims = JSONObject.fromObject(decodedAccessToken.getClaims()); + try { + bearerTokenValidator.verifyToken( + (OpenIdConnectConfiguration) configuration, accessTokenClaims, userinfoMap); + } catch (Exception e) { + throw new IOException("Attached Bearer Token is invalid", e); + } + } + + return result; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectLoginService.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectLoginService.java new file mode 100644 index 00000000..07dde56d --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectLoginService.java @@ -0,0 +1,103 @@ +/* ==================================================================== + * + * Copyright (C) 2024 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.openid_connect; + +import static it.geosolutions.geostore.services.rest.SessionServiceDelegate.PROVIDER_KEY; +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.*; + +import it.geosolutions.geostore.services.rest.IdPLoginRest; +import it.geosolutions.geostore.services.rest.security.oauth2.Oauth2LoginService; +import it.geosolutions.geostore.services.rest.security.oauth2.TokenDetails; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.Response; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +/** + * Extension point to customize the login and redirect after login performed from the {@link + * IdPLoginRest}; + */ +public class OpenIdConnectLoginService extends Oauth2LoginService { + + public OpenIdConnectLoginService(IdPLoginRest loginRest) { + loginRest.registerService("oidc", this); + } + + /** + * @param request the request. + * @param response the response. + * @param provider the provider name. + * @return + */ + @Override + public Response doInternalRedirect( + HttpServletRequest request, HttpServletResponse response, String provider) { + String token = getAccessToken(); + String refreshToken = getRefreshAccessToken(); + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + if (token == null + && SecurityContextHolder.getContext() != null + && requestAttributes != null) { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth != null + && auth.getDetails() != null + && auth.getDetails() instanceof TokenDetails) { + TokenDetails tokenDetails = ((TokenDetails) auth.getDetails()); + OAuth2AccessToken accessTokenDetails = tokenDetails.getAccessToken(); + if (accessTokenDetails != null) { + token = accessTokenDetails.getValue(); + requestAttributes.setAttribute(ACCESS_TOKEN_PARAM, accessTokenDetails, 0); + requestAttributes.setAttribute( + OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, token, 0); + if (accessTokenDetails.getRefreshToken().getValue() != null) { + refreshToken = accessTokenDetails.getRefreshToken().getValue(); + requestAttributes.setAttribute( + REFRESH_TOKEN_PARAM, + accessTokenDetails.getRefreshToken().getValue(), + 0); + } + } + if (tokenDetails.getIdToken() != null) { + requestAttributes.setAttribute(ID_TOKEN_PARAM, tokenDetails.getIdToken(), 0); + requestAttributes.setAttribute( + OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, + tokenDetails.getIdToken(), + 0); + } + } + } + assert requestAttributes != null; + requestAttributes.setAttribute(PROVIDER_KEY, provider, 0); + return buildCallbackResponse(token, refreshToken, provider); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectSecurityConfiguration.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectSecurityConfiguration.java new file mode 100644 index 00000000..8d4513e6 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectSecurityConfiguration.java @@ -0,0 +1,163 @@ +/* ==================================================================== + * + * Copyright (C) 2024 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.openid_connect; + +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Configuration.CONFIG_NAME_SUFFIX; + +import it.geosolutions.geostore.services.rest.security.TokenAuthenticationCache; +import it.geosolutions.geostore.services.rest.security.oauth2.GeoStoreOAuthRestTemplate; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Configuration; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2GeoStoreSecurityConfiguration; +import it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.bearer.AudienceAccessTokenValidator; +import it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.bearer.MultiTokenValidator; +import it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.bearer.OpenIdTokenValidator; +import it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.bearer.SubjectTokenValidator; +import it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.enancher.ClientSecretRequestEnhancer; +import it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.enancher.PKCERequestEnhancer; +import java.util.Arrays; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.client.token.AccessTokenProvider; +import org.springframework.security.oauth2.client.token.AccessTokenProviderChain; +import org.springframework.security.oauth2.client.token.DefaultRequestEnhancer; +import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; +import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; +import org.springframework.security.oauth2.provider.token.store.jwk.JwkTokenStore; +import org.springframework.web.context.WebApplicationContext; + +/** Configuration class for OpenID Connect client. */ +@Configuration("oidcSecConfig") +@EnableOAuth2Client +public class OpenIdConnectSecurityConfiguration extends OAuth2GeoStoreSecurityConfiguration { + + static final String CONF_BEAN_NAME = "oidc" + CONFIG_NAME_SUFFIX; + private static final Logger LOGGER = + LogManager.getLogger(OpenIdConnectSecurityConfiguration.class); + + @Override + public OAuth2ProtectedResourceDetails resourceDetails() { + AuthorizationCodeResourceDetails details = + (AuthorizationCodeResourceDetails) super.resourceDetails(); + details.setTokenName("authorization_code"); + return details; + } + + @Override + @Bean(value = CONF_BEAN_NAME) + public OAuth2Configuration configuration() { + return new OpenIdConnectConfiguration(); + } + + /** Must have "request" scope */ + @Override + @Bean(value = "oidcOpenIdRestTemplate") + @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) + public GeoStoreOAuthRestTemplate oauth2RestTemplate() { + GeoStoreOAuthRestTemplate oAuth2RestTemplate = restTemplate(); + setJacksonConverter(oAuth2RestTemplate); + + AuthorizationCodeAccessTokenProvider authorizationAccessTokenProvider = + authorizationAccessTokenProvider(); + + OpenIdConnectConfiguration idConfig = (OpenIdConnectConfiguration) configuration(); + if (idConfig.isUsePKCE()) { + LOGGER.info("Using PKCE for OpenID Connect"); + authorizationAccessTokenProvider.setTokenRequestEnhancer( + new PKCERequestEnhancer(idConfig)); + } else if (idConfig.isSendClientSecret()) { + LOGGER.info("Using client secret for OpenID Connect"); + authorizationAccessTokenProvider.setTokenRequestEnhancer( + new ClientSecretRequestEnhancer()); + } else { + LOGGER.info("Using default request enhancer for OpenID Connect"); + authorizationAccessTokenProvider.setTokenRequestEnhancer(new DefaultRequestEnhancer()); + } + + AccessTokenProvider accessTokenProviderChain = + new AccessTokenProviderChain( + Arrays.asList( + authorizationAccessTokenProvider, + new ImplicitAccessTokenProvider(), + new ResourceOwnerPasswordAccessTokenProvider(), + new ClientCredentialsAccessTokenProvider())); + + oAuth2RestTemplate.setAccessTokenProvider(accessTokenProviderChain); + + if (idConfig.getJwkURI() != null && !"".equals(idConfig.getJwkURI())) { + LOGGER.info("Using JWK for OpenID Connect"); + oAuth2RestTemplate.setTokenStore(new JwkTokenStore(idConfig.getJwkURI())); + } + + return oAuth2RestTemplate; + } + + @Bean(name = "authorizationAccessTokenProvider") + @Scope(value = "prototype") + public AuthorizationCodeAccessTokenProvider authorizationAccessTokenProvider() { + AuthorizationCodeAccessTokenProvider authorizationCodeAccessTokenProvider = + new AuthorizationCodeAccessTokenProvider(); + authorizationCodeAccessTokenProvider.setStateMandatory(false); + authorizationCodeAccessTokenProvider.setTokenRequestEnhancer(new DefaultRequestEnhancer()); + return authorizationCodeAccessTokenProvider; + } + + @Bean + public OpenIdConnectFilter oidcOpenIdFilter() { + return new OpenIdConnectFilter( + oidcTokenServices(), + oauth2RestTemplate(), + configuration(), + oidcCache(), + openIdConnectBearerTokenValidator()); + } + + @Bean + public OpenIdTokenValidator openIdConnectBearerTokenValidator() { + return new MultiTokenValidator( + Arrays.asList(new AudienceAccessTokenValidator(), new SubjectTokenValidator())); + } + + @Bean + public OpenIdConnectTokenServices oidcTokenServices() { + return new OpenIdConnectTokenServices(configuration().getPrincipalKey()); + } + + @Bean + public TokenAuthenticationCache oidcCache() { + return new TokenAuthenticationCache(); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectSessionServiceDelegate.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectSessionServiceDelegate.java new file mode 100644 index 00000000..bdf93f7d --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectSessionServiceDelegate.java @@ -0,0 +1,48 @@ +/* ==================================================================== + * + * Copyright (C) 2024 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.openid_connect; + +import it.geosolutions.geostore.services.UserService; +import it.geosolutions.geostore.services.rest.RESTSessionService; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2SessionServiceDelegate; +import it.geosolutions.geostore.services.rest.utils.GeoStoreContext; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; + +/** OpenID Connect implementation of the {@link OAuth2SessionServiceDelegate}. */ +public class OpenIdConnectSessionServiceDelegate extends OAuth2SessionServiceDelegate { + + public OpenIdConnectSessionServiceDelegate( + RESTSessionService restSessionService, UserService userService) { + super(restSessionService, "oidc", userService); + } + + @Override + protected OAuth2RestTemplate restTemplate() { + return GeoStoreContext.bean("oidcOpenIdRestTemplate", OAuth2RestTemplate.class); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectTokenServices.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectTokenServices.java new file mode 100644 index 00000000..757a1438 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/OpenIdConnectTokenServices.java @@ -0,0 +1,59 @@ +/* ==================================================================== + * + * Copyright (C) 2024 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.openid_connect; + +import it.geosolutions.geostore.services.rest.security.oauth2.GeoStoreRemoteTokenServices; +import java.util.Map; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +/** RemoteTokenServices that handles specifically the GoogleResponse. */ +public class OpenIdConnectTokenServices extends GeoStoreRemoteTokenServices { + + public OpenIdConnectTokenServices(String principalKey) { + super(new OpenIdConnectAccessTokenConverter(principalKey)); + } + + @Override + protected Map checkToken(String accessToken) { + LOGGER.debug("Checking token: " + accessToken); + MultiValueMap formData = new LinkedMultiValueMap<>(); + formData.add("token", accessToken); + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", getAuthorizationHeader(accessToken)); + LOGGER.debug("Headers: " + headers); + String accessTokenUrl = checkTokenEndpointUrl + "?access_token=" + accessToken; + LOGGER.debug("Checking token with url: " + accessTokenUrl); + Map reults = + sendRequestForMap(accessTokenUrl, formData, headers, HttpMethod.GET); + LOGGER.debug("Got sendRequestForMap results: " + reults); + return reults; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/bearer/AudienceAccessTokenValidator.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/bearer/AudienceAccessTokenValidator.java new file mode 100644 index 00000000..aacf7f6f --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/bearer/AudienceAccessTokenValidator.java @@ -0,0 +1,73 @@ +package it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.bearer; + +import it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.OpenIdConnectConfiguration; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * This checks that the token is connected to this application. This will prevent a token for + * another application being used by us. + * + *

    NOTE: Azure AD's JWT AccessToken has a fixed "aud", no "azp", but an "appid" (should be our + * client id). example; "aud": "00000003-0000-0000-c000-000000000000", "appid": + * "b9e8d05a-08b6-48a5-81c8-9590a0f550f3", + * + *

    NOTE: Keycloak has the audience as "account", no "appid", but "azp" should be our client id. + * example; "aud": "account", "azp": "live-key", + */ +public class AudienceAccessTokenValidator implements OpenIdTokenValidator { + + private final String AUDIENCE_CLAIM_NAME = "aud"; + private final String APPID_CLAIM_NAME = "appid"; + private final String KEYCLOAK_AUDIENCE_CLAIM_NAME = "azp"; + + /** + * "aud" must be our client id OR "azp" must be our client id (or, if its a list, contain our + * client id) OR "appid" must be our client id. + * + *

    Otherwise, it's token not for us... + * + *

    This checks that the audience of the JWT access token is us. The main attack this tries to + * prevent is someone getting an access token (i.e., from keycloak or azure) that was meant for + * another application (say a silly calendar app), and then using that token here. The IDP + * provider (keycloak/azure) will validate the token as "good", but it wasn't generated for us. + * This does a check of the token that OUR client ID is mentioned (not another app). + */ + @Override + public void verifyToken(OpenIdConnectConfiguration config, Map claimsJWT, Map userInfoClaims) + throws Exception { + String clientId = config.getClientId(); + if ((claimsJWT.get(AUDIENCE_CLAIM_NAME) != null)) { + if (claimsJWT.get(AUDIENCE_CLAIM_NAME).equals(clientId)) { + return; + } else if (claimsJWT.get(AUDIENCE_CLAIM_NAME) instanceof Collection + && ((Collection) claimsJWT.get(AUDIENCE_CLAIM_NAME)).contains(clientId)) { + return; + } + } + + if ((claimsJWT.get(APPID_CLAIM_NAME) != null) + && claimsJWT.get(APPID_CLAIM_NAME).equals(clientId)) { + return; // azure specific + } + + // azp - keycloak + Object azp = claimsJWT.get(KEYCLOAK_AUDIENCE_CLAIM_NAME); + if (azp != null) { + if (azp instanceof String) { + if (azp.equals(config.getClientId())) { + return; + } + } else if (azp instanceof List) { + List azps = (List) azp; + for (Object o : azps) { + if ((o instanceof String) && (o.equals(clientId))) { + return; + } + } + } + } + throw new Exception("JWT Bearer token - probably not meant for this application"); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/bearer/MultiTokenValidator.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/bearer/MultiTokenValidator.java new file mode 100644 index 00000000..27d9ceee --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/bearer/MultiTokenValidator.java @@ -0,0 +1,30 @@ +package it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.bearer; + +import it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.OpenIdConnectConfiguration; +import java.util.List; +import java.util.Map; + +/** + * This is a token validator that runs a list of TokenValidators. This doesn't do any validation on + * its own... + */ +public class MultiTokenValidator implements OpenIdTokenValidator { + + List validators; + + public MultiTokenValidator(List validators) { + this.validators = validators; + } + + @Override + public void verifyToken( + OpenIdConnectConfiguration config, Map accessTokenClaims, Map userInfoClaims) + throws Exception { + if (validators == null) { + return; // nothing to do + } + for (OpenIdTokenValidator validator : validators) { + validator.verifyToken(config, accessTokenClaims, userInfoClaims); + } + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/bearer/OpenIdTokenValidator.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/bearer/OpenIdTokenValidator.java new file mode 100644 index 00000000..f25174b7 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/bearer/OpenIdTokenValidator.java @@ -0,0 +1,19 @@ +package it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.bearer; + +import it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.OpenIdConnectConfiguration; +import java.util.Map; + +/** + * Bearer tokens should be checked to make sure they are applicable to this application (to prevent + * token reuse from another application) + */ +public interface OpenIdTokenValidator { + + /** + * @param accessTokenClaims - map of claims in the Access Token + * @param userInfoClaims - map of claims from the oidc "userInfo" endpoint + * @throws Exception - if there is a problem, throw an exception. + */ + void verifyToken(OpenIdConnectConfiguration config, Map accessTokenClaims, Map userInfoClaims) + throws Exception; +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/bearer/SubjectTokenValidator.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/bearer/SubjectTokenValidator.java new file mode 100644 index 00000000..9a749efb --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/bearer/SubjectTokenValidator.java @@ -0,0 +1,42 @@ +package it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.bearer; + +import it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.OpenIdConnectConfiguration; +import java.util.Map; + +/** + * This verifies that the token is about our user (i.e. the access token and userinfo endpoint agree + * on who). + * + *

    for keycloak, the "sub" of the JWT and userInfo are the same. for Azure AD, the "sub" of the + * userInfo is in the JWT "xms_st" claim. "xms_st": { "sub": + * "982kuI1hxIANLB__lrKejDgDnyjPnhbKLdPUF0JmOD1" }, + * + *

    The spec suggests verifying the user vs token subjects match, so this does that check. + */ +public class SubjectTokenValidator implements OpenIdTokenValidator { + + private final String SUBJECT_CLAIM_NAME = "sub"; + private final String AZURE_SUBJECT_CONTAINER_NAME = "xms_st"; + + @Override + public void verifyToken(OpenIdConnectConfiguration config, Map claims, Map userInfoClaims) + throws Exception { + // normal case - subjects are the same + if ((claims.get(SUBJECT_CLAIM_NAME) != null) + && (userInfoClaims.get(SUBJECT_CLAIM_NAME) != null)) { + if (claims.get(SUBJECT_CLAIM_NAME).equals(userInfoClaims.get(SUBJECT_CLAIM_NAME))) + return; + } + + // Azure AD case - use accesstoken.xms_st.sub vs userinfo.sub + if ((claims.get(AZURE_SUBJECT_CONTAINER_NAME) != null) + && (claims.get(AZURE_SUBJECT_CONTAINER_NAME) instanceof Map)) { + Map xmls_st = (Map) claims.get(AZURE_SUBJECT_CONTAINER_NAME); + if (xmls_st.get(SUBJECT_CLAIM_NAME) != null) { + if (xmls_st.get(SUBJECT_CLAIM_NAME).equals(userInfoClaims.get(SUBJECT_CLAIM_NAME))) + return; + } + } + throw new Exception("JWT Bearer token VS UserInfo - subjects dont match"); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/enancher/ClientSecretRequestEnhancer.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/enancher/ClientSecretRequestEnhancer.java new file mode 100644 index 00000000..0aff3a83 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/enancher/ClientSecretRequestEnhancer.java @@ -0,0 +1,51 @@ +/* ==================================================================== + * + * Copyright (C) 2024 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.enancher; + +import java.util.Collections; +import org.springframework.http.HttpHeaders; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.client.token.AccessTokenRequest; +import org.springframework.security.oauth2.client.token.RequestEnhancer; +import org.springframework.util.MultiValueMap; + +/** This class is used to add to Token Requests the client_secret in the query string. */ +public class ClientSecretRequestEnhancer implements RequestEnhancer { + + /** {@code client_secret} - used in Token Request. */ + public static final String CLIENT_SECRET = "client_secret"; + + @Override + public void enhance( + AccessTokenRequest request, + OAuth2ProtectedResourceDetails resource, + MultiValueMap form, + HttpHeaders headers) { + form.put(CLIENT_SECRET, Collections.singletonList(resource.getClientSecret())); + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/enancher/PKCERequestEnhancer.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/enancher/PKCERequestEnhancer.java new file mode 100644 index 00000000..eae4f373 --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/security/oauth2/openid_connect/enancher/PKCERequestEnhancer.java @@ -0,0 +1,73 @@ +/* ==================================================================== + * + * Copyright (C) 2024 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.enancher; + +import it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.OpenIdConnectConfiguration; +import java.util.Base64; +import java.util.Collections; +import org.springframework.http.HttpHeaders; +import org.springframework.security.crypto.keygen.Base64StringKeyGenerator; +import org.springframework.security.crypto.keygen.StringKeyGenerator; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.client.token.AccessTokenRequest; +import org.springframework.security.oauth2.client.token.RequestEnhancer; +import org.springframework.security.oauth2.core.endpoint.PkceParameterNames; +import org.springframework.util.MultiValueMap; + +/** Used to enhance Token Requests with previously generated code_verifier. */ +@SuppressWarnings("PMD.UnusedPrivateField") +public class PKCERequestEnhancer implements RequestEnhancer { + + private final StringKeyGenerator secureKeyGenerator = + new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96); + private final OpenIdConnectConfiguration config; + + public PKCERequestEnhancer(OpenIdConnectConfiguration oidcConfig) { + this.config = oidcConfig; + } + + @Override + public void enhance( + AccessTokenRequest request, + OAuth2ProtectedResourceDetails resource, + MultiValueMap form, + HttpHeaders headers) { + + if (config.isSendClientSecret()) { + form.put( + ClientSecretRequestEnhancer.CLIENT_SECRET, + Collections.singletonList(resource.getClientSecret())); + } + if (config.isUsePKCE()) { + java.util.List codeVerifier = request.get(PkceParameterNames.CODE_VERIFIER); + if (codeVerifier != null) { + form.put(PkceParameterNames.CODE_VERIFIER, codeVerifier); + } + } + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/AbstractGeoStoreAuthenticationInterceptor.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/AbstractGeoStoreAuthenticationInterceptor.java index 65490533..833272ef 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/AbstractGeoStoreAuthenticationInterceptor.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/AbstractGeoStoreAuthenticationInterceptor.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,7 +21,6 @@ import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.security.password.PwEncoder; - import org.apache.cxf.configuration.security.AuthorizationPolicy; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.interceptor.security.AccessDeniedException; @@ -29,19 +28,21 @@ import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; import org.apache.cxf.security.SecurityContext; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** - * - * Class AbstractGeoStoreAuthenticationInterceptor. Envelop for different authentication provider interceptors. - * + * Class AbstractGeoStoreAuthenticationInterceptor. Envelop for different authentication provider + * interceptors. + * * @author adiaz (alejandro.diaz at geo-solutions.it) */ -public abstract class AbstractGeoStoreAuthenticationInterceptor extends - AbstractPhaseInterceptor { +@SuppressWarnings("PMD.UnusedPrivateField") +public abstract class AbstractGeoStoreAuthenticationInterceptor + extends AbstractPhaseInterceptor { - protected static final Logger LOGGER = Logger - .getLogger(AbstractGeoStoreAuthenticationInterceptor.class); + protected static final Logger LOGGER = + LogManager.getLogger(AbstractGeoStoreAuthenticationInterceptor.class); public AbstractGeoStoreAuthenticationInterceptor() { super(Phase.UNMARSHAL); @@ -49,7 +50,7 @@ public AbstractGeoStoreAuthenticationInterceptor() { /* * (non-Javadoc) - * + * * @see org.apache.cxf.interceptor.Interceptor#handleMessage(org.apache.cxf.message.Message) */ @Override @@ -64,43 +65,37 @@ public void handleMessage(Message message) throws Fault { User user = null; - AuthorizationPolicy policy = (AuthorizationPolicy) message.get(AuthorizationPolicy.class); + AuthorizationPolicy policy = message.get(AuthorizationPolicy.class); if (policy != null) { username = policy.getUserName(); password = policy.getPassword(); - if (password == null) - password = ""; + if (password == null) password = ""; - if (LOGGER.isInfoEnabled()) - LOGGER.info("Requesting user: " + username); + if (LOGGER.isInfoEnabled()) LOGGER.info("Requesting user: " + username); // ////////////////////////////////////////////////////////////////// // read user from the interceptor: If user and PW do not match, // throw new AuthenticationException("Unauthorized"); // /////////////////////////////////////////////////////////////////// - - String encodedPw = null; try { user = getUser(username, message); } catch (Exception e) { - LOGGER.error("Exception while checking pw: " + username, e); + LOGGER.error("Exception while checking pw: {}", username, e); throw new AccessDeniedException("Authorization error"); } - - if (!PwEncoder.isPasswordValid(user.getPassword(),password)) { - if (LOGGER.isInfoEnabled()) - LOGGER.info("Bad pw for user " + username ); + + if (!PwEncoder.isPasswordValid(user.getPassword(), password)) { + if (LOGGER.isInfoEnabled()) LOGGER.info("Bad pw for user {}", username); throw new AccessDeniedException("Not authorized"); } } else { - if (LOGGER.isInfoEnabled()) - LOGGER.info("No requesting user -- GUEST access"); + if (LOGGER.isInfoEnabled()) LOGGER.info("No requesting user -- GUEST access"); } GeoStoreSecurityContext securityContext = new GeoStoreSecurityContext(); - GeoStorePrincipal principal = user != null ? new GeoStorePrincipal(user) - : GeoStorePrincipal.createGuest(); + GeoStorePrincipal principal = + user != null ? new GeoStorePrincipal(user) : GeoStorePrincipal.createGuest(); securityContext.setPrincipal(principal); message.put(SecurityContext.class, securityContext); @@ -108,12 +103,10 @@ public void handleMessage(Message message) throws Fault { /** * Obtain an user from his username - * + * * @param username of the user * @param message intercepted - * * @return user identified with the username */ protected abstract User getUser(String username, Message message); - -} \ No newline at end of file +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/AutoUserCreateGeostoreAuthenticationInterceptor.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/AutoUserCreateGeostoreAuthenticationInterceptor.java index 6a4607be..60711f06 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/AutoUserCreateGeostoreAuthenticationInterceptor.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/AutoUserCreateGeostoreAuthenticationInterceptor.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2014 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -23,49 +23,40 @@ import it.geosolutions.geostore.core.model.enums.Role; import it.geosolutions.geostore.services.UserService; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; - import java.util.List; import java.util.Map; - import org.apache.cxf.interceptor.security.AccessDeniedException; import org.apache.cxf.message.Message; /** - * - * Class AutoUserCreateGeostoreAuthenticationInterceptor. Geostore authentication interceptor that allows users auto creation - * + * Class AutoUserCreateGeostoreAuthenticationInterceptor. Geostore authentication interceptor that + * allows users auto creation + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author adiaz (alejandro.diaz at geo-solutions.it) */ -public class AutoUserCreateGeostoreAuthenticationInterceptor extends - AbstractGeoStoreAuthenticationInterceptor { +public class AutoUserCreateGeostoreAuthenticationInterceptor + extends AbstractGeoStoreAuthenticationInterceptor { private UserService userService; - /** - * Flag to indicate if an user that not exists could be created when it's used - */ + /** Flag to indicate if an user that not exists could be created when it's used */ private Boolean autoCreateUsers = false; - /** - * Role for the new user - */ + /** Role for the new user */ private Role newUsersRole = Role.USER; - /** - * New password strategy @see {@link NewPasswordStrategy} - */ + /** New password strategy @see {@link NewPasswordStrategy} */ private NewPasswordStrategy newUsersPassword = NewPasswordStrategy.NONE; /** - * Header key for the new password if the selected strategy is {@link NewPasswordStrategy#FROMHEADER} + * Header key for the new password if the selected strategy is {@link + * NewPasswordStrategy#FROMHEADER} */ private String newUsersPasswordHeader = ""; - /** - * @param userService the userService to set - */ + /** @param userService the userService to set */ public void setUserService(UserService userService) { this.userService = userService; } @@ -88,37 +79,35 @@ public void setNewUsersPasswordHeader(String newUsersPasswordHeader) { /** * Obtain the new password for a new user - * + * * @param message * @param username - * * @return password for the new user */ private String getNewUserPassword(Message message, String username) { switch (newUsersPassword) { - case NONE: - return ""; - case USERNAME: - return username; - case FROMHEADER: - @SuppressWarnings("unchecked") - Map> headers = (Map>) message - .get(Message.PROTOCOL_HEADERS); - if (headers.containsKey(newUsersPasswordHeader)) { - return headers.get(newUsersPasswordHeader).get(0); - } - return ""; - default: - return ""; + case NONE: + return ""; + case USERNAME: + return username; + case FROMHEADER: + @SuppressWarnings("unchecked") + Map> headers = + (Map>) message.get(Message.PROTOCOL_HEADERS); + if (headers.containsKey(newUsersPasswordHeader)) { + return headers.get(newUsersPasswordHeader).get(0); + } + return ""; + default: + return ""; } } /** * Obtain an user from his username - * + * * @param username of the user * @param message intercepted - * * @return user identified with the username */ protected User getUser(String username, Message message) { @@ -127,8 +116,7 @@ protected User getUser(String username, Message message) { // Search on db user = userService.get(username); } catch (NotFoundServiceEx e) { - if (LOGGER.isInfoEnabled()) - LOGGER.info("Requested user not found: " + username); + if (LOGGER.isInfoEnabled()) LOGGER.info("Requested user not found: " + username); // Auto create user if (autoCreateUsers) { @@ -154,7 +142,5 @@ protected User getUser(String username, Message message) { } return user; - } - -} \ No newline at end of file +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/Convert.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/Convert.java index f938fa80..f5208716 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/Convert.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/Convert.java @@ -1,62 +1,54 @@ /* * Copyright (C) 2007 - 2012 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.services.rest.utils; -import it.geosolutions.geostore.core.model.Attribute; -import it.geosolutions.geostore.core.model.Category; -import it.geosolutions.geostore.core.model.Resource; -import it.geosolutions.geostore.core.model.SecurityRule; -import it.geosolutions.geostore.core.model.StoredData; -import it.geosolutions.geostore.core.model.User; -import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.*; import it.geosolutions.geostore.services.dto.ShortAttribute; import it.geosolutions.geostore.services.rest.exception.BadRequestWebEx; import it.geosolutions.geostore.services.rest.exception.InternalErrorWebEx; import it.geosolutions.geostore.services.rest.model.RESTResource; import it.geosolutions.geostore.services.rest.model.RESTSecurityRule; import it.geosolutions.geostore.services.rest.model.RESTStoredData; - import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; - import org.apache.commons.collections.CollectionUtils; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class Convert { public static Resource convertResource(RESTResource resource) { Category category = new Category(); if (resource.getCategory().getName() != null) category.setName(resource.getCategory().getName()); - if (resource.getCategory().getId() != null) - category.setId(resource.getCategory().getId()); + if (resource.getCategory().getId() != null) category.setId(resource.getCategory().getId()); Resource r = new Resource(); r.setDescription(resource.getDescription()); r.setMetadata(resource.getMetadata()); r.setName(resource.getName()); r.setCategory(category); + r.setCreator(resource.getCreator()); + r.setEditor(resource.getEditor()); + r.setAdvertised(resource.isAdvertised()); // Parsing Attributes list if (CollectionUtils.isNotEmpty(resource.getAttribute())) { @@ -93,25 +85,26 @@ public static Attribute convertAttribute(ShortAttribute shattr) throws InternalE throw new BadRequestWebEx("Missing type for attribute " + shattr); switch (ret.getType()) { - case DATE: - try { - ret.setDateValue(Attribute.DATE_FORMAT.parse(shattr.getValue())); - } catch (ParseException e) { - throw new BadRequestWebEx("Error parsing attribute date value " + shattr); - } - break; - case NUMBER: - try { - ret.setNumberValue(Double.valueOf(shattr.getValue())); - } catch (NumberFormatException ex) { - throw new BadRequestWebEx("Error parsing number value " + shattr); - } - break; - case STRING: - ret.setTextValue(shattr.getValue()); - break; - default: - throw new InternalErrorWebEx("Unknown attribute type " + shattr); + case DATE: + try { + ret.setDateValue( + new SimpleDateFormat(Attribute.DATE_FORMAT).parse(shattr.getValue())); + } catch (ParseException e) { + throw new BadRequestWebEx("Error parsing attribute date value " + shattr); + } + break; + case NUMBER: + try { + ret.setNumberValue(Double.valueOf(shattr.getValue())); + } catch (NumberFormatException ex) { + throw new BadRequestWebEx("Error parsing number value " + shattr); + } + break; + case STRING: + ret.setTextValue(shattr.getValue()); + break; + default: + throw new InternalErrorWebEx("Unknown attribute type " + shattr); } return ret; } @@ -124,38 +117,37 @@ public static List convertToShortAttributeList(List l return attributes; } - public static List convertSecurityRuleList( - List list, Long resourceId) { - if(list==null){ - list = new ArrayList(); - } - List rules = new ArrayList(list.size()); - for(RESTSecurityRule rule : list) { - SecurityRule securityRule = new SecurityRule(); - Resource resource = new Resource(); - resource.setId(resourceId); - securityRule.setResource(resource); - - if(rule.getUser() != null) { - User user = new User(); - user.setId(rule.getUser().getId()); - user.setName(rule.getUser().getName()); - securityRule.setUser(user); - } - - if(rule.getGroup() != null) { - UserGroup group = new UserGroup(); - group.setId(rule.getGroup().getId()); - group.setGroupName(rule.getGroup().getGroupName()); - securityRule.setGroup(group); - } - - securityRule.setCanRead(rule.isCanRead()); - securityRule.setCanWrite(rule.isCanWrite()); - - rules.add(securityRule); - } - return rules; - } + public static List convertSecurityRuleList( + List list, Long resourceId) { + if (list == null) { + list = new ArrayList(); + } + List rules = new ArrayList(list.size()); + for (RESTSecurityRule rule : list) { + SecurityRule securityRule = new SecurityRule(); + Resource resource = new Resource(); + resource.setId(resourceId); + securityRule.setResource(resource); + + if (rule.getUser() != null) { + User user = new User(); + user.setId(rule.getUser().getId()); + user.setName(rule.getUser().getName()); + securityRule.setUser(user); + } + + if (rule.getGroup() != null) { + UserGroup group = new UserGroup(); + group.setId(rule.getGroup().getId()); + group.setGroupName(rule.getGroup().getGroupName()); + securityRule.setGroup(group); + } + + securityRule.setCanRead(rule.isCanRead()); + securityRule.setCanWrite(rule.isCanWrite()); + rules.add(securityRule); + } + return rules; + } } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/DataURIDecoder.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/DataURIDecoder.java index ffad1ac6..597d3884 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/DataURIDecoder.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/DataURIDecoder.java @@ -23,18 +23,13 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -/** - * - * @author Emanuele Tajariol (etj at geo-solutions.it) - */ -public class DataURIDecoder -{ - final String DATA_URI_REGEX = "data:((?(?\\w+)/(?\\w+));)?(charset=(?[\\w\\s]+);)?(?\\w+)?"; - - public final static String DEFAULT_MEDIA_TYPE = "text/plain"; - public final static String DEFAULT_CHARSET = "US-ASCII"; - - private boolean valid; +/** @author Emanuele Tajariol (etj at geo-solutions.it) */ +public class DataURIDecoder { + public static final String DEFAULT_MEDIA_TYPE = "text/plain"; + public static final String DEFAULT_CHARSET = "US-ASCII"; + final String DATA_URI_REGEX = + "data:((?(?\\w+)/(?\\w+));)?(charset=(?[\\w\\s]+);)?(?\\w+)?"; + private final boolean valid; private String mediatype; private String charset; @@ -45,7 +40,7 @@ public class DataURIDecoder public DataURIDecoder(String header) { Matcher matcher = Pattern.compile(DATA_URI_REGEX).matcher(header); - if(matcher.matches()) { + if (matcher.matches()) { valid = true; mediatype = matcher.group("mediatype"); @@ -53,8 +48,7 @@ public DataURIDecoder(String header) { encoding = matcher.group("encoding"); base64Encoded = "base64".equals(encoding); - } else - { + } else { valid = false; } } @@ -80,6 +74,6 @@ public boolean isBase64Encoded() { } public String getNormalizedMediatype() { - return mediatype != null? mediatype : DEFAULT_MEDIA_TYPE; + return mediatype != null ? mediatype : DEFAULT_MEDIA_TYPE; } } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/FixedCacheControlOutInterceptor.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/FixedCacheControlOutInterceptor.java index 1b54207a..ca7cb10d 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/FixedCacheControlOutInterceptor.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/FixedCacheControlOutInterceptor.java @@ -1,36 +1,37 @@ package it.geosolutions.geostore.services.rest.utils; -import java.io.OutputStream; +import java.io.OutputStream; import javax.ws.rs.core.MultivaluedMap; - import org.apache.cxf.jaxrs.impl.MetadataMap; import org.apache.cxf.message.Message; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; + /** * Attach to the response the header CacheControl - * - * @author Lorenzo Natali, GeoSolutions SAS * + * @author Lorenzo Natali, GeoSolutions SAS */ +@SuppressWarnings("PMD.CloseResource") public class FixedCacheControlOutInterceptor extends AbstractPhaseInterceptor { - public FixedCacheControlOutInterceptor () { + public FixedCacheControlOutInterceptor() { super(Phase.MARSHAL); } @SuppressWarnings("unchecked") - public final void handleMessage(Message message) { + public final void handleMessage(Message message) { OutputStream os = message.getContent(OutputStream.class); if (os == null) { return; } - MultivaluedMap headers = (MetadataMap) message.get(Message.PROTOCOL_HEADERS); + MultivaluedMap headers = + (MetadataMap) message.get(Message.PROTOCOL_HEADERS); if (headers == null) { headers = new MetadataMap(); - } - headers.add("Cache-Control", new String("no-cache")); - headers.add("Expires", new String("-1")); + } + headers.add("Cache-Control", "no-cache"); + headers.add("Expires", "-1"); message.put(Message.PROTOCOL_HEADERS, headers); } -} \ No newline at end of file +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/GeoStoreContext.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/GeoStoreContext.java new file mode 100644 index 00000000..2f49303b --- /dev/null +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/GeoStoreContext.java @@ -0,0 +1,59 @@ +package it.geosolutions.geostore.services.rest.utils; + +import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +public class GeoStoreContext implements ApplicationContextAware { + + private static final Logger LOGGER = LogManager.getLogger(GeoStoreContext.class); + /** + * Static application context provided to {@link #setApplicationContext(ApplicationContext)} + * during initialization. + * + *

    This context is used by methods such as {@link #bean(String)}, {@link #bean(Class)}. + */ + static ApplicationContext context; + + public static Map beans(Class clazz) { + Map result = null; + try { + if (context != null) result = context.getBeansOfType(clazz); + } catch (Exception e) { + LOGGER.error("Error while retrieving the bean of type {}", clazz.getSimpleName(), e); + } + return result; + } + + public static T bean(Class clazz) { + T result = null; + try { + if (context != null) result = context.getBean(clazz); + } catch (Exception e) { + LOGGER.error("Error while retrieving the bean of type {}", clazz.getSimpleName(), e); + } + return result; + } + + public static Object bean(String name) { + Object result = null; + try { + if (context != null) result = context.getBean(name); + } catch (BeansException e) { + LOGGER.error("Error while retrieving the bean with name {}", name, e); + } + return result; + } + + public static T bean(String name, Class clazz) { + return clazz.cast(bean(name)); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + context = applicationContext; + } +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/GeoStorePrincipal.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/GeoStorePrincipal.java index 4c2f8592..3df4f323 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/GeoStorePrincipal.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/GeoStorePrincipal.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -24,7 +24,7 @@ /** * Class GeoStorePrincipal. - * + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ @@ -32,25 +32,19 @@ public class GeoStorePrincipal implements Principal { private User user = null; - public GeoStorePrincipal() { - } + public GeoStorePrincipal() {} - /** - * @param user - */ + /** @param user */ public GeoStorePrincipal(User user) { // // is using this ctor, caller may want to enforce user existance // - if (user == null) - throw new NullPointerException("Null user"); + if (user == null) throw new NullPointerException("Null user"); this.user = user; } - /** - * @return GeoStorePrincipal - */ + /** @return GeoStorePrincipal */ public static GeoStorePrincipal createGuest() { return new GeoStorePrincipal() { @Override @@ -60,23 +54,19 @@ public void setUser(User user) { }; } - /** - * @param user - */ - public void setUser(User user) { - this.user = user; - } - - /** - * @return User - */ + /** @return User */ public User getUser() { return user; } + /** @param user */ + public void setUser(User user) { + this.user = user; + } + /* * (non-Javadoc) - * + * * @see java.security.Principal#getName() */ @Override @@ -90,7 +80,7 @@ public boolean isGuest() { /* * (non-Javadoc) - * + * * @see java.lang.Object#equals(java.lang.Object) */ @Override @@ -102,16 +92,13 @@ public boolean equals(Object obj) { return false; } final GeoStorePrincipal other = (GeoStorePrincipal) obj; - if (this.user != other.user - && (this.user == null || !this.user.getName().equals(other.user.getName()))) { - return false; - } - return true; + return this.user == other.user + || (this.user != null && this.user.getName().equals(other.user.getName())); } /* * (non-Javadoc) - * + * * @see java.lang.Object#hashCode() */ @Override diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/GeoStoreSecurityContext.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/GeoStoreSecurityContext.java index 7297dbf0..21e7db92 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/GeoStoreSecurityContext.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/GeoStoreSecurityContext.java @@ -1,46 +1,43 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.services.rest.utils; import it.geosolutions.geostore.core.model.enums.Role; - import java.security.Principal; - import org.apache.cxf.security.SecurityContext; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * Class GeoStoreSecurityContext. - * + * * @author ETj (etj at geo-solutions.it) * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) */ public class GeoStoreSecurityContext implements SecurityContext { - private final static Logger LOGGER = Logger.getLogger(GeoStoreSecurityContext.class); + private static final Logger LOGGER = LogManager.getLogger(GeoStoreSecurityContext.class); private GeoStorePrincipal principal; - /** - * @param principal - */ + /** @param principal */ public void setPrincipal(GeoStorePrincipal principal) { this.principal = principal; } @@ -52,7 +49,7 @@ public Principal getUserPrincipal() { /* * (non-Javadoc) - * + * * @see org.apache.cxf.security.SecurityContext#isUserInRole(java.lang.String) */ @Override @@ -68,16 +65,10 @@ public boolean isUserInRole(String role) { */ public boolean isUserInRoleAux(String role) { if (Role.GUEST.name().equals(role)) { - if (principal.isGuest()) - return true; + return principal.isGuest(); } else { - if (principal.isGuest()) - return false; - else - return principal.getUser().getRole().name().equals(role); + if (principal.isGuest()) return false; + else return principal.getUser().getRole().name().equals(role); } - - return false; } - } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/GroupMapper.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/GroupMapper.java index 4f984792..bf1b2563 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/GroupMapper.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/GroupMapper.java @@ -2,9 +2,9 @@ /** * Interface to transform group names - * @author Lorenzo Natali, GeoSolutions S.a.s. * + * @author Lorenzo Natali, GeoSolutions S.a.s. */ public interface GroupMapper { - public String transform(String group); + String transform(String group); } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/JAXBContextResolver.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/JAXBContextResolver.java index 38b19a4f..78b1c3b3 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/JAXBContextResolver.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/JAXBContextResolver.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,32 +21,29 @@ package it.geosolutions.geostore.services.rest.utils; import it.geosolutions.geostore.services.dto.search.SearchFilter; - import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; import javax.xml.bind.JAXBContext; /** * Class JAXBContextResolver. - * + * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) * @author ETj (etj at geo-solutions.it) */ @Provider public class JAXBContextResolver implements ContextResolver { - private final static JAXBContext context = GeoStoreJAXBContext.getContext(); + private static final JAXBContext context = GeoStoreJAXBContext.getContext(); /* * (non-Javadoc) - * + * * @see javax.ws.rs.ext.ContextResolver#getContext(java.lang.Class) */ @Override public JAXBContext getContext(Class clazz) { - if (clazz.equals(SearchFilter.class)) - return context; + if (clazz.equals(SearchFilter.class)) return context; return null; } - -} \ No newline at end of file +} diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/NewPasswordStrategy.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/NewPasswordStrategy.java index b3648aaa..8bce22b7 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/NewPasswordStrategy.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/NewPasswordStrategy.java @@ -2,9 +2,11 @@ /** * Strategy for new user's password generation - * + * * @see GeoStoreAuthenticationInterceptor */ public enum NewPasswordStrategy { - NONE, USERNAME, FROMHEADER + NONE, + USERNAME, + FROMHEADER } diff --git a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/SpelMapper.java b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/SpelMapper.java index 1b62fab5..3148b3fa 100644 --- a/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/SpelMapper.java +++ b/src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/utils/SpelMapper.java @@ -5,34 +5,36 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; public class SpelMapper implements GroupMapper { - private String expression; - private class Context { - private String name; - Context (String name) { - this.name = name; - } - @SuppressWarnings("unused") - public String getName() { - return name; - } - - @SuppressWarnings("unused") - public void setName(String name) { - this.name = name; - } - } - private SpelExpressionParser parser = new SpelExpressionParser(); - - public SpelMapper(String expression) { - this.expression = expression; - } - - @Override - public String transform(String groupName) { - Context context = new Context(groupName); + private final String expression; + private final SpelExpressionParser parser = new SpelExpressionParser(); + + public SpelMapper(String expression) { + this.expression = expression; + } + + @Override + public String transform(String groupName) { + Context context = new Context(groupName); Expression exp = parser.parseExpression(expression); StandardEvaluationContext evaluationContext = new StandardEvaluationContext(context); return exp.getValue(evaluationContext, String.class); - } + } + + private class Context { + private String name; + + Context(String name) { + this.name = name; + } + + @SuppressWarnings("unused") + public String getName() { + return name; + } + @SuppressWarnings("unused") + public void setName(String name) { + this.name = name; + } + } } diff --git a/src/modules/rest/impl/src/main/resources/applicationContext.xml b/src/modules/rest/impl/src/main/resources/applicationContext.xml index 5061caab..eebff07e 100644 --- a/src/modules/rest/impl/src/main/resources/applicationContext.xml +++ b/src/modules/rest/impl/src/main/resources/applicationContext.xml @@ -1,295 +1,356 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd" + default-autowire="byName"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - classpath*:geostore-ovr.properties - classpath*:${ovrdir}/geostore-ovr.properties - - - - - + + + + + + + + + + + + + + classpath*:geostore-ovr.properties + classpath*:${ovrdir}/geostore-ovr.properties + + + + + - - - - - - - - - + + + + + + + + + diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/GeoStoreAuthenticationFilterTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/GeoStoreAuthenticationFilterTest.java index 00b21dc9..6c53d2f8 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/GeoStoreAuthenticationFilterTest.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/GeoStoreAuthenticationFilterTest.java @@ -1,28 +1,26 @@ /* * Copyright (C) 2015 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.rest.security; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; + import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserAttribute; import it.geosolutions.geostore.core.model.enums.Role; @@ -30,14 +28,8 @@ import it.geosolutions.geostore.services.exception.NotFoundServiceEx; import it.geosolutions.geostore.services.rest.security.GeoStoreRequestHeadersAuthenticationFilter; import it.geosolutions.geostore.services.rest.utils.MockedUserService; - import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Vector; - +import java.util.*; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; @@ -51,14 +43,14 @@ import org.springframework.security.core.context.SecurityContextHolder; public class GeoStoreAuthenticationFilterTest { - + private static final String USERNAME_HEADER = "username"; private static final String SAMPLE_USER = "myuser"; private MockedUserService userService; private GeoStoreRequestHeadersAuthenticationFilter filter; private HttpServletRequest req; private HttpServletResponse resp; - + @Before public void setUp() { userService = new MockedUserService(); @@ -66,83 +58,76 @@ public void setUp() { filter.setUserNameHeader(USERNAME_HEADER); filter.setUserService(userService); filter.setAutoCreateUser(true); - + req = Mockito.mock(HttpServletRequest.class); resp = Mockito.mock(HttpServletResponse.class); - + Mockito.when(req.getHeader(USERNAME_HEADER)).thenReturn(SAMPLE_USER); Mockito.when(req.getHeader("header1")).thenReturn("value1"); - Mockito.when(req.getHeaderNames()).thenReturn( - new Vector(Arrays.asList(USERNAME_HEADER, "header1")).elements()); + Mockito.when(req.getHeaderNames()) + .thenReturn(new Vector(Arrays.asList(USERNAME_HEADER, "header1")).elements()); } - + @After public void tearDown() { SecurityContextHolder.getContext().setAuthentication(null); } - + @Test public void testAutoCreate() throws IOException, ServletException, NotFoundServiceEx { - - - - - filter.doFilter(req, resp, new FilterChain() { - - @Override - public void doFilter(ServletRequest arg0, ServletResponse arg1) throws IOException, - ServletException { - - - } - - }); - + + filter.doFilter( + req, + resp, + new FilterChain() { + + @Override + public void doFilter(ServletRequest arg0, ServletResponse arg1) + throws IOException, ServletException {} + }); + User user = userService.get(SAMPLE_USER); checkUser(user); assertTrue(user.isEnabled()); } - @Test public void testAutoCreateDisabled() throws IOException, ServletException, NotFoundServiceEx { - + filter.setEnableAutoCreatedUsers(false); - filter.doFilter(req, resp, new FilterChain() { - - @Override - public void doFilter(ServletRequest arg0, ServletResponse arg1) throws IOException, - ServletException { - - - } - - }); - + filter.doFilter( + req, + resp, + new FilterChain() { + + @Override + public void doFilter(ServletRequest arg0, ServletResponse arg1) + throws IOException, ServletException {} + }); + User user = userService.get(SAMPLE_USER); checkUser(user); assertFalse(user.isEnabled()); } - + @Test - public void testAutoCreateAttributesMapping() throws IOException, ServletException, NotFoundServiceEx { - + public void testAutoCreateAttributesMapping() + throws IOException, ServletException, NotFoundServiceEx { + Map attributeMappings = new HashMap(); attributeMappings.put("attr1", "header1"); filter.setUserMapper(new MapExpressionUserMapper(attributeMappings)); - - - filter.doFilter(req, resp, new FilterChain() { - - @Override - public void doFilter(ServletRequest arg0, ServletResponse arg1) throws IOException, - ServletException { - - - } - - }); - + + filter.doFilter( + req, + resp, + new FilterChain() { + + @Override + public void doFilter(ServletRequest arg0, ServletResponse arg1) + throws IOException, ServletException {} + }); + User user = userService.get(SAMPLE_USER); checkUser(user); List attributes = user.getAttribute(); @@ -150,7 +135,6 @@ public void doFilter(ServletRequest arg0, ServletResponse arg1) throws IOExcepti assertEquals("attr1", attributes.get(0).getName()); assertEquals("value1", attributes.get(0).getValue()); } - private void checkUser(User user) { assertNotNull(user); diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/GeoStoreLdapAuthoritiesPopulatorTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/GeoStoreLdapAuthoritiesPopulatorTest.java index 8001171f..6ae407c6 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/GeoStoreLdapAuthoritiesPopulatorTest.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/GeoStoreLdapAuthoritiesPopulatorTest.java @@ -1,96 +1,164 @@ package it.geosolutions.geostore.rest.security; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import javax.naming.Name; +import it.geosolutions.geostore.core.ldap.IterableNamingEnumeration; +import it.geosolutions.geostore.core.ldap.MockContextSource; +import it.geosolutions.geostore.core.security.SimpleGrantedAuthoritiesMapper; +import it.geosolutions.geostore.services.rest.security.GeoStoreLdapAuthoritiesPopulator; +import java.util.*; +import java.util.stream.Collectors; import javax.naming.NamingEnumeration; import javax.naming.NamingException; -import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttributes; import javax.naming.directory.DirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; - import org.junit.Test; import org.springframework.ldap.core.DirContextAdapter; import org.springframework.security.core.GrantedAuthority; -import it.geosolutions.geostore.core.ldap.IterableNamingEnumeration; -import it.geosolutions.geostore.core.ldap.MockContextSource; -import it.geosolutions.geostore.services.rest.security.GeoStoreLdapAuthoritiesPopulator; public class GeoStoreLdapAuthoritiesPopulatorTest { - - DirContext ctx = new DirContextAdapter() { - @Override - public NamingEnumeration search(String name, String filter, SearchControls cons) - throws NamingException { - if ("ou=groups".equals(name)) { - if("(member=uid=bill,ou=people)".equals(filter)) { - List groups = new ArrayList(); - SearchResult sr = new SearchResult("cn=group1", null, new MockDirContextOperations() { - - @Override - public String getNameInNamespace() { - return "cn=group1,ou=groups"; - } - @Override - public String getStringAttribute(String name) { - if ("cn".equals(name)) { - return "group1"; - } - return ""; - } - - }, new BasicAttributes()); - return new IterableNamingEnumeration(Collections.singletonList(sr)); - } else if ("(member=cn=group1,ou=groups)".equals(filter)) { - List groups = new ArrayList(); - SearchResult sr = new SearchResult("cn=parentgroup1", null, new MockDirContextOperations() { - - @Override - public String getNameInNamespace() { - return "cn=parentgroup1,ou=groups"; - } + DirContext ctx = + new DirContextAdapter() { + @Override + public NamingEnumeration search( + String name, String filter, SearchControls cons) throws NamingException { + if ("ou=groups".equals(name)) { + if ("(member=uid=bill,ou=people)".equals(filter)) { + List groups = new ArrayList(); + SearchResult sr = + new SearchResult( + "cn=group1", + null, + new MockDirContextOperations() { + + @Override + public String getNameInNamespace() { + return "cn=group1,ou=groups"; + } + + @Override + public String getStringAttribute(String name) { + if ("cn".equals(name)) { + return "group1"; + } + return ""; + } + }, + new BasicAttributes()); + + SearchResult sr2 = + new SearchResult( + "cn=group2", + null, + new MockDirContextOperations() { + + @Override + public String getNameInNamespace() { + return "cn=group2,ou=groups"; + } + + @Override + public String getStringAttribute(String name) { + if ("cn".equals(name)) { + return "group2"; + } + return ""; + } + }, + new BasicAttributes()); + return new IterableNamingEnumeration(Arrays.asList(sr, sr2)); + } else if ("(member=cn=group1,ou=groups)".equals(filter)) { + List groups = new ArrayList(); + SearchResult sr = + new SearchResult( + "cn=parentgroup1", + null, + new MockDirContextOperations() { - @Override - public String getStringAttribute(String name) { - if ("cn".equals(name)) { - return "parentgroup1"; - } - return ""; + @Override + public String getNameInNamespace() { + return "cn=parentgroup1,ou=groups"; + } + + @Override + public String getStringAttribute(String name) { + if ("cn".equals(name)) { + return "parentgroup1"; + } + return ""; + } + }, + new BasicAttributes()); + return new IterableNamingEnumeration(Collections.singletonList(sr)); } - - }, new BasicAttributes()); - return new IterableNamingEnumeration(Collections.singletonList(sr)); + } + return new IterableNamingEnumeration(Collections.EMPTY_LIST); } - } - return new IterableNamingEnumeration(Collections.EMPTY_LIST); - } - - }; - + }; + @Test public void testNestedGroupsEnabled() { - GeoStoreLdapAuthoritiesPopulator authoritiesPopulator = - new GeoStoreLdapAuthoritiesPopulator(new MockContextSource(ctx), "ou=groups", "ou=roles"); + GeoStoreLdapAuthoritiesPopulator authoritiesPopulator = + new GeoStoreLdapAuthoritiesPopulator( + new MockContextSource(ctx), "ou=groups", "ou=roles"); authoritiesPopulator.setEnableHierarchicalGroups(true); - Set authorities = authoritiesPopulator.getGroupMembershipRoles("uid=bill,ou=people", "bill"); - assertEquals(2, authorities.size()); + Set authorities = + authoritiesPopulator.getGroupMembershipRoles("uid=bill,ou=people", "bill"); + assertEquals(3, authorities.size()); } - + @Test public void testNestedGroupsDisabled() { - - GeoStoreLdapAuthoritiesPopulator authoritiesPopulator = - new GeoStoreLdapAuthoritiesPopulator(new MockContextSource(ctx), "ou=groups", "ou=roles"); + + GeoStoreLdapAuthoritiesPopulator authoritiesPopulator = + new GeoStoreLdapAuthoritiesPopulator( + new MockContextSource(ctx), "ou=groups", "ou=roles"); + authoritiesPopulator.setEnableHierarchicalGroups(false); + Set authorities = + authoritiesPopulator.getGroupMembershipRoles("uid=bill,ou=people", "bill"); + assertEquals(2, authorities.size()); + } + + @Test + public void testDropUnmappedRoles() { + + GeoStoreLdapAuthoritiesPopulator authoritiesPopulator = + new GeoStoreLdapAuthoritiesPopulator( + new MockContextSource(ctx), "ou=groups", "ou=groups"); + authoritiesPopulator.setEnableHierarchicalGroups(false); + Map mappings = new HashMap<>(); + mappings.put("ROLE_GROUP2", "ROLE_ADMIN"); + SimpleGrantedAuthoritiesMapper mapper = new SimpleGrantedAuthoritiesMapper(mappings); + mapper.setDropUnmapped(true); + authoritiesPopulator.setRoleMapper(mapper); + Set authorities = + authoritiesPopulator.getGroupMembershipRoles("uid=bill,ou=people", "bill"); + List expected = Arrays.asList("ROLE_ADMIN", "GROUP1", "GROUP2"); + List stringAuthorities = + authorities.stream().map(a -> a.getAuthority()).collect(Collectors.toList()); + assertEquals(expected.size(), stringAuthorities.size()); + assertTrue(stringAuthorities.containsAll(expected)); + } + + @Test + public void testDropUnmappedGroups() { + + GeoStoreLdapAuthoritiesPopulator authoritiesPopulator = + new GeoStoreLdapAuthoritiesPopulator( + new MockContextSource(ctx), "ou=groups", "ou=roles"); authoritiesPopulator.setEnableHierarchicalGroups(false); - Set authorities = authoritiesPopulator.getGroupMembershipRoles("uid=bill,ou=people", "bill"); + Map mappings = new HashMap<>(); + mappings.put("GROUP2", "MAPPED_GROUP"); + SimpleGrantedAuthoritiesMapper mapper = new SimpleGrantedAuthoritiesMapper(mappings); + mapper.setDropUnmapped(true); + authoritiesPopulator.setGroupMapper(mapper); + Set authorities = + authoritiesPopulator.getGroupMembershipRoles("uid=bill,ou=people", "bill"); assertEquals(1, authorities.size()); + assertEquals("MAPPED_GROUP", authorities.iterator().next().getAuthority()); } } diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/HeadersAuthenticationFilterTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/HeadersAuthenticationFilterTest.java index a0120a3e..185578c4 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/HeadersAuthenticationFilterTest.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/HeadersAuthenticationFilterTest.java @@ -27,16 +27,21 @@ */ package it.geosolutions.geostore.rest.security; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; +import com.google.common.collect.Lists; +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.enums.Role; +import it.geosolutions.geostore.core.security.GrantedAuthoritiesMapper; +import it.geosolutions.geostore.services.exception.BadRequestServiceEx; +import it.geosolutions.geostore.services.exception.NotFoundServiceEx; +import it.geosolutions.geostore.services.rest.security.HeadersAuthenticationFilter; +import it.geosolutions.geostore.services.rest.utils.SpelMapper; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; - import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -46,21 +51,10 @@ import org.junit.Test; import org.mockito.Mockito; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.GrantedAuthorityImpl; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; -import com.google.common.collect.Lists; -import it.geosolutions.geostore.core.model.User; -import it.geosolutions.geostore.core.model.UserGroup; -import it.geosolutions.geostore.core.model.enums.Role; -import it.geosolutions.geostore.core.security.GrantedAuthoritiesMapper; -import it.geosolutions.geostore.services.exception.BadRequestServiceEx; -import it.geosolutions.geostore.services.exception.NotFoundServiceEx; -import it.geosolutions.geostore.services.rest.security.HeadersAuthenticationFilter; -import it.geosolutions.geostore.services.rest.utils.SpelMapper; public class HeadersAuthenticationFilterTest { - private HeadersAuthenticationFilter filter; - private static final String SAMPLE_USER = "user"; private static final String SAMPLE_GROUP1 = "group1"; private static final String SAMPLE_GROUP2 = "group2"; @@ -69,127 +63,171 @@ public class HeadersAuthenticationFilterTest { HttpServletRequest request = null; HttpServletResponse response = null; FilterChain chain = null; - + private HeadersAuthenticationFilter filter; + @Before public void setUp() { SecurityContextHolder.getContext().setAuthentication(null); filter = new HeadersAuthenticationFilter(); - + request = Mockito.mock(HttpServletRequest.class); response = Mockito.mock(HttpServletResponse.class); chain = Mockito.mock(FilterChain.class); } - + @After public void tearDown() { SecurityContextHolder.getContext().setAuthentication(null); } - + @Test - public void usernameHeaderAuthentication() throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { - Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_USERNAME_HEADER)).thenReturn(SAMPLE_USER); - + public void usernameHeaderAuthentication() + throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { + Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_USERNAME_HEADER)) + .thenReturn(SAMPLE_USER); + filter.doFilter(request, response, chain); - + assertNotNull(SecurityContextHolder.getContext().getAuthentication()); - User authUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + User authUser = + (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); assertEquals(SAMPLE_USER, authUser.getName()); assertNotNull(authUser.getId()); } - + @Test - public void noAuthenticationWithoutUsernameHeader() throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { - Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_USERNAME_HEADER)).thenReturn(null); - + public void noAuthenticationWithoutUsernameHeader() + throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { + Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_USERNAME_HEADER)) + .thenReturn(null); + filter.doFilter(request, response, chain); - + assertNull(SecurityContextHolder.getContext().getAuthentication()); } - + @Test - public void usernameAndGroupsHeaderAuthentication() throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { - Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_USERNAME_HEADER)).thenReturn(SAMPLE_USER); - Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_GROUPS_HEADER)).thenReturn(SAMPLE_GROUP1+","+SAMPLE_GROUP2); - + public void usernameAndGroupsHeaderAuthentication() + throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { + Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_USERNAME_HEADER)) + .thenReturn(SAMPLE_USER); + Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_GROUPS_HEADER)) + .thenReturn(SAMPLE_GROUP1 + "," + SAMPLE_GROUP2); + filter.doFilter(request, response, chain); - + assertNotNull(SecurityContextHolder.getContext().getAuthentication()); - User authUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + User authUser = + (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); assertEquals(2, authUser.getGroups().size()); } + @Test - public void defaultNoPrefixedGroupsHeaderAuthentication() throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { - Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_USERNAME_HEADER)).thenReturn(SAMPLE_USER); - Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_GROUPS_HEADER)).thenReturn(SAMPLE_GROUP1+","+"ROLE_"+SAMPLE_GROUP2); - + public void defaultNoPrefixedGroupsHeaderAuthentication() + throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { + Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_USERNAME_HEADER)) + .thenReturn(SAMPLE_USER); + Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_GROUPS_HEADER)) + .thenReturn(SAMPLE_GROUP1 + "," + "ROLE_" + SAMPLE_GROUP2); + filter.doFilter(request, response, chain); - + assertNotNull(SecurityContextHolder.getContext().getAuthentication()); - User authUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + User authUser = + (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); assertEquals(2, authUser.getGroups().size()); List groups = new ArrayList(); groups.add(SAMPLE_GROUP1); - groups.add("ROLE_"+SAMPLE_GROUP2); - for(UserGroup ug : authUser.getGroups()) { - assertTrue(groups.contains(ug.getGroupName())); + groups.add("ROLE_" + SAMPLE_GROUP2); + for (UserGroup ug : authUser.getGroups()) { + assertTrue(groups.contains(ug.getGroupName())); } } + @Test - public void prefixedGroupsHeaderAuthentication() throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { - filter.setGroupMapper(new SpelMapper("name.replace('ROLE_', '')")); - Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_USERNAME_HEADER)).thenReturn(SAMPLE_USER); - Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_GROUPS_HEADER)).thenReturn(SAMPLE_GROUP1+","+"ROLE_"+SAMPLE_GROUP2); - + public void prefixedGroupsHeaderAuthentication() + throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { + filter.setGroupMapper(new SpelMapper("name.replace('ROLE_', '')")); + Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_USERNAME_HEADER)) + .thenReturn(SAMPLE_USER); + Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_GROUPS_HEADER)) + .thenReturn(SAMPLE_GROUP1 + "," + "ROLE_" + SAMPLE_GROUP2); + filter.doFilter(request, response, chain); - + assertNotNull(SecurityContextHolder.getContext().getAuthentication()); - User authUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + User authUser = + (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); assertEquals(2, authUser.getGroups().size()); List groups = new ArrayList(); groups.add(SAMPLE_GROUP1); groups.add(SAMPLE_GROUP2); - for(UserGroup ug : authUser.getGroups()) { - assertTrue(groups.contains(ug.getGroupName())); + for (UserGroup ug : authUser.getGroups()) { + assertTrue(groups.contains(ug.getGroupName())); } } - - + @Test - public void usernameAndRoleHeaderAuthentication() throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { - Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_USERNAME_HEADER)).thenReturn(SAMPLE_USER); - Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_ROLE_HEADER)).thenReturn(ADMIN_ROLE); - + public void usernameAndRoleHeaderAuthentication() + throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { + Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_USERNAME_HEADER)) + .thenReturn(SAMPLE_USER); + Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_ROLE_HEADER)) + .thenReturn(ADMIN_ROLE); + filter.doFilter(request, response, chain); - + assertNotNull(SecurityContextHolder.getContext().getAuthentication()); - User authUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + User authUser = + (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); assertEquals(Role.ADMIN, authUser.getRole()); - assertEquals(1, SecurityContextHolder.getContext().getAuthentication().getAuthorities().size()); - assertEquals("ROLE_ADMIN", SecurityContextHolder.getContext().getAuthentication().getAuthorities().iterator().next().getAuthority()); + assertEquals( + 1, SecurityContextHolder.getContext().getAuthentication().getAuthorities().size()); + assertEquals( + "ROLE_ADMIN", + SecurityContextHolder.getContext() + .getAuthentication() + .getAuthorities() + .iterator() + .next() + .getAuthority()); } - + @Test - public void rolesMapper() throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { - Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_USERNAME_HEADER)).thenReturn(SAMPLE_USER); - Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_GROUPS_HEADER)).thenReturn(ROLE_GROUP); - filter.setAuthoritiesMapper(new GrantedAuthoritiesMapper() { - @Override - public Collection mapAuthorities( - Collection authorities) { - for (GrantedAuthority authority : authorities) { - if (ROLE_GROUP.equals(authority.getAuthority())) { - return Lists.newArrayList(new GrantedAuthorityImpl("ADMIN")); + public void rolesMapper() + throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { + Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_USERNAME_HEADER)) + .thenReturn(SAMPLE_USER); + Mockito.when(request.getHeader(HeadersAuthenticationFilter.DEFAULT_GROUPS_HEADER)) + .thenReturn(ROLE_GROUP); + filter.setAuthoritiesMapper( + new GrantedAuthoritiesMapper() { + @Override + public Collection mapAuthorities( + Collection authorities) { + for (GrantedAuthority authority : authorities) { + if (ROLE_GROUP.equals(authority.getAuthority())) { + return Lists.newArrayList(new SimpleGrantedAuthority("ADMIN")); + } + } + return Lists.newArrayList(); } - } - return Lists.newArrayList(); - } - }); + }); filter.doFilter(request, response, chain); - + assertNotNull(SecurityContextHolder.getContext().getAuthentication()); - User authUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + User authUser = + (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); assertEquals(Role.ADMIN, authUser.getRole()); - assertEquals(1, SecurityContextHolder.getContext().getAuthentication().getAuthorities().size()); - assertEquals("ROLE_ADMIN", SecurityContextHolder.getContext().getAuthentication().getAuthorities().iterator().next().getAuthority()); + assertEquals( + 1, SecurityContextHolder.getContext().getAuthentication().getAuthorities().size()); + assertEquals( + "ROLE_ADMIN", + SecurityContextHolder.getContext() + .getAuthentication() + .getAuthorities() + .iterator() + .next() + .getAuthority()); } } diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/MockDirContextOperations.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/MockDirContextOperations.java index 1d11565a..0d29a1a2 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/MockDirContextOperations.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/MockDirContextOperations.java @@ -2,20 +2,8 @@ import java.util.Hashtable; import java.util.SortedSet; - -import javax.naming.Binding; -import javax.naming.Context; -import javax.naming.Name; -import javax.naming.NameClassPair; -import javax.naming.NameParser; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.Attributes; -import javax.naming.directory.DirContext; -import javax.naming.directory.ModificationItem; -import javax.naming.directory.SearchControls; -import javax.naming.directory.SearchResult; - +import javax.naming.*; +import javax.naming.directory.*; import org.springframework.ldap.core.DirContextOperations; public class MockDirContextOperations implements DirContextOperations { @@ -143,15 +131,17 @@ public NamingEnumeration search(String name, Attributes matchingAt } @Override - public NamingEnumeration search(Name name, Attributes matchingAttributes, - String[] attributesToReturn) throws NamingException { + public NamingEnumeration search( + Name name, Attributes matchingAttributes, String[] attributesToReturn) + throws NamingException { // TODO Auto-generated method stub return null; } @Override - public NamingEnumeration search(String name, Attributes matchingAttributes, - String[] attributesToReturn) throws NamingException { + public NamingEnumeration search( + String name, Attributes matchingAttributes, String[] attributesToReturn) + throws NamingException { // TODO Auto-generated method stub return null; } @@ -171,15 +161,17 @@ public NamingEnumeration search(String name, String filter, Search } @Override - public NamingEnumeration search(Name name, String filterExpr, Object[] filterArgs, - SearchControls cons) throws NamingException { + public NamingEnumeration search( + Name name, String filterExpr, Object[] filterArgs, SearchControls cons) + throws NamingException { // TODO Auto-generated method stub return null; } @Override - public NamingEnumeration search(String name, String filterExpr, - Object[] filterArgs, SearchControls cons) throws NamingException { + public NamingEnumeration search( + String name, String filterExpr, Object[] filterArgs, SearchControls cons) + throws NamingException { // TODO Auto-generated method stub return null; } @@ -388,6 +380,12 @@ public Name getDn() { return null; } + @Override + public void setDn(Name arg0) { + // TODO Auto-generated method stub + + } + @Override public String getNameInNamespace() { // TODO Auto-generated method stub @@ -467,15 +465,14 @@ public void setAttributeValues(String arg0, Object[] arg1, boolean arg2) { } @Override - public void setDn(Name arg0) { + public void update() { // TODO Auto-generated method stub } @Override - public void update() { + public boolean attributeExists(String arg0) { // TODO Auto-generated method stub - + return false; } - } diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/MockLdapAuthenticator.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/MockLdapAuthenticator.java index c24b6899..1de58750 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/MockLdapAuthenticator.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/MockLdapAuthenticator.java @@ -1,9 +1,9 @@ package it.geosolutions.geostore.rest.security; +import it.geosolutions.geostore.core.ldap.MockDirContextOperations; import org.springframework.ldap.core.DirContextOperations; import org.springframework.security.core.Authentication; import org.springframework.security.ldap.authentication.LdapAuthenticator; -import it.geosolutions.geostore.core.ldap.MockDirContextOperations; public class MockLdapAuthenticator implements LdapAuthenticator { @@ -11,5 +11,4 @@ public class MockLdapAuthenticator implements LdapAuthenticator { public DirContextOperations authenticate(Authentication authentication) { return new MockDirContextOperations(); } - } diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/MockLdapAuthoritiesPopulator.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/MockLdapAuthoritiesPopulator.java index d90d0a10..cd549092 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/MockLdapAuthoritiesPopulator.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/MockLdapAuthoritiesPopulator.java @@ -1,11 +1,9 @@ package it.geosolutions.geostore.rest.security; import it.geosolutions.geostore.services.rest.security.GroupsRolesService; - import java.util.Collection; import java.util.Collections; import java.util.Set; - import org.springframework.ldap.core.DirContextOperations; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; @@ -13,8 +11,8 @@ public class MockLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator, GroupsRolesService { @Override - public Collection getGrantedAuthorities(DirContextOperations userData, - String username) { + public Collection getGrantedAuthorities( + DirContextOperations userData, String username) { return Collections.EMPTY_LIST; } @@ -27,7 +25,4 @@ public Set getAllGroups() { public Set getAllRoles() { return Collections.EMPTY_SET; } - - - } diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/SessionTokenAuthenticationFilterTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/SessionTokenAuthenticationFilterTest.java index 69a867db..aad59a8a 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/SessionTokenAuthenticationFilterTest.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/SessionTokenAuthenticationFilterTest.java @@ -30,18 +30,20 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.enums.Role; +import it.geosolutions.geostore.services.InMemoryUserSessionServiceImpl; +import it.geosolutions.geostore.services.dto.UserSessionImpl; +import it.geosolutions.geostore.services.exception.BadRequestServiceEx; +import it.geosolutions.geostore.services.exception.NotFoundServiceEx; +import it.geosolutions.geostore.services.rest.security.SessionTokenAuthenticationFilter; +import it.geosolutions.geostore.services.rest.utils.MockedUserService; import java.io.IOException; -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - +import java.util.*; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -50,91 +52,83 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import it.geosolutions.geostore.core.model.User; -import it.geosolutions.geostore.core.model.enums.Role; -import it.geosolutions.geostore.services.InMemoryUserSessionServiceImpl; -import it.geosolutions.geostore.services.dto.UserSessionImpl; -import it.geosolutions.geostore.services.exception.BadRequestServiceEx; -import it.geosolutions.geostore.services.exception.NotFoundServiceEx; -import it.geosolutions.geostore.services.rest.security.SessionTokenAuthenticationFilter; -import it.geosolutions.geostore.services.rest.utils.MockedUserService; - public class SessionTokenAuthenticationFilterTest { private static final String DEFAULT_PREFIX = "Bearer "; private static final String DEFAULT_HEADER = "Authorization"; - - private SessionTokenAuthenticationFilter filter; - - private Map tokens; - private static final String SAMPLE_USER = "user"; private static final String SAMPLE_TOKEN = UUID.randomUUID().toString(); private static final String WRONG_TOKEN = UUID.randomUUID().toString(); - private static final Authentication SAMPLE_AUTH = new UsernamePasswordAuthenticationToken(SAMPLE_USER, ""); - - - + private static final Authentication SAMPLE_AUTH = + new UsernamePasswordAuthenticationToken(SAMPLE_USER, ""); HttpServletRequest request = null; HttpServletResponse response = null; FilterChain chain = null; - + private SessionTokenAuthenticationFilter filter; + private Map tokens; + @Before public void setUp() { tokens = new HashMap(); tokens.put(SAMPLE_TOKEN, SAMPLE_AUTH); - + filter = new SessionTokenAuthenticationFilter(); - + filter.setUserService(new MockedUserService()); filter.setUserSessionService(new InMemoryUserSessionServiceImpl()); request = Mockito.mock(HttpServletRequest.class); response = Mockito.mock(HttpServletResponse.class); chain = Mockito.mock(FilterChain.class); } - + @After public void tearDown() { SecurityContextHolder.getContext().setAuthentication(null); } + /** - * Checks that when using an external auth service (like LDAP) the - * user object is anyway retrieved using username. + * Checks that when using an external auth service (like LDAP) the user object is anyway + * retrieved using username. + * * @throws IOException * @throws ServletException * @throws BadRequestServiceEx * @throws NotFoundServiceEx */ @Test - public void userWorksWithNameOnlyTest() throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { - User user = new User(); - user.setName(SAMPLE_USER); - user.setRole(Role.USER); - filter.setCacheExpiration(1); - // different users, same name - User sessionUser = new User(); - sessionUser.setName(SAMPLE_USER); - sessionUser.setRole(Role.USER); - // here the mock service sets the id. - filter.getUserService().insert(user); - - Mockito.when(request.getHeader(DEFAULT_HEADER)).thenReturn(DEFAULT_PREFIX + SAMPLE_TOKEN); - - Calendar expires = new GregorianCalendar(); - expires.add(Calendar.SECOND, (int) 60* 60 * 24* 15); - - filter.getUserSessionService().registerNewSession(SAMPLE_TOKEN, new UserSessionImpl(sessionUser, expires)); - + public void userWorksWithNameOnlyTest() + throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { + User user = new User(); + user.setName(SAMPLE_USER); + user.setRole(Role.USER); + filter.setCacheExpiration(1); + // different users, same name + User sessionUser = new User(); + sessionUser.setName(SAMPLE_USER); + sessionUser.setRole(Role.USER); + // here the mock service sets the id. + filter.getUserService().insert(user); + + Mockito.when(request.getHeader(DEFAULT_HEADER)).thenReturn(DEFAULT_PREFIX + SAMPLE_TOKEN); + + Calendar expires = new GregorianCalendar(); + expires.add(Calendar.SECOND, 60 * 60 * 24 * 15); + + filter.getUserSessionService() + .registerNewSession(SAMPLE_TOKEN, new UserSessionImpl(sessionUser, expires)); + filter.doFilter(request, response, chain); - + assertNotNull(SecurityContextHolder.getContext().getAuthentication()); - User authUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + User authUser = + (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); assertEquals(SAMPLE_USER, authUser.getName()); assertNotNull(authUser.getId()); } - + @Test - public void userWorksWithFakeIdTest() throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { + public void userWorksWithFakeIdTest() + throws IOException, ServletException, BadRequestServiceEx, NotFoundServiceEx { User user = new User(); user.setName(SAMPLE_USER); user.setRole(Role.USER); @@ -146,20 +140,21 @@ public void userWorksWithFakeIdTest() throws IOException, ServletException, BadR sessionUser.setRole(Role.USER); // here the mock service sets the id. filter.getUserService().insert(user); - + Mockito.when(request.getHeader(DEFAULT_HEADER)).thenReturn(DEFAULT_PREFIX + SAMPLE_TOKEN); - + Calendar expires = new GregorianCalendar(); - expires.add(Calendar.SECOND, (int) 60* 60 * 24* 15); - - filter.getUserSessionService().registerNewSession(SAMPLE_TOKEN, new UserSessionImpl(sessionUser, expires)); - + expires.add(Calendar.SECOND, 60 * 60 * 24 * 15); + + filter.getUserSessionService() + .registerNewSession(SAMPLE_TOKEN, new UserSessionImpl(sessionUser, expires)); + filter.doFilter(request, response, chain); - + assertNotNull(SecurityContextHolder.getContext().getAuthentication()); - User authUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + User authUser = + (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); assertEquals(SAMPLE_USER, authUser.getName()); assertNotNull(authUser.getId()); } - } diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/TokenAuthenticationFilterTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/TokenAuthenticationFilterTest.java index ca8e5082..924cf64a 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/TokenAuthenticationFilterTest.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/TokenAuthenticationFilterTest.java @@ -27,22 +27,18 @@ */ package it.geosolutions.geostore.rest.security; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.junit.Assert.*; + import it.geosolutions.geostore.services.rest.security.TokenAuthenticationFilter; import it.geosolutions.geostore.services.rest.utils.MockedUserService; - import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.UUID; - import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -55,103 +51,97 @@ public class TokenAuthenticationFilterTest { private static final String DEFAULT_PREFIX = "Bearer "; private static final String DEFAULT_HEADER = "Authorization"; - - private TokenAuthenticationFilter filter; - - private Map tokens; - private static final String SAMPLE_USER = "user"; private static final String SAMPLE_TOKEN = UUID.randomUUID().toString(); private static final String WRONG_TOKEN = UUID.randomUUID().toString(); - private static final Authentication SAMPLE_AUTH = new UsernamePasswordAuthenticationToken(SAMPLE_USER, ""); - - - + private static final Authentication SAMPLE_AUTH = + new UsernamePasswordAuthenticationToken(SAMPLE_USER, ""); HttpServletRequest request = null; HttpServletResponse response = null; FilterChain chain = null; - + private TokenAuthenticationFilter filter; + private Map tokens; + @Before public void setUp() { tokens = new HashMap(); tokens.put(SAMPLE_TOKEN, SAMPLE_AUTH); - - filter = new TokenAuthenticationFilter() { - - @Override - protected Authentication checkToken(String token) { - return tokens.get(token); - } - - }; - + + filter = + new TokenAuthenticationFilter() { + + @Override + protected Authentication checkToken(String token) { + return tokens.get(token); + } + }; + filter.setUserService(new MockedUserService()); request = Mockito.mock(HttpServletRequest.class); response = Mockito.mock(HttpServletResponse.class); chain = Mockito.mock(FilterChain.class); } - + @After public void tearDown() { SecurityContextHolder.getContext().setAuthentication(null); } - + @Test public void testExistingToken() throws IOException, ServletException { Mockito.when(request.getHeader(DEFAULT_HEADER)).thenReturn(DEFAULT_PREFIX + SAMPLE_TOKEN); filter.doFilter(request, response, chain); - + assertNotNull(SecurityContextHolder.getContext().getAuthentication()); assertEquals(SAMPLE_USER, SecurityContextHolder.getContext().getAuthentication().getName()); } - + @Test public void testUnknownToken() throws IOException, ServletException { Mockito.when(request.getHeader(DEFAULT_HEADER)).thenReturn(DEFAULT_PREFIX + WRONG_TOKEN); filter.doFilter(request, response, chain); - + assertNull(SecurityContextHolder.getContext().getAuthentication()); } - + @Test public void testCustomHeader() throws IOException, ServletException { Mockito.when(request.getHeader("Custom")).thenReturn(DEFAULT_PREFIX + SAMPLE_TOKEN); filter.setTokenHeader("Custom"); filter.doFilter(request, response, chain); - + assertNotNull(SecurityContextHolder.getContext().getAuthentication()); assertEquals(SAMPLE_USER, SecurityContextHolder.getContext().getAuthentication().getName()); } - + @Test public void testCustomPrefix() throws IOException, ServletException { Mockito.when(request.getHeader(DEFAULT_HEADER)).thenReturn("Custom" + SAMPLE_TOKEN); filter.setTokenPrefix("Custom"); filter.doFilter(request, response, chain); - + assertNotNull(SecurityContextHolder.getContext().getAuthentication()); assertEquals(SAMPLE_USER, SecurityContextHolder.getContext().getAuthentication().getName()); } - - + @Test public void testCacheExpiration() throws IOException, ServletException, InterruptedException { Mockito.when(request.getHeader(DEFAULT_HEADER)).thenReturn(DEFAULT_PREFIX + SAMPLE_TOKEN); filter.setCacheExpiration(1); filter.doFilter(request, response, chain); - + assertNotNull(SecurityContextHolder.getContext().getAuthentication()); assertEquals(SAMPLE_USER, SecurityContextHolder.getContext().getAuthentication().getName()); - + tokens.clear(); SecurityContextHolder.getContext().setAuthentication(null); filter.doFilter(request, response, chain); // still there, cached value assertNotNull(SecurityContextHolder.getContext().getAuthentication()); - + // wait for cache expiration Thread.sleep(2000); - + SecurityContextHolder.getContext().setAuthentication(null); filter.doFilter(request, response, chain); // gone diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/UserLdapAuthenticationProviderTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/UserLdapAuthenticationProviderTest.java index a5102454..380ce805 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/UserLdapAuthenticationProviderTest.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/UserLdapAuthenticationProviderTest.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2015 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,45 +21,47 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; + import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; import it.geosolutions.geostore.services.rest.security.UserLdapAuthenticationProvider; import it.geosolutions.geostore.services.rest.utils.MockedUserGroupService; import it.geosolutions.geostore.services.rest.utils.MockedUserService; - import java.util.Collections; import java.util.Set; - import org.junit.Before; import org.junit.Test; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.GrantedAuthorityImpl; +import org.springframework.security.core.authority.SimpleGrantedAuthority; public class UserLdapAuthenticationProviderTest { private static final String TEST_GROUP = "testgroup"; - + private UserLdapAuthenticationProvider provider; private MockedUserService userService; private MockedUserGroupService userGroupService; - + @Before public void setUp() { - provider = new UserLdapAuthenticationProvider(new MockLdapAuthenticator(), new MockLdapAuthoritiesPopulator(){ + provider = + new UserLdapAuthenticationProvider( + new MockLdapAuthenticator(), + new MockLdapAuthoritiesPopulator() { - @Override - public Set getAllGroups() { - return Collections.singleton((GrantedAuthority)new GrantedAuthorityImpl(TEST_GROUP)); - } - - }); + @Override + public Set getAllGroups() { + return Collections.singleton( + new SimpleGrantedAuthority(TEST_GROUP)); + } + }); userService = new MockedUserService(); userGroupService = new MockedUserGroupService(); provider.setUserService(userService); provider.setUserGroupService(userGroupService); } - + @Test public void testNullPassword() throws NotFoundServiceEx { provider.authenticate(new UsernamePasswordAuthenticationToken("user", "password")); @@ -67,13 +69,13 @@ public void testNullPassword() throws NotFoundServiceEx { assertNotNull(user); assertNull(user.getPassword()); } - + @Test public void testSynchronizeGroups() throws BadRequestServiceEx { assertNull(userGroupService.get(TEST_GROUP)); - + provider.synchronizeGroups(); - + assertNotNull(userGroupService.get(TEST_GROUP)); } } diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeyCloakConfigurationTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeyCloakConfigurationTest.java new file mode 100644 index 00000000..88c52c8b --- /dev/null +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeyCloakConfigurationTest.java @@ -0,0 +1,15 @@ +package it.geosolutions.geostore.rest.security.keycloak; + +import static org.junit.Assert.assertNull; + +import it.geosolutions.geostore.services.rest.security.keycloak.KeyCloakConfiguration; +import org.junit.Test; + +public class KeyCloakConfigurationTest { + + @Test + public void testNPENotThrownOnNullJSONConfig() { + KeyCloakConfiguration configuration = new KeyCloakConfiguration(); + assertNull(configuration.readAdapterConfig()); + } +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakFilterTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakFilterTest.java new file mode 100644 index 00000000..2f05057e --- /dev/null +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakFilterTest.java @@ -0,0 +1,245 @@ +package it.geosolutions.geostore.rest.security.keycloak; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; + +import com.fasterxml.jackson.databind.ObjectMapper; +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.services.rest.security.TokenAuthenticationCache; +import it.geosolutions.geostore.services.rest.security.keycloak.*; +import java.io.IOException; +import java.util.Set; +import java.util.stream.Collectors; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.keycloak.adapters.*; +import org.keycloak.adapters.rotation.AdapterTokenVerifier; +import org.keycloak.adapters.spi.AdapterSessionStore; +import org.keycloak.adapters.spi.HttpFacade; +import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticator; +import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade; +import org.keycloak.common.VerificationException; +import org.keycloak.jose.jws.JWSInput; +import org.keycloak.representations.adapters.config.AdapterConfig; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.mock.web.MockFilterChain; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +public class KeycloakFilterTest extends KeycloakTestSupport { + + private final MockUserGroupService groupService = new MockUserGroupService(); + private final MockUserService userService = new MockUserService(); + private MockHttpServletRequest request; + private MockHttpServletResponse response; + private FilterChain chain; + + private static org.keycloak.representations.AccessToken verifyToken() { + try { + JWSInput jwsInput = new JWSInput(JWT_2018_2037); + return jwsInput.readJsonContent(org.keycloak.representations.AccessToken.class); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Before + public void before() { + setUpAdapter(AUTH_URL); + request = new MockHttpServletRequest(); + response = new MockHttpServletResponse(); + request.setRequestURI(APP_URL); + request.setScheme("http"); + request.setServerPort(8080); + request.setServerName("localhost"); + this.chain = new MockFilterChain(); + RequestAttributes requestAttributes = new ServletRequestAttributes(request, response); + RequestContextHolder.setRequestAttributes(requestAttributes); + } + + @Test + public void testKeyCloakFilterRedirect() throws IOException, ServletException { + KeyCloakConfiguration configuration = createConfiguration(); + KeyCloakFilter filter = createFilter(configuration); + filter.doFilter(request, response, chain); + Object redirect = request.getAttribute("KEYCLOAK_REDIRECT"); + assertTrue(redirect instanceof AuthenticationEntryPoint); + } + + @Test + public void testAuthentication() throws IOException, ServletException, VerificationException { + KeyCloakConfiguration configuration = createConfiguration(); + AdapterConfig config = configuration.readAdapterConfig(); + config.setRealmKey(PUBLIC_KEY); + ObjectMapper om = new ObjectMapper(); + String stringConfig = om.writeValueAsString(config); + configuration.setJsonConfig(stringConfig); + try (MockedStatic utilities = + Mockito.mockStatic(AdapterTokenVerifier.class)) { + utilities + .when( + () -> + AdapterTokenVerifier.verifyToken( + eq(JWT_2018_2037), any(KeycloakDeployment.class))) + .thenReturn(verifyToken()); + TokenAuthenticationCache cache = new TokenAuthenticationCache(); + KeyCloakFilter filter = createFilter(configuration, cache); + String auth_header = "bearer " + JWT_2018_2037; + request.addHeader("AUTHORIZATION", auth_header); + filter.doFilter(request, response, chain); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + assertTrue(authentication.getPrincipal() instanceof User); + User user = (User) authentication.getPrincipal(); + Set groupSet = user.getGroups(); + groupSet = + groupSet.stream() + .filter(g -> !g.getGroupName().equals("everyone")) + .collect(Collectors.toSet()); + for (UserGroup group : groupSet) { + assertNotNull(groupService.get(group.getGroupName())); + } + assertTrue(authentication.getDetails() instanceof KeycloakTokenDetails); + KeycloakTokenDetails tokenDetails = (KeycloakTokenDetails) authentication.getDetails(); + assertNotNull(cache.get(tokenDetails.getAccessToken())); + } + } + + @Test + public void testAuthenticationFailure() throws IOException, ServletException { + KeyCloakConfiguration configuration = createConfiguration(); + AdapterConfig config = configuration.readAdapterConfig(); + config.setRealmKey(PUBLIC_KEY); + ObjectMapper om = new ObjectMapper(); + String stringConfig = om.writeValueAsString(config); + configuration.setJsonConfig(stringConfig); + TokenAuthenticationCache cache = new TokenAuthenticationCache(); + KeyCloakFilter filter = createFilter(configuration, cache); + String auth_header = "bearer " + "wrong_token"; + request.addHeader("AUTHORIZATION", auth_header); + filter.doFilter(request, response, chain); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + assertNull(authentication); + } + + private KeyCloakFilter createFilter(KeyCloakConfiguration configuration) { + return createFilter(configuration, new TokenAuthenticationCache()); + } + + private KeyCloakFilter createFilter( + KeyCloakConfiguration configuration, TokenAuthenticationCache cache) { + KeycloakDeployment deployment = + KeycloakDeploymentBuilder.build(configuration.readAdapterConfig()); + AdapterDeploymentContext context = new AdapterDeploymentContext(deployment); + KeyCloakHelper helper = + new KeyCloakHelper(context) { + @Override + public RequestAuthenticator getAuthenticator( + HttpServletRequest request, + HttpServletResponse response, + KeycloakDeployment deployment) { + request = new KeyCloakRequestWrapper(request); + AdapterTokenStore tokenStore = + adapterTokenStoreFactory.createAdapterTokenStore( + deployment, request, response); + SimpleHttpFacade simpleHttpFacade = new SimpleHttpFacade(request, response); + return new TestAuthenticator( + simpleHttpFacade, request, deployment, tokenStore, -1); + } + }; + GeoStoreKeycloakAuthProvider authProvider = new GeoStoreKeycloakAuthProvider(configuration); + authProvider.setUserService(userService); + authProvider.setGroupService(groupService); + return new KeyCloakFilter(helper, cache, configuration, authProvider); + } + + @After + public void cleanUp() { + RequestContextHolder.resetRequestAttributes(); + SecurityContextHolder.clearContext(); + } + + @Test + public void testDropUnMappedGroups() throws IOException, ServletException { + KeyCloakConfiguration configuration = createConfiguration(); + configuration.setDropUnmapped(true); + configuration.setGroupMappings("uma_authorization:TEST_GROUP"); + AdapterConfig config = configuration.readAdapterConfig(); + config.setRealmKey(PUBLIC_KEY); + ObjectMapper om = new ObjectMapper(); + String stringConfig = om.writeValueAsString(config); + configuration.setJsonConfig(stringConfig); + try (MockedStatic utilities = + Mockito.mockStatic(AdapterTokenVerifier.class)) { + utilities + .when( + () -> + AdapterTokenVerifier.verifyToken( + eq(JWT_2018_2037), any(KeycloakDeployment.class))) + .thenReturn(verifyToken()); + TokenAuthenticationCache cache = new TokenAuthenticationCache(); + KeyCloakFilter filter = createFilter(configuration, cache); + String auth_header = "bearer " + JWT_2018_2037; + request.addHeader("AUTHORIZATION", auth_header); + filter.doFilter(request, response, chain); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + assertTrue(authentication.getPrincipal() instanceof User); + User user = (User) authentication.getPrincipal(); + Set groupSet = user.getGroups(); + assertEquals(2, groupSet.size()); + groupSet.stream().anyMatch(g -> g.getGroupName().equals("TEST_GROUP")); + groupSet.stream().anyMatch(g -> g.getGroupName().equals("everyone")); + assertTrue(authentication.getDetails() instanceof KeycloakTokenDetails); + KeycloakTokenDetails tokenDetails = (KeycloakTokenDetails) authentication.getDetails(); + assertNotNull(cache.get(tokenDetails.getAccessToken())); + } + } + + private class TestOAuthAuthenticator extends GeoStoreOAuthAuthenticator { + + public TestOAuthAuthenticator( + RequestAuthenticator requestAuthenticator, + HttpFacade facade, + KeycloakDeployment deployment, + int sslRedirectPort, + AdapterSessionStore tokenStore) { + super(requestAuthenticator, facade, deployment, sslRedirectPort, tokenStore); + } + + @Override + protected String getRedirectUri(String state) { + return AUTH_URL; + } + } + + private class TestAuthenticator extends SpringSecurityRequestAuthenticator { + + public TestAuthenticator( + HttpFacade facade, + HttpServletRequest request, + KeycloakDeployment deployment, + AdapterTokenStore tokenStore, + int sslRedirectPort) { + super(facade, request, deployment, tokenStore, sslRedirectPort); + } + + @Override + protected OAuthRequestAuthenticator createOAuthAuthenticator() { + return new TestOAuthAuthenticator( + this, facade, deployment, sslRedirectPort, tokenStore); + } + } +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakLoginTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakLoginTest.java new file mode 100644 index 00000000..6301e2d9 --- /dev/null +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakLoginTest.java @@ -0,0 +1,145 @@ +package it.geosolutions.geostore.rest.security.keycloak; + +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import com.fasterxml.jackson.core.JsonProcessingException; +import it.geosolutions.geostore.services.rest.IdPLoginRest; +import it.geosolutions.geostore.services.rest.model.SessionToken; +import it.geosolutions.geostore.services.rest.security.IdPConfiguration; +import it.geosolutions.geostore.services.rest.security.keycloak.KeyCloakConfiguration; +import it.geosolutions.geostore.services.rest.security.keycloak.KeyCloakLoginService; +import it.geosolutions.geostore.services.rest.security.keycloak.KeycloakTokenDetails; +import it.geosolutions.geostore.services.rest.security.oauth2.IdPLoginRestImpl; +import it.geosolutions.geostore.services.rest.security.oauth2.InMemoryTokenStorage; +import it.geosolutions.geostore.services.rest.security.oauth2.TokenStorage; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.adapters.KeycloakDeploymentBuilder; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +public class KeycloakLoginTest extends KeycloakTestSupport { + + private IdPLoginRest loginRest; + + private Object key; + + @Before + public void setUp() throws JsonProcessingException { + setUpAdapter(AUTH_URL); + KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(adapterConfig); + KeyCloakConfiguration configuration = createConfiguration(); + loginRest = new IdPLoginRestImpl(); + // autoregister to the loginRest object + TokenStorage storage = new InMemoryTokenStorage(); + key = storage.buildTokenKey(); + SessionToken token = new SessionToken(); + token.setAccessToken(ACCESS_TOKEN_PARAM); + token.setRefreshToken(REFRESH_TOKEN_PARAM); + storage.saveToken(key, token); + new KeyCloakLoginService(loginRest) { + @Override + protected TokenStorage tokenStorage() { + return storage; + } + + @Override + protected IdPConfiguration configuration(String provider) { + return configuration; + } + }; + } + + @Test + public void testLoginEndpoint() { + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + ServletRequestAttributes attributes = new ServletRequestAttributes(request, response); + AuthenticationEntryPoint entryPoint = + new AuthenticationEntryPoint() { + @Override + public void commence( + HttpServletRequest request, + HttpServletResponse response, + AuthenticationException authException) + throws IOException, ServletException { + response.sendRedirect("/"); + } + }; + attributes.setAttribute("KEYCLOAK_REDIRECT", entryPoint, 0); + RequestContextHolder.setRequestAttributes(attributes); + loginRest.login("keycloak"); + assertEquals(302, response.getStatus()); + assertNotNull(response.getRedirectedUrl()); + } + + @Test + public void testLoginEndpointInternalRedirect() { + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse httpResponse = new MockHttpServletResponse(); + ServletRequestAttributes attributes = new ServletRequestAttributes(request, httpResponse); + RequestContextHolder.setRequestAttributes(attributes); + PreAuthenticatedAuthenticationToken authentication = + new PreAuthenticatedAuthenticationToken("username", "", new ArrayList<>()); + KeycloakTokenDetails details = + new KeycloakTokenDetails("accessToken", "refreshToken", 10202L); + authentication.setDetails(details); + SecurityContextHolder.getContext().setAuthentication(authentication); + Response response = loginRest.callback("keycloak"); + assertEquals(302, response.getStatus()); + assertEquals("../../../", response.getHeaderString("Location")); + MultivaluedMap meta = response.getMetadata(); + List cookies = meta.get("Set-Cookie"); + List tokenCookies = + cookies.stream() + .filter( + c -> + ((String) c).contains(AUTH_PROVIDER) + || ((String) c).contains(TOKENS_KEY)) + .collect(Collectors.toList()); + assertEquals(2, tokenCookies.size()); + } + + @Test + public void testGetTokenByIdentifier() { + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse httpResponse = new MockHttpServletResponse(); + ServletRequestAttributes attributes = new ServletRequestAttributes(request, httpResponse); + RequestContextHolder.setRequestAttributes(attributes); + PreAuthenticatedAuthenticationToken authentication = + new PreAuthenticatedAuthenticationToken("username", "", new ArrayList<>()); + KeycloakTokenDetails details = + new KeycloakTokenDetails("accessToken", "refreshToken", 10202L); + authentication.setDetails(details); + SecurityContextHolder.getContext().setAuthentication(authentication); + SessionToken sessionToken = + loginRest.getTokensByTokenIdentifier("keycloak", key.toString()); + assertEquals(ACCESS_TOKEN_PARAM, sessionToken.getAccessToken()); + assertEquals(REFRESH_TOKEN_PARAM, sessionToken.getRefreshToken()); + } + + @After + public void afterTest() { + RequestContextHolder.resetRequestAttributes(); + SecurityContextHolder.clearContext(); + } +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakSessionServiceTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakSessionServiceTest.java new file mode 100644 index 00000000..544d1651 --- /dev/null +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakSessionServiceTest.java @@ -0,0 +1,225 @@ +package it.geosolutions.geostore.rest.security.keycloak; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static it.geosolutions.geostore.services.rest.SessionServiceDelegate.PROVIDER_KEY; +import static it.geosolutions.geostore.services.rest.security.keycloak.KeyCloakSecurityConfiguration.CACHE_BEAN_NAME; +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.ACCESS_TOKEN_PARAM; +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.REFRESH_TOKEN_PARAM; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.common.ConsoleNotifier; +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.services.rest.RESTSessionService; +import it.geosolutions.geostore.services.rest.impl.RESTSessionServiceImpl; +import it.geosolutions.geostore.services.rest.model.SessionToken; +import it.geosolutions.geostore.services.rest.security.TokenAuthenticationCache; +import it.geosolutions.geostore.services.rest.security.keycloak.KeyCloakConfiguration; +import it.geosolutions.geostore.services.rest.security.keycloak.KeyCloakHelper; +import it.geosolutions.geostore.services.rest.security.keycloak.KeycloakSessionServiceDelegate; +import it.geosolutions.geostore.services.rest.security.keycloak.KeycloakTokenDetails; +import it.geosolutions.geostore.services.rest.utils.GeoStoreContext; +import java.text.ParseException; +import java.util.ArrayList; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.keycloak.adapters.AdapterDeploymentContext; +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.adapters.KeycloakDeploymentBuilder; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import wiremock.org.eclipse.jetty.http.HttpStatus; + +public class KeycloakSessionServiceTest extends KeycloakTestSupport { + + private static final String REFRESH_TOKEN = "refresh_token"; + private static final String ACCESS_TOKEN = "access_token"; + private static WireMockServer keycloakService; + private KeyCloakHelper helper; + + @BeforeClass + public static void mockServerSetup() { + + keycloakService = + new WireMockServer( + wireMockConfig() + .port(12345) + // uncomment the following to get wiremock logging + .notifier(new ConsoleNotifier(true))); + keycloakService.start(); + + keycloakService.stubFor( + WireMock.post(urlPathMatching("/auth/realms/master/protocol/openid-connect/token")) + .withRequestBody(containing("grant_type=refresh_token")) + .withRequestBody(containing("client_id=" + CLIENT_ID)) + .withRequestBody(containing("client_secret=" + SECRET)) + .withRequestBody(containing("refresh_token=" + REFRESH_TOKEN)) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("token_response_keycloak.json"))); + keycloakService.stubFor( + WireMock.post(urlPathMatching("/auth/realms/master/protocol/openid-connect/logout")) + .withRequestBody(containing("client_id=" + CLIENT_ID)) + .withRequestBody(containing("client_secret=" + SECRET)) + .withRequestBody(containing("refresh_token=" + REFRESH_TOKEN)) + .willReturn(aResponse().withStatus(204))); + + keycloakService.stubFor( + WireMock.get( + urlPathMatching( + "/auth/realms/master/.well-known/openid-configuration")) + .willReturn( + aResponse() + .withStatus(200) + .withBodyFile("keycloak_discovery.json"))); + } + + @AfterClass + public static void afterClass() { + keycloakService.stop(); + } + + @Before + public void setUp() { + setUpAdapter(AUTH_URL); + adapterConfig.setAuthServerUrl("http://localhost:" + keycloakService.port() + "/auth"); + KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(adapterConfig); + AdapterDeploymentContext context = new AdapterDeploymentContext(deployment); + KeyCloakHelper helper = new KeyCloakHelper(context); + this.helper = helper; + } + + @Test + public void testRefreshToken() throws JsonProcessingException, ParseException { + KeyCloakConfiguration configuration = createConfiguration(); + TokenAuthenticationCache cache = new TokenAuthenticationCache(); + PreAuthenticatedAuthenticationToken authenticationToken = + new PreAuthenticatedAuthenticationToken("user", "", new ArrayList<>()); + KeycloakTokenDetails details = new KeycloakTokenDetails("access_token", REFRESH_TOKEN, 0L); + authenticationToken.setDetails(details); + cache.putCacheEntry(ACCESS_TOKEN, authenticationToken); + try (MockedStatic utilities = Mockito.mockStatic(GeoStoreContext.class)) { + utilities + .when(() -> GeoStoreContext.bean(KeyCloakConfiguration.class)) + .thenReturn(configuration); + utilities.when(() -> GeoStoreContext.bean(KeyCloakHelper.class)).thenReturn(helper); + utilities + .when( + () -> + GeoStoreContext.bean( + CACHE_BEAN_NAME, TokenAuthenticationCache.class)) + .thenReturn(cache); + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + ServletRequestAttributes attributes = new ServletRequestAttributes(request, response); + attributes.setAttribute(PROVIDER_KEY, "keycloak", 0); + attributes.setAttribute(ACCESS_TOKEN_PARAM, ACCESS_TOKEN, 0); + attributes.setAttribute(REFRESH_TOKEN_PARAM, REFRESH_TOKEN, 0); + RequestContextHolder.setRequestAttributes(attributes); + RESTSessionService sessionService = new RESTSessionServiceImpl(); + new KeycloakSessionServiceDelegate(sessionService, null); + SessionToken token = new SessionToken(); + token.setTokenType("Bearer"); + token.setAccessToken(ACCESS_TOKEN); + token.setRefreshToken(REFRESH_TOKEN); + token.setExpires(0L); + + // start the test + SessionToken result = sessionService.refresh(token); + assertEquals("new_access_token", result.getAccessToken()); + assertEquals("new_refresh_token", result.getRefreshToken()); + } + } + + @Test + public void testGetUser() throws JsonProcessingException, ParseException { + KeyCloakConfiguration configuration = createConfiguration(); + TokenAuthenticationCache cache = new TokenAuthenticationCache(); + PreAuthenticatedAuthenticationToken authenticationToken = + new PreAuthenticatedAuthenticationToken("user", "", new ArrayList<>()); + KeycloakTokenDetails details = new KeycloakTokenDetails("access_token", REFRESH_TOKEN, 0L); + authenticationToken.setDetails(details); + cache.putCacheEntry(ACCESS_TOKEN, authenticationToken); + try (MockedStatic utilities = Mockito.mockStatic(GeoStoreContext.class)) { + utilities + .when(() -> GeoStoreContext.bean(KeyCloakConfiguration.class)) + .thenReturn(configuration); + utilities.when(() -> GeoStoreContext.bean(KeyCloakHelper.class)).thenReturn(helper); + utilities + .when( + () -> + GeoStoreContext.bean( + CACHE_BEAN_NAME, TokenAuthenticationCache.class)) + .thenReturn(cache); + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + ServletRequestAttributes attributes = new ServletRequestAttributes(request, response); + attributes.setAttribute(PROVIDER_KEY, "keycloak", 0); + attributes.setAttribute(ACCESS_TOKEN_PARAM, ACCESS_TOKEN, 0); + attributes.setAttribute(REFRESH_TOKEN_PARAM, REFRESH_TOKEN, 0); + RequestContextHolder.setRequestAttributes(attributes); + RESTSessionService sessionService = new RESTSessionServiceImpl(); + new KeycloakSessionServiceDelegate(sessionService, null); + + // start the test + User user = sessionService.getUser(ACCESS_TOKEN, false); + assertEquals("user", user.getName()); + } + } + + @Test + public void testLogout() throws JsonProcessingException, ParseException { + KeyCloakConfiguration configuration = createConfiguration(); + PreAuthenticatedAuthenticationToken authenticationToken = + new PreAuthenticatedAuthenticationToken("user", "", new ArrayList<>()); + KeycloakTokenDetails details = new KeycloakTokenDetails("access_token", REFRESH_TOKEN, 0L); + authenticationToken.setDetails(details); + TokenAuthenticationCache cache = new TokenAuthenticationCache(); + cache.putCacheEntry(ACCESS_TOKEN, authenticationToken); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + try (MockedStatic utilities = Mockito.mockStatic(GeoStoreContext.class)) { + utilities + .when(() -> GeoStoreContext.bean(KeyCloakConfiguration.class)) + .thenReturn(configuration); + utilities.when(() -> GeoStoreContext.bean(KeyCloakHelper.class)).thenReturn(helper); + utilities + .when( + () -> + GeoStoreContext.bean( + CACHE_BEAN_NAME, TokenAuthenticationCache.class)) + .thenReturn(cache); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setParameter(ACCESS_TOKEN_PARAM, ACCESS_TOKEN); + // test request.logout(); + request.setUserPrincipal(authenticationToken); + MockHttpServletResponse response = new MockHttpServletResponse(); + ServletRequestAttributes attributes = new ServletRequestAttributes(request, response); + attributes.setAttribute(PROVIDER_KEY, "keycloak", 0); + RequestContextHolder.setRequestAttributes(attributes); + RESTSessionService sessionService = new RESTSessionServiceImpl(); + new KeycloakSessionServiceDelegate(sessionService, null); + + // start the test + sessionService.removeSession(); + assertEquals(response.getStatus(), HttpStatus.OK_200); + assertNull(SecurityContextHolder.getContext().getAuthentication()); + assertNull(request.getUserPrincipal()); + } + } +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakTestSupport.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakTestSupport.java new file mode 100644 index 00000000..160ab870 --- /dev/null +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakTestSupport.java @@ -0,0 +1,70 @@ +package it.geosolutions.geostore.rest.security.keycloak; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import it.geosolutions.geostore.services.rest.security.keycloak.KeyCloakConfiguration; +import org.keycloak.representations.adapters.config.AdapterConfig; + +public abstract class KeycloakTestSupport { + + // identifiers for the auth context + public static final String REALM = "master"; + public static final String CLIENT_ID = "nginx-authn"; + public static final String SECRET = "nginx-secret"; + + // locations for useful resources + public static final String APP_URL = "/app"; + public static final String AUTH_URL = "https://cas.core.maui.mda.ca:8040/auth"; + + // some pre-generated data from keycloak that should work until the year 2037 + public static final String PUBLIC_KEY = + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzkRIC4ow7QqXed+4WICpF5gU2AqXrKT2lPBZOyG6NETv7X" + + "g2FmlGA5KIPxcweexgJCcRY1oFEpulBhVo8zc7WVKX1gc8myXvqvdOMHTUMZ0C4l8Q8ls4fE8B4FiALv/48u" + + "T1YWXKKvsaBPSeh3QTINwtYsAxIrqTjW5wJVaH8L+EazeKep+JSKPvworT9Q8K4u0XURI9MZi983LEx4Wufc" + + "iTPqhD8v6h7Yr+Iy6H/vHHBulwIHZ4MnQBod1aiKuOhM8bsD+FPBVcKCanATVhz6pZoaZXv7j2ZnVSvh6iGi" + + "qP80DknLOyY3IqVST9w8KP1UG0upQ+Zsk8ohCg4Qlm6QIDAQAB"; + public static final String JWT_2018_2037 = + "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJqS2RPZS0zNmhrLVI2R1puQk5tb2JfTFdtMUZJQU" + + "tWVXlKblEzTnNuU21RIn0.eyJqdGkiOiIzNTc5MDQ5MS0yNzI5LTRiNTAtOGIwOC1kYzNhYTM1NDE0ZjgiLC" + + "JleHAiOjIxMjE4MTY5OTYsIm5iZiI6MCwiaWF0IjoxNTE3MDE2OTk2LCJpc3MiOiJodHRwczovL2Nhcy5jb3" + + "JlLm1hdWkubWRhLmNhOjgwNDAvYXV0aC9yZWFsbXMvbWFzdGVyIiwiYXVkIjoibmdpbngtYXV0aG4iLCJzdW" + + "IiOiIxMDM3NzU0OC04OTZhLTQwODUtODY2OC0zNmM4OWQzYzU0OTMiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOi" + + "JuZ2lueC1hdXRobiIsImF1dGhfdGltZSI6MCwic2Vzc2lvbl9zdGF0ZSI6IjY5MWQwOTZiLTkzNjctNDdlZi" + + "04OGEyLTQ1ZjIwZGI4ZjMxNCIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOltdLCJyZWFsbV9hY2Nlc3" + + "MiOnsicm9sZXMiOlsiY3JlYXRlLXJlYWxtIiwiYWRtaW4iLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3" + + "VyY2VfYWNjZXNzIjp7Im1hc3Rlci1yZWFsbSI6eyJyb2xlcyI6WyJ2aWV3LWlkZW50aXR5LXByb3ZpZGVycy" + + "IsInZpZXctcmVhbG0iLCJtYW5hZ2UtaWRlbnRpdHktcHJvdmlkZXJzIiwiaW1wZXJzb25hdGlvbiIsImNyZW" + + "F0ZS1jbGllbnQiLCJtYW5hZ2UtdXNlcnMiLCJxdWVyeS1yZWFsbXMiLCJ2aWV3LWF1dGhvcml6YXRpb24iLC" + + "JxdWVyeS1jbGllbnRzIiwicXVlcnktdXNlcnMiLCJtYW5hZ2UtZXZlbnRzIiwibWFuYWdlLXJlYWxtIiwidm" + + "lldy1ldmVudHMiLCJ2aWV3LXVzZXJzIiwidmlldy1jbGllbnRzIiwibWFuYWdlLWF1dGhvcml6YXRpb24iLC" + + "JtYW5hZ2UtY2xpZW50cyIsInF1ZXJ5LWdyb3VwcyJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYW" + + "Njb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwicHJlZmVycmVkX3VzZX" + + "JuYW1lIjoiYWRtaW4ifQ.deouu-Gqb1MNmfMYARKtkIaM4ztP2tDowG_X0yRxPPSefhQd0rUjLgUl_FS9yiM" + + "wJoZBCIYBEvgqBlQW1836SfDTiPXSUlhQRQElJwoXWCS1UaO8neVa-vt8uGo2vBBsOv8pGVM1dsunA3-BMF7" + + "P-MX9y0ZmMp4T5VOe4iK3K_uP1teTDyGg455WlL18CsVxKKSvOIrd2xF4M2qNny2fgU7Ca1s-7Jo555VB7fs" + + "Uu4nLYvoELb0f_4U4H3Yui_J4m2FplsGoqY7RgM_yTBZ9ZvS-W7ddEjpjyM_D1aFaSByzMYVA6yvnqWIsAVZ" + + "e4sZnjoVZM0sMCQtXtNQaUk7Rbg"; + + protected AdapterConfig adapterConfig; + + protected void setUpAdapter(String serviceUrl) { + AdapterConfig aConfig = new AdapterConfig(); + aConfig.setRealm(REALM); + aConfig.setResource(CLIENT_ID); + aConfig.getCredentials().put("secret", SECRET); + aConfig.setAuthServerUrl(serviceUrl); + this.adapterConfig = aConfig; + } + + protected KeyCloakConfiguration createConfiguration() throws JsonProcessingException { + ObjectMapper om = new ObjectMapper(); + String stringConfig = om.writeValueAsString(adapterConfig); + KeyCloakConfiguration configuration = new KeyCloakConfiguration(); + configuration.setJsonConfig(stringConfig); + configuration.setEnabled(true); + configuration.setAutoCreateUser(true); + configuration.setBeanName("keycloakOAuth2Config"); + configuration.setInternalRedirectUri("../../../"); + return configuration; + } +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakUserDAOTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakUserDAOTest.java new file mode 100644 index 00000000..36f8a0a8 --- /dev/null +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakUserDAOTest.java @@ -0,0 +1,120 @@ +package it.geosolutions.geostore.rest.security.keycloak; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.junit.Assert.*; + +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.googlecode.genericdao.search.Search; +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.services.rest.security.keycloak.KeycloakAdminClientConfiguration; +import it.geosolutions.geostore.services.rest.security.keycloak.KeycloakUserDAO; +import java.util.Arrays; +import java.util.List; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.springframework.http.MediaType; + +public class KeycloakUserDAOTest { + + @Rule public WireMockRule wireMockServer = new WireMockRule(wireMockConfig().dynamicPort()); + private String authService; + private KeycloakUserDAO userDAO; + + @Before + public void before() { + authService = "http://localhost:" + wireMockServer.port(); + KeycloakAdminClientConfiguration configuration = new KeycloakAdminClientConfiguration(); + configuration.setServerUrl(authService); + configuration.setRealm("master"); + configuration.setUsername("username"); + configuration.setPassword("password"); + configuration.setClientId("clientId"); + this.userDAO = new KeycloakUserDAO(configuration); + wireMockServer.stubFor( + WireMock.post(urlEqualTo("/realms/master/protocol/openid-connect/token")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("keycloak_token_response.json"))); + + wireMockServer.stubFor( + WireMock.get(urlEqualTo("/admin/realms/master/users")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("keycloak_users.json"))); + wireMockServer.stubFor( + WireMock.get( + urlEqualTo( + "/admin/realms/master/users/0e72c14e-53d8-4619-a05a-a605dc2102b9/role-mappings/realm/composite")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("keycloak_user_roles.json"))); + wireMockServer.stubFor( + WireMock.get( + urlEqualTo( + "/admin/realms/master/users?username=test&first=-1&max=-1&briefRepresentation=false")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("keycloak_two_users.json"))); + wireMockServer.stubFor( + WireMock.get(urlEqualTo("/admin/realms/master/users?username=admin&exact=true")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("keycloak_admin_user.json"))); + } + + @Test + public void testGetAll() { + List userList = userDAO.findAll(); + assertEquals(3, userList.size()); + for (User u : userList) { + assertNotNull(u.getRole()); + assertEquals(5, u.getGroups().size()); + } + } + + @Test + public void testSearchNameLike() { + Search searchCriteria = new Search(User.class); + searchCriteria.addFilterILike("name", "test"); + List userList = userDAO.search(searchCriteria); + assertEquals(2, userList.size()); + List validNames = Arrays.asList("test-user", "test-user-2"); + for (User u : userList) { + assertNotNull(u.getRole()); + assertEquals(5, u.getGroups().size()); + assertTrue(validNames.contains(u.getName())); + } + } + + @Test + public void testGetOneByName() { + Search searchCriteria = new Search(User.class); + searchCriteria.addFilterEqual("name", "admin"); + List userList = userDAO.search(searchCriteria); + assertEquals(1, userList.size()); + for (User u : userList) { + assertNotNull(u.getRole()); + assertEquals(5, u.getGroups().size()); + assertEquals("admin", u.getName()); + } + } +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakUserGroupDAOTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakUserGroupDAOTest.java new file mode 100644 index 00000000..63404b92 --- /dev/null +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/KeycloakUserGroupDAOTest.java @@ -0,0 +1,114 @@ +package it.geosolutions.geostore.rest.security.keycloak; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.googlecode.genericdao.search.Search; +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.services.rest.security.keycloak.KeycloakAdminClientConfiguration; +import it.geosolutions.geostore.services.rest.security.keycloak.KeycloakUserGroupDAO; +import java.util.Arrays; +import java.util.List; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.springframework.http.MediaType; + +public class KeycloakUserGroupDAOTest { + + @Rule public WireMockRule wireMockServer = new WireMockRule(wireMockConfig().dynamicPort()); + private String authService; + private KeycloakUserGroupDAO userGroupDAO; + + @Before + public void before() { + authService = "http://localhost:" + wireMockServer.port(); + KeycloakAdminClientConfiguration configuration = new KeycloakAdminClientConfiguration(); + configuration.setServerUrl(authService); + configuration.setRealm("master"); + configuration.setUsername("username"); + configuration.setPassword("password"); + configuration.setClientId("clientId"); + this.userGroupDAO = new KeycloakUserGroupDAO(configuration); + wireMockServer.stubFor( + WireMock.post(urlEqualTo("/realms/master/protocol/openid-connect/token")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("keycloak_token_response.json"))); + + wireMockServer.stubFor( + WireMock.get(urlEqualTo("/admin/realms/master/roles")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("keycloak_roles.json"))); + wireMockServer.stubFor( + WireMock.get( + urlEqualTo( + "/admin/realms/master/users/0e72c14e-53d8-4619-a05a-a605dc2102b9/role-mappings/realm/composite")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("keycloak_user_roles.json"))); + wireMockServer.stubFor( + WireMock.get(urlEqualTo("/admin/realms/master/roles?search=create")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("keycloak_two_roles.json"))); + wireMockServer.stubFor( + WireMock.get(urlEqualTo("/admin/realms/master/roles/test-create-group")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("keycloak_one_role.json"))); + } + + @Test + public void testGetAll() { + List groupList = userGroupDAO.findAll(); + assertEquals(5, groupList.size()); + } + + @Test + public void testSearchNameLike() { + Search searchCriteria = new Search(User.class); + searchCriteria.addFilterILike("groupName", "create"); + List groups = userGroupDAO.search(searchCriteria); + assertEquals(2, groups.size()); + List valid = Arrays.asList("test-create-group", "create-realm"); + for (UserGroup ug : groups) assertTrue(valid.contains(ug.getGroupName())); + } + + @Test + public void testGetOneByName() { + Search searchCriteria = new Search(User.class); + searchCriteria.addFilterEqual("groupName", "test-create-group"); + List groups = userGroupDAO.search(searchCriteria); + UserGroup group = groups.get(0); + assertEquals("test-create-group", group.getGroupName()); + } + + @Test + public void testGetOneByName2() { + UserGroup group = userGroupDAO.findByName("test-create-group"); + assertEquals("test-create-group", group.getGroupName()); + } +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/MockUserGroupService.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/MockUserGroupService.java new file mode 100644 index 00000000..b43ec860 --- /dev/null +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/MockUserGroupService.java @@ -0,0 +1,131 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.rest.security.keycloak; + +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.UserGroupAttribute; +import it.geosolutions.geostore.services.UserGroupService; +import it.geosolutions.geostore.services.dto.ShortResource; +import it.geosolutions.geostore.services.exception.BadRequestServiceEx; +import it.geosolutions.geostore.services.exception.NotFoundServiceEx; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +/** MockUserGroupService for testing purpose with KeycloakFilter */ +class MockUserGroupService implements UserGroupService { + + private final Map groups = new ConcurrentHashMap<>(); + private final AtomicLong atomicLong = new AtomicLong(); + + @Override + public long insert(UserGroup userGroup) throws BadRequestServiceEx { + Long id = atomicLong.incrementAndGet(); + userGroup.setId(id); + groups.put(userGroup.getGroupName(), userGroup); + return id; + } + + @Override + public boolean delete(long id) throws NotFoundServiceEx, BadRequestServiceEx { + return false; + } + + @Override + public void assignUserGroup(long userId, long groupId) throws NotFoundServiceEx {} + + @Override + public void deassignUserGroup(long userId, long groupId) throws NotFoundServiceEx {} + + @Override + public List getAll(Integer page, Integer entries) throws BadRequestServiceEx { + return null; + } + + @Override + public List getAllAllowed( + User user, Integer page, Integer entries, String nameLike, boolean all) + throws BadRequestServiceEx { + return null; + } + + @Override + public UserGroup get(long id) throws BadRequestServiceEx { + return groups.values().stream().filter(g -> g.getId().equals(id)).findAny().get(); + } + + @Override + public List updateSecurityRules( + Long groupId, List resourcesToSet, boolean canRead, boolean canWrite) + throws NotFoundServiceEx, BadRequestServiceEx { + return null; + } + + @Override + public boolean insertSpecialUsersGroups() { + return false; + } + + @Override + public boolean removeSpecialUsersGroups() { + return false; + } + + @Override + public UserGroup get(String name) { + return groups.get(name); + } + + @Override + public long getCount(User authUser, String nameLike) throws BadRequestServiceEx { + return 0; + } + + @Override + public long getCount(User authUser, String nameLike, boolean all) throws BadRequestServiceEx { + return 0; + } + + @Override + public void updateAttributes(long id, List attributes) + throws NotFoundServiceEx {} + + @Override + public long update(UserGroup group) throws NotFoundServiceEx, BadRequestServiceEx { + return 0; + } + + @Override + public Collection findByAttribute( + String name, List values, boolean ignoreCase) { + return null; + } +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/MockUserService.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/MockUserService.java new file mode 100644 index 00000000..c0c04491 --- /dev/null +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/keycloak/MockUserService.java @@ -0,0 +1,111 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.rest.security.keycloak; + +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.UserAttribute; +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.services.UserService; +import it.geosolutions.geostore.services.exception.BadRequestServiceEx; +import it.geosolutions.geostore.services.exception.NotFoundServiceEx; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +/** MockUserService for testing purpose with KeycloakFilter */ +class MockUserService implements UserService { + + private final Map users = new ConcurrentHashMap<>(); + private final AtomicLong atomicLong = new AtomicLong(); + + @Override + public long insert(User user) throws BadRequestServiceEx, NotFoundServiceEx { + Long id = atomicLong.incrementAndGet(); + user.setId(id); + users.put(user.getName(), user); + return id; + } + + @Override + public long update(User user) throws NotFoundServiceEx, BadRequestServiceEx { + return 0; + } + + @Override + public boolean delete(long id) { + return false; + } + + @Override + public User get(long id) { + return users.values().stream().filter(u -> u.getId().equals(id)).findAny().get(); + } + + @Override + public User get(String name) throws NotFoundServiceEx { + return users.get(name); + } + + @Override + public List getAll(Integer page, Integer entries) throws BadRequestServiceEx { + return null; + } + + @Override + public List getAll( + Integer page, Integer entries, String nameLike, boolean includeAttributes) + throws BadRequestServiceEx { + return null; + } + + @Override + public long getCount(String nameLike) { + return 0; + } + + @Override + public void updateAttributes(long id, List attributes) + throws NotFoundServiceEx {} + + @Override + public boolean insertSpecialUsers() { + return false; + } + + @Override + public Collection getByAttribute(UserAttribute attribute) { + return null; + } + + @Override + public Collection getByGroup(UserGroup group) { + return null; + } +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/oauth2/DiscoveryClientTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/oauth2/DiscoveryClientTest.java new file mode 100644 index 00000000..9f8f6e86 --- /dev/null +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/oauth2/DiscoveryClientTest.java @@ -0,0 +1,58 @@ +package it.geosolutions.geostore.rest.security.oauth2; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.junit.Assert.assertEquals; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.common.ConsoleNotifier; +import it.geosolutions.geostore.services.rest.security.oauth2.DiscoveryClient; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Configuration; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.http.MediaType; + +public class DiscoveryClientTest { + + private static WireMockServer openIdService; + private static String authService; + + @BeforeClass + public static void beforeClass() throws Exception { + openIdService = + new WireMockServer( + wireMockConfig() + .dynamicPort() + // uncomment the following to get wiremock logging + .notifier(new ConsoleNotifier(true))); + openIdService.start(); + + openIdService.stubFor( + any((urlEqualTo("/.well-known/openid-configuration"))) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("discovery_result.json"))); + authService = "http://localhost:" + openIdService.port(); + } + + @Test + public void testDiscovery() { + DiscoveryClient discoveryClient = + new DiscoveryClient(authService + "/.well-known/openid-configuration"); + OAuth2Configuration configuration = new OAuth2Configuration(); + discoveryClient.autofill(configuration); + assertEquals("https://oauth2.googleapis.com/token", configuration.getAccessTokenUri()); + assertEquals( + "https://accounts.google.com/o/oauth2/v2/auth", + configuration.getAuthorizationUri()); + assertEquals("https://oauth2.googleapis.com/revoke", configuration.getRevokeEndpoint()); + assertEquals( + "https://openidconnect.googleapis.com/v1/userinfo", + configuration.getCheckTokenEndpointUrl()); + assertEquals("https://www.googleapis.com/oauth2/v3/certs", configuration.getIdTokenUri()); + assertEquals("openid,email,profile", configuration.getScopes()); + } +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/oauth2/OAuth2LoginTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/oauth2/OAuth2LoginTest.java new file mode 100644 index 00000000..580f43a2 --- /dev/null +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/oauth2/OAuth2LoginTest.java @@ -0,0 +1,107 @@ +package it.geosolutions.geostore.rest.security.oauth2; + +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.*; +import static org.junit.Assert.assertEquals; + +import it.geosolutions.geostore.services.rest.IdPLoginRest; +import it.geosolutions.geostore.services.rest.security.IdPConfiguration; +import it.geosolutions.geostore.services.rest.security.oauth2.*; +import java.util.List; +import java.util.stream.Collectors; +import javax.ws.rs.core.Response; +import org.junit.After; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +public class OAuth2LoginTest { + + private final IdPLoginRest idPLoginRest = new IdPLoginRestImpl(); + + @Test + public void testLogin() { + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + ServletRequestAttributes attributes = new ServletRequestAttributes(request, response); + RequestContextHolder.setRequestAttributes(attributes); + OAuth2Configuration configuration = new OAuth2Configuration(); + configuration.setScopes("openid"); + configuration.setClientId("mockClientId"); + configuration.setAuthorizationUri("http://localhost:8080/authorization"); + SetConfOAuthLoginService setConfiguration = new SetConfOAuthLoginService(idPLoginRest); + setConfiguration.setConfiguration(configuration); + idPLoginRest.login("mock"); + assertEquals(302, response.getStatus()); + assertEquals( + "http://localhost:8080/authorization?response_type=code&client_id=mockClientId&scope=openid&redirect_uri=null", + response.getRedirectedUrl()); + } + + @Test + public void testCallback() { + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + ServletRequestAttributes attributes = new ServletRequestAttributes(request, response); + RequestContextHolder.setRequestAttributes(attributes); + attributes.setAttribute(REFRESH_TOKEN_PARAM, "mockRefreshToken", 0); + attributes.setAttribute(ACCESS_TOKEN_PARAM, "mockAccessToken", 0); + OAuth2Configuration configuration = new OAuth2Configuration(); + configuration.setInternalRedirectUri("http://localhost:8080/geostore/redirect"); + SetConfOAuthLoginService setConfiguration = new SetConfOAuthLoginService(idPLoginRest); + setConfiguration.setConfiguration(configuration); + setConfiguration.setTokenStorage(new InMemoryTokenStorage()); + Response result = idPLoginRest.callback("mock"); + assertEquals(302, result.getStatus()); + List cookies = result.getMetadata().get("Set-Cookie"); + List tokenCookies = + cookies.stream() + .filter( + c -> + ((String) c).contains(AUTH_PROVIDER) + || ((String) c).contains(TOKENS_KEY)) + .collect(Collectors.toList()); + assertEquals(2, tokenCookies.size()); + assertEquals("http://localhost:8080/geostore/redirect", result.getHeaderString("Location")); + } + + @After + public void afterTest() { + RequestContextHolder.resetRequestAttributes(); + } + + private class SetConfOAuthLoginService extends Oauth2LoginService { + + private OAuth2Configuration configuration; + + private TokenStorage tokenStorage; + + public SetConfOAuthLoginService(IdPLoginRest loginRest) { + loginRest.registerService("mock", this); + } + + public void setConfiguration(OAuth2Configuration configuration) { + this.configuration = configuration; + } + + @Override + protected OAuth2Configuration oauth2Configuration(String provider) { + return configuration; + } + + @Override + protected IdPConfiguration configuration(String provider) { + return configuration; + } + + @Override + protected TokenStorage tokenStorage() { + return tokenStorage; + } + + public void setTokenStorage(TokenStorage tokenStorage) { + this.tokenStorage = tokenStorage; + } + } +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/oauth2/OAuth2SessionServiceTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/oauth2/OAuth2SessionServiceTest.java new file mode 100644 index 00000000..d9a37609 --- /dev/null +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/oauth2/OAuth2SessionServiceTest.java @@ -0,0 +1,130 @@ +/* ==================================================================== + * + * Copyright (C) 2022 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.rest.security.oauth2; + +import static it.geosolutions.geostore.services.rest.SessionServiceDelegate.PROVIDER_KEY; +import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.ACCESS_TOKEN_PARAM; +import static org.junit.Assert.*; +import static org.mockito.Mockito.when; + +import it.geosolutions.geostore.services.rest.RESTSessionService; +import it.geosolutions.geostore.services.rest.impl.RESTSessionServiceImpl; +import it.geosolutions.geostore.services.rest.security.TokenAuthenticationCache; +import it.geosolutions.geostore.services.rest.security.oauth2.GeoStoreOAuthRestTemplate; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Configuration; +import it.geosolutions.geostore.services.rest.security.oauth2.TokenDetails; +import it.geosolutions.geostore.services.rest.security.oauth2.google.GoogleOAuth2Configuration; +import it.geosolutions.geostore.services.rest.security.oauth2.google.GoogleSessionServiceDelegate; +import it.geosolutions.geostore.services.rest.security.oauth2.google.OAuthGoogleSecurityConfiguration; +import it.geosolutions.geostore.services.rest.utils.GeoStoreContext; +import java.util.ArrayList; +import java.util.HashMap; +import org.junit.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import wiremock.org.eclipse.jetty.http.HttpStatus; + +public class OAuth2SessionServiceTest { + + private static final String ID_TOKEN = "test.id.token"; + + private static final String ACCESS_TOKEN = "access_token"; + + @Test + public void testLogout() { + GoogleOAuth2Configuration configuration = new GoogleOAuth2Configuration(); + configuration.setEnabled(true); + configuration.setIdTokenUri("https://www.googleapis.com/oauth2/v3/certs"); + configuration.setRevokeEndpoint("http://google.foo"); + PreAuthenticatedAuthenticationToken authenticationToken = + new PreAuthenticatedAuthenticationToken("user", "", new ArrayList<>()); + OAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(ACCESS_TOKEN); + TokenDetails details = Mockito.mock(TokenDetails.class); + when(details.getProvider()).thenReturn("google"); + when(details.getAccessToken()).thenReturn(accessToken); + when(details.getIdToken()).thenReturn(ID_TOKEN); + authenticationToken.setDetails(details); + TokenAuthenticationCache cache = new TokenAuthenticationCache(); + cache.putCacheEntry(ACCESS_TOKEN, authenticationToken); + OAuth2RestTemplate restTemplate = + new GeoStoreOAuthRestTemplate( + new OAuthGoogleSecurityConfiguration().resourceDetails(), + new DefaultOAuth2ClientContext(), + configuration); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + HashMap configurations = new HashMap<>(); + configurations.put("googleOAuth2Config", configuration); + try (MockedStatic utilities = Mockito.mockStatic(GeoStoreContext.class)) { + utilities + .when(() -> GeoStoreContext.beans(OAuth2Configuration.class)) + .thenReturn(configurations); + utilities + .when( + () -> + GeoStoreContext.bean( + "googleOAuth2Config", OAuth2Configuration.class)) + .thenReturn(configuration); + utilities + .when(() -> GeoStoreContext.bean("oAuth2Cache", TokenAuthenticationCache.class)) + .thenReturn(cache); + utilities + .when( + () -> + GeoStoreContext.bean( + "googleOpenIdRestTemplate", OAuth2RestTemplate.class)) + .thenReturn(restTemplate); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setParameter(ACCESS_TOKEN_PARAM, ACCESS_TOKEN); + // test request.logout(); + request.setUserPrincipal(authenticationToken); + MockHttpServletResponse response = new MockHttpServletResponse(); + ServletRequestAttributes attributes = new ServletRequestAttributes(request, response); + attributes.setAttribute(PROVIDER_KEY, "google", 0); + RequestContextHolder.setRequestAttributes(attributes); + RESTSessionService sessionService = new RESTSessionServiceImpl(); + new GoogleSessionServiceDelegate(sessionService, null); + + // start the test + sessionService.removeSession(); + assertEquals(response.getStatus(), HttpStatus.OK_200); + // if the end-session URI is null, the session won't be invalidated + assertNull(SecurityContextHolder.getContext().getAuthentication()); + assertNull(request.getUserPrincipal()); + } + } +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/oauth2/OpenIdIntegrationTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/oauth2/OpenIdIntegrationTest.java new file mode 100644 index 00000000..1bf07ea9 --- /dev/null +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/oauth2/OpenIdIntegrationTest.java @@ -0,0 +1,204 @@ +package it.geosolutions.geostore.rest.security.oauth2; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.junit.Assert.assertEquals; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.common.ConsoleNotifier; +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.enums.Role; +import it.geosolutions.geostore.services.rest.security.oauth2.GeoStoreOAuthRestTemplate; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Configuration; +import it.geosolutions.geostore.services.rest.security.oauth2.google.OAuthGoogleSecurityConfiguration; +import it.geosolutions.geostore.services.rest.security.oauth2.google.OpenIdFilter; +import java.io.IOException; +import javax.servlet.ServletException; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockFilterChain; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; +import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +public class OpenIdIntegrationTest { + + private static final String CLIENT_ID = "kbyuFDidLLm280LIwVFiazOqjO3ty8KH"; + private static final String CLIENT_SECRET = + "60Op4HFM0I8ajz0WdiStAbziZ-VFQttXuxixHHs2R7r7-CW8GR79l-mmLqMhc-Sa"; + private static final String CODE = "R-2CqM7H1agwc7Cx"; + private static WireMockServer openIdService; + private String authService; + private OpenIdFilter filter; + + private OAuth2Configuration configuration; + + @BeforeClass + public static void beforeClass() { + openIdService = + new WireMockServer( + wireMockConfig() + .dynamicPort() + // uncomment the following to get wiremock logging + .notifier(new ConsoleNotifier(true))); + openIdService.start(); + + openIdService.stubFor( + WireMock.get(urlEqualTo("/certs")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("jkws.json"))); + + openIdService.stubFor( + WireMock.post(urlPathEqualTo("/token")) + .withRequestBody(containing("grant_type=authorization_code")) + .withRequestBody(containing("client_id=" + CLIENT_ID)) + .withRequestBody(containing("code=" + CODE)) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("token_response.json"))); + openIdService.stubFor( + any(urlPathEqualTo("/userinfo")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile( + "userinfo.json"))); // disallow query parameters + } + + @Before + public void before() { + // prepare mock server base path + authService = "http://localhost:" + openIdService.port(); + OAuth2Configuration configuration = new OAuth2Configuration(); + configuration.setClientId(CLIENT_ID); + configuration.setClientSecret(CLIENT_SECRET); + configuration.setRevokeEndpoint(authService + "/revoke"); + configuration.setAccessTokenUri(authService + "/token"); + configuration.setAuthorizationUri(authService + "/authorize"); + configuration.setCheckTokenEndpointUrl(authService + "/userinfo"); + configuration.setEnabled(true); + configuration.setAutoCreateUser(true); + configuration.setIdTokenUri(authService + "/certs"); + configuration.setBeanName("googleOAuth2Config"); + configuration.setEnableRedirectEntryPoint(true); + configuration.setRedirectUri("../../../geostore/rest/users/user/details"); + configuration.setScopes("openId,email"); + this.configuration = configuration; + OAuthGoogleSecurityConfiguration securityConfiguration = + new OAuthGoogleSecurityConfiguration() { + + @Override + protected GeoStoreOAuthRestTemplate restTemplate() { + return new GeoStoreOAuthRestTemplate( + resourceDetails(), + new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest()), + configuration()); + } + + @Override + public OAuth2Configuration configuration() { + return configuration; + } + }; + GeoStoreOAuthRestTemplate restTemplate = securityConfiguration.oauth2RestTemplate(); + OpenIdFilter filter = + new OpenIdFilter( + securityConfiguration.googleTokenServices(), + restTemplate, + configuration, + securityConfiguration.oAuth2Cache()); + this.filter = filter; + } + + @After + public void afterTest() { + SecurityContextHolder.clearContext(); + RequestContextHolder.resetRequestAttributes(); + } + + @Test + public void testRedirect() throws IOException, ServletException { + MockHttpServletRequest request = createRequest("google/login"); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain chain = new MockFilterChain(); + filter.doFilter(request, response, chain); + assertEquals(302, response.getStatus()); + assertEquals(response.getRedirectedUrl(), configuration.buildLoginUri()); + } + + @Test + public void testAuthentication() throws IOException, ServletException { + MockHttpServletRequest request = createRequest("google/login"); + request.setParameter("authorization_code", CODE); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain chain = new MockFilterChain(); + filter.restTemplate + .getOAuth2ClientContext() + .getAccessTokenRequest() + .setAuthorizationCode(CODE); + filter.doFilter(request, response, chain); + assertEquals(200, response.getStatus()); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + User user = (User) authentication.getPrincipal(); + assertEquals("ritter@erdukunde.de", user.getName()); + assertEquals(Role.USER, user.getRole()); + } + + @Test + public void testGroupsAndRolesFromToken() throws IOException, ServletException { + configuration.setGroupsClaim("hd"); + MockHttpServletRequest request = createRequest("google/login"); + request.setParameter("authorization_code", CODE); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain chain = new MockFilterChain(); + filter.restTemplate + .getOAuth2ClientContext() + .getAccessTokenRequest() + .setAuthorizationCode(CODE); + filter.doFilter(request, response, chain); + assertEquals(200, response.getStatus()); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + User user = (User) authentication.getPrincipal(); + assertEquals("ritter@erdukunde.de", user.getName()); + assertEquals(Role.USER, user.getRole()); + UserGroup group = user.getGroups().stream().findAny().get(); + assertEquals("geosolutionsgroup.com", group.getGroupName()); + } + + private MockHttpServletRequest createRequest(String path) { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(8080); + request.setContextPath("/geostore"); + request.setRequestURI("/geostore/" + path); + // request.setRequestURL(ResponseUtils.appendPath("http://localhost:8080/geoserver", path ) + // ); + request.setRemoteAddr("127.0.0.1"); + request.setServletPath("/geostore"); + request.setPathInfo(path); + request.addHeader("Host", "localhost:8080"); + ServletRequestAttributes attributes = new ServletRequestAttributes(request); + RequestContextHolder.setRequestAttributes(attributes); + return request; + } +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/oauth2/openid_connect/OpenIdConnectIntegrationTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/oauth2/openid_connect/OpenIdConnectIntegrationTest.java new file mode 100644 index 00000000..7e98249f --- /dev/null +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/security/oauth2/openid_connect/OpenIdConnectIntegrationTest.java @@ -0,0 +1,232 @@ +/* ==================================================================== + * + * Copyright (C) 2024 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * ==================================================================== + * + * This software consists of voluntary contributions made by developers + * of GeoSolutions. For more information on GeoSolutions, please see + * . + * + */ +package it.geosolutions.geostore.rest.security.oauth2.openid_connect; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.junit.Assert.assertEquals; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.common.ConsoleNotifier; +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.enums.Role; +import it.geosolutions.geostore.services.rest.security.oauth2.GeoStoreOAuthRestTemplate; +import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Configuration; +import it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.OpenIdConnectConfiguration; +import it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.OpenIdConnectFilter; +import it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.OpenIdConnectSecurityConfiguration; +import java.io.IOException; +import javax.servlet.ServletException; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockFilterChain; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; +import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +public class OpenIdConnectIntegrationTest { + + private static final String CLIENT_ID = "kbyuFDidLLm280LIwVFiazOqjO3ty8KH"; + private static final String CLIENT_SECRET = + "60Op4HFM0I8ajz0WdiStAbziZ-VFQttXuxixHHs2R7r7-CW8GR79l-mmLqMhc-Sa"; + private static final String CODE = "R-2CqM7H1agwc7Cx"; + private static WireMockServer openIdConnectService; + private String authService; + private OpenIdConnectFilter filter; + private OpenIdConnectConfiguration configuration; + + @BeforeClass + public static void beforeClass() { + openIdConnectService = + new WireMockServer( + wireMockConfig() + .dynamicPort() + // uncomment the following to get wiremock logging + .notifier(new ConsoleNotifier(true))); + openIdConnectService.start(); + + openIdConnectService.stubFor( + WireMock.get(urlEqualTo("/certs")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("jkws.json"))); + + openIdConnectService.stubFor( + WireMock.post(urlPathEqualTo("/token")) + .withRequestBody(containing("grant_type=authorization_code")) + .withRequestBody(containing("client_id=" + CLIENT_ID)) + .withRequestBody(containing("code=" + CODE)) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile("token_response.json"))); + openIdConnectService.stubFor( + any(urlPathEqualTo("/userinfo")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader( + "Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBodyFile( + "userinfo.json"))); // disallow query parameters + } + + @Before + public void before() { + // prepare mock server base path + authService = "http://localhost:" + openIdConnectService.port(); + OpenIdConnectConfiguration configuration = new OpenIdConnectConfiguration(); + configuration.setClientId(CLIENT_ID); + configuration.setClientSecret(CLIENT_SECRET); + configuration.setRevokeEndpoint(authService + "/revoke"); + configuration.setAccessTokenUri(authService + "/token"); + configuration.setAuthorizationUri(authService + "/authorize"); + configuration.setCheckTokenEndpointUrl(authService + "/userinfo"); + configuration.setEnabled(true); + configuration.setAutoCreateUser(true); + configuration.setIdTokenUri(authService + "/certs"); + configuration.setBeanName("oidcOAuth2Config"); + configuration.setEnableRedirectEntryPoint(true); + configuration.setRedirectUri("../../../geostore/rest/users/user/details"); + configuration.setScopes("openId,email"); + configuration.setSendClientSecret(true); + this.configuration = configuration; + OpenIdConnectSecurityConfiguration securityConfiguration = + new OpenIdConnectSecurityConfiguration() { + + @Override + protected GeoStoreOAuthRestTemplate restTemplate() { + return new GeoStoreOAuthRestTemplate( + resourceDetails(), + new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest()), + configuration()); + } + + @Override + public OAuth2Configuration configuration() { + return configuration; + } + }; + GeoStoreOAuthRestTemplate restTemplate = securityConfiguration.oauth2RestTemplate(); + this.filter = + new OpenIdConnectFilter( + securityConfiguration.oidcTokenServices(), + restTemplate, + configuration, + securityConfiguration.oidcCache(), + securityConfiguration.openIdConnectBearerTokenValidator()); + } + + @After + public void afterTest() { + SecurityContextHolder.clearContext(); + RequestContextHolder.resetRequestAttributes(); + } + + @Test + public void testRedirect() throws IOException, ServletException { + MockHttpServletRequest request = createRequest("oidc/login"); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain chain = new MockFilterChain(); + filter.doFilter(request, response, chain); + assertEquals(302, response.getStatus()); + assertEquals(response.getRedirectedUrl(), configuration.buildLoginUri()); + } + + @Test + public void testAuthentication() throws IOException, ServletException { + MockHttpServletRequest request = createRequest("oidc/login"); + request.setParameter("authorization_code", CODE); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain chain = new MockFilterChain(); + filter.restTemplate + .getOAuth2ClientContext() + .getAccessTokenRequest() + .setAuthorizationCode(CODE); + filter.doFilter(request, response, chain); + assertEquals(200, response.getStatus()); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + User user = (User) authentication.getPrincipal(); + assertEquals("ritter@erdukunde.de", user.getName()); + assertEquals(Role.USER, user.getRole()); + } + + @Test + public void testGroupsAndRolesFromToken() throws IOException, ServletException { + configuration.setGroupsClaim("hd"); + MockHttpServletRequest request = createRequest("oidc/login"); + request.setParameter("authorization_code", CODE); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain chain = new MockFilterChain(); + filter.restTemplate + .getOAuth2ClientContext() + .getAccessTokenRequest() + .setAuthorizationCode(CODE); + filter.doFilter(request, response, chain); + assertEquals(200, response.getStatus()); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + User user = (User) authentication.getPrincipal(); + assertEquals("ritter@erdukunde.de", user.getName()); + assertEquals(Role.USER, user.getRole()); + UserGroup group = user.getGroups().stream().findAny().get(); + assertEquals("geosolutionsgroup.com", group.getGroupName()); + } + + private MockHttpServletRequest createRequest(String path) { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(8080); + request.setContextPath("/geostore"); + request.setRequestURI("/geostore/" + path); + // request.setRequestURL(ResponseUtils.appendPath("http://localhost:8080/geoserver", path ) + // ); + request.setRemoteAddr("127.0.0.1"); + request.setServletPath("/geostore"); + request.setPathInfo(path); + request.addHeader("Host", "localhost:8080"); + ServletRequestAttributes attributes = new ServletRequestAttributes(request); + RequestContextHolder.setRequestAttributes(attributes); + return request; + } +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTResourceServiceImplTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTResourceServiceImplTest.java index c4b45e2f..3258aad1 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTResourceServiceImplTest.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTResourceServiceImplTest.java @@ -19,14 +19,7 @@ */ package it.geosolutions.geostore.rest.service.impl; -import javax.ws.rs.core.SecurityContext; - -import org.junit.Before; -import org.junit.Test; - -import it.geosolutions.geostore.core.model.Attribute; -import it.geosolutions.geostore.core.model.Resource; -import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.core.model.*; import it.geosolutions.geostore.core.model.enums.DataType; import it.geosolutions.geostore.core.model.enums.Role; import it.geosolutions.geostore.services.ServiceTestBase; @@ -35,34 +28,59 @@ import it.geosolutions.geostore.services.rest.impl.RESTResourceServiceImpl; import it.geosolutions.geostore.services.rest.model.RESTAttribute; import it.geosolutions.geostore.services.rest.utils.MockSecurityContext; +import java.util.ArrayList; +import java.util.List; +import javax.ws.rs.core.SecurityContext; +import org.junit.Before; +import org.junit.Test; /** * Class ResourceServiceImplTest. * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class RESTResourceServiceImplTest extends ServiceTestBase { - RESTResourceServiceImpl restService; - long adminID; + RESTResourceServiceImpl restService; + long adminID; - @Before + @Before public void setUp() throws BadRequestServiceEx, NotFoundServiceEx { - restService = new RESTResourceServiceImpl(); - restService.setResourceService(resourceService); + restService = new RESTResourceServiceImpl(); + restService.setResourceService(resourceService); } @Test - public void testUpdateResourceAttribute() throws Exception { - // create a semple resource - long resourceId = createResource("name1", "description1", "MAP"); + public void testUpdateResource_editorUpdate() throws Exception { + // insert fake user for security context + long u0ID = createUser("u0", Role.USER, "p0"); + User user = new User(); + user.setId(u0ID); + user.setName("u0"); + + List rules = new ArrayList<>(); + + SecurityRule rule = new SecurityRule(); + rule.setUser(user); + rule.setCanRead(true); + rule.setCanWrite(true); + rules.add(rule); + + long groupId = createGroup("group1"); + UserGroup group = new UserGroup(); + group.setId(groupId); - // insert fake admin user for security context - long adminID = createUser("admin", Role.ADMIN, "admin"); + rule = new SecurityRule(); + rule.setCanRead(true); + rule.setCanWrite(true); + rule.setGroup(group); + rules.add(rule); + + // create a sample resource + long resourceId = createResource("name1", "description1", "MAP", rules); // create security context for the request - SecurityContext sc = new MockSecurityContext(userService.get(adminID)); + SecurityContext sc = new MockSecurityContext(userService.get(u0ID)); // prepare request content RESTAttribute attribute = new RESTAttribute(); @@ -74,13 +92,45 @@ public void testUpdateResourceAttribute() throws Exception { // attempt to update the attribute from rest service restService.updateAttribute(sc, resourceId, attribute); - // retrieve the modified resource - Resource res = resourceService.get(resourceId); + Resource sr = restService.get(sc, resourceId, false); + + // verify the attribute has been changed + Attribute a = sr.getAttribute().get(0); + assertEquals(a.getName(), NAME); + assertEquals(a.getValue(), VALUE); + assertEquals(a.getType(), DataType.STRING); + + assertEquals(sr.getCreator(), "u0"); + assertEquals(sr.getEditor(), "u0"); + + // Update rule as "user1" + // insert fake user for security context + long u1ID = createUser("u1", Role.USER, "p1", groupId); + user = new User(); + user.setId(u1ID); + user.setName("u1"); + + sc = new MockSecurityContext(userService.get(u1ID)); + + // prepare request content + attribute = new RESTAttribute(); + NAME = "NAME"; + VALUE = "VALUE1"; + attribute.setName(NAME); + attribute.setValue(VALUE); + + // attempt to update the attribute from rest service + restService.updateAttribute(sc, resourceId, attribute); + + sr = restService.get(sc, resourceId, false); // verify the attribute has been changed - Attribute a = res.getAttribute().get(0); + a = sr.getAttribute().get(0); assertEquals(a.getName(), NAME); assertEquals(a.getValue(), VALUE); assertEquals(a.getType(), DataType.STRING); + + assertEquals(sr.getCreator(), "u0"); + assertEquals(sr.getEditor(), "u1"); } } diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTServiceImplTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTServiceImplTest.java index eaa03473..c226cc46 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTServiceImplTest.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTServiceImplTest.java @@ -1,36 +1,26 @@ /* * Copyright (C) 2018 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.rest.service.impl; +import static org.junit.Assert.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import java.security.Principal; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import org.junit.Before; -import org.junit.Test; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import it.geosolutions.geostore.core.model.SecurityRule; import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserAttribute; @@ -42,256 +32,266 @@ import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; import it.geosolutions.geostore.services.rest.impl.RESTServiceImpl; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; /** - * This test checks that the resourceAccessRead and resourceAccessWrite - * methods properly check and return expected canEdit and canWrite for a mock SecurityService. - * - * @author Lorenzo Natali, GeoSolutions S.a.s. + * This test checks that the resourceAccessRead and resourceAccessWrite methods properly check and + * return expected canEdit and canWrite for a mock SecurityService. * + * @author Lorenzo Natali, GeoSolutions S.a.s. */ public class RESTServiceImplTest { - TESTRESTServiceImpl restService; - TestSecurityService securityService; - TestUserService userService; - User user; - UserGroup group; - UserGroup everyone; - UserGroup extGroup; - private class TestUserService implements UserService { - - @Override - public long insert(User user) throws BadRequestServiceEx, NotFoundServiceEx { - // TODO Auto-generated method stub - return 0; - } - - @Override - public long update(User user) throws NotFoundServiceEx, BadRequestServiceEx { - // TODO Auto-generated method stub - return 0; - } - - @Override - public boolean delete(long id) { - // TODO Auto-generated method stub - return false; - } - - @Override - public User get(long id) { - // TODO Auto-generated method stub - return null; - } - - @Override - public User get(String name) throws NotFoundServiceEx { - throw new NotFoundServiceEx(name); - } + TESTRESTServiceImpl restService; + TestSecurityService securityService; + TestUserService userService; + User user; + UserGroup group; + UserGroup everyone; + UserGroup extGroup; - @Override - public List getAll(Integer page, Integer entries) throws BadRequestServiceEx { - // TODO Auto-generated method stub - return null; - } - - @Override - public List getAll(Integer page, Integer entries, String nameLike, - boolean includeAttributes) throws BadRequestServiceEx { - // TODO Auto-generated method stub - return null; - } - - @Override - public long getCount(String nameLike) { - // TODO Auto-generated method stub - return 0; - } - - @Override - public void updateAttributes(long id, List attributes) - throws NotFoundServiceEx { - // TODO Auto-generated method stub - - } - - @Override - public boolean insertSpecialUsers() { - // TODO Auto-generated method stub - return false; - } - - @Override - public Collection getByAttribute(UserAttribute attribute) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection getByGroup(UserGroup group) { - // TODO Auto-generated method stub - return null; - } - - } - private class TestSecurityService implements SecurityService { - private List userSecurityRules = null; - private List groupSecurityRules = null; - public void setUserSecurityRules(List userSecurityRules) { - this.userSecurityRules = userSecurityRules; - } - - public void setGroupSecurityRules(List groupSecurityRules) { - this.groupSecurityRules = groupSecurityRules; - } - - @Override - public List getUserSecurityRule(String userName, long entityId) { - return userSecurityRules; - } - - @Override - public List getGroupSecurityRule(List groupNames, long entityId) { - return groupSecurityRules; - } - - } - private class TESTRESTServiceImpl extends RESTServiceImpl { - private SecurityService securityService = null; - public void setSecurityService(SecurityService s) { - securityService = s; - } - @Override - protected SecurityService getSecurityService() { - return securityService; - } + private SecurityRule createSecurityRule( + long id, User user, UserGroup group, boolean canRead, boolean canWrite) { + SecurityRule sr = new SecurityRule(); + sr.setId(id); + sr.setUser(user); + sr.setGroup(group); + sr.setCanRead(canRead); + sr.setCanWrite(canWrite); + return sr; } - - private SecurityRule createSecurityRule(long id,User user,UserGroup group,boolean canRead,boolean canWrite) { - SecurityRule sr = new SecurityRule(); - sr.setId(id); - sr.setUser(user); - sr.setGroup(group); - sr.setCanRead(canRead); - sr.setCanWrite(canWrite); - return sr; - } - + @Before - public void setUp () { - // set up services - restService = new TESTRESTServiceImpl(); - securityService = new TestSecurityService(); - userService = new TestUserService(); + public void setUp() { + // set up services + restService = new TESTRESTServiceImpl(); + securityService = new TestSecurityService(); + userService = new TestUserService(); restService.setSecurityService(securityService); restService.setUserService(userService); - - // set up users and groups + + // set up users and groups user = new User(); user.setName("TEST_USER"); - user.setId(new Long(100)); + user.setId(Long.valueOf(100)); user.setRole(Role.USER); everyone = new UserGroup(); - everyone.setId(new Long(200)); + everyone.setId(Long.valueOf(200)); everyone.setGroupName("everyone"); group = new UserGroup(); group.setGroupName("TEST_GROUP"); - group.setId(new Long(201)); + group.setId(Long.valueOf(201)); HashSet groups = new HashSet(); groups.add(everyone); groups.add(group); user.setGroups(groups); user.setGroups(groups); - } - + @Test public void testRulesReadWrite() { - + // set up rules for group write access List groupSecurityRules = new ArrayList(); groupSecurityRules.add(createSecurityRule(1, null, group, true, true)); - + List userSecurityRules = new ArrayList(); userSecurityRules.add(createSecurityRule(1, user, null, true, false)); securityService.setGroupSecurityRules(groupSecurityRules); securityService.setUserSecurityRules(userSecurityRules); - + assertTrue(restService.resourceAccessRead(user, 1)); assertTrue(restService.resourceAccessWrite(user, 1)); - + groupSecurityRules = new ArrayList(); groupSecurityRules.add(createSecurityRule(1, null, group, true, false)); - + userSecurityRules = new ArrayList(); userSecurityRules.add(createSecurityRule(1, user, null, true, true)); securityService.setGroupSecurityRules(groupSecurityRules); securityService.setUserSecurityRules(userSecurityRules); - + assertTrue(restService.resourceAccessRead(user, 1)); assertTrue(restService.resourceAccessWrite(user, 1)); - } + @Test public void testRulesReadOnly() { - // set up rules + // set up rules List groupSecurityRules = new ArrayList(); groupSecurityRules.add(createSecurityRule(1, null, group, true, false)); - + List userSecurityRules = new ArrayList(); userSecurityRules.add(createSecurityRule(1, user, null, true, false)); securityService.setGroupSecurityRules(groupSecurityRules); securityService.setUserSecurityRules(userSecurityRules); - + assertTrue(restService.resourceAccessRead(user, 1)); assertFalse(restService.resourceAccessWrite(user, 1)); } - + @Test public void testRulesAccessDenied() { - // set up rules + // set up rules List groupSecurityRules = new ArrayList(); groupSecurityRules.add(createSecurityRule(1, null, group, false, false)); - + List userSecurityRules = new ArrayList(); userSecurityRules.add(createSecurityRule(1, user, null, false, false)); securityService.setGroupSecurityRules(groupSecurityRules); securityService.setUserSecurityRules(userSecurityRules); - + assertFalse(restService.resourceAccessRead(user, 1)); assertFalse(restService.resourceAccessWrite(user, 1)); } - + @Test - public void testIgnoreNotValidUserRules () { - // set up rules + public void testIgnoreNotValidUserRules() { + // set up rules List groupSecurityRules = new ArrayList(); groupSecurityRules.add(createSecurityRule(1, null, group, false, false)); - + List userSecurityRules = new ArrayList(); - userSecurityRules.add(createSecurityRule(1, null, group, true, false)); // this should be skipped + userSecurityRules.add( + createSecurityRule(1, null, group, true, false)); // this should be skipped userSecurityRules.add(createSecurityRule(1, user, null, true, true)); securityService.setGroupSecurityRules(groupSecurityRules); securityService.setUserSecurityRules(userSecurityRules); - + assertTrue(restService.resourceAccessRead(user, 1)); assertTrue(restService.resourceAccessWrite(user, 1)); } - + @Test public void testGuestHasEveryoneGroup() { Principal principal = restService.createGuestPrincipal(); assertTrue(principal instanceof UsernamePasswordAuthenticationToken); - UsernamePasswordAuthenticationToken userPrincipal = (UsernamePasswordAuthenticationToken)principal; + UsernamePasswordAuthenticationToken userPrincipal = + (UsernamePasswordAuthenticationToken) principal; assertTrue(userPrincipal.getPrincipal() instanceof User); - User user = (User)userPrincipal.getPrincipal(); + User user = (User) userPrincipal.getPrincipal(); assertEquals(1, user.getGroups().size()); - assertEquals(GroupReservedNames.EVERYONE.groupName(), user.getGroups().iterator().next().getGroupName()); + assertEquals( + GroupReservedNames.EVERYONE.groupName(), + user.getGroups().iterator().next().getGroupName()); + } + + private class TestUserService implements UserService { + + @Override + public long insert(User user) throws BadRequestServiceEx, NotFoundServiceEx { + // TODO Auto-generated method stub + return 0; + } + + @Override + public long update(User user) throws NotFoundServiceEx, BadRequestServiceEx { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean delete(long id) { + // TODO Auto-generated method stub + return false; + } + + @Override + public User get(long id) { + // TODO Auto-generated method stub + return null; + } + + @Override + public User get(String name) throws NotFoundServiceEx { + throw new NotFoundServiceEx(name); + } + + @Override + public List getAll(Integer page, Integer entries) throws BadRequestServiceEx { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getAll( + Integer page, Integer entries, String nameLike, boolean includeAttributes) + throws BadRequestServiceEx { + // TODO Auto-generated method stub + return null; + } + + @Override + public long getCount(String nameLike) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void updateAttributes(long id, List attributes) + throws NotFoundServiceEx { + // TODO Auto-generated method stub + + } + + @Override + public boolean insertSpecialUsers() { + // TODO Auto-generated method stub + return false; + } + + @Override + public Collection getByAttribute(UserAttribute attribute) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Collection getByGroup(UserGroup group) { + // TODO Auto-generated method stub + return null; + } } + private class TestSecurityService implements SecurityService { + private List userSecurityRules = null; + private List groupSecurityRules = null; - - - + public void setUserSecurityRules(List userSecurityRules) { + this.userSecurityRules = userSecurityRules; + } + + public void setGroupSecurityRules(List groupSecurityRules) { + this.groupSecurityRules = groupSecurityRules; + } + + @Override + public List getUserSecurityRule(String userName, long entityId) { + return userSecurityRules; + } + + @Override + public List getGroupSecurityRule(List groupNames, long entityId) { + return groupSecurityRules; + } + } + + private class TESTRESTServiceImpl extends RESTServiceImpl { + private SecurityService securityService = null; + + @Override + protected SecurityService getSecurityService() { + return securityService; + } + + public void setSecurityService(SecurityService s) { + securityService = s; + } + } } -; \ No newline at end of file diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTSessionServiceDelegateImplTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTSessionServiceDelegateImplTest.java new file mode 100644 index 00000000..288b8a60 --- /dev/null +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTSessionServiceDelegateImplTest.java @@ -0,0 +1,45 @@ +package it.geosolutions.geostore.rest.service.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import it.geosolutions.geostore.services.InMemoryUserSessionServiceImpl; +import it.geosolutions.geostore.services.rest.exception.ForbiddenErrorWebEx; +import it.geosolutions.geostore.services.rest.impl.SessionServiceDelegateImpl; +import it.geosolutions.geostore.services.rest.model.SessionToken; +import java.text.ParseException; +import javax.ws.rs.core.Response; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +public class RESTSessionServiceDelegateImplTest { + + @Test + public void testForbiddenOnRefreshTokenInvalidOrExpired() throws ParseException { + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + SessionServiceDelegateImpl sessionServiceDelegate = new SessionServiceDelegateImpl(); + sessionServiceDelegate.setUserSessionService(new InMemoryUserSessionServiceImpl()); + ServletRequestAttributes attributes = new ServletRequestAttributes(request, response); + RequestContextHolder.setRequestAttributes(attributes); + String access_token = "a_random_access_token"; + String refresh_token = "a_random_refresh_token"; + SessionToken sessionToken = new SessionToken(); + sessionToken.setAccessToken(access_token); + sessionToken.setRefreshToken(refresh_token); + SessionToken result = null; + int status = 0; + try { + result = + sessionServiceDelegate.refresh( + sessionToken.getRefreshToken(), sessionToken.getAccessToken()); + } catch (ForbiddenErrorWebEx e) { + status = e.getResponse().getStatus(); + } + assertNull(result); + assertEquals(status, Response.Status.FORBIDDEN.getStatusCode()); + } +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTUserGroupServiceImplTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTUserGroupServiceImplTest.java index a9a3fa1d..7bcfe800 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTUserGroupServiceImplTest.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTUserGroupServiceImplTest.java @@ -19,14 +19,6 @@ */ package it.geosolutions.geostore.rest.service.impl; -import java.util.List; - -import javax.ws.rs.core.SecurityContext; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - import it.geosolutions.geostore.core.model.enums.Role; import it.geosolutions.geostore.services.ServiceTestBase; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; @@ -35,38 +27,42 @@ import it.geosolutions.geostore.services.rest.model.RESTUserGroup; import it.geosolutions.geostore.services.rest.model.UserGroupList; import it.geosolutions.geostore.services.rest.utils.MockSecurityContext; +import java.util.List; +import javax.ws.rs.core.SecurityContext; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; /** * Class ResourceServiceImplTest. * * @author Tobia di Pisa (tobia.dipisa at geo-solutions.it) - * */ public class RESTUserGroupServiceImplTest extends ServiceTestBase { - RESTUserGroupServiceImpl restService; - long adminID; + RESTUserGroupServiceImpl restService; + long adminID; - @Before + @Before public void setUp() throws BadRequestServiceEx, NotFoundServiceEx { - restService = new RESTUserGroupServiceImpl(); - restService.setUserGroupService(userGroupService); - restService.setUserService(userService); + restService = new RESTUserGroupServiceImpl(); + restService.setUserGroupService(userGroupService); + restService.setUserService(userService); + } + + @After + public void tearDown() throws Exception { + removeAll(); } - - @After - public void tearDown() throws Exception { - removeAll(); - } @Test public void testGetAllWithUsers() throws Exception { - // create some sample users - long adminID = createUser("admin", Role.ADMIN, "admin"); - long userID = createUser("user", Role.USER, "user"); + // create some sample users + long adminID = createUser("admin", Role.ADMIN, "admin"); + long userID = createUser("user", Role.USER, "user"); - // create a some sample usergroup - createUserGroup("group", new long[] {adminID, userID}); + // create a some sample usergroup + createUserGroup("group", new long[] {adminID, userID}); // create security context for the request SecurityContext sc = new MockSecurityContext(userService.get(adminID)); @@ -77,15 +73,15 @@ public void testGetAllWithUsers() throws Exception { RESTUserGroup group = groups.get(0); assertEquals(2, group.getRestUsers().getList().size()); } - + @Test public void testGetAllWithoutUsers() throws Exception { - // create some sample users - long adminID = createUser("admin", Role.ADMIN, "admin"); - long userID = createUser("user", Role.USER, "user"); + // create some sample users + long adminID = createUser("admin", Role.ADMIN, "admin"); + long userID = createUser("user", Role.USER, "user"); - // create a some sample usergroup - createUserGroup("group", new long[] {adminID, userID}); + // create a some sample usergroup + createUserGroup("group", new long[] {adminID, userID}); // create security context for the request SecurityContext sc = new MockSecurityContext(userService.get(adminID)); diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/AutoUserCreateGeostoreAuthenticationInterceptorTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/AutoUserCreateGeostoreAuthenticationInterceptorTest.java index 9b0818be..ead28eb2 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/AutoUserCreateGeostoreAuthenticationInterceptorTest.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/AutoUserCreateGeostoreAuthenticationInterceptorTest.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2014 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,44 +21,41 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; + import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; - import java.util.HashMap; import java.util.Map; - import org.apache.cxf.interceptor.security.AccessDeniedException; import org.junit.Ignore; import org.junit.Test; /** - * - * Test for AutoUserCreateGeostoreAuthenticationInterceptor. Test different configurations for the interceptor - * + * Test for AutoUserCreateGeostoreAuthenticationInterceptor. Test different configurations for the + * interceptor + * * @author adiaz (alejandro.diaz at geo-solutions.it) */ -public class AutoUserCreateGeostoreAuthenticationInterceptorTest extends - BaseAuthenticationInterceptorTest { +public class AutoUserCreateGeostoreAuthenticationInterceptorTest + extends BaseAuthenticationInterceptorTest { - /** - * Access denied for a new user if the interceptor doesn't create new users - */ + /** Access denied for a new user if the interceptor doesn't create new users */ @Ignore @Test(expected = AccessDeniedException.class) public void testNotCreateUsers() { - AutoUserCreateGeostoreAuthenticationInterceptor interceptor = new AutoUserCreateGeostoreAuthenticationInterceptor(); + AutoUserCreateGeostoreAuthenticationInterceptor interceptor = + new AutoUserCreateGeostoreAuthenticationInterceptor(); interceptor.setAutoCreateUsers(false); interceptor.setUserService(userService); interceptor.handleMessage(getMockedMessage("test", "", null)); } - /** - * Create a user with a empty password - */ + /** Create a user with a empty password */ @Ignore @Test public void testCreateUsers() { - AutoUserCreateGeostoreAuthenticationInterceptor interceptor = new AutoUserCreateGeostoreAuthenticationInterceptor(); + AutoUserCreateGeostoreAuthenticationInterceptor interceptor = + new AutoUserCreateGeostoreAuthenticationInterceptor(); interceptor.setAutoCreateUsers(true); interceptor.setNewUsersPassword(NewPasswordStrategy.NONE); interceptor.setUserService(userService); @@ -71,13 +68,12 @@ public void testCreateUsers() { } } - /** - * Create a user with password as user name - */ + /** Create a user with password as user name */ @Ignore @Test public void testCreateUsersStrategyUserName() { - AutoUserCreateGeostoreAuthenticationInterceptor interceptor = new AutoUserCreateGeostoreAuthenticationInterceptor(); + AutoUserCreateGeostoreAuthenticationInterceptor interceptor = + new AutoUserCreateGeostoreAuthenticationInterceptor(); interceptor.setAutoCreateUsers(true); interceptor.setNewUsersPassword(NewPasswordStrategy.USERNAME); interceptor.setUserService(userService); @@ -90,13 +86,12 @@ public void testCreateUsersStrategyUserName() { } } - /** - * Create a user with password from a header - */ + /** Create a user with password from a header */ @Ignore @Test public void testCreateUsersStrategyFromHeader() { - AutoUserCreateGeostoreAuthenticationInterceptor interceptor = new AutoUserCreateGeostoreAuthenticationInterceptor(); + AutoUserCreateGeostoreAuthenticationInterceptor interceptor = + new AutoUserCreateGeostoreAuthenticationInterceptor(); interceptor.setAutoCreateUsers(true); interceptor.setNewUsersPassword(NewPasswordStrategy.FROMHEADER); interceptor.setNewUsersPasswordHeader("newPassword"); @@ -111,5 +106,4 @@ public void testCreateUsersStrategyFromHeader() { fail("Couldn't found user"); } } - -} \ No newline at end of file +} diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/BaseAuthenticationInterceptorTest.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/BaseAuthenticationInterceptorTest.java index 4b0c2f79..04d75f5e 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/BaseAuthenticationInterceptorTest.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/BaseAuthenticationInterceptorTest.java @@ -1,47 +1,45 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.services.rest.utils; import it.geosolutions.geostore.services.UserService; - import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; - import org.apache.cxf.configuration.security.AuthorizationPolicy; import org.apache.cxf.message.Message; import org.apache.cxf.message.MessageImpl; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.Before; import org.junit.Ignore; /** - * * Test for AuthenticationInterceptors. It use a mocked user service and a mocked message - * + * * @author adiaz (alejandro.diaz at geo-solutions.it) */ public abstract class BaseAuthenticationInterceptorTest { - protected final Logger LOGGER = Logger.getLogger(getClass()); + protected final Logger LOGGER = LogManager.getLogger(getClass()); protected UserService userService; @@ -53,15 +51,15 @@ public void init() { /** * Mock a message to be handled by the interceptor - * + * * @param username for the authorization policy * @param password for the authorization policy * @param headers for the request - * * @return Message to be handled */ - protected Message getMockedMessage(String username, String password, Map headers) { - MessageImpl messageImpl = (MessageImpl) new MessageImpl(); + protected Message getMockedMessage( + String username, String password, Map headers) { + MessageImpl messageImpl = new MessageImpl(); AuthorizationPolicy policy = new AuthorizationPolicy(); policy.setUserName(username); policy.setPassword(password); @@ -77,5 +75,4 @@ protected Message getMockedMessage(String username, String password, Map headers = (MetadataMap) message.get(Message.PROTOCOL_HEADERS); - assertEquals(((List)headers.get("Expires")).get(0), "-1"); - assertEquals(((List)headers.get("Cache-Control")).get(0), "no-cache"); + Message message = new MessageImpl(); + ByteArrayOutputStream sw = new ByteArrayOutputStream(); + message.setContent(OutputStream.class, sw); + FixedCacheControlOutInterceptor interceptor = new FixedCacheControlOutInterceptor(); + interceptor.handleMessage(message); + @SuppressWarnings("unchecked") + MetadataMap headers = + (MetadataMap) message.get(Message.PROTOCOL_HEADERS); + assertEquals(headers.get("Expires").get(0), "-1"); + assertEquals(headers.get("Cache-Control").get(0), "no-cache"); } + @SuppressWarnings("unchecked") - @Test + @Test public void testCacheControlPreserveExisting() { - Message message = new MessageImpl(); - ByteArrayOutputStream sw = new ByteArrayOutputStream(); - message.setContent(OutputStream.class , sw); - MetadataMap headers = (MetadataMap) message.get(Message.PROTOCOL_HEADERS); - if (headers == null) { - headers = new MetadataMap(); - } - headers.add("Test", new String("Test")); + Message message = new MessageImpl(); + ByteArrayOutputStream sw = new ByteArrayOutputStream(); + message.setContent(OutputStream.class, sw); + MetadataMap headers = + (MetadataMap) message.get(Message.PROTOCOL_HEADERS); + if (headers == null) { + headers = new MetadataMap(); + } + headers.add("Test", "Test"); message.put(Message.PROTOCOL_HEADERS, headers); - FixedCacheControlOutInterceptor interceptor = new FixedCacheControlOutInterceptor(); - - interceptor.handleMessage(message); - - headers = (MetadataMap) message.get(Message.PROTOCOL_HEADERS); - ((List)headers.get("Cache-Control")).get(0); - assertEquals(((List)headers.get("Cache-Control")).get(0), "no-cache"); - assertEquals(((List)headers.get("Expires")).get(0), "-1"); - assertEquals(((List)headers.get("Test")).get(0), new String("Test")); - } + FixedCacheControlOutInterceptor interceptor = new FixedCacheControlOutInterceptor(); + interceptor.handleMessage(message); + headers = (MetadataMap) message.get(Message.PROTOCOL_HEADERS); + headers.get("Cache-Control").get(0); + assertEquals(headers.get("Cache-Control").get(0), "no-cache"); + assertEquals(headers.get("Expires").get(0), "-1"); + assertEquals(headers.get("Test").get(0), "Test"); + } } diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/MockSecurityContext.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/MockSecurityContext.java index 85cae784..d4ceb143 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/MockSecurityContext.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/MockSecurityContext.java @@ -1,88 +1,88 @@ package it.geosolutions.geostore.services.rest.utils; +import it.geosolutions.geostore.core.model.User; import java.security.Principal; import java.util.Collection; - import javax.ws.rs.core.SecurityContext; - import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; -import it.geosolutions.geostore.core.model.User; - /** * Mock class to simulate REST Services Requests - * @author Lorenzo Natali, GeoSolutions s.a.s. * + * @author Lorenzo Natali, GeoSolutions s.a.s. */ -public class MockSecurityContext implements SecurityContext{ - - User user; - Authentication principal; - - public MockSecurityContext(User uu) { - this.user = uu; - principal = new Authentication() { - - @Override - public String getName() { - return user.getName(); - } - - @Override - public Collection getAuthorities() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Object getCredentials() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Object getDetails() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Object getPrincipal() { - return user; - } - - @Override - public boolean isAuthenticated() { - // TODO Auto-generated method stub - return false; - } - - @Override - public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { - // TODO Auto-generated method stub - } - }; - } - @Override - public Principal getUserPrincipal() { - return principal; - } - - @Override - public boolean isUserInRole(String role) { - return role != null && role.equals(user.getRole()); - } - - @Override - public boolean isSecure() { - // TODO Auto-generated method stub - return false; - } - - @Override - public String getAuthenticationScheme() { - // TODO Auto-generated method stub - return null; - } +public class MockSecurityContext implements SecurityContext { + + User user; + Authentication principal; + + public MockSecurityContext(User uu) { + this.user = uu; + principal = + new Authentication() { + + @Override + public String getName() { + return user.getName(); + } + + @Override + public Collection getAuthorities() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Object getCredentials() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Object getDetails() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Object getPrincipal() { + return user; + } + + @Override + public boolean isAuthenticated() { + // TODO Auto-generated method stub + return false; + } + + @Override + public void setAuthenticated(boolean isAuthenticated) + throws IllegalArgumentException { + // TODO Auto-generated method stub + } + }; + } + + @Override + public Principal getUserPrincipal() { + return principal; + } + + @Override + public boolean isUserInRole(String role) { + return role != null && role.equals(user.getRole()); + } + + @Override + public boolean isSecure() { + // TODO Auto-generated method stub + return false; + } + + @Override + public String getAuthenticationScheme() { + // TODO Auto-generated method stub + return null; + } } diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/MockedUserGroupService.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/MockedUserGroupService.java index 2811e213..be599e5a 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/MockedUserGroupService.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/MockedUserGroupService.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2015 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,47 +21,44 @@ import it.geosolutions.geostore.core.model.User; import it.geosolutions.geostore.core.model.UserGroup; +import it.geosolutions.geostore.core.model.UserGroupAttribute; import it.geosolutions.geostore.services.UserGroupService; import it.geosolutions.geostore.services.dto.ShortResource; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Random; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class MockedUserGroupService implements UserGroupService { - private static Random RANDOM = new Random(); + private static final Random RANDOM = new Random(); + + private final Map GROUPS = new ConcurrentHashMap(); - private Map GROUPS = new ConcurrentHashMap(); - @Override public long insert(UserGroup userGroup) throws BadRequestServiceEx { Long id = RANDOM.nextLong(); userGroup.setId(id); - + GROUPS.put(id, userGroup); return id; } @Override public boolean delete(long id) throws NotFoundServiceEx, BadRequestServiceEx { - return GROUPS.containsKey(new Long(id)) && GROUPS.remove(new Long(id)) != null; + return GROUPS.containsKey(Long.valueOf(id)) && GROUPS.remove(Long.valueOf(id)) != null; } @Override public void assignUserGroup(long userId, long groupId) throws NotFoundServiceEx { // TODO Auto-generated method stub - + } @Override public void deassignUserGroup(long userId, long groupId) throws NotFoundServiceEx { // TODO Auto-generated method stub - + } @Override @@ -71,12 +68,13 @@ public List getAll(Integer page, Integer entries) throws BadRequestSe @Override public UserGroup get(long id) throws BadRequestServiceEx { - return GROUPS.get(new Long(id)); + return GROUPS.get(Long.valueOf(id)); } @Override - public List updateSecurityRules(Long groupId, List resourcesToSet, - boolean canRead, boolean canWrite) throws NotFoundServiceEx, BadRequestServiceEx { + public List updateSecurityRules( + Long groupId, List resourcesToSet, boolean canRead, boolean canWrite) + throws NotFoundServiceEx, BadRequestServiceEx { // TODO Auto-generated method stub return null; } @@ -87,6 +85,12 @@ public boolean insertSpecialUsersGroups() { return false; } + @Override + public boolean removeSpecialUsersGroups() { + // TODO Auto-generated method stub + return false; + } + @Override public UserGroup get(String name) { for (UserGroup userGroup : GROUPS.values()) { @@ -97,26 +101,38 @@ public UserGroup get(String name) { return null; } - @Override - public List getAllAllowed(User user, Integer page, - Integer entries, String nameLike, boolean all) - throws BadRequestServiceEx { - // TODO Auto-generated method stub - return null; - } - - @Override - public long getCount(User authUser, String nameLike) - throws BadRequestServiceEx { - // TODO Auto-generated method stub - return 0; - } - - @Override - public long getCount(User authUser, String nameLike, boolean all) - throws BadRequestServiceEx { - // TODO Auto-generated method stub - return 0; - } + @Override + public List getAllAllowed( + User user, Integer page, Integer entries, String nameLike, boolean all) + throws BadRequestServiceEx { + // TODO Auto-generated method stub + return null; + } + + @Override + public long getCount(User authUser, String nameLike) throws BadRequestServiceEx { + // TODO Auto-generated method stub + return 0; + } + + @Override + public long getCount(User authUser, String nameLike, boolean all) throws BadRequestServiceEx { + // TODO Auto-generated method stub + return 0; + } + @Override + public void updateAttributes(long id, List attributes) + throws NotFoundServiceEx {} + + @Override + public long update(UserGroup group) throws NotFoundServiceEx, BadRequestServiceEx { + return 0; + } + + @Override + public Collection findByAttribute( + String name, List values, boolean ignoreCase) { + return null; + } } diff --git a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/MockedUserService.java b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/MockedUserService.java index 21c5639a..9e8c3d88 100644 --- a/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/MockedUserService.java +++ b/src/modules/rest/impl/src/test/java/it/geosolutions/geostore/services/rest/utils/MockedUserService.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -26,38 +26,33 @@ import it.geosolutions.geostore.services.UserService; import it.geosolutions.geostore.services.exception.BadRequestServiceEx; import it.geosolutions.geostore.services.exception.NotFoundServiceEx; - -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * Mocked user service for GeoStoreAuthenticationInterceptorTest - * + * * @author adiaz (alejandro.diaz at geo-solutions.it) - * */ public class MockedUserService implements UserService { - private static Random RANDOM = new Random(); + private static final Random RANDOM = new Random(); - private Map USERS = new ConcurrentHashMap(); + private final Map USERS = new ConcurrentHashMap(); /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#insert(it.geosolutions. geostore.core.model.User) */ @Override public long insert(User user) throws BadRequestServiceEx, NotFoundServiceEx { Long id = RANDOM.nextLong(); user.setId(id); - String password = user.getPassword() != null ? user.getPassword() - : user.getNewPassword() != null ? user.getNewPassword() : null; + String password = + user.getPassword() != null + ? user.getPassword() + : user.getNewPassword() != null ? user.getNewPassword() : null; user.setPassword(password == null ? null : PwEncoder.encode(password)); USERS.put(id, user); return id; @@ -65,7 +60,7 @@ public long insert(User user) throws BadRequestServiceEx, NotFoundServiceEx { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#update(it.geosolutions. geostore.core.model.User) */ @Override @@ -80,27 +75,27 @@ public long update(User user) throws NotFoundServiceEx, BadRequestServiceEx { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#delete(long) */ @Override public boolean delete(long id) { - return USERS.containsKey(new Long(id)) && USERS.remove(new Long(id)) != null; + return USERS.containsKey(Long.valueOf(id)) && USERS.remove(Long.valueOf(id)) != null; } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#get(long) */ @Override public User get(long id) { - return USERS.get(new Long(id)); + return USERS.get(Long.valueOf(id)); } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#get(java.lang.String) */ @Override @@ -113,9 +108,7 @@ public User get(String name) throws NotFoundServiceEx { throw new NotFoundServiceEx("User not found"); } - /** - * Don't use page for the mocked service - */ + /** Don't use page for the mocked service */ public List getAll(Integer page, Integer entries) throws BadRequestServiceEx { List users = new LinkedList(); for (User user : USERS.values()) { @@ -124,17 +117,16 @@ public List getAll(Integer page, Integer entries) throws BadRequestService return users; } - /** - * Don't filter in the mocked service - */ - public List getAll(Integer page, Integer entries, String nameLike, - boolean includeAttributes) throws BadRequestServiceEx { + /** Don't filter in the mocked service */ + public List getAll( + Integer page, Integer entries, String nameLike, boolean includeAttributes) + throws BadRequestServiceEx { return getAll(page, entries); } /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#getCount(java.lang.String) */ @Override @@ -145,7 +137,7 @@ public long getCount(String nameLike) { /* * (non-Javadoc) - * + * * @see it.geosolutions.geostore.services.UserService#updateAttributes(long, java.util.List) */ @Override @@ -158,7 +150,7 @@ public void updateAttributes(long id, List attributes) throws Not */ @Override public boolean insertSpecialUsers() { - // Don't needed + // Don't needed return false; } @@ -173,9 +165,10 @@ public Collection getByGroup(UserGroup ug) { List ret = new LinkedList<>(); for (User user : USERS.values()) { Set groups = user.getGroups(); - if(groups != null) { + if (groups != null) { for (UserGroup group : groups) { - if(group.getId() == ug.getId() || group.getGroupName().equals(ug.getGroupName())) { + if (group.getId() == ug.getId() + || group.getGroupName().equals(ug.getGroupName())) { ret.add(user); break; } diff --git a/src/modules/rest/impl/src/test/resources/__files/discovery_result.json b/src/modules/rest/impl/src/test/resources/__files/discovery_result.json new file mode 100644 index 00000000..5bd7634a --- /dev/null +++ b/src/modules/rest/impl/src/test/resources/__files/discovery_result.json @@ -0,0 +1,58 @@ +{ + "issuer": "https://accounts.google.com", + "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth", + "device_authorization_endpoint": "https://oauth2.googleapis.com/device/code", + "token_endpoint": "https://oauth2.googleapis.com/token", + "userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo", + "revocation_endpoint": "https://oauth2.googleapis.com/revoke", + "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs", + "response_types_supported": [ + "code", + "token", + "id_token", + "code token", + "code id_token", + "token id_token", + "code token id_token", + "none" + ], + "subject_types_supported": [ + "public" + ], + "id_token_signing_alg_values_supported": [ + "RS256" + ], + "scopes_supported": [ + "openid", + "email", + "profile" + ], + "token_endpoint_auth_methods_supported": [ + "client_secret_post", + "client_secret_basic" + ], + "claims_supported": [ + "aud", + "email", + "email_verified", + "exp", + "family_name", + "given_name", + "iat", + "iss", + "locale", + "name", + "picture", + "sub" + ], + "code_challenge_methods_supported": [ + "plain", + "S256" + ], + "grant_types_supported": [ + "authorization_code", + "refresh_token", + "urn:ietf:params:oauth:grant-type:device_code", + "urn:ietf:params:oauth:grant-type:jwt-bearer" + ] +} \ No newline at end of file diff --git a/src/modules/rest/impl/src/test/resources/__files/jkws.json b/src/modules/rest/impl/src/test/resources/__files/jkws.json new file mode 100644 index 00000000..ed25206f --- /dev/null +++ b/src/modules/rest/impl/src/test/resources/__files/jkws.json @@ -0,0 +1,28 @@ +{ + "keys": [ + { + "e": "AQAB", + "kty": "RSA", + "alg": "RS256", + "n": "pKFKLmDnNozPu24-nCrbvbBjO0gith7VmxPm1px9lkJfwXOWEbNzyfDhd3SjylKTyH5lq6zMtJvNvKCr2TyQTwypvmZBpeMK29Zz0WvgPBFJ6HHsu9apa21hgE9iPf0A1lNG5Nd2kP9m2Kc8ekKy4OrshDp6gs1ntHPWiW2iSjttruFjTB20P7p5GTdlxzg6LCrJ03E7FJlrfXcEEKRDzZ4h3b6hN2aZ_heOZxRSZ6Uwx8_tbFRe2TIfw9IXaXXuodHMRottSIZBtdJdOanbIFAuhEev9b-lFTksFQXDvicUiz_ri4JDliGuEAgINHjI6pcNo2vkYx3NupROmkDRIQ", + "use": "sig", + "kid": "486f16482005a2cdaf26d9214018d029ca46fb56" + }, + { + "kid": "38f3883468fc659abb4475f36313d22585c2d7ca", + "use": "sig", + "alg": "RS256", + "kty": "RSA", + "n": "h0G4nnQc39gn04fTuSJB6l1JhE9ggG-SHrc9yfr85tzQEjxG55jipmnmW1x-IHS1PvWgtNbpeEDya9IUzkC8BxnBht7WVDV9mLBZK8szrCrj6l7SwvQOM6cw0-KgGgmzwE_IGkn452wmlVYz9IIY-etasrkstJ0G2smQJ3Db3YbZe32FoYnXbbK38O2hIf4bffegccrfmsEEbSisq0903IEbCi0qpJbhrgqvYg6_y0KeOxxX5u-_Jg-KM0Aub4YGT2LRsiE_ygskabuoh6nQDr8M9-SDC_bEind2FUk761ZSw_QMk9_Nc9orllaMZpQ4FpKRv5LXHUawz_er8P3c0w", + "e": "AQAB" + }, + { + "e": "AQAB", + "alg": "RS256", + "use": "sig", + "n": "yF2BwlYB66C_-FKSk4riKizNKGjTyi2LYz_MImk95lPcW3SDpheJRghc_5iaZxpf1xAMDwHphdkEPal_kNnqLu49B7XGv9iPRUgzAEBD-w9E1CjrxzCRhM8MRVgUo7Msnydd6wOqNDKFCD1TlKpkGrUS32PB1jsu-OuwG6HCL1cxSNzP7SCDgCKecPvnpwcNarc3FzzJDraUaPX639cvvBf5PDeltBzkAIbSY0YS6RVBmZpKEDO_C1GtbADqvpz95uSdYO6H4tjUQtFwooNsAL8tv1TmLSehClFyYzaESOBfSCpxudNmPgBEzfAZM_WoZ2JukAo73Ynu3YxJOJtMTQ", + "kty": "RSA", + "kid": "7483a088d4ffc00609f0e22f3c22da5fe3906ccc" + } + ] +} \ No newline at end of file diff --git a/src/modules/rest/impl/src/test/resources/__files/keycloak_admin_user.json b/src/modules/rest/impl/src/test/resources/__files/keycloak_admin_user.json new file mode 100644 index 00000000..9a8e6ab0 --- /dev/null +++ b/src/modules/rest/impl/src/test/resources/__files/keycloak_admin_user.json @@ -0,0 +1,25 @@ +[ + { + "id": "0e72c14e-53d8-4619-a05a-a605dc2102b9", + "createdTimestamp": 1654599284064, + "username": "admin", + "enabled": true, + "totp": false, + "emailVerified": false, + "attributes": { + "id": [ + "1" + ] + }, + "disableableCredentialTypes": [], + "requiredActions": [], + "notBefore": 0, + "access": { + "manageGroupMembership": true, + "view": true, + "mapRoles": true, + "impersonate": true, + "manage": true + } + } +] \ No newline at end of file diff --git a/src/modules/rest/impl/src/test/resources/__files/keycloak_discovery.json b/src/modules/rest/impl/src/test/resources/__files/keycloak_discovery.json new file mode 100644 index 00000000..3c83e896 --- /dev/null +++ b/src/modules/rest/impl/src/test/resources/__files/keycloak_discovery.json @@ -0,0 +1,290 @@ +{ + "issuer": "http://localhost:8080/realms/master", + "authorization_endpoint": "http://localhost:8080/realms/master/protocol/openid-connect/auth", + "token_endpoint": "http://localhost:12345/auth/realms/master/protocol/openid-connect/token", + "introspection_endpoint": "http://localhost:12345/auth/realms/master/protocol/openid-connect/token/introspect", + "userinfo_endpoint": "http://localhost:12345/auth/realms/master/protocol/openid-connect/userinfo", + "end_session_endpoint": "http://localhost:12345/auth/realms/master/protocol/openid-connect/logout", + "frontchannel_logout_session_supported": true, + "frontchannel_logout_supported": true, + "jwks_uri": "http://localhost:12345/auth/realms/master/protocol/openid-connect/certs", + "check_session_iframe": "http://localhost:12345/auth/realms/master/protocol/openid-connect/login-status-iframe.html", + "grant_types_supported": [ + "authorization_code", + "implicit", + "refresh_token", + "password", + "client_credentials", + "urn:ietf:params:oauth:grant-type:device_code", + "urn:openid:params:grant-type:ciba" + ], + "acr_values_supported": [ + "0", + "1" + ], + "response_types_supported": [ + "code", + "none", + "id_token", + "token", + "id_token token", + "code id_token", + "code token", + "code id_token token" + ], + "subject_types_supported": [ + "public", + "pairwise" + ], + "id_token_signing_alg_values_supported": [ + "PS384", + "ES384", + "RS384", + "HS256", + "HS512", + "ES256", + "RS256", + "HS384", + "ES512", + "PS256", + "PS512", + "RS512" + ], + "id_token_encryption_alg_values_supported": [ + "RSA-OAEP", + "RSA-OAEP-256", + "RSA1_5" + ], + "id_token_encryption_enc_values_supported": [ + "A256GCM", + "A192GCM", + "A128GCM", + "A128CBC-HS256", + "A192CBC-HS384", + "A256CBC-HS512" + ], + "userinfo_signing_alg_values_supported": [ + "PS384", + "ES384", + "RS384", + "HS256", + "HS512", + "ES256", + "RS256", + "HS384", + "ES512", + "PS256", + "PS512", + "RS512", + "none" + ], + "userinfo_encryption_alg_values_supported": [ + "RSA-OAEP", + "RSA-OAEP-256", + "RSA1_5" + ], + "userinfo_encryption_enc_values_supported": [ + "A256GCM", + "A192GCM", + "A128GCM", + "A128CBC-HS256", + "A192CBC-HS384", + "A256CBC-HS512" + ], + "request_object_signing_alg_values_supported": [ + "PS384", + "ES384", + "RS384", + "HS256", + "HS512", + "ES256", + "RS256", + "HS384", + "ES512", + "PS256", + "PS512", + "RS512", + "none" + ], + "request_object_encryption_alg_values_supported": [ + "RSA-OAEP", + "RSA-OAEP-256", + "RSA1_5" + ], + "request_object_encryption_enc_values_supported": [ + "A256GCM", + "A192GCM", + "A128GCM", + "A128CBC-HS256", + "A192CBC-HS384", + "A256CBC-HS512" + ], + "response_modes_supported": [ + "query", + "fragment", + "form_post", + "query.jwt", + "fragment.jwt", + "form_post.jwt", + "jwt" + ], + "registration_endpoint": "http://localhost:12345/auth/realms/master/clients-registrations/openid-connect", + "token_endpoint_auth_methods_supported": [ + "private_key_jwt", + "client_secret_basic", + "client_secret_post", + "tls_client_auth", + "client_secret_jwt" + ], + "token_endpoint_auth_signing_alg_values_supported": [ + "PS384", + "ES384", + "RS384", + "HS256", + "HS512", + "ES256", + "RS256", + "HS384", + "ES512", + "PS256", + "PS512", + "RS512" + ], + "introspection_endpoint_auth_methods_supported": [ + "private_key_jwt", + "client_secret_basic", + "client_secret_post", + "tls_client_auth", + "client_secret_jwt" + ], + "introspection_endpoint_auth_signing_alg_values_supported": [ + "PS384", + "ES384", + "RS384", + "HS256", + "HS512", + "ES256", + "RS256", + "HS384", + "ES512", + "PS256", + "PS512", + "RS512" + ], + "authorization_signing_alg_values_supported": [ + "PS384", + "ES384", + "RS384", + "HS256", + "HS512", + "ES256", + "RS256", + "HS384", + "ES512", + "PS256", + "PS512", + "RS512" + ], + "authorization_encryption_alg_values_supported": [ + "RSA-OAEP", + "RSA-OAEP-256", + "RSA1_5" + ], + "authorization_encryption_enc_values_supported": [ + "A256GCM", + "A192GCM", + "A128GCM", + "A128CBC-HS256", + "A192CBC-HS384", + "A256CBC-HS512" + ], + "claims_supported": [ + "aud", + "sub", + "iss", + "auth_time", + "name", + "given_name", + "family_name", + "preferred_username", + "email", + "acr" + ], + "claim_types_supported": [ + "normal" + ], + "claims_parameter_supported": true, + "scopes_supported": [ + "openid", + "profile", + "address", + "phone", + "offline_access", + "email", + "acr", + "roles", + "web-origins", + "microprofile-jwt" + ], + "request_parameter_supported": true, + "request_uri_parameter_supported": true, + "require_request_uri_registration": true, + "code_challenge_methods_supported": [ + "plain", + "S256" + ], + "tls_client_certificate_bound_access_tokens": true, + "revocation_endpoint": "http://localhost:12345/auth/realms/master/protocol/openid-connect/revoke", + "revocation_endpoint_auth_methods_supported": [ + "private_key_jwt", + "client_secret_basic", + "client_secret_post", + "tls_client_auth", + "client_secret_jwt" + ], + "revocation_endpoint_auth_signing_alg_values_supported": [ + "PS384", + "ES384", + "RS384", + "HS256", + "HS512", + "ES256", + "RS256", + "HS384", + "ES512", + "PS256", + "PS512", + "RS512" + ], + "backchannel_logout_supported": true, + "backchannel_logout_session_supported": true, + "device_authorization_endpoint": "http://localhost:12345/auth/realms/master/protocol/openid-connect/auth/device", + "backchannel_token_delivery_modes_supported": [ + "poll", + "ping" + ], + "backchannel_authentication_endpoint": "http://localhost:12345/auth/realms/master/protocol/openid-connect/ext/ciba/auth", + "backchannel_authentication_request_signing_alg_values_supported": [ + "PS384", + "ES384", + "RS384", + "ES256", + "RS256", + "ES512", + "PS256", + "PS512", + "RS512" + ], + "require_pushed_authorization_requests": false, + "pushed_authorization_request_endpoint": "http://localhost:12345/auth/realms/master/protocol/openid-connect/ext/par/request", + "mtls_endpoint_aliases": { + "token_endpoint": "http://localhost:12345/auth/realms/master/protocol/openid-connect/token", + "revocation_endpoint": "http://localhost:12345/auth/realms/master/protocol/openid-connect/revoke", + "introspection_endpoint": "http://localhost:12345/auth/realms/master/protocol/openid-connect/token/introspect", + "device_authorization_endpoint": "http://localhost:12345/auth/realms/master/protocol/openid-connect/auth/device", + "registration_endpoint": "http://localhost:12345/auth/realms/master/clients-registrations/openid-connect", + "userinfo_endpoint": "http://localhost:12345/auth/realms/master/protocol/openid-connect/userinfo", + "pushed_authorization_request_endpoint": "http://localhost:12345/auth/realms/master/protocol/openid-connect/ext/par/request", + "backchannel_authentication_endpoint": "http://localhost:12345/auth/realms/master/protocol/openid-connect/ext/ciba/auth" + } +} \ No newline at end of file diff --git a/src/modules/rest/impl/src/test/resources/__files/keycloak_one_role.json b/src/modules/rest/impl/src/test/resources/__files/keycloak_one_role.json new file mode 100644 index 00000000..6cec1820 --- /dev/null +++ b/src/modules/rest/impl/src/test/resources/__files/keycloak_one_role.json @@ -0,0 +1,8 @@ +{ + "id": "f83352cd-37aa-47bb-97c4-48a1e2d27a51", + "name": "test-create-group", + "description": "${role-test-create}", + "composite": false, + "clientRole": false, + "containerId": "d7a7e795-974a-41b1-9a0d-0cc5fcf94a31" +} \ No newline at end of file diff --git a/src/modules/rest/impl/src/test/resources/__files/keycloak_roles.json b/src/modules/rest/impl/src/test/resources/__files/keycloak_roles.json new file mode 100644 index 00000000..2b4704f7 --- /dev/null +++ b/src/modules/rest/impl/src/test/resources/__files/keycloak_roles.json @@ -0,0 +1,50 @@ +[ + { + "id": "e3928f3f-4077-4073-9a2e-f68d29a73f01", + "name": "default-roles-master", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "d7a7e795-974a-41b1-9a0d-0cc5fcf94a31" + }, + { + "id": "f64c653d-4ef6-474f-964e-785a90e5d6a9", + "name": "create-realm", + "description": "${role_create-realm}", + "composite": false, + "clientRole": false, + "containerId": "d7a7e795-974a-41b1-9a0d-0cc5fcf94a31" + }, + { + "id": "f83352cd-37aa-47bb-97c4-48a1e2d27a51", + "name": "test-create-group", + "description": "${role-test-create}", + "composite": false, + "clientRole": false, + "containerId": "d7a7e795-974a-41b1-9a0d-0cc5fcf94a31" + }, + { + "id": "161a87f6-f293-4235-a486-30547dbfdc0d", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "d7a7e795-974a-41b1-9a0d-0cc5fcf94a31" + }, + { + "id": "abe053dd-725c-45ff-8bc9-c2a70192f448", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "d7a7e795-974a-41b1-9a0d-0cc5fcf94a31" + }, + { + "id": "a7b9fb8d-1012-4a16-b7c0-ef9fb4bc7649", + "name": "admin", + "description": "${role_admin}", + "composite": true, + "clientRole": false, + "containerId": "d7a7e795-974a-41b1-9a0d-0cc5fcf94a31" + } +] \ No newline at end of file diff --git a/src/modules/rest/impl/src/test/resources/__files/keycloak_token_response.json b/src/modules/rest/impl/src/test/resources/__files/keycloak_token_response.json new file mode 100644 index 00000000..11b0bbac --- /dev/null +++ b/src/modules/rest/impl/src/test/resources/__files/keycloak_token_response.json @@ -0,0 +1,6 @@ +{ + "access_token": "access_token", + "refresh_token": "refresh_token", + "expires_in": 9223372036854775807, + "refresh_expires_in": 9223372036854775807 +} \ No newline at end of file diff --git a/src/modules/rest/impl/src/test/resources/__files/keycloak_two_roles.json b/src/modules/rest/impl/src/test/resources/__files/keycloak_two_roles.json new file mode 100644 index 00000000..89ffd58f --- /dev/null +++ b/src/modules/rest/impl/src/test/resources/__files/keycloak_two_roles.json @@ -0,0 +1,18 @@ +[ + { + "id": "f64c653d-4ef6-474f-964e-785a90e5d6a9", + "name": "create-realm", + "description": "${role_create-realm}", + "composite": false, + "clientRole": false, + "containerId": "d7a7e795-974a-41b1-9a0d-0cc5fcf94a31" + }, + { + "id": "f83352cd-37aa-47bb-97c4-48a1e2d27a51", + "name": "test-create-group", + "description": "${role-test-create}", + "composite": false, + "clientRole": false, + "containerId": "d7a7e795-974a-41b1-9a0d-0cc5fcf94a31" + } +] \ No newline at end of file diff --git a/src/modules/rest/impl/src/test/resources/__files/keycloak_two_users.json b/src/modules/rest/impl/src/test/resources/__files/keycloak_two_users.json new file mode 100644 index 00000000..8086f8d4 --- /dev/null +++ b/src/modules/rest/impl/src/test/resources/__files/keycloak_two_users.json @@ -0,0 +1,44 @@ +[ + { + "id": "0e72c14e-53d8-4619-a05a-a605dc2102b9", + "createdTimestamp": 1655903731184, + "username": "test-user", + "enabled": true, + "totp": false, + "emailVerified": false, + "firstName": "Marco", + "lastName": "Volpini", + "email": "mail@mail.com", + "disableableCredentialTypes": [], + "requiredActions": [], + "notBefore": 0, + "access": { + "manageGroupMembership": true, + "view": true, + "mapRoles": true, + "impersonate": true, + "manage": true + } + }, + { + "id": "0e72c14e-53d8-4619-a05a-a605dc2102b9", + "createdTimestamp": 1655903783995, + "username": "test-user-2", + "enabled": true, + "totp": false, + "emailVerified": false, + "firstName": "Marco", + "lastName": "Volpini2", + "email": "mail@mail.com", + "disableableCredentialTypes": [], + "requiredActions": [], + "notBefore": 0, + "access": { + "manageGroupMembership": true, + "view": true, + "mapRoles": true, + "impersonate": true, + "manage": true + } + } +] \ No newline at end of file diff --git a/src/modules/rest/impl/src/test/resources/__files/keycloak_user_roles.json b/src/modules/rest/impl/src/test/resources/__files/keycloak_user_roles.json new file mode 100644 index 00000000..602a23c6 --- /dev/null +++ b/src/modules/rest/impl/src/test/resources/__files/keycloak_user_roles.json @@ -0,0 +1,42 @@ +[ + { + "id": "e3928f3f-4077-4073-9a2e-f68d29a73f01", + "name": "default-roles-master", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "d7a7e795-974a-41b1-9a0d-0cc5fcf94a31" + }, + { + "id": "f64c653d-4ef6-474f-964e-785a90e5d6a9", + "name": "create-realm", + "description": "${role_create-realm}", + "composite": false, + "clientRole": false, + "containerId": "d7a7e795-974a-41b1-9a0d-0cc5fcf94a31" + }, + { + "id": "161a87f6-f293-4235-a486-30547dbfdc0d", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "d7a7e795-974a-41b1-9a0d-0cc5fcf94a31" + }, + { + "id": "abe053dd-725c-45ff-8bc9-c2a70192f448", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "d7a7e795-974a-41b1-9a0d-0cc5fcf94a31" + }, + { + "id": "a7b9fb8d-1012-4a16-b7c0-ef9fb4bc7649", + "name": "admin", + "description": "${role_admin}", + "composite": true, + "clientRole": false, + "containerId": "d7a7e795-974a-41b1-9a0d-0cc5fcf94a31" + } +] \ No newline at end of file diff --git a/src/modules/rest/impl/src/test/resources/__files/keycloak_users.json b/src/modules/rest/impl/src/test/resources/__files/keycloak_users.json new file mode 100644 index 00000000..d6f517c5 --- /dev/null +++ b/src/modules/rest/impl/src/test/resources/__files/keycloak_users.json @@ -0,0 +1,67 @@ +[ + { + "id": "0e72c14e-53d8-4619-a05a-a605dc2102b9", + "createdTimestamp": 1654599284064, + "username": "admin", + "enabled": true, + "totp": false, + "emailVerified": false, + "attributes": { + "id": [ + "1" + ] + }, + "disableableCredentialTypes": [], + "requiredActions": [], + "notBefore": 0, + "access": { + "manageGroupMembership": true, + "view": true, + "mapRoles": true, + "impersonate": true, + "manage": true + } + }, + { + "id": "0e72c14e-53d8-4619-a05a-a605dc2102b9", + "createdTimestamp": 1655903731184, + "username": "test-user", + "enabled": true, + "totp": false, + "emailVerified": false, + "firstName": "Marco", + "lastName": "Volpini", + "email": "mail@mail.com", + "disableableCredentialTypes": [], + "requiredActions": [], + "notBefore": 0, + "access": { + "manageGroupMembership": true, + "view": true, + "mapRoles": true, + "impersonate": true, + "manage": true + } + }, + { + "id": "0e72c14e-53d8-4619-a05a-a605dc2102b9", + "createdTimestamp": 1655903783995, + "username": "test-user-2", + "enabled": true, + "totp": false, + "emailVerified": false, + "firstName": "Marco", + "lastName": "Volpini2", + "email": "mail@mail.com", + "disableableCredentialTypes": [], + "requiredActions": [], + "notBefore": 0, + "access": { + "manageGroupMembership": true, + "view": true, + "mapRoles": true, + "impersonate": true, + "manage": true + } + } +] \ No newline at end of file diff --git a/src/modules/rest/impl/src/test/resources/__files/token_response.json b/src/modules/rest/impl/src/test/resources/__files/token_response.json new file mode 100644 index 00000000..9c5849ef --- /dev/null +++ b/src/modules/rest/impl/src/test/resources/__files/token_response.json @@ -0,0 +1,7 @@ +{ + "access_token": "CPURR33RUz-gGhjwODTd9zXo5JkQx4wS", + "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjM4ZjM4ODM0NjhmYzY1OWFiYjQ0NzVmMzYzMTNkMjI1ODVjMmQ3Y2EiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIxMDA5NzI5MzYzODY0LTJpNWNvYzRlZjBmZ3VvZTB1cWNmYnVxN3I1Z25vMGZ2LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiYXVkIjoiMTAwOTcyOTM2Mzg2NC0yaTVjb2M0ZWYwZmd1b2UwdXFjZmJ1cTdyNWdubzBmdi5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInN1YiI6IjExMjMyMjI0MjYzNjY0NzkyNDMyOSIsImhkIjoiZ2Vvc29sdXRpb25zZ3JvdXAuY29tIiwiZW1haWwiOiJtYXJjby52b2xwaW5pQGdlb3NvbHV0aW9uc2dyb3VwLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJhdF9oYXNoIjoic2c1dlVmV181b2lRNXZUYnJIYmRnUSIsIm5hbWUiOiJNYXJjbyBWb2xwaW5pIiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9hL0FBVFhBSncyRkI0Ui1tMkNTWlFmSEtPSVVNSHZTTjJZR0VHbDhkWkpVdkI0PXM5Ni1jIiwiZ2l2ZW5fbmFtZSI6Ik1hcmNvIiwiZmFtaWx5X25hbWUiOiJWb2xwaW5pIiwibG9jYWxlIjoiZW4iLCJpYXQiOjE2NTQwNjUwNjYsImV4cCI6MTY1NDA2ODY2Nn0.Yi3PBD51lz8tJYDQVSqC3LPDYje6fUBx9sIk2i5P4BGGWk2bnqFaQe0pj04nOsboIPNEmVkQn6Bqsi_RXWhFc5hUt6z-yGMy8QZ_xRlAfxAzxjkQA3PCALebnGCECg7dnG3PjJAkS-gd6DFc9RY34UAQtsFmLv405SclrFLhtVZNRA6ZXiTZ2Pg_NfNjnR7qoT1EErbZzF3tiLjbS_O1H8tPCECleJYjESTCJ1PqVcjBrSBDXUb5CkmxvUGQN74GeguaiZm2ZZuzU5H9PKmubgn2IHoc6gmJ8PSDRZTDp2pI9YpMYvUIOWepzK4JZp11LwMH4HiZYAHUxBjCslTSag", + "scope": "openid profile email phone address", + "expires_in": 86400, + "token_type": "Bearer" +} \ No newline at end of file diff --git a/src/modules/rest/impl/src/test/resources/__files/token_response_keycloak.json b/src/modules/rest/impl/src/test/resources/__files/token_response_keycloak.json new file mode 100644 index 00000000..4d5620e0 --- /dev/null +++ b/src/modules/rest/impl/src/test/resources/__files/token_response_keycloak.json @@ -0,0 +1,8 @@ +{ + "access_token": "new_access_token", + "refresh_token": "new_refresh_token", + "id_token": "id_token", + "scope": "openid keycloak profile email phone address", + "expires_in": 86400, + "token_type": "Bearer" +} \ No newline at end of file diff --git a/src/modules/rest/impl/src/test/resources/__files/userinfo.json b/src/modules/rest/impl/src/test/resources/__files/userinfo.json new file mode 100644 index 00000000..5ed17d0b --- /dev/null +++ b/src/modules/rest/impl/src/test/resources/__files/userinfo.json @@ -0,0 +1,26 @@ +{ + "clientID": "kbyuFDidLLm280LIwVFiazOqjO3ty8KH", + "created_at": "2020-12-01T16:10:14.659Z", + "email": "ritter@erdukunde.de", + "email_verified": true, + "family_name": "Karl", + "given_name": "Ritter", + "roles": "ADMIN", + "groups": [ + "group1", + "group2" + ], + "locale": "it", + "name": "Karl Ritter", + "nickname": "Ritz", + "updated_at": "2022-05-274T16:27:04.884Z", + "user_id": "100301874944276879963462152", + "persistent": {}, + "user_metadata": {}, + "app_metadata": {}, + "iss": "https://samples.auth0.com/", + "sub": "100301874944276879963462152", + "aud": "kbyuFDidLLm280LIwVFiazOqjO3ty8KH", + "iat": 1607963235, + "qu": "geosolutionsgroup.com" +} \ No newline at end of file diff --git a/src/modules/rest/pom.xml b/src/modules/rest/pom.xml index ecdb63c2..e74169c3 100644 --- a/src/modules/rest/pom.xml +++ b/src/modules/rest/pom.xml @@ -18,14 +18,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - + 4.0.0 it.geosolutions.geostore geostore-modules - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-rest-root diff --git a/src/modules/rest/test/pom.xml b/src/modules/rest/test/pom.xml index 00d1675e..4d046251 100644 --- a/src/modules/rest/test/pom.xml +++ b/src/modules/rest/test/pom.xml @@ -4,7 +4,7 @@ it.geosolutions.geostore geostore-rest-root - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-rest-test @@ -33,24 +33,21 @@ it.geosolutions.geostore geostore-rest-impl + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-api + - - - org.apache.maven.plugins - maven-compiler-plugin - - utf8 - 1.7 - 1.7 - - - org.apache.maven.plugins maven-war-plugin @@ -78,9 +75,7 @@ manual - - diff --git a/src/modules/rest/test/src/main/java/it/geosolutions/geostore/services/rest/RESTTest.java b/src/modules/rest/test/src/main/java/it/geosolutions/geostore/services/rest/RESTTest.java index 13d85bb7..77e5dec4 100644 --- a/src/modules/rest/test/src/main/java/it/geosolutions/geostore/services/rest/RESTTest.java +++ b/src/modules/rest/test/src/main/java/it/geosolutions/geostore/services/rest/RESTTest.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -24,16 +24,14 @@ import it.geosolutions.geostore.core.model.enums.Role; import it.geosolutions.geostore.services.CategoryService; import it.geosolutions.geostore.services.UserService; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.InitializingBean; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class RESTTest implements InitializingBean { - private final static Logger LOGGER = Logger.getLogger(RESTTest.class); + private static final Logger LOGGER = LogManager.getLogger(RESTTest.class); protected UserService userService; @@ -47,7 +45,7 @@ public void afterPropertiesSet() throws Exception { long catCnt = categoryService.getCount(null); if (catCnt == 0) { LOGGER.info("No category found. Creating default."); - for (String name : new String[] { "TestCategory1", "TestCategory2" }) { + for (String name : new String[] {"TestCategory1", "TestCategory2"}) { Category c = new Category(); c.setName(name); categoryService.insert(c); diff --git a/src/modules/rest/test/src/main/resources/log4j.properties b/src/modules/rest/test/src/main/resources/log4j.properties deleted file mode 100644 index d81feb72..00000000 --- a/src/modules/rest/test/src/main/resources/log4j.properties +++ /dev/null @@ -1,9 +0,0 @@ -log4j.rootLogger=INFO, consoleAppender - -log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender -log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout -log4j.appender.consoleAppender.layout.ConversionPattern=%p %d{yyyy-MM-dd HH:mm:ss.SSS} %c::%M:%L - %m%n - -log4j.logger.it.geosolutions.geostore.services.rest=DEBUG -log4j.logger.org.hibernate=INFO -log4j.logger.com.trg=INFO diff --git a/src/modules/rest/test/src/main/resources/log4j2.properties b/src/modules/rest/test/src/main/resources/log4j2.properties new file mode 100644 index 00000000..411701e7 --- /dev/null +++ b/src/modules/rest/test/src/main/resources/log4j2.properties @@ -0,0 +1,17 @@ +rootLogger.level = INFO +appenders = console + + +appender.console.type = Console +appender.console.name = LogToConsole +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = %p %d{yyyy-MM-dd HH:mm:ss.SSS} %c::%M:%L - %m%n +rootLogger.appenderRef.stdout.ref = LogToConsole +rootLogger.appenderRef.console.ref = LogToConsole + +logger.restsrv.name=it.geosolutions.geostore.services.rest +logger.restsrv.level= DEBUG +logger.hibernate1.name=org.hibernate +logger.hibernate1.level=INFO +logger.trg1.name=com.trg +logger.trg1.level=INFO diff --git a/src/pom.xml b/src/pom.xml index 0f645b80..bc16a824 100644 --- a/src/pom.xml +++ b/src/pom.xml @@ -18,13 +18,14 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - + 4.0.0 it.geosolutions geostore - 1.7-SNAPSHOT + 2.2-SNAPSHOT it.geosolutions.geostore @@ -33,87 +34,101 @@ GeoStore - 0 Server Root - - core - modules - web - - - 1.7-SNAPSHOT geostore - - 2.3.2 + 3.5.7 5.3.0.4-fuse - 2.5.1 - 3.0.5.RELEASE - + 5.3.18 - - 3.0.5.RELEASE - 3.1.3.RELEASE + + 5.7.12 + 2.5.2.RELEASE 1.6.1.2-fuse - 1.6.2 1.4 - 1.0 - 2.7.0 2.8.1 1.3.03 - 2.1 - + 2.3.1 1.1.1 2.3 3.2.2 - 1.7.0 + 1.9.4 1.2.2 1.4 - 1.1 4.0 - 2.1 2.1.2 - - 1.2.16 - 1.7.2 - 1.6.1 - + 2.19.0 2.2-beta-5 - 3.1.0.2-fuse - 2.0.8 1.0.1 1.5.4 2.0 - 6.1.0.1-fuse - 1.2 - - - - + 6.1H.22 + 1.5.4 1.0 - 3.3.2.GA - 3.3.1.GA - 3.3.0.ga - 0.5.1 + 5.4.33.Final + geosolutions-1.3.0 2.2.3 2.1_3 18.0 - 1.10.19 - + 3.8.0 3.8.0.GA - 2.3-M1 2.7-RC2 + 2.16.1 + 2.9.0 + 1.5.2 + 1.0 + 2.7 + 4.5.13 + 1.0.7 + 1.0.2.Final + 3.0.1 + 4.13.2 + geosolutions-1.3.0 + 1.0-beta-2 + 1.1.1 + 1.1 + 1.1.3 + 1.1.1 + 1.3.3 + 42.3.9 + 1.10 + 1.3.175 + 4.6.1 + 11.2.0 + 2.0.17.RELEASE + 1.9.3 + 18.0.0 + 1.0.11.RELEASE + 3.18.3 + 2.1.12 + 1.3 + 1.12 + 2.3.20 + 1.5.5 + 0.9.16 + 6.1.23 + 1.2 + 2.4.2-geoserver + 3.11.0 + 3.8.0 + true + deprecation + 3.1.12.2 + 6.42.0 + + 2.18.0 @@ -122,24 +137,24 @@ - - - false - geosolutions - ftp://maven.geo-solutions.it/ - - - - false - geosolutions - ftp://maven.geo-solutions.it/ - + + + false + geosolutions + ftp://maven.geo-solutions.it/ + + + + false + geosolutions + ftp://maven.geo-solutions.it/ + - + @@ -152,37 +167,37 @@ it.geosolutions.geostore geostore-core - ${geostore-version} + ${project.version} it.geosolutions.geostore geostore-model - ${geostore-version} + ${project.version} it.geosolutions.geostore geostore-security - ${geostore-version} + ${project.version} it.geosolutions.geostore geostore-persistence - ${geostore-version} + ${project.version} it.geosolutions.geostore geostore-services-api - ${geostore-version} + ${project.version} it.geosolutions.geostore geostore-services-impl - ${geostore-version} + ${project.version} @@ -191,86 +206,93 @@ it.geosolutions.geostore geostore-generic-api - ${geostore-version} + ${project.version} it.geosolutions.geostore geostore-login-parent - ${geostore-version} + ${project.version} it.geosolutions.geostore geostore-login-api - ${geostore-version} + ${project.version} it.geosolutions.geostore geostore-login-impl - ${geostore-version} + ${project.version} it.geosolutions.geostore geostore-webgis - ${geostore-version} + ${project.version} it.geosolutions.geostore geostore-rest-root - ${geostore-version} + ${project.version} it.geosolutions.geostore geostore-rest-api - ${geostore-version} + ${project.version} it.geosolutions.geostore geostore-rest-impl - ${geostore-version} + ${project.version} it.geosolutions.geostore geostore-rest-test - ${geostore-version} + ${project.version} it.geosolutions.geostore geostore-rest-extjs - ${geostore-version} + ${project.version} it.geosolutions.geostore geostore-rest-auditing - ${geostore-version} + ${project.version} + + + + com.google.code.gson + gson + ${gson.version} + it.geosolutions.geostore geostore-services-impl - ${geostore-version} + ${project.version} test-jar test - - - + + + quartz quartz - 1.5.2 + ${quartz.version} javassist @@ -279,46 +301,41 @@ runtime - - - + + + - org.slf4j - slf4j-api - ${slf4japi-version} + org.apache.logging.log4j + log4j-core + ${log4j-version} - log4j - log4j + org.apache.logging.log4j + log4j-api ${log4j-version} - org.slf4j - slf4j-log4j12 - ${slf4japi-version} + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j-version} - dom4j - dom4j - ${dom4j-version} + org.apache.logging.log4j + log4j-1.2-api + ${log4j-version} - + jdom jdom - 1.0 + ${jdom.version} - - - - + + + commons-lang commons-lang @@ -327,12 +344,12 @@ commons-io commons-io - 2.7 + ${commons-io.version} - commons-httpclient - commons-httpclient - 3.1 + org.apache.httpcomponents + httpclient + ${httpclient.version} commons-dbcp @@ -353,7 +370,6 @@ commons-codec commons-codec ${commons-codec-version} - @@ -361,33 +377,60 @@ guava ${guava-version} - org.mockito mockito-core ${mockito-version} test + + org.mockito + mockito-inline + ${mockito-version} + test + + + com.github.tomakehurst + wiremock-standalone + ${wiremock-standalone.version} + test + + + org.hamcrest + hamcrest-core + ${hamcrest-core.version} + test + - - - - + + + org.glassfish.jersey.containers jersey-container-servlet ${jersey-version} - - org.glassfish.jersey.ext - jersey-spring3 - ${jersey-version} - + + org.glassfish.jersey.ext + jersey-spring3 + ${jersey-version} + + + + com.sun.jersey.contribs + jersey-apache-client + ${jersey-apache-client.version} + - - - + + com.sun.jersey + jersey-client + ${jersey-apache-client.version} + + + + javax.xml.ws jaxws-api @@ -410,6 +453,23 @@ cxf-rt-transports-http ${cxf-version} + + org.apache.cxf + cxf-rt-rs-extension-providers + ${cxf-version} + + + + org.apache.cxf + cxf-rt-rs-json-basic + ${cxf-version} + + + + org.apache.cxf + cxf-rt-rs-client + ${cxf-version} + org.apache.cxf cxf-rt-transports-http-jetty @@ -461,11 +521,11 @@ cxf-rt-core ${cxf-version} - - com.google.code.cxf-spring-security - cxf-spring-security - ${cxf-version} - + + com.google.code.cxf-spring-security + cxf-spring-security + ${cxf-version} + @@ -482,9 +542,9 @@ ${javax-mail-version} - - - + + + javax.xml.bind jaxb-api @@ -496,9 +556,17 @@ ${jaxb-impl-version} - - - + + + + + org.springframework + spring-framework-bom + ${spring-version} + pom + import + + org.springframework spring-core @@ -508,19 +576,22 @@ org.springframework spring-jmx ${spring-support} - + + + log4j + log4j + + org.springframework spring-beans ${spring-version} - org.springframework spring-context ${spring-version} - org.springframework @@ -536,7 +607,13 @@ org.springframework spring-jdbc ${spring-version} - + + + + org.springframework + spring-test + ${spring-version} + test @@ -557,15 +634,28 @@ ${spring-version} + - org.springframework - spring-remoting - ${spring-version} + com.fasterxml.jackson.core + jackson-core + ${jackson-version} + + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson-version} + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-version} - - + + org.springframework.security spring-security-core @@ -592,7 +682,37 @@ spring-security-config ${spring-security-version} - + + org.springframework.security.oauth + spring-security-oauth2 + ${spring-security-oauth2.version} + + + org.springframework + spring-context + + + org.springframework + spring-core + + + org.springframework + spring-webmvc + + + org.springframework + spring-beans + + + org.codehaus.jackson + jackson-core-asl + + + org.codehaus.jackson + jackson-mapper-asl + + + org.springframework.security spring-security-ldap @@ -602,35 +722,84 @@ org.springframework.security spring-security-crypto - 3.1.3.RELEASE + ${spring-security-version} + + + org.jasypt + jasypt-acegisecurity + ${jasypt.version} + + + org.acegisecurity + acegi-security-tiger + ${acegi-security-tiger.version} + + + log4j + log4j + + + org.springframework + spring-support + + + + + org.keycloak + keycloak-spring-security-adapter + ${keycloak-spring-security-adapter.version} + + + org.keycloak + keycloak-authz-client + ${keycloak-spring-security-adapter.version} + + + org.keycloak + keycloak-admin-client + ${keycloak-spring-security-adapter.version} - org.jasypt - jasypt - 1.8 + org.springframework.security + spring-security-jwt + ${spring-security-jwt.version} - - - + com.auth0 + java-jwt + ${java-jwt.version} + + + + + + + + + + + org.hibernate.javax.persistence + hibernate-jpa-2.1-api + ${hibernate-jpa-2.1-api.version} - - - + + + javax.servlet - servlet-api - 2.3 + javax.servlet-api + ${javax.servlet-api.version} provided - - - + + + asm asm @@ -647,9 +816,9 @@ ${cglib-version} - - - + + + org.codehaus.jettison jettison @@ -667,118 +836,104 @@ ${aspectj-version} - - - + + + junit junit - 4.12 + ${junit.version} test - - - + + + + com.googlecode.genericdao - dao - 1.1.0 - + dao-hibernate + ${dao-hibernate.version} + com.googlecode.genericdao search-jpa-hibernate - 1.1.0 - - - - - - - org.hibernate - hibernate-annotations - ${hibernate-annotations-version} - - - org.hibernate - hibernate-commons-annotations - ${hibernate-commons-annotations-version} + ${dao-hibernate.version} + + + + org.hibernate - hibernate-entitymanager + hibernate-core ${hibernate-version} + org.hibernate - hibernate-validator - 3.0.0.ga + hibernate-ehcache + ${hibernate-version} - - - + + + org.hibernatespatial hibernate-spatial-postgis - 1.1.1 + ${hibernate-spatial-postgis.version} - - - + + + org.postgis postgis-jdbc - 1.3.3 + ${postgis-jdbc.version} - postgresql + org.postgresql postgresql - 8.4-702.jdbc3 + ${postgresql.version} com.vividsolutions jts - 1.10 + ${jts.version} - - - - + + + javax.media jai_core - 1.1.3 + ${jai_codec.version} javax.media jai_codec - 1.1.3 + ${jai_codec.version} javax.media jai_imageio - 1.1 + ${jai_imageio.version} com.googlecode.jaitools jt-utils - 1.1.1 + ${jt-utils.version} net.java.dev.jsr-275 jsr-275 - 1.0-beta-2 + ${jsr-275.version} @@ -787,9 +942,9 @@ ${javax-annotation-version} - - - + + + org.opengis geoapi @@ -866,6 +1021,65 @@ ${gt-version} + + + com.h2database + h2 + ${h2.version} + + + + + com.oracle + ojdbc6 + ${ojdbc6.version} + + + + + net.sf.json-lib + json-lib + ${json-lib.version} + + + + info.picocli + picocli + ${picocli.version} + + + + + org.freemarker + freemarker + ${freemarker.version} + + + + + org.apache.directory.server + apacheds-all + ${apacheds-all.version} + test + + + org.apache.directory.shared + shared-ldap + ${shared-ldap.version} + test + + + org.mortbay.jetty + jetty + ${jetty.version} + test + + + jstl + jstl + ${jstl.version} + test + @@ -875,20 +1089,6 @@ org.apache.maven.plugins maven-compiler-plugin - 3.5.1 - - 1.7 - 1.7 - true - UTF-8 - - true - 128M - 1512M - @@ -908,12 +1108,10 @@ see http://jira.codehaus.org/browse/SUREFIRE-121 --> 2.4.2 - -Dfile.encoding=UTF-8 - + -Dfile.encoding=UTF-8 -noverify + - - @@ -921,34 +1119,41 @@ geosolutions GeoSolutions Repository - http://maven.geo-solutions.it + https://maven.geo-solutions.it + + + + maven2-repository.maven + Repository for Maven2 + https://repo1.maven.org/maven2/ + default maven2-repository.dev.java.net Java.net Repository for Maven - http://download.java.net/maven/2/ + https://download.java.net/maven/2/ default false - + - + osgeo @@ -970,10 +1175,10 @@ true + - repo1.maven.org-maven2 repo1.maven.org-maven2 @@ -985,7 +1190,164 @@ true - + + + default + + true + + + core + modules + web + + + + all + + core + modules + web + cli + + + + cli + + cli + + + + pmd + + + qa + !false + + + + + + org.apache.maven.plugins + maven-pmd-plugin + ${maven-pmd-plugin.version} + + + net.sourceforge.pmd + pmd-core + ${pmd.version} + + + net.sourceforge.pmd + pmd-java + ${pmd.version} + + + net.sourceforge.pmd + pmd-javascript + ${pmd.version} + + + net.sourceforge.pmd + pmd-jsp + ${pmd.version} + + + + + ${geostore.basedir}/qa/pmd-ruleset.xml + + 3 + 3 + true + true + + target/generated-sources + + + + + + check + + + + + + + + + + spotbugs + + + qa + !false + + + + + + com.github.spotbugs + spotbugs-maven-plugin + ${spotbugs-maven-pligin} + + More + + true + 15 + ${geostore.basedir}/qa/spotbugs-exclude.xml + -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + + + + + check + + + + + + + + + diff --git a/src/web/app/pom.xml b/src/web/app/pom.xml index cf5baf91..1a95fe2f 100644 --- a/src/web/app/pom.xml +++ b/src/web/app/pom.xml @@ -1,10 +1,11 @@ - + 4.0.0 it.geosolutions.geostore geostore-web - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-webapp @@ -45,18 +46,30 @@ cxf-rt-frontend-jaxrs + + org.apache.cxf + cxf-rt-rs-extension-providers + + + org.apache.cxf + cxf-rt-rs-json-basic + + + org.codehaus.jettison + jettison + - + - commons-httpclient - commons-httpclient + org.apache.httpcomponents + httpclient @@ -65,10 +78,10 @@ test - org.slf4j - slf4j-api + org.apache.cxf + cxf-rt-rs-client + test - @@ -84,21 +97,26 @@ org.apache.directory.server apacheds-all - 1.5.5 test org.apache.directory.shared shared-ldap - 0.9.16 test + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-1.2-api + org.mortbay.jetty jetty - 6.1.14 test @@ -106,13 +124,12 @@ javax.servlet - servlet-api + javax.servlet-api test jstl jstl - 1.2 test @@ -136,7 +153,9 @@ process-classes - + @@ -151,7 +170,7 @@ - postgresql + org.postgresql postgresql @@ -170,7 +189,9 @@ process-classes - + @@ -197,7 +218,9 @@ process-classes - + @@ -214,7 +237,6 @@ com.oracle ojdbc6 - 11.2.0 @@ -243,16 +265,6 @@ geostore - - org.apache.maven.plugins - maven-compiler-plugin - - utf8 - 1.7 - 1.7 - - - org.apache.maven.plugins maven-war-plugin diff --git a/src/web/app/src/main/java/it/geosolutions/geostore/init/GeoStoreInit.java b/src/web/app/src/main/java/it/geosolutions/geostore/init/GeoStoreInit.java index a0e506c3..409c8202 100644 --- a/src/web/app/src/main/java/it/geosolutions/geostore/init/GeoStoreInit.java +++ b/src/web/app/src/main/java/it/geosolutions/geostore/init/GeoStoreInit.java @@ -1,19 +1,19 @@ /* * Copyright (C) 2007 - 2012 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -36,57 +36,48 @@ import it.geosolutions.geostore.services.rest.model.RESTUserGroup; import it.geosolutions.geostore.services.rest.model.UserGroupList; import it.geosolutions.geostore.services.rest.utils.GeoStoreJAXBContext; - import java.io.File; import java.util.List; - import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; - -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.InitializingBean; -/** - * - * @author ETj (etj at geo-solutions.it) - */ +/** @author ETj (etj at geo-solutions.it) */ public class GeoStoreInit implements InitializingBean { - private final static Logger LOGGER = Logger.getLogger(GeoStoreInit.class); + private static final Logger LOGGER = LogManager.getLogger(GeoStoreInit.class); protected UserService userService; protected CategoryService categoryService; protected UserGroupService userGroupService; - + protected File userListInitFile = null; protected File categoryListInitFile = null; - + protected File userGroupListInitFile = null; - /** - * The password encoder to be set as default - */ + /** The password encoder to be set as default */ protected GeoStorePasswordEncoder passwordEncoder = null; - /** - * If set to true, the recoding of the password is automatic - */ + /** If set to true, the recoding of the password is automatic */ protected boolean allowPasswordRecoding = false; - @Override + @Override public void afterPropertiesSet() throws Exception { LOGGER.info("===== Starting GeoStore services ====="); - //initialize password encoding + // initialize password encoding initPasswordEncoding(); long catCnt = categoryService.getCount(null); if (catCnt == 0) { LOGGER.warn("No category found."); if (categoryListInitFile != null) { LOGGER.warn("Initializing categories from file " + categoryListInitFile); - initCategories(categoryListInitFile); + initCategories(categoryListInitFile); } else { LOGGER.info("No category initializer defined."); } @@ -105,9 +96,8 @@ public void afterPropertiesSet() throws Exception { } } else { LOGGER.info("UsersGroup already in db: " + userGroupCnt); - } - + long userCnt = userService.getCount(null); if (userCnt == 0) { LOGGER.warn("No user found."); @@ -121,57 +111,64 @@ public void afterPropertiesSet() throws Exception { LOGGER.info("Users already in db: " + userCnt); } } - - private void initPasswordEncoding(){ - LOGGER.info("=== Set up the security system ===="); - LOGGER.info("Encoding Type:" + passwordEncoder.getEncodingType()); - - PwEncoder.setEncoder(this.passwordEncoder); - //check and convert passwords - try { - List users = userService.getAll(0, 1); - if(users != null && users.size()>0){ - //check password encription of the first user availabe - boolean responsible = this.passwordEncoder.isResponsibleForEncoding(users.get(0).getPassword()); - - //if the current password encoder is the responsible for the encoding of the password - //we suppose the conversion is already happended - if(responsible) return; - LOGGER.warn("======================================================================================="); - LOGGER.warn(" WARNING: USERS PASSWORDS ARE NOT SYNCRONIZED WITH THE CONFIGURED PASSWORD ENCODER "); - LOGGER.warn("======================================================================================="); - //check if the password is old legacy, so GeoStoreAESEncoder is the responsible for the encoding - GeoStoreAESEncoder e = new GeoStoreAESEncoder(); - boolean isLegacy = e.isResponsibleForEncoding(users.get(0).getPassword()); - if(isLegacy){ - if (!allowPasswordRecoding){ - LOGGER.warn("To convert old passwords to new ones use geostoreInitializer.allowPasswordRecoding=true"); - return; - } - LOGGER.info("Starting password conversion..."); - for(User u : userService.getAll(null, null)){ - String p = u.getPassword(); - if (e.isResponsibleForEncoding(p)){ - String dec = e.decode(p); - String enc = this.passwordEncoder.encodePassword(dec.toCharArray(), null); - u.setPassword(enc); - try { - userService.update(u); - LOGGER.info("UPDATED USER PASSWORD for the user:"+u.getName()); - } catch (NotFoundServiceEx e1) { - LOGGER.error("===> ERROR updating user password for user" + u.getName() ); - - } - } - } - LOGGER.info("Password conversion finished!"); - } - - } - } catch (BadRequestServiceEx e) { - //error getting users is not a problem at this stage. - //e.printStackTrace(); - } + + private void initPasswordEncoding() { + LOGGER.info("=== Set up the security system ===="); + LOGGER.info("Encoding Type:" + passwordEncoder.getEncodingType()); + + PwEncoder.setEncoder(this.passwordEncoder); + // check and convert passwords + try { + List users = userService.getAll(0, 1); + if (users != null && users.size() > 0) { + // check password encription of the first user availabe + boolean responsible = + this.passwordEncoder.isResponsibleForEncoding(users.get(0).getPassword()); + + // if the current password encoder is the responsible for the encoding of the + // password + // we suppose the conversion is already happended + if (responsible) return; + LOGGER.warn( + "======================================================================================="); + LOGGER.warn( + " WARNING: USERS PASSWORDS ARE NOT SYNCRONIZED WITH THE CONFIGURED PASSWORD ENCODER "); + LOGGER.warn( + "======================================================================================="); + // check if the password is old legacy, so GeoStoreAESEncoder is the responsible for + // the encoding + GeoStoreAESEncoder e = new GeoStoreAESEncoder(); + boolean isLegacy = e.isResponsibleForEncoding(users.get(0).getPassword()); + if (isLegacy) { + if (!allowPasswordRecoding) { + LOGGER.warn( + "To convert old passwords to new ones use geostoreInitializer.allowPasswordRecoding=true"); + return; + } + LOGGER.info("Starting password conversion..."); + for (User u : userService.getAll(null, null)) { + String p = u.getPassword(); + if (e.isResponsibleForEncoding(p)) { + String dec = e.decode(p); + String enc = + this.passwordEncoder.encodePassword(dec.toCharArray(), null); + u.setPassword(enc); + try { + userService.update(u); + LOGGER.info("UPDATED USER PASSWORD for the user:" + u.getName()); + } catch (NotFoundServiceEx e1) { + LOGGER.error( + "===> ERROR updating user password for user" + u.getName()); + } + } + } + LOGGER.info("Password conversion finished!"); + } + } + } catch (BadRequestServiceEx e) { + // error getting users is not a problem at this stage. + // e.printStackTrace(); + } } private void initCategories(File file) { @@ -201,7 +198,6 @@ private void initCategories(File file) { throw new RuntimeException("Error while initting categories."); } - } private void initUsers(File file) { @@ -234,7 +230,7 @@ private void initUsers(File file) { throw new RuntimeException("Error while initting users."); } } - + private void initUsersGroup(File file) throws NotFoundServiceEx, BadRequestServiceEx { try { userGroupService.insertSpecialUsersGroups(); @@ -245,11 +241,11 @@ private void initUsersGroup(File file) throws NotFoundServiceEx, BadRequestServi UserGroup ug = new UserGroup(); ug.setGroupName(userGroup.getGroupName()); ug.setDescription(userGroup.getDescription()); - try{ - userGroupService.insert(ug); - } - catch(ReservedUserGroupNameEx e){ - // If a reserved username is in the init usergroup file log the exception and skip this insertion + try { + userGroupService.insert(ug); + } catch (ReservedUserGroupNameEx e) { + // If a reserved username is in the init usergroup file log the exception and + // skip this insertion LOGGER.warn(e.getMessage()); } } @@ -278,8 +274,11 @@ private static JAXBContext getUserContext() { allClasses.add(InitUserList.class); if (LOGGER.isDebugEnabled()) - LOGGER.debug("Initializing JAXBContext with " + allClasses.size() + " classes " - + allClasses); + LOGGER.debug( + "Initializing JAXBContext with " + + allClasses.size() + + " classes " + + allClasses); try { return JAXBContext.newInstance(allClasses.toArray(new Class[allClasses.size()])); @@ -298,7 +297,7 @@ public void setUserListInitFile(File userListInitFile) { public void setCategoryListInitFile(File categoryListInitFile) { this.categoryListInitFile = categoryListInitFile; } - + public void setUserGroupListInitFile(File userGroupListInitFile) { this.userGroupListInitFile = userGroupListInitFile; } @@ -312,29 +311,27 @@ public void setCategoryService(CategoryService categoryService) { public void setUserService(UserService userService) { this.userService = userService; } - public void setUserGroupService(UserGroupService userGroupService) { this.userGroupService = userGroupService; } - + // ========================================================================== - - public GeoStorePasswordEncoder getPasswordEncoder() { - return passwordEncoder; - } - public void setPasswordEncoder(GeoStorePasswordEncoder passwordEncoder) { - this.passwordEncoder = passwordEncoder; - } + public GeoStorePasswordEncoder getPasswordEncoder() { + return passwordEncoder; + } - // =========================================================================== - public boolean isAllowPasswordRecoding() { - return allowPasswordRecoding; - } + public void setPasswordEncoder(GeoStorePasswordEncoder passwordEncoder) { + this.passwordEncoder = passwordEncoder; + } - public void setAllowPasswordRecoding(boolean allowPasswordRecoding) { - this.allowPasswordRecoding = allowPasswordRecoding; - } + // =========================================================================== + public boolean isAllowPasswordRecoding() { + return allowPasswordRecoding; + } + public void setAllowPasswordRecoding(boolean allowPasswordRecoding) { + this.allowPasswordRecoding = allowPasswordRecoding; + } } diff --git a/src/web/app/src/main/java/it/geosolutions/geostore/init/LDAPInit.java b/src/web/app/src/main/java/it/geosolutions/geostore/init/LDAPInit.java index c85ec124..8acd2b17 100644 --- a/src/web/app/src/main/java/it/geosolutions/geostore/init/LDAPInit.java +++ b/src/web/app/src/main/java/it/geosolutions/geostore/init/LDAPInit.java @@ -1,31 +1,30 @@ /* * Copyright (C) 2015 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.init; import it.geosolutions.geostore.services.rest.security.UserLdapAuthenticationProvider; - import org.springframework.beans.factory.InitializingBean; public class LDAPInit implements InitializingBean { private UserLdapAuthenticationProvider authenticationProvider; - + public LDAPInit(UserLdapAuthenticationProvider authenticationProvider) { super(); this.authenticationProvider = authenticationProvider; @@ -35,5 +34,4 @@ public LDAPInit(UserLdapAuthenticationProvider authenticationProvider) { public void afterPropertiesSet() throws Exception { authenticationProvider.synchronizeGroups(); } - } diff --git a/src/web/app/src/main/java/it/geosolutions/geostore/init/model/InitUserList.java b/src/web/app/src/main/java/it/geosolutions/geostore/init/model/InitUserList.java index a9eb62b1..1fe864f1 100644 --- a/src/web/app/src/main/java/it/geosolutions/geostore/init/model/InitUserList.java +++ b/src/web/app/src/main/java/it/geosolutions/geostore/init/model/InitUserList.java @@ -36,37 +36,28 @@ /** * Class UserList. - * + * * @author ETj (etj at geo-solutions.it) - * */ @XmlRootElement(name = "InitUserList") public class InitUserList implements Iterable { private List list; - public InitUserList() { - - } + public InitUserList() {} - /** - * @param list - */ + /** @param list */ public InitUserList(List list) { this.list = list; } - /** - * @return List - */ + /** @return List */ @XmlElement(name = "User") public List getList() { return list; } - /** - * @param list - */ + /** @param list */ public void setList(List list) { this.list = list; } @@ -75,5 +66,4 @@ public void setList(List list) { public Iterator iterator() { return list == null ? Collections.EMPTY_LIST.iterator() : list.iterator(); } - } diff --git a/src/web/app/src/main/resources/applicationContext.xml b/src/web/app/src/main/resources/applicationContext.xml index 38cf6c42..d98ffb81 100644 --- a/src/web/app/src/main/resources/applicationContext.xml +++ b/src/web/app/src/main/resources/applicationContext.xml @@ -27,7 +27,6 @@ - - + \ No newline at end of file diff --git a/src/web/app/src/main/resources/geostore-ovr.properties b/src/web/app/src/main/resources/geostore-ovr.properties index 6a744da4..0136944b 100644 --- a/src/web/app/src/main/resources/geostore-ovr.properties +++ b/src/web/app/src/main/resources/geostore-ovr.properties @@ -23,4 +23,63 @@ #geostoreInitializer.allowPasswordRecoding=true # Configure session duration. -#restSessionService.sessionTimeout=3600 \ No newline at end of file +#restSessionService.sessionTimeout=3600 + + +#################### +# OpenId Properties +#################### + +# ---------- +# Google +# ---------- +## mandatory props +# googleOAuth2Config.clientId= +# googleOAuth2Config.clientSecret= +# googleOAuth2Config.autoCreateUser=true +# googleOAuth2Config.redirectUri= +# googleOAuth2Config.internalRedirectUri= +# googleOAuth2Config.enabled=false +# googleOAuth2Config.authenticatedDefaultRole=USER + + + +## needed if groups and roles should be extracted from the claims of the id_token. +# googleOAuth2Config.groupsClaim= +# googleOAuth2Config.rolesClaim= + + +## if not specified the optional properties needs to be populated +# googleOAuth2Config.discoveryUrl=https://accounts.google.com/.well-known/openid-configuration + +## the following are optional and auto-populated if the discoveryUrl is provided +# googleOAuth2Config.accessTokenUri= +# googleOAuth2Config.authorizationUri= +# googleOAuth2Config.checkTokenEndpointUrl= +# googleOAuth2Config.logoutUri= +# googleOAuth2Config.scopes= +# googleOAuth2Config.idTokenUri= + +# ---------- +# Keycloak +# ---------- + +## Keycloak Authentication + +# keycloakOAuth2Config.jsonConfig= +# keycloakOAuth2Config.internalRedirectUri=../../rest/users/user/details +# keycloakOAuth2Config.enabled=false +# keycloakOAuth2Config.autoCreateUser=true +# keycloakOAuth2Config.redirectUri=http://localhost:9191/geostore/rest/openid/keycloak/callback +# keycloakOAuth2Config.forceConfiguredRedirectURI=true +# keycloakOAuth2Config.authenticatedDefaultRole=USER +# keycloakOAuth2Config.roleMappings=admin:ADMIN,user:USER,guest:GUEST +# keycloakOAuth2Config.groupMappings= +# keycloakOAuth2Config.dropUnmapped=false + +## Keycloak as User and UserGroup repository +# keycloakRESTClient.serverUrl=http://localhost:8080 +# keycloakRESTClient.realm=master +# keycloakRESTClient.username=admin +# keycloakRESTClient.password=admin +# keycloakRESTClient.clientId=client-id diff --git a/src/web/app/src/main/resources/geostore-spring-security.xml b/src/web/app/src/main/resources/geostore-spring-security.xml index 59a6e7e6..9feadb99 100644 --- a/src/web/app/src/main/resources/geostore-spring-security.xml +++ b/src/web/app/src/main/resources/geostore-spring-security.xml @@ -22,14 +22,17 @@ + - + + + + + + @@ -38,35 +41,54 @@ + + + + + + + + + + + + + + - + - + + + - - - + + diff --git a/src/web/app/src/main/resources/security-integration-keycloak-direct.xml b/src/web/app/src/main/resources/security-integration-keycloak-direct.xml new file mode 100644 index 00000000..f692bc0f --- /dev/null +++ b/src/web/app/src/main/resources/security-integration-keycloak-direct.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/src/web/app/src/main/webapp/WEB-INF/web.xml b/src/web/app/src/main/webapp/WEB-INF/web.xml index 82364a03..1ece7f25 100644 --- a/src/web/app/src/main/webapp/WEB-INF/web.xml +++ b/src/web/app/src/main/webapp/WEB-INF/web.xml @@ -20,9 +20,9 @@ --> - + springSecurityFilterChain diff --git a/src/web/app/src/test/java/it/geosolutions/geostore/Start.java b/src/web/app/src/test/java/it/geosolutions/geostore/Start.java index 0dd344b9..6919acf8 100644 --- a/src/web/app/src/test/java/it/geosolutions/geostore/Start.java +++ b/src/web/app/src/test/java/it/geosolutions/geostore/Start.java @@ -11,7 +11,6 @@ import java.io.InputStreamReader; import java.util.logging.Level; import java.util.logging.Logger; - import org.mortbay.jetty.Connector; import org.mortbay.jetty.Server; import org.mortbay.jetty.bio.SocketConnector; @@ -20,130 +19,126 @@ /** * Jetty starter, will run geoserver inside the Jetty web container.
    - * Useful for debugging, especially in IDE were you have direct dependencies - * between the sources of the various modules (such as Eclipse). - * + * Useful for debugging, especially in IDE were you have direct dependencies between the sources of + * the various modules (such as Eclipse). + * * @author wolf - * */ public class Start { - private static final Logger log = Logger.getLogger(Start.class.getName()); + private static final Logger log = Logger.getLogger(Start.class.getName()); - public static void main(String[] args) { - final Server jettyServer = new Server(); + public static void main(String[] args) { + final Server jettyServer = new Server(); - try { - SocketConnector conn = new SocketConnector(); - String portVariable = System.getProperty("jetty.port"); - int port = parsePort(portVariable); - if (port <= 0) - port = 8280; - conn.setPort(port); - conn.setAcceptQueueSize(100); - conn.setMaxIdleTime(1000 * 60 * 60); - conn.setSoLingerTime(-1); + try { + SocketConnector conn = new SocketConnector(); + String portVariable = System.getProperty("jetty.port"); + int port = parsePort(portVariable); + if (port <= 0) port = 8280; + conn.setPort(port); + conn.setAcceptQueueSize(100); + conn.setMaxIdleTime(1000 * 60 * 60); + conn.setSoLingerTime(-1); - // Use this to set a limit on the number of threads used to respond - // requests - // BoundedThreadPool tp = new BoundedThreadPool(); - // tp.setMinThreads(8); - // tp.setMaxThreads(8); - // conn.setThreadPool(tp); + // Use this to set a limit on the number of threads used to respond + // requests + // BoundedThreadPool tp = new BoundedThreadPool(); + // tp.setMinThreads(8); + // tp.setMaxThreads(8); + // conn.setThreadPool(tp); - jettyServer.setConnectors(new Connector[] { conn }); - - /* - * Constraint constraint = new Constraint(); - * constraint.setName(Constraint.__BASIC_AUTH);; - * constraint.setRoles(new String[]{"user","admin","moderator"}); - * constraint.setAuthenticate(true); - * - * ConstraintMapping cm = new ConstraintMapping(); - * cm.setConstraint(constraint); cm.setPathSpec("/*"); - * - * SecurityHandler sh = new SecurityHandler(); sh.setUserRealm(new - * HashUserRealm("MyRealm","/Users/jdeolive/realm.properties")); - * sh.setConstraintMappings(new ConstraintMapping[]{cm}); - * - * WebAppContext wah = new WebAppContext(sh, null, null, null); - */ - WebAppContext wah = new WebAppContext(); - wah.setContextPath("/geostore"); - wah.setWar("src/main/webapp"); + jettyServer.setConnectors(new Connector[] {conn}); - jettyServer.setHandler(wah); - wah.setTempDirectory(new File("target/work")); - // this allows to send large SLD's from the styles form - wah.getServletContext().getContextHandler() - .setMaxFormContentSize(1024 * 1024 * 2); + /* + * Constraint constraint = new Constraint(); + * constraint.setName(Constraint.__BASIC_AUTH);; + * constraint.setRoles(new String[]{"user","admin","moderator"}); + * constraint.setAuthenticate(true); + * + * ConstraintMapping cm = new ConstraintMapping(); + * cm.setConstraint(constraint); cm.setPathSpec("/*"); + * + * SecurityHandler sh = new SecurityHandler(); sh.setUserRealm(new + * HashUserRealm("MyRealm","/Users/jdeolive/realm.properties")); + * sh.setConstraintMappings(new ConstraintMapping[]{cm}); + * + * WebAppContext wah = new WebAppContext(sh, null, null, null); + */ + WebAppContext wah = new WebAppContext(); + wah.setContextPath("/geostore"); + wah.setWar("src/main/webapp"); - String jettyConfigFile = System.getProperty("jetty.config.file"); - if (jettyConfigFile != null) { - log.info("Loading Jetty config from file: " + jettyConfigFile); - (new XmlConfiguration(new FileInputStream(jettyConfigFile))) - .configure(jettyServer); - } + jettyServer.setHandler(wah); + wah.setTempDirectory(new File("target/work")); + // this allows to send large SLD's from the styles form + wah.getServletContext().getContextHandler().setMaxFormContentSize(1024 * 1024 * 2); - jettyServer.start(); + String jettyConfigFile = System.getProperty("jetty.config.file"); + if (jettyConfigFile != null) { + log.info("Loading Jetty config from file: " + jettyConfigFile); + (new XmlConfiguration(new FileInputStream(jettyConfigFile))).configure(jettyServer); + } - /* - * Reads from System.in looking for the string "stop\n" in order to - * gracefully terminate the jetty server and shut down the JVM. This - * way we can invoke the shutdown hooks while debugging in eclipse. - * Can't catch CTRL-C to emulate SIGINT as the eclipse console is - * not propagating that event - */ - Thread stopThread = new Thread() { - @Override - public void run() { - BufferedReader reader = new BufferedReader( - new InputStreamReader(System.in)); - String line; - try { - while (true) { - line = reader.readLine(); - if ("stop".equals(line)) { - jettyServer.stop(); - System.exit(0); - } - } - } catch (Exception e) { - e.printStackTrace(); - System.exit(1); - } - } - }; - stopThread.setDaemon(true); - stopThread.run(); + jettyServer.start(); - // use this to test normal stop behaviour, that is, to check stuff - // that - // need to be done on container shutdown (and yes, this will make - // jetty stop just after you started it...) - // jettyServer.stop(); - } catch (Exception e) { - log.log(Level.SEVERE, - "Could not start the Jetty server: " + e.getMessage(), e); + /* + * Reads from System.in looking for the string "stop\n" in order to + * gracefully terminate the jetty server and shut down the JVM. This + * way we can invoke the shutdown hooks while debugging in eclipse. + * Can't catch CTRL-C to emulate SIGINT as the eclipse console is + * not propagating that event + */ + Thread stopThread = + new Thread() { + @Override + public void run() { + BufferedReader reader = + new BufferedReader(new InputStreamReader(System.in)); + String line; + try { + while (true) { + line = reader.readLine(); + if ("stop".equals(line)) { + jettyServer.stop(); + System.exit(0); + } + } + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + }; + stopThread.setDaemon(true); + stopThread.run(); - if (jettyServer != null) { - try { - jettyServer.stop(); - } catch (Exception e1) { - log.log(Level.SEVERE, "Unable to stop the " - + "Jetty server:" + e1.getMessage(), e1); - } - } - } - } + // use this to test normal stop behaviour, that is, to check stuff + // that + // need to be done on container shutdown (and yes, this will make + // jetty stop just after you started it...) + // jettyServer.stop(); + } catch (Exception e) { + log.log(Level.SEVERE, "Could not start the Jetty server: " + e.getMessage(), e); - private static int parsePort(String portVariable) { - if (portVariable == null) - return -1; - try { - return Integer.valueOf(portVariable).intValue(); - } catch (NumberFormatException e) { - return -1; - } - } + if (jettyServer != null) { + try { + jettyServer.stop(); + } catch (Exception e1) { + log.log( + Level.SEVERE, + "Unable to stop the " + "Jetty server:" + e1.getMessage(), + e1); + } + } + } + } + private static int parsePort(String portVariable) { + if (portVariable == null) return -1; + try { + return Integer.valueOf(portVariable).intValue(); + } catch (NumberFormatException e) { + return -1; + } + } } diff --git a/src/web/app/src/test/java/it/geosolutions/geostore/services/rest/BaseAuthenticationTest.java b/src/web/app/src/test/java/it/geosolutions/geostore/services/rest/BaseAuthenticationTest.java index 025d2f4c..793f2343 100644 --- a/src/web/app/src/test/java/it/geosolutions/geostore/services/rest/BaseAuthenticationTest.java +++ b/src/web/app/src/test/java/it/geosolutions/geostore/services/rest/BaseAuthenticationTest.java @@ -1,39 +1,36 @@ /* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it - * + * * GPLv3 + Classpath exception - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package it.geosolutions.geostore.services.rest; import it.geosolutions.geostore.services.UserService; - import java.io.File; import java.io.InputStream; - import javax.servlet.http.HttpServletRequest; - import junit.framework.TestCase; - import org.apache.commons.io.FileDeleteStrategy; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.cxf.common.util.Base64Utility; import org.apache.cxf.io.CachedOutputStream; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -41,14 +38,13 @@ import org.springframework.security.core.context.SecurityContextHolder; /** - * * Test for AuthenticationManagers. - * + * * @author afabiani (alessio.fabiani at geo-solutions.it) */ public abstract class BaseAuthenticationTest extends TestCase { - protected final Logger LOGGER = Logger.getLogger(getClass()); + protected final Logger LOGGER = LogManager.getLogger(getClass()); protected UserService userService; @@ -58,13 +54,14 @@ public abstract class BaseAuthenticationTest extends TestCase { protected void setUp() throws Exception { super.setUp(); - File securityTempFolder = new File(System.getProperty("java.io.tmpdir"), - "apacheds-spring-security"); + File securityTempFolder = + new File(System.getProperty("java.io.tmpdir"), "apacheds-spring-security"); int i = 0; for (i = 0; i < 10; i++) { try { - if (securityTempFolder.exists() && securityTempFolder.isDirectory() + if (securityTempFolder.exists() + && securityTempFolder.isDirectory() && securityTempFolder.canWrite()) { FileDeleteStrategy.FORCE.delete(securityTempFolder); FileUtils.forceDelete(securityTempFolder); @@ -78,7 +75,7 @@ protected void setUp() throws Exception { } LOGGER.info(100); - String[] paths = { "classpath*:applicationContext-test.xml" }; + String[] paths = {"classpath*:applicationContext-test.xml"}; context = new ClassPathXmlApplicationContext(paths); LOGGER.info("Built test context: " + context); } @@ -90,12 +87,14 @@ protected void tearDown() throws Exception { protected void doAutoLogin(String username, String password, HttpServletRequest request) { try { - // Must be called from request filtered by Spring Security, otherwise SecurityContextHolder is not updated - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( - username, password); + // Must be called from request filtered by Spring Security, otherwise + // SecurityContextHolder is not updated + UsernamePasswordAuthenticationToken token = + new UsernamePasswordAuthenticationToken(username, password); // token.setDetails(new WebAuthenticationDetails(request)); - Authentication authentication = ((AuthenticationProvider) context - .getBean("geostoreLdapProvider")).authenticate(token); + Authentication authentication = + ((AuthenticationProvider) context.getBean("geostoreLdapProvider")) + .authenticate(token); LOGGER.info("Logging in with [{" + authentication.getPrincipal() + "}]"); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (Exception e) { @@ -116,5 +115,4 @@ protected String getStringFromInputStream(InputStream in) throws Exception { protected String base64Encode(String value) { return Base64Utility.encode(value.getBytes()); } - -} \ No newline at end of file +} diff --git a/src/web/app/src/test/java/it/geosolutions/geostore/services/rest/SecurityTest.java b/src/web/app/src/test/java/it/geosolutions/geostore/services/rest/SecurityTest.java index 99cd7c31..448af583 100644 --- a/src/web/app/src/test/java/it/geosolutions/geostore/services/rest/SecurityTest.java +++ b/src/web/app/src/test/java/it/geosolutions/geostore/services/rest/SecurityTest.java @@ -16,13 +16,11 @@ */ package it.geosolutions.geostore.services.rest; +import it.geosolutions.geostore.core.model.User; +import it.geosolutions.geostore.services.rest.impl.RESTCategoryServiceImpl; import java.io.IOException; import java.net.DatagramSocket; import java.net.ServerSocket; - -import it.geosolutions.geostore.core.model.User; -import it.geosolutions.geostore.services.rest.impl.RESTCategoryServiceImpl; - import org.apache.cxf.jaxrs.client.JAXRSClientFactory; import org.apache.cxf.jaxrs.client.WebClient; import org.junit.Test; @@ -31,35 +29,31 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.ldap.userdetails.LdapUserDetailsImpl; -/** - * @author Alessio - * - */ +/** @author Alessio */ public class SecurityTest extends BaseAuthenticationTest { - private final static String ENDPOINT_ADDRESS = "http://localhost:9000/rest/categories"; + private static final String ENDPOINT_ADDRESS = "http://localhost:9000/rest/categories"; - private final static String WADL_ADDRESS = ENDPOINT_ADDRESS + "?_wadl&_type=xml"; + private static final String WADL_ADDRESS = ENDPOINT_ADDRESS + "?_wadl&_type=xml"; - private boolean serverStarted = false; @Override protected void setUp() throws Exception { - - if(!portIsBusy("localhost", 33389) && !portIsBusy("localhost", 9000)) { + + if (!portIsBusy("localhost", 33389) && !portIsBusy("localhost", 9000)) { try { super.setUp(); serverStarted = true; - } catch(Exception e) { - + } catch (Exception e) { + } } } - + /** * Checks if a network host / port is already occupied. - * + * * @param host * @param port * @return @@ -96,7 +90,7 @@ protected void tearDown() throws Exception { @Test public void testSuite() { - if(serverStarted) { + if (serverStarted) { springAuthenticationTest(); webClientAccessTest(); proxyAccessTest(); @@ -109,30 +103,30 @@ protected void springAuthenticationTest() { assertNotNull(SecurityContextHolder.getContext()); assertNotNull(SecurityContextHolder.getContext().getAuthentication()); - final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + final Authentication authentication = + SecurityContextHolder.getContext().getAuthentication(); authentication.getName(); assertEquals("admin", authentication.getCredentials()); Object principal = authentication.getPrincipal(); assertNotNull(principal); - + if (principal instanceof User) { - User user = (User) principal; - - assertEquals("admin", user.getName()); + User user = (User) principal; + + assertEquals("admin", user.getName()); } else if (principal instanceof LdapUserDetailsImpl) { - LdapUserDetailsImpl userDetails = (LdapUserDetailsImpl) principal; + LdapUserDetailsImpl userDetails = (LdapUserDetailsImpl) principal; - assertEquals("uid=admin,ou=people,dc=geosolutions,dc=it", userDetails.getDn()); + assertEquals("uid=admin,ou=people,dc=geosolutions,dc=it", userDetails.getDn()); } - + assertEquals(authentication.getAuthorities().size(), 1); for (GrantedAuthority authority : authentication.getAuthorities()) { assertEquals("ROLE_ADMIN", authority.getAuthority()); } - } // protected void testHTTPClientAccess() { @@ -178,8 +172,8 @@ protected void webClientAccessTest() { protected void proxyAccessTest() { doAutoLogin("admin", "admin", null); - RESTCategoryServiceImpl client = JAXRSClientFactory.create(ENDPOINT_ADDRESS, - RESTCategoryServiceImpl.class); + RESTCategoryServiceImpl client = + JAXRSClientFactory.create(ENDPOINT_ADDRESS, RESTCategoryServiceImpl.class); assertNotNull(client); diff --git a/src/web/app/src/test/resources/applicationContext-test.xml b/src/web/app/src/test/resources/applicationContext-test.xml index 3a3903d3..fcfc3daf 100644 --- a/src/web/app/src/test/resources/applicationContext-test.xml +++ b/src/web/app/src/test/resources/applicationContext-test.xml @@ -27,7 +27,7 @@ - + @@ -234,7 +234,7 @@ - + diff --git a/src/web/pom.xml b/src/web/pom.xml index f299e408..59e2d1df 100644 --- a/src/web/pom.xml +++ b/src/web/pom.xml @@ -18,14 +18,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - + 4.0.0 it.geosolutions.geostore geostore-root - 1.7-SNAPSHOT + 2.2-SNAPSHOT geostore-web