diff --git a/.circleci/config.yml b/.circleci/config.yml
index 263cf797f..50ef45912 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -17,16 +17,16 @@ jobs:
# Download and cache dependencies
- restore_cache:
keys:
- - v1-dependencies-{{ checksum "pom.xml" }}
+ - v2-dependencies-{{ checksum "pom.xml" }}
# fallback to using the latest cache if no exact match is found
- - v1-dependencies-
+ - v2-dependencies-
- run:
name: Fetch Maven Dependencies
command: mvn dependency:go-offline
- save_cache:
paths:
- ~/.m2
- key: v1-dependencies-{{ checksum "pom.xml" }}
+ key: v2-dependencies-{{ checksum "pom.xml" }}
# Run Tests
- run:
@@ -35,6 +35,36 @@ jobs:
mvn test \
-D spring.profiles.active=test
+ build-docker-image:
+ machine: true
+
+ steps:
+ - checkout
+
+ - run: docker login -u $DOCKER_USER -p $DOCKER_PASS
+
+ - run: docker build -f Dockerfile.prod . -t overture/ego:$(git describe --always)-alpine
+
+ - run: docker push overture/ego:$(git describe --always)-alpine
+
+ deploy-staging:
+ machine: true
+
+ steps:
+ - checkout
+
+ - run: mkdir ~/.kube && echo $KUBE_CONFIG | base64 --decode > ~/.kube/config
+
+ - run: wget https://storage.googleapis.com/kubernetes-helm/helm-v2.12.1-linux-amd64.tar.gz
+
+ - run: tar -xvf helm-v2.12.1-linux-amd64.tar.gz
+
+ - run: linux-amd64/helm init --client-only
+
+ - run: linux-amd64/helm repo add overture https://overture-stack.github.io/charts/
+
+ - run: linux-amd64/helm upgrade ego-staging overture/ego --reuse-values --set image.tag=$(git describe --always)-alpine
+
deploy:
docker:
- image: circleci/openjdk:8-jdk
@@ -78,10 +108,9 @@ jobs:
name: Deploy Script
command: ./.circleci/deploy.sh
-
workflows:
version: 2
- test-deploy:
+ test-build-deploy:
jobs:
- test
- deploy:
@@ -91,4 +120,11 @@ workflows:
branches:
only:
- develop
-
\ No newline at end of file
+ - build-docker-image:
+ filters:
+ branches:
+ only:
+ - develop
+ - deploy-staging:
+ requires:
+ - build-docker-image
diff --git a/.gitignore b/.gitignore
index c80de70f6..16c9ecbca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,7 @@ _build/
_source/
_templates/
.DS_Store
+
+classes/
+
+local.log
\ No newline at end of file
diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100755
index 000000000..fa4f7b499
--- /dev/null
+++ b/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,110 @@
+/*
+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
+
+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.
+*/
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL =
+ "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if(mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if(mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: : " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if(!outputFile.getParentFile().exists()) {
+ if(!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
old mode 100644
new mode 100755
index 9cc84ea9b..01e679973
Binary files a/.mvn/wrapper/maven-wrapper.jar and b/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
old mode 100644
new mode 100755
index c31504370..00d32aab1
--- a/.mvn/wrapper/maven-wrapper.properties
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -1 +1 @@
-distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..d83b5122a
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,72 @@
+# Contributing
+
+When contributing to this repository, please first discuss the change you wish to make via issue,
+email, or any other method with the owners of this repository before making a change.
+
+Please note we have a code of conduct, please follow it in all your interactions with the project.
+
+## Code Standards
+
+#### General
+1. Do not use field injection (ie. `@Value`, `@Autowired`)
+ - Instead use an `@Autowired` or `@Value` annotated constructor
+ - Provide a static builder (ie. Lombok `@Builder` annotation)
+ - This helps to improves testability
+ - Helps to decouple from Spring
+ - If your constructor is feeling messy or too big - you are probably overloading the class you are working on
+2. Do not use any implementation specific JPA code (ie. Hibernate-only annotations)
+ - Exception for when no alternative functionality exists (ie. Postgres JSON field search)
+3. All of our code is auto-formatted to Google Java Format using the [fmt-maven-plugin](https://mvnrepository.com/artifact/com.coveo/fmt-maven-plugin) on build:
+```xml
+
+ com.coveo
+ fmt-maven-plugin
+ ${FMT_MVN_PLG.VERSION}
+
+
+
+ format
+
+
+
+
+```
+5. Constants
+- must be declared in a `@NoArgsConstructor(access=PRIVATE)` annotated class with a name representative of the type of constants. For example, the class `Tables` under the package `constants` would contain sql table names.
+- Constant variable names should be consistent throughout code base. For example, the text `egoUserPermissions` should be defined by the variable `EGO_USER_PERMISSION`.
+6. If a method is not stateful and not an interface/abstract method, then it should be static
+7. Never allow a method to return `null`. Instead, it should return `Optiona` or an empty container type (something that has `.isEmpty()`)
+
+#### Service Layer
+1. Get * should always return Optional
+2. Find * should always return a Collection
+
+#### JPA
+1. Entity member declarations should take the following presidence:
+ 1. @Id (identifier)
+ 2. Non-relationship @Column
+ 3. @OneToOne
+ 4. @OneToMany
+ 5. @ManyToOne
+ 6. @ManyToMany
+2. As explained in this [article](https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/), you should prefer bidirectional associations since they are more efficient than unidirectional ones in terms of SQL performance [source](https://vladmihalcea.com/merge-entity-collections-jpa-hibernate/)
+3. Always lazy load for @OneToMany and @ManyToMany
+4. Never use CascadeType.ALL or CascadeType.REMOVE becuase they are too destructive. Use CascadeType.MERGE and CascadeType.PERSIST instead
+5. Name methods with `remove` indicating an entity was deleted
+6. Name methods with `dissociate` indicating a child relationship with its parent will be destoryed
+7. For performance reasons, @ManyToMany collections should be a Set as described [here](https://thoughts-on-java.org/association-mappings-bag-list-set/)
+8. For performance reasons, @OneToMany collections should be a list as described [here](https://vladmihalcea.com/hibernate-facts-favoring-sets-vs-bags/)
+9. In ManyToMany relationships, the JoinTable should only be defined on the **owning** side , and on the inverse side the `mappedBy` ManyToMany annotation parameter should be defined, as described [here](https://www.baeldung.com/hibernate-many-to-many)
+
+### Testing
+1. Test FEATURES not methods
+2. Test method names should follow this convention: `[the name of the tested feature]_[expected input / tested state]_[expected behavior]`.
+
+#### General
+1. DB via Test Containers - no in-memory DB or OS specific services
+2. No dependencies on any external services (ie. production micro-service)
+3. Tests **DO NOT** clear their data between runs, meaning that no test should rely on or expect a clean DB when running
+
+##### Unit Testing
+
+##### Integration Testing
diff --git a/Dockerfile b/Dockerfile
index 7fbcdf221..9b4f0ceb5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -27,7 +27,8 @@ RUN mkdir -p /srv/ego/install \
# setup required environment variables
ENV EGO_INSTALL_PATH /srv/ego
+ENV CONFIG_FILE /usr/src/app/src/main/resources/flyway/conf/flyway.conf
# start ego server
WORKDIR $EGO_INSTALL_PATH
-CMD $EGO_INSTALL_PATH/exec/run.sh
+CMD cd /usr/src/app;mvn "flyway:migrate" -Dflyway.configFiles=$CONFIG_FILE -Dflyway.password=password -Dflyway.url=jdbc:postgresql://postgres:5432/ego?stringtype=unspecified;$EGO_INSTALL_PATH/exec/run.sh
diff --git a/Dockerfile.prod b/Dockerfile.prod
new file mode 100644
index 000000000..f4cebe1e5
--- /dev/null
+++ b/Dockerfile.prod
@@ -0,0 +1,16 @@
+FROM maven:3.6-jdk-8
+
+WORKDIR /usr/src/app
+
+ADD . .
+
+RUN mvn package -Dmaven.test.skip=true
+
+FROM java:8-alpine
+
+COPY --from=0 /usr/src/app/target/ego-*-SNAPSHOT-exec.jar /usr/bin/ego.jar
+COPY --from=0 /usr/src/app/src/main/resources/flyway/sql /usr/src/flyway-migration-sql
+
+ENTRYPOINT ["java", "-jar", "/usr/bin/ego.jar"]
+
+EXPOSE 8081/tcp
diff --git a/EgoDatabaseDiagram.pdf b/EgoDatabaseDiagram.pdf
new file mode 100644
index 000000000..c97e6a878
Binary files /dev/null and b/EgoDatabaseDiagram.pdf differ
diff --git a/NOTES.md b/NOTES.md
new file mode 100644
index 000000000..ef4ee3961
--- /dev/null
+++ b/NOTES.md
@@ -0,0 +1,38 @@
+Notes
+------
+
+## 1. Reason for using the `@Type` hibernate annotation
+
+#### Problem
+In the entity `Policy`, the field `accessLevel` of type `AccessLevel` (which is an enum), the `@Type` annotation is used, which is a hibernate specific and not JPA annotation. The goal is to minimize or eliminate use of hibernate specific syntax, and just use JPA related code.
+
+#### Solutions
+The goal is to map the enum to the database. In the end, solution 3 was chosen.
+
+##### 1. Middleware Level Enum Handling without Hibernate Annotations
+Set the type of the field in the database to a `VARCHAR` and only use the `@Enumerated` JPA annotation
+Pros:
+ - Hibernate will handle the logic of converting an AccessLevel to a string. This means Enum conversion is handelled by the middleware naturally.
+ - Simple and clean solution using only JPA annotations
+Cons:
+ - Enum type is represented as an Enum at the application level but as a VARCHAR at the database level. If someone was to backdoor the database and update the `accessLevel` of a policy, they could potentially break the application. There is no safeguard outside of hibernate/JPA
+
+##### 2. Application Level Enum Handling
+Set the type of the field in the postgres database to a `AccessLevelType` and in the Java DAO, represent the field as a `String`. The application level (i.e the service layers) will manage the conversion of the Policies `String` accessLevel field to the `AccessLevel` Java enum type. Hibernate will pass the string to the postgres database, and since the url ends with `?stringtype=unspecified`, postgres will cast the string to the Database enum type
+Pros:
+ - No need for Hibernate annotations
+ - Since conversions are done manually at application layer, functionality is more obvious and cleaner
+Cons:
+ - Its manual, meaning, if the developer forgets to check that the conversion from `AccessLevel->String` and `String->AccessLevel` is correct, a potentially wrong string value will be passed from hibernate to postrgres, resulting in a postgres error
+
+
+##### 3. Middleware Level Enum Handling WITH Hibernate Annotations
+Follow the instructions from this [blog post](https://vladmihalcea.com/the-best-way-to-map-an-enum-type-with-jpa-and-hiberate/) under the heading `Mapping a Java Enum to a database-specific Enumarated column type`. This results in the use of the `@Type` hibernate specific annotation for the `Policy` entity. This annotation modifies the way hibernate process the `accessLevel` field.
+Pros:
+ - All processing is done at the middleware (hibernate) and the developer does not need to add any extra code at the application layer.
+ - Hibernate properly process the Java enum type to a Postgres enum type. This means **BOTH** the java code (the `Policy` entity) and the Policy table in postgres are types and protected from values outside the enumeration.
+ - Almost no developer effort and minimizes developer mistakes
+Cons:
+ - The `Policy` entity is using a hibernate annotation with a custom `PostgresSQLEnumType` processor to assist hibernate in supporting Postgres enum types.
+
+
diff --git a/README.md b/README.md
index 34d259837..e006c596a 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@
+[](https://www.browserstack.com/automate/public-build/U3dLZnRFNWI2MWNFY2NGcXVtVTB3WDcyU2dPVjlVeEFYUEdxUnpYZlhrUT0tLTFzY0taYTA0MVFEa3ErNkRZdTBRWVE9PQ==--690f89a41a0eedf7b4975bd7df2eac162e04e775%)
[](https://travis-ci.org/overture-stack/ego)
[](https://circleci.com/gh/overture-stack/ego/tree/develop)
[](http://slack.overture.bio)
@@ -23,6 +24,8 @@
- [Step 2 - Run](#step-2---run)
- [Tech Specifications](#tech-specification)
- [Usage](#usage)
+- [Shoutouts](#shoutouts)
+ - [Browserstack](#browserstack)
## Introduction
@@ -91,13 +94,13 @@ Database migrations and versioning is managed by [flyway](https://flywaydb.org/)
Get current version information:
```bash
-./flyway -configFiles=/ego/src/main/resources/flyway/conf/flyway.conf -locations=filesystem:/ego/src/main/resources/flyway/sql info
+./fly
```
Run outstanding migrations:
```bash
-./flyway -configFiles=/ego/src/main/resources/flyway/conf/flyway.conf -locations=filesystem:/ego/src/main/resources/flyway/sql migrate
+./fly migrate
```
To see the migration naming convention, [click here.](https://flywaydb.org/documentation/migrations#naming)
@@ -163,7 +166,6 @@ An example ego JWT is mentioned below:
```
#### Notes
-
- "aud" field can contain one or more client IDs. This field indicates the client services that are authorized to use this JWT.
- "groups" will differ based on the domain of client services - each domain of service should get list of groups from that domain's ego service.
- "permissions" will differ based on domain of client service - each domain of service should get list of permissions from that domain's ego service.
@@ -204,3 +206,11 @@ curl example:
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials&client_id=my-app-id&client_secret=secretpassword'
```
+
+## Shoutouts
+
+### Browserstack
+Many thanks to [Browserstack](https://www.browserstack.com/) for giving our test capabilities a powerup!
+
+
+
diff --git a/build b/build
new file mode 100755
index 000000000..35b787af9
--- /dev/null
+++ b/build
@@ -0,0 +1,3 @@
+#!/bin/sh
+./fly migrate
+mvn clean package
diff --git a/docker-compose.yml b/docker-compose.yml
index b77648e93..3bbf3baaa 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -25,8 +25,6 @@ services:
environment:
- POSTGRES_DB=ego
- POSTGRES_PASSWORD=password
- volumes:
- - ./src/main/resources/schemas/01-psql-schema.sql:/docker-entrypoint-initdb.d/init.sql
expose:
- "5432"
ports:
diff --git a/docs/conf.py b/docs/conf.py
index 0d9ed79f3..001666906 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -55,9 +55,9 @@
# built documents.
#
# The short X.Y version.
-version = '0.0.1'
+version = '0.1.0'
# The full version, including alpha/beta/rc tags.
-release = '0.0.1'
+release = '0.1.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/index.rst b/docs/index.rst
index 7d2bf98d6..fef572200 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -3,47 +3,43 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
-===============================
-Welcome to Ego's documentation!
-===============================
-
-Ego is an OAuth2 based Authorization Provider microservice. It is designed to allow users to log in with social logins such as Google and Facebook.
-
-Users, Groups, and Applications can be managed through Ego and allows for stateless authorization of user actions by client applications through the issuing of JWT Bearer tokens and the publishing of a public key for the verification of tokens.
-
-.. image:: ego-arch.png
+============
+Ego Documentation
+============
.. toctree::
- :maxdepth: 2
+ :maxdepth: 4
+ :caption: First Steps
-Installation
-============
-The easiest way to get up and running is with docker.
-
- **docker pull overture/ego**
+ src/introduction.rst
+ src/gettingstarted.rst
-Otherwise, you can build from source. The prerequisites are Java 8 and Maven.
+.. toctree::
+ :maxdepth: 4
+ :caption: User Documentation
-.. code-block:: bash
+ src/admins.rst
+ src/appdevelopers.rst
+ src/tokens.rst
- git clone https://github.com/overture-stack/ego.git
- cd ego
- mvn clean package
+.. toctree::
+ :maxdepth: 4
+ :caption: Developer Documentation
+ src/installation.rst
+ src/architecture.rst
+ src/technology.rst
+ src/contribution.rst
-Documentation
-=============
-.. toctree::
- :maxdepth: 4
-
- src/quickstart
- src/technology
+Contribute
+------------
+If you'd like to contribute to this project, it's hosted on github.
+See https://github.com/overture-stack/ego
Indices and tables
==================
* :ref:`genindex`
-* :ref:`modindex`
* :ref:`search`
diff --git a/docs/src/admins.rst b/docs/src/admins.rst
new file mode 100644
index 000000000..0a7ac4145
--- /dev/null
+++ b/docs/src/admins.rst
@@ -0,0 +1,35 @@
+=======================
+Ego for Administrators
+=======================
+
+Tutorial
+======================
+
+To administer Ego, the admin must:
+
+**1. Install Ego.**
+
+ View the installation instructions.
+
+**2. Insert a new user with the admin’s Oauth Id into the “egousers” table, with role ADMIN.**
+
+**3. A developer creates a new Ego-aware application**
+
+ a. Admin creates a new application in Ego with the client_id and password.
+ b. Admin creates new policies with new policy names
+ c. Admin assigns permissions to users/groups to permit/deny them access to the new application and policies
+
+**4. Admin creates or deletes groups, assigns user/group permissions, revoke tokens, etc. as necessary.**
+
+ For example, an administrator might want to:
+
+ - Create a new group called **“QA”**, whose members are all the people in the “QA department”
+ - Create a group called “Access Denied” with access level “DENY” set for every policy in Ego
+ - Grant another user administrative rights (role ADMIN)
+ - Add a former employee to the group “AccessDenied”, and revoke all of their active tokens.
+ - In general, manage permissions and access controls within Ego.
+
+Using the Admin Portal
+======================
+
+Ego provides an intuitive GUI for painless user management.
diff --git a/docs/src/appdevelopers.rst b/docs/src/appdevelopers.rst
new file mode 100644
index 000000000..777e5e830
--- /dev/null
+++ b/docs/src/appdevelopers.rst
@@ -0,0 +1,13 @@
+================================
+Ego for Application Developers
+================================
+
+To create an Ego-aware application, a developer must:
+
+1. Pick a unique policy name for each type of authorization that the application requires.
+
+2. Write the application. Ensure that the application does it’s authorization by performing a call to Ego’s “check_token” REST endpoint, and only grants access to the service for the user id returned by “check_token” if the permissions returned by “check_token” include the required permission.
+
+3. Configure the program with a meaningful client_id and a secret password.
+
+4. Give the client_id, password, and policy names to an Ego administrator, and ask them to configure Ego for you.
diff --git a/docs/src/architecture.rst b/docs/src/architecture.rst
new file mode 100644
index 000000000..867e9bb60
--- /dev/null
+++ b/docs/src/architecture.rst
@@ -0,0 +1,2 @@
+Architecture
+============================
diff --git a/docs/src/contribution.rst b/docs/src/contribution.rst
new file mode 100644
index 000000000..cd3b3c248
--- /dev/null
+++ b/docs/src/contribution.rst
@@ -0,0 +1,2 @@
+Contributing to the Ego Project
+============================
diff --git a/docs/src/gettingstarted.rst b/docs/src/gettingstarted.rst
new file mode 100644
index 000000000..e4b564921
--- /dev/null
+++ b/docs/src/gettingstarted.rst
@@ -0,0 +1,84 @@
+Getting Started
+============================
+
+The easiest way to understand EGO, is to simply use it!
+
+Below is a description of how to get Ego quickly up and running, as well as a description of how Ego works and some important terms.
+
+Quick Start
+----------------------------------------------------
+
+The goal of this quick start is to get a working application quickly up and running.
+
+Using `Docker `_:
+
+1. Download the latest version of Ego.
+2. From the Ego root directory, set the API_HOST_PORT where Ego is to be run, then run `docker-compose `_:
+
+.. code-block:: python
+
+ $ API_HOST_PORT=8080 docker-compose up -d
+
+Ego should now be deployed locally with the Swagger UI at http://localhost:8080/swagger-ui.html
+
+Alternatively, see the `Installation instructions `_.
+
+
+How Ego Works
+-------------------------------------------
+**1. An Ego administrator configures Ego.**
+ - Registers a unique client-id and application password for each application that will use Ego for Authorization.
+ - Creates a policy for every authorization scope that an application will use.
+ - Registers users and groups, and sets them up with appropriate permissions for policies and applications.
+
+
+**2. Ego grants secret authorization tokens to individual users to represent their permissions.**
+ - Authorization tokens expire, and can be revoked if compromised.
+ - Individuals can issue tokens for part or all of their authority, and can limit the authority to specific applications.
+ - Users (and programs operating on their behalf) can then use these tokens to access services.
+
+**3. Individual services make a REST call to EGO to determine the user and authority represented by a token.**
+ - Makes a call to Ego's check_token endpoint and validates the user's authorization to access the requested services.
+
+
+Terms Used in Ego
+-------------------------------------------
+
+.. image :: terms.png
+
+.. glossary::
+
+ User
+ A user is any individual registered in Ego who needs to authorize themselves with Ego-aware applications.
+
+ Admin
+ An admin is a power user whose role is set to 'ADMIN'. Only admins are authorized to register users, groups, applications & policies using Ego's REST endpoints.
+
+ Group
+ A group of users with similar properties. Admins can create new groups and add users to them. They can then assign permissions to an entire group which will be reflected for each user in that group.
+
+ Policy
+ A policy is a scope or context for which an application may want to grant a user or group READ/WRITE/DENY permissions.
+
+ Permission
+ A user or group can be given READ/WRITE/DENY permissions for a particular policy.
+
+ Application
+ An application is a third party service that registers itself with EGO so that EGO can authorize users on its behalf. Upon registration, the service must provide a client_id and client secret.
+
+ Application Authentication Token
+ This a Basic JWT token which encodes a client id and secret, and authorizes an application to interact with Ego. This is passed in the authorization request header when an application uses the check_token endpoint in order to check a user's token.
+
+ User Authentication Token
+ This is a Bearer token which encodes user information, and is passed to a user when they are authenticated through OAuth single sign-on. This Bearer token is passed in the request authorization header whenever the user wants to access Ego's resources.
+ If the JWT denotes that a user has an ADMIN role, they are permitted to create and modify resources (users, groups, permissions, policies).
+
+ User Authorization Token
+ This is a random token which is generated to authorize a user for a specific scope, in the context of an application.
+
+
+Play with the REST API from your browser
+--------------------------------------------
+If you want to play with EGO from your browser, you can visit the Swagger UI located here :
+
+https://ego.overture.cancercollaboratory.org/swagger-ui.html
diff --git a/docs/src/quickstart.rst b/docs/src/installation.rst
similarity index 73%
rename from docs/src/quickstart.rst
rename to docs/src/installation.rst
index 0437d05e0..fc91a5383 100644
--- a/docs/src/quickstart.rst
+++ b/docs/src/installation.rst
@@ -1,7 +1,7 @@
-Quick Start
-===========
+.. _installation:
-The goal of this quick start is to get a working application quickly up and running.
+Installation
+============================
Step 1 - Setup Database
-----------------------
@@ -10,6 +10,11 @@ Step 1 - Setup Database
2. Create a Database: ego with user postgres and empty password
3. Execute SQL Script to setup tables.
+Database Migrations with Flyway
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Database migrations and versioning is managed by `flyway `_.
+
Step 2 - Run
------------
@@ -23,7 +28,7 @@ Run using Maven. Maven can be used to prepare a runnable jar file, as well as th
.. code-block:: bash
- $ mvn clean package
+ $ mvn clean package ; ./fly migrate
To run from command line with maven:
diff --git a/docs/src/introduction.rst b/docs/src/introduction.rst
new file mode 100644
index 000000000..20fa8c4dd
--- /dev/null
+++ b/docs/src/introduction.rst
@@ -0,0 +1,47 @@
+==============
+Introduction
+==============
+
+
+What is Ego?
+=============
+
+`EGO `_ is an OAuth2 based authentication and authorization management microservice. It allows users to login and authenticate themselves using their existing logins from sites such as Google and Facebook, create and manage authorization tokens, and use those tokens to interact with Ego-aware third party applications which they are authorized for.
+
+OAuth single sign-on means that Ego does not need to manage users and their passwords; and similarly, none of the services that use Ego need to worry about how to manage users, logins, authentication or authorization. The end user simply sends them a token, and the service checks with Ego to learn who the token is for, and what permissions the token grants.
+EGO is one of many products provided by `Overture `_ and is completely open-source and free for everyone to use.
+
+.. seealso::
+
+ For additional information on other products in the Overture stack, please visit https://overture.bio
+
+.. _introduction_features:
+
+Features
+===========
+
+- Single sign-on for microservices
+- User authentication through federated identities such as Google, Facebook, Linkedin, Github (Coming Soon), ORCID (Coming Soon)
+- Provides stateless authorization using `JSON Web Tokens (JWT) `_
+- Can scale very well to large number of users
+- Provides ability to create permission lists for users and/or groups on user-defined permission entities
+- Standard REST API that is easy to understand and work with
+- Interactive documentation of the API is provided using Swagger UI. When run locally, this can be found at : http://localhost:8080/swagger-ui.html
+- Built using well established Frameworks - Spring Boot, Spring Security
+
+License
+==========
+Copyright (c) 2018. Ontario Institute for Cancer Research
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see https://www.gnu.org/licenses.
diff --git a/docs/src/jwt.png b/docs/src/jwt.png
new file mode 100644
index 000000000..7feb70b7d
Binary files /dev/null and b/docs/src/jwt.png differ
diff --git a/docs/src/jwt.rst b/docs/src/jwt.rst
deleted file mode 100644
index aad6755e1..000000000
--- a/docs/src/jwt.rst
+++ /dev/null
@@ -1,53 +0,0 @@
-JSON Web Token
-==============
-
-Basics
-------
-
-Ego makes use of JSON Web Tokens (JWTs) for providing users with a Bearer token.
-
-The RFC for JWTs can be found here: https://tools.ietf.org/html/rfc7519
-
-The following is a useful site for understanding JWTs: https://jwt.io/
-
-
-The following is the structure of an ego JWT:
-
-.. code-block:: guess
-
- {
- "alg": "HS512"
- }
- .
- {
- "sub": "1234567",
- "iss": "ego:56fc3842ccf2c1c7ec5c5d14",
- "iat": 1459458458,
- "exp": 1459487258,
- "jti": "56fd919accf2c1c7ec5c5d16",
- "aud": [
- "service1-id",
- "service2-id",
- "service3-id"
- ],
- "context": {
- "user": {
- "name": "Demo.User@example.com",
- "email": "Demo.User@example.com",
- "status": "Approved",
- "firstName": "Demo",
- "lastName": "User",
- "createdAt": "2017-11-23 10:24:41",
- "lastLogin": "2017-11-23 11:23:58",
- "preferredLanguage": null,
- "roles": ["ADMIN"]
- }
- }
- }
- .
- [signature]
-
-Library Support
----------------
-
-The Java JWT library is used in Ego for providing support for encoding, decoding, and validating JWTs: https://github.com/jwtk/jjwt
diff --git a/docs/src/spring.rst b/docs/src/spring.rst
deleted file mode 100644
index 21e3ab7c7..000000000
--- a/docs/src/spring.rst
+++ /dev/null
@@ -1,12 +0,0 @@
-Spring-Boot
-===========
-
-Ego is a Microservice written in Java 8 and Spring-Boot.
-It makes use of the following parts of the Spring and Spring-Boot framework:
-
-- Web / Spring MVC w/ embedded tomcat
-- Security
-- JDBC
-- Spring Data JPA
-
-Swagger docs are generated by Springfox. https://springfox.github.io/springfox/docs/current/
diff --git a/docs/src/technology.rst b/docs/src/technology.rst
index ca98e4904..e36f1cb4e 100644
--- a/docs/src/technology.rst
+++ b/docs/src/technology.rst
@@ -1,9 +1,96 @@
-Technology
-==========
+Technology Stack
+============================
-.. toctree::
- :maxdepth: 2
+This application is written in JAVA using Spring Boot and the Spring Security Frameworks.
- jwt
- spring
-
\ No newline at end of file
+JSON Web Token
+---------------
+
+Basics
+^^^^^^^
+
+Ego makes use of JSON Web Tokens (JWTs) for providing users with a Bearer token.
+
+The RFC for JWTs can be found here: https://tools.ietf.org/html/rfc7519
+
+The following is a useful site for understanding JWTs: https://jwt.io/
+
+
+The following is the structure of an ego JWT:
+
+.. code-block:: guess
+
+ {
+ "alg": "HS512"
+ }
+ .
+ {
+ "sub": "1234567",
+ "iss": "ego:56fc3842ccf2c1c7ec5c5d14",
+ "iat": 1459458458,
+ "exp": 1459487258,
+ "jti": "56fd919accf2c1c7ec5c5d16",
+ "aud": [
+ "service1-id",
+ "service2-id",
+ "service3-id"
+ ],
+ "context": {
+ "user": {
+ "name": "Demo.User@example.com",
+ "email": "Demo.User@example.com",
+ "status": "Approved",
+ "firstName": "Demo",
+ "lastName": "User",
+ "createdAt": "2017-11-23 10:24:41",
+ "lastLogin": "2017-11-23 11:23:58",
+ "preferredLanguage": null,
+ "roles": ["ADMIN"]
+ }
+ }
+ }
+ .
+ [signature]
+Notes
+ - "aud" field can contain one or more client IDs. This field indicates the client services that are authorized to use this JWT.
+ - "groups" will differ based on the domain of client services - each domain of service should get list of groups from that domain's ego service.
+ - "permissions" will differ based on domain of client service - each domain of service should get list of permissions from that domain's ego service.
+ Unit Tests using testcontainers will also run flyway migrations to ensure database has the correct structure
+
+Library Support
+^^^^^^^^^^^^^^^^
+
+The Java JWT library is used in Ego for providing support for encoding, decoding, and validating JWTs: https://github.com/jwtk/jjwt
+
+Spring-Boot
+------------
+
+Ego is a microservice written in Java 8 and Spring-Boot.
+It makes use of the following parts of the Spring and Spring-Boot framework:
+
+- Web / Spring MVC w/ embedded tomcat
+- `Spring Security `_
+- JDBC
+- Spring Data JPA
+
+Swagger docs are generated by Springfox : https://springfox.github.io/springfox/docs/current/
+
+Ego Design Notes
+-----------------
+
+1. OAuth Single Sign-On means that Ego doesn't need to manage users and their passwords; users don't need a new username or password, and don't need to trust any service other than Google / Facebook.
+
+2. Ego lets users be in charge of the authority they give out; so they can issue secret tokens that are limited to
+ the exact authority level they need to do a given task.
+
+ Even if a such a token becomes publicly known, it can't grant an outsider accesses to services or permissions
+ that the token doesn't have -- regardless of whether the user has more authority that they could have granted.
+
+ Tokens also automatically expire (by default, within 24 hours), and if a user suspects that a token may have
+ become known to outsiders, they can simply revoke the compromised token, removing all of it's authority,
+ then issue themselves a new secret token, and use it.
+
+3. None of the services that use Ego uses need to manage worry about how to manage users, logins, authentication,
+ or authorization. The end user simply sends them a token, and the service checks with Ego to learn who the
+ token is for, and what permissions the token grants. If the permissions granted don't include the permissions
+ the service needs, it denies access; otherwise, it runs the service for the given user.
diff --git a/docs/src/terms.png b/docs/src/terms.png
new file mode 100644
index 000000000..8d1d21ed7
Binary files /dev/null and b/docs/src/terms.png differ
diff --git a/docs/src/tokens.rst b/docs/src/tokens.rst
new file mode 100644
index 000000000..e807a93aa
--- /dev/null
+++ b/docs/src/tokens.rst
@@ -0,0 +1,40 @@
+Tokens
+============================
+
+User Authentication Tokens
+----------------------------------------------------
+Authentication concerns *who the user is*.
+
+User Authentication tokens are used to verify a user’s identity.
+
+Ego’s User Authentication tokens are signed JSON Web Tokens (see http://jwt.io) that Ego issues when a user successfully logs into Ego using their Google or Facebook credentials.
+
+Ego's authentication tokens confirm the user’s identity, and contain information about a user’s name, their role (user/administrator), and any applications, permissions, and groups associated with their Ego account etc.
+
+This data is current as of the time the token is issued, and the token is digitally signed by Ego with a publicly available signing key that applications have to use to verify that an authentication token is valid. Most of Ego’s REST endpoints require an Ego authentication token to be provided in the authorization header, in order to validate the user’s identity before operating on their data.
+
+.. image :: jwt.png
+
+User Authorization Tokens
+----------------------------------------------------
+Authorization concerns *what a user is allowed to do*.
+
+User Authorization tokens are used to verify a user's permissions to execute on a desired scope.
+
+Ego’s User Authorization tokens are random numbers that Ego issues to users so they can interact with Ego-aware applications with a chosen level of authority.
+
+Each token is a unique secret password that is associated with a specific user, permissions, and optionally, an allowed set of applications.
+
+Unlike passwords, Authorization tokens automatically expire, and they can be revoked if the user suspects that they have been compromised.
+
+The user can then use their token with Ego-authorized applications as proof of who they are and what they are allowed to do. Typically, the user will configure a client program (such as SING, the client program used with SONG, the ICGC Metadata management service) with their secret token, and the program will then operate with the associated level of authority.
+
+In more detail, when an Ego-aware application wants to know if it is authorized to do something on behalf of a given user, it just sends their user authorization token to Ego, and gets back the associated information about who the user is (their user id), and what they are allowed to do (the permissions associated with their token). If the permissions that the user have include the permission the application wants, the application know it is authorized to perform the requested service on behalf of the user.
+
+
+Application Authentication Tokens
+----------------------------------------------------
+
+For security reasons, applications need to be able to prove to Ego that they are the legitimate applications that Ego has been configured to work with.
+
+For this reason, every Ego-aware application must be configured in Ego with it’s own unique CLIENT ID and CLIENT SECRET, and the application must send a token with this information to Ego whenever it makes a request to get the identity and credentials associated with a user’s authorization token.
diff --git a/fly b/fly
new file mode 100755
index 000000000..3f342256e
--- /dev/null
+++ b/fly
@@ -0,0 +1,8 @@
+#!/bin/bash
+CMD=${1:-info}
+CONFIG_FILE=${2:-`pwd`/src/main/resources/flyway/conf/flyway.conf}
+
+# To debug, use this line instead...
+# mvn flyway:${CMD} -X -Dflyway.configFile=$CONFIG_FILE
+
+mvn "flyway:$CMD" -Dflyway.configFiles=${CONFIG_FILE}
diff --git a/mvnw b/mvnw
index 5bf251c07..5551fde8e 100755
--- a/mvnw
+++ b/mvnw
@@ -108,7 +108,7 @@ if $cygwin ; then
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
-# For Migwn, ensure paths are in UNIX format before anything is touched
+# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
@@ -200,8 +200,69 @@ if [ -z "$BASE_DIR" ]; then
exit 1;
fi
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ wget "$jarUrl" -O "$wrapperJarPath"
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ curl -o "$wrapperJarPath" "$jarUrl"
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
-echo $MAVEN_PROJECTBASEDIR
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
diff --git a/mvnw.cmd b/mvnw.cmd
old mode 100644
new mode 100755
index 019bd74d7..48363fa60
--- a/mvnw.cmd
+++ b/mvnw.cmd
@@ -1,143 +1,161 @@
-@REM ----------------------------------------------------------------------------
-@REM Licensed to the Apache Software Foundation (ASF) under one
-@REM or more contributor license agreements. See the NOTICE file
-@REM distributed with this work for additional information
-@REM regarding copyright ownership. The ASF licenses this file
-@REM to you under the Apache License, Version 2.0 (the
-@REM "License"); you may not use this file except in compliance
-@REM with the License. You may obtain a copy of the License at
-@REM
-@REM http://www.apache.org/licenses/LICENSE-2.0
-@REM
-@REM Unless required by applicable law or agreed to in writing,
-@REM software distributed under the License is distributed on an
-@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-@REM KIND, either express or implied. See the License for the
-@REM specific language governing permissions and limitations
-@REM under the License.
-@REM ----------------------------------------------------------------------------
-
-@REM ----------------------------------------------------------------------------
-@REM Maven2 Start Up Batch script
-@REM
-@REM Required ENV vars:
-@REM JAVA_HOME - location of a JDK home dir
-@REM
-@REM Optional ENV vars
-@REM M2_HOME - location of maven2's installed home dir
-@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
-@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
-@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
-@REM e.g. to debug Maven itself, use
-@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
-@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
-@REM ----------------------------------------------------------------------------
-
-@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
-@echo off
-@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
-@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
-
-@REM set %HOME% to equivalent of $HOME
-if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
-
-@REM Execute a user defined script before this one
-if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
-@REM check for pre script, once with legacy .bat ending and once with .cmd ending
-if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
-if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
-:skipRcPre
-
-@setlocal
-
-set ERROR_CODE=0
-
-@REM To isolate internal variables from possible post scripts, we use another setlocal
-@setlocal
-
-@REM ==== START VALIDATION ====
-if not "%JAVA_HOME%" == "" goto OkJHome
-
-echo.
-echo Error: JAVA_HOME not found in your environment. >&2
-echo Please set the JAVA_HOME variable in your environment to match the >&2
-echo location of your Java installation. >&2
-echo.
-goto error
-
-:OkJHome
-if exist "%JAVA_HOME%\bin\java.exe" goto init
-
-echo.
-echo Error: JAVA_HOME is set to an invalid directory. >&2
-echo JAVA_HOME = "%JAVA_HOME%" >&2
-echo Please set the JAVA_HOME variable in your environment to match the >&2
-echo location of your Java installation. >&2
-echo.
-goto error
-
-@REM ==== END VALIDATION ====
-
-:init
-
-@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
-@REM Fallback to current working directory if not found.
-
-set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
-IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
-
-set EXEC_DIR=%CD%
-set WDIR=%EXEC_DIR%
-:findBaseDir
-IF EXIST "%WDIR%"\.mvn goto baseDirFound
-cd ..
-IF "%WDIR%"=="%CD%" goto baseDirNotFound
-set WDIR=%CD%
-goto findBaseDir
-
-:baseDirFound
-set MAVEN_PROJECTBASEDIR=%WDIR%
-cd "%EXEC_DIR%"
-goto endDetectBaseDir
-
-:baseDirNotFound
-set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
-cd "%EXEC_DIR%"
-
-:endDetectBaseDir
-
-IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
-
-@setlocal EnableExtensions EnableDelayedExpansion
-for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
-@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
-
-:endReadAdditionalConfig
-
-SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
-
-set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
-set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
-
-%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
-if ERRORLEVEL 1 goto error
-goto end
-
-:error
-set ERROR_CODE=1
-
-:end
-@endlocal & set ERROR_CODE=%ERROR_CODE%
-
-if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
-@REM check for post script, once with legacy .bat ending and once with .cmd ending
-if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
-if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
-:skipRcPost
-
-@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
-if "%MAVEN_BATCH_PAUSE%" == "on" pause
-
-if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
-
-exit /B %ERROR_CODE%
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
+FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ echo Found %WRAPPER_JAR%
+) else (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"
+ echo Finished downloading %WRAPPER_JAR%
+)
+@REM End of extension
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/pom.xml b/pom.xml
index b8e67fe61..d8f9650aa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,9 +3,9 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- org.overture
+ bio.overtureego
- 1.2.3-SNAPSHOT
+ 2.0.0-SNAPSHOTegoOAuth 2.0 Authorization service that supports multiple OpenID Connect Providers
@@ -21,6 +21,7 @@
UTF-8UTF-81.8
+ 1.2.0.Final
@@ -31,7 +32,12 @@
org.springframework.security.oauthspring-security-oauth2
- 2.0.16.RELEASE
+ 2.0.17.RELEASE
+
+
+ org.springframework.security.oauth.boot
+ spring-security-oauth2-autoconfigure
+ 2.0.2.RELEASEorg.springframework.boot
@@ -56,6 +62,12 @@
springfox-swagger22.6.1compile
+
+
+ org.mapstruct
+ mapstruct
+
+ io.springfox
@@ -81,6 +93,7 @@
org.springframework.securityspring-security-jwt
+ 1.0.8.RELEASE
@@ -142,7 +155,7 @@
com.google.api-clientgoogle-api-client
- 1.22.0
+ 1.23.0
@@ -155,7 +168,37 @@
com.fasterxml.jackson.corejackson-databind
- 2.9.5
+ 2.9.8
+
+
+
+
+ org.assertj
+ assertj-core
+ 3.11.1
+ test
+
+
+ net.javacrumbs.json-unit
+ json-unit-fluent
+ 2.1.1
+ test
+
+
+ ca.andricdu
+ selenium-shaded
+ 3.141.59
+
+
+ org.apache.commons
+ commons-exec
+ 1.3
+
+
+ com.browserstack
+ browserstack-local-java
+ 0.1.0
+ test
@@ -180,27 +223,39 @@
-
+
- io.projectreactor
- reactor-bus
- 2.0.8.RELEASE
+ org.mapstruct
+ mapstruct-jdk8
+ ${mapstruct.version}
- io.projectreactor
- reactor-core
- 2.0.8.RELEASE
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
- org.springframework.security
- spring-security-jwt
- 1.0.8.RELEASE
+ org.springframework.boot
+ spring-boot-devtools
+ true
-
+
+ com.coveo
+ fmt-maven-plugin
+ 2.8
+
+
+
+ format
+
+
+
+ org.springframework.bootspring-boot-maven-plugin
@@ -257,6 +312,10 @@
+
+ dcc-dependencies
+ https://artifacts.oicr.on.ca/artifactory/dcc-dependencies
+ spring-snapshotsSpring Snapshots
diff --git a/src/main/assembly/bin.xml b/src/main/assembly/bin.xml
index 668a412d5..7623db13f 100644
--- a/src/main/assembly/bin.xml
+++ b/src/main/assembly/bin.xml
@@ -1,5 +1,6 @@
-dist
diff --git a/src/main/conf/logback.xml b/src/main/conf/logback.xml
index 35c778e3f..b8ec7000c 100644
--- a/src/main/conf/logback.xml
+++ b/src/main/conf/logback.xml
@@ -19,9 +19,9 @@ ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF S
-
-
-
+
+
+ true
@@ -54,18 +54,18 @@ ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF S
-
+
-
+
-
+
-
+
diff --git a/src/main/conf/wrapper.conf b/src/main/conf/wrapper.conf
index 8298a2c2d..7327dc70d 100644
--- a/src/main/conf/wrapper.conf
+++ b/src/main/conf/wrapper.conf
@@ -30,40 +30,40 @@
#********************************************************************
# Locate the java binary on the system PATH:
-wrapper.java.command=java
+wrapper.java.command = java
# Java Main class. This class must implement the WrapperListener interface
-wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp
+wrapper.java.mainclass = org.tanukisoftware.wrapper.WrapperSimpleApp
# Java Classpath (include wrapper.jar) Add class path elements as needed starting from 1
-wrapper.java.classpath.1=../lib/ego.jar
-wrapper.java.classpath.2=../lib/wrapper.jar
+wrapper.java.classpath.1 = ../lib/ego.jar
+wrapper.java.classpath.2 = ../lib/wrapper.jar
# Java Library Path (location of Wrapper.DLL or libwrapper.so)
-wrapper.java.library.path.1=../lib
+wrapper.java.library.path.1 = ../lib
# Java Bits. On applicable platforms, tells the JVM to run in 32 or 64-bit mode.
-wrapper.java.additional.auto_bits=TRUE
+wrapper.java.additional.auto_bits = TRUE
# Java Additional Parameters
-wrapper.java.additional.1=-Dlog.path=../logs
-wrapper.java.additional.2=-Dcom.sun.management.jmxremote.port=10015
-wrapper.java.additional.3=-Dcom.sun.management.jmxremote.ssl=false
-wrapper.java.additional.4=-Dcom.sun.management.jmxremote.authenticate=false
-wrapper.java.additional.5=-Djava.security.egd=file:/dev/./urandom
+wrapper.java.additional.1 = -Dlog.path=../logs
+wrapper.java.additional.2 = -Dcom.sun.management.jmxremote.port=10015
+wrapper.java.additional.3 = -Dcom.sun.management.jmxremote.ssl=false
+wrapper.java.additional.4 = -Dcom.sun.management.jmxremote.authenticate=false
+wrapper.java.additional.5 = -Djava.security.egd=file:/dev/./urandom
# Initial Java Heap Size (in MB)
#wrapper.java.initmemory=3
# Maximum Java Heap Size (in MB)
-wrapper.java.maxmemory=8192
+wrapper.java.maxmemory = 8192
# Application parameters. Add parameters as needed starting from 1
-wrapper.app.parameter.1=org.springframework.boot.loader.JarLauncher
-wrapper.app.parameter.2=--spring.config.location=../conf/
-wrapper.app.parameter.3=--logging.config=../conf/logback.xml
-wrapper.app.parameter.4=--spring.profiles.active=auth
-wrapper.app.parameter.5=--token.key-store=src/main/resources/ego-jwt.jks
+wrapper.app.parameter.1 = org.springframework.boot.loader.JarLauncher
+wrapper.app.parameter.2 = --spring.config.location=../conf/
+wrapper.app.parameter.3 = --logging.config=../conf/logback.xml
+wrapper.app.parameter.4 = --spring.profiles.active=auth
+wrapper.app.parameter.5 = --token.key-store=src/main/resources/ego-jwt.jks
#********************************************************************
# Wrapper Logging Properties
@@ -72,58 +72,58 @@ wrapper.app.parameter.5=--token.key-store=src/main/resources/ego-jwt.jks
# wrapper.debug=TRUE
# Format of output for the console. (See docs for formats)
-wrapper.console.format=PM
+wrapper.console.format = PM
# Log Level for console output. (See docs for log levels)
-wrapper.console.loglevel=INFO
+wrapper.console.loglevel = INFO
# Log file to use for wrapper output logging.
-wrapper.logfile=../logs/wrapper.YYYYMMDD.log
+wrapper.logfile = ../logs/wrapper.YYYYMMDD.log
# Format of output for the log file. (See docs for formats)
-wrapper.logfile.format=LPTM
+wrapper.logfile.format = LPTM
# Log Level for log file output. (See docs for log levels)
-wrapper.logfile.loglevel=INFO
+wrapper.logfile.loglevel = INFO
# Maximum number of rolled log files which will be allowed before old
# files are deleted. The default value of 0 implies no limit.
-wrapper.logfile.maxfiles=0
+wrapper.logfile.maxfiles = 0
# The roll mode of the log file
-wrapper.logfile.rollmode=DATE
+wrapper.logfile.rollmode = DATE
# Log Level for sys/event log output. (See docs for log levels)
-wrapper.syslog.loglevel=NONE
+wrapper.syslog.loglevel = NONE
#********************************************************************
# Wrapper General Properties
#********************************************************************
# Allow for the use of non-contiguous numbered properties
-wrapper.ignore_sequence_gaps=TRUE
+wrapper.ignore_sequence_gaps = TRUE
# Do not start if the pid file already exists.
-wrapper.pidfile.strict=TRUE
+wrapper.pidfile.strict = TRUE
# Title to use when running as a console
-wrapper.console.title=EGO Server
+wrapper.console.title = EGO Server
#********************************************************************
# Wrapper JVM Checks
#********************************************************************
# Detect DeadLocked Threads in the JVM. (Requires Standard Edition)
-wrapper.check.deadlock=TRUE
-wrapper.check.deadlock.interval=10
-wrapper.check.deadlock.action=RESTART
-wrapper.check.deadlock.output=FULL
+wrapper.check.deadlock = TRUE
+wrapper.check.deadlock.interval = 10
+wrapper.check.deadlock.action = RESTART
+wrapper.check.deadlock.output = FULL
# Out Of Memory detection.
# (Ignore output from dumping the configuration to the console. This is only needed by the TestWrapper sample application.)
-wrapper.filter.trigger.999=wrapper.filter.trigger.*java.lang.OutOfMemoryError
-wrapper.filter.allow_wildcards.999=TRUE
-wrapper.filter.action.999=NONE
+wrapper.filter.trigger.999 = wrapper.filter.trigger.*java.lang.OutOfMemoryError
+wrapper.filter.allow_wildcards.999 = TRUE
+wrapper.filter.action.999 = NONE
# Ignore -verbose:class output to avoid false positives.
-wrapper.filter.trigger.1000=[Loaded java.lang.OutOfMemoryError
+wrapper.filter.trigger.1000 = [Loaded java.lang.OutOfMemoryError
wrapper.filter.action.1000=NONE
# (Simple match)
wrapper.filter.trigger.1001=java.lang.OutOfMemoryError
@@ -142,4 +142,4 @@ wrapper.filter.trigger.1000=[Loaded java.lang.OutOfMemoryError
wrapper.filter.message.1002=An application restart request has been detected.
wrapper.shutdown.timeoutMs=60
- wrapper.ping.timeoutMs.action=DUMP,RESTART
\ No newline at end of file
+ wrapper.ping.timeoutMs.action=DUMP, RESTART
\ No newline at end of file
diff --git a/src/main/java/org/overture/ego/AuthorizationServiceMain.java b/src/main/java/bio/overture/ego/AuthorizationServiceMain.java
similarity index 97%
rename from src/main/java/org/overture/ego/AuthorizationServiceMain.java
rename to src/main/java/bio/overture/ego/AuthorizationServiceMain.java
index c8b74725c..1fbc9b94a 100644
--- a/src/main/java/org/overture/ego/AuthorizationServiceMain.java
+++ b/src/main/java/bio/overture/ego/AuthorizationServiceMain.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.overture.ego;
+package bio.overture.ego;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
diff --git a/src/main/java/org/overture/ego/config/AuthConfig.java b/src/main/java/bio/overture/ego/config/AuthConfig.java
similarity index 71%
rename from src/main/java/org/overture/ego/config/AuthConfig.java
rename to src/main/java/bio/overture/ego/config/AuthConfig.java
index 9e623ce79..5957ce382 100644
--- a/src/main/java/org/overture/ego/config/AuthConfig.java
+++ b/src/main/java/bio/overture/ego/config/AuthConfig.java
@@ -14,20 +14,21 @@
* limitations under the License.
*/
-package org.overture.ego.config;
+package bio.overture.ego.config;
+import bio.overture.ego.security.CorsFilter;
+import bio.overture.ego.service.ApplicationService;
+import bio.overture.ego.service.TokenService;
+import bio.overture.ego.token.CustomTokenEnhancer;
+import bio.overture.ego.token.signer.TokenSigner;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.TimeZone;
import lombok.extern.slf4j.Slf4j;
-import org.overture.ego.security.CorsFilter;
-import org.overture.ego.service.ApplicationService;
-import org.overture.ego.token.CustomTokenEnhancer;
-import org.overture.ego.token.signer.TokenSigner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
-import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
@@ -40,23 +41,14 @@
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.TimeZone;
-
@Slf4j
@Configuration
@EnableAuthorizationServer
public class AuthConfig extends AuthorizationServerConfigurerAdapter {
- @Autowired
- private ApplicationService clientDetailsService;
-
- @Autowired
- private AuthenticationManager authenticationManager;
-
- @Autowired
- TokenSigner tokenSigner;
+ @Autowired TokenSigner tokenSigner;
+ @Autowired TokenService tokenService;
+ @Autowired private ApplicationService clientDetailsService;
@Bean
@Primary
@@ -65,9 +57,8 @@ public CorsFilter corsFilter() {
}
@Bean
- public SimpleDateFormat formatter(){
- SimpleDateFormat formatter =
- new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
+ public SimpleDateFormat formatter() {
+ SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
return formatter;
}
@@ -77,15 +68,10 @@ public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
- @Bean
- public PasswordEncoder passwordEncoder() {
- return new BCryptPasswordEncoder();
- }
-
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
- if(tokenSigner.getKeyPair().isPresent()) {
+ if (tokenSigner.getKeyPair().isPresent()) {
converter.setKeyPair(tokenSigner.getKeyPair().get());
}
return converter;
@@ -101,8 +87,7 @@ public DefaultTokenServices tokenServices() {
}
@Override
- public void configure(ClientDetailsServiceConfigurer clients)
- throws Exception {
+ public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
@@ -112,19 +97,18 @@ public TokenEnhancer tokenEnhancer() {
}
@Override
- public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
+ public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
- tokenEnhancerChain.setTokenEnhancers(
- Arrays.asList(tokenEnhancer()));
- endpoints.tokenStore(tokenStore())
- .tokenEnhancer(tokenEnhancerChain)
- .accessTokenConverter(accessTokenConverter());
- endpoints.authenticationManager(this.authenticationManager);
+ tokenEnhancerChain.setTokenEnhancers(Collections.singletonList(tokenEnhancer()));
+ endpoints
+ .tokenStore(tokenStore())
+ .tokenEnhancer(tokenEnhancerChain)
+ .accessTokenConverter(accessTokenConverter());
}
@Override
- public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
+ public void configure(AuthorizationServerSecurityConfigurer security) {
security.allowFormAuthenticationForClients();
}
}
diff --git a/src/main/java/bio/overture/ego/config/EncoderConfig.java b/src/main/java/bio/overture/ego/config/EncoderConfig.java
new file mode 100644
index 000000000..3d300006c
--- /dev/null
+++ b/src/main/java/bio/overture/ego/config/EncoderConfig.java
@@ -0,0 +1,15 @@
+package bio.overture.ego.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+@Configuration
+public class EncoderConfig {
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+}
diff --git a/src/main/java/bio/overture/ego/config/OAuth2ClientConfig.java b/src/main/java/bio/overture/ego/config/OAuth2ClientConfig.java
new file mode 100644
index 000000000..6db5b89dd
--- /dev/null
+++ b/src/main/java/bio/overture/ego/config/OAuth2ClientConfig.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019. The Ontario Institute for Cancer Research. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 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.
+ *
+ */
+
+package bio.overture.ego.config;
+
+import bio.overture.ego.security.OAuth2ClientResources;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class OAuth2ClientConfig {
+
+ @Bean
+ @ConfigurationProperties("google")
+ public OAuth2ClientResources google() {
+ return new OAuth2ClientResources();
+ }
+
+ @Bean
+ @ConfigurationProperties("facebook")
+ public OAuth2ClientResources facebook() {
+ return new OAuth2ClientResources();
+ }
+
+ @Bean
+ @ConfigurationProperties("github")
+ public OAuth2ClientResources github() {
+ return new OAuth2ClientResources();
+ }
+
+ @Bean
+ @ConfigurationProperties("linkedin")
+ public OAuth2ClientResources linkedin() {
+ return new OAuth2ClientResources();
+ }
+}
diff --git a/src/main/java/bio/overture/ego/config/RequestLoggingFilterConfig.java b/src/main/java/bio/overture/ego/config/RequestLoggingFilterConfig.java
new file mode 100644
index 000000000..d119f99cd
--- /dev/null
+++ b/src/main/java/bio/overture/ego/config/RequestLoggingFilterConfig.java
@@ -0,0 +1,20 @@
+package bio.overture.ego.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.filter.CommonsRequestLoggingFilter;
+
+@Configuration
+public class RequestLoggingFilterConfig {
+
+ @Bean
+ public CommonsRequestLoggingFilter logFilter() {
+ CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
+ filter.setIncludeQueryString(true);
+ filter.setIncludePayload(true);
+ filter.setMaxPayloadLength(10000);
+ filter.setIncludeHeaders(false);
+ filter.setIncludeClientInfo(true);
+ return filter;
+ }
+}
diff --git a/src/main/java/bio/overture/ego/config/SecureServerConfig.java b/src/main/java/bio/overture/ego/config/SecureServerConfig.java
new file mode 100644
index 000000000..8bb19497e
--- /dev/null
+++ b/src/main/java/bio/overture/ego/config/SecureServerConfig.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2017. The Ontario Institute for Cancer Research. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 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.
+ */
+
+package bio.overture.ego.config;
+
+import bio.overture.ego.security.AuthorizationManager;
+import bio.overture.ego.security.JWTAuthorizationFilter;
+import bio.overture.ego.security.OAuth2SsoFilter;
+import bio.overture.ego.security.SecureAuthorizationManager;
+import lombok.SneakyThrows;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.security.SecurityProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
+import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
+
+@Configuration
+@EnableWebSecurity
+@EnableOAuth2Client
+@Profile("auth")
+public class SecureServerConfig {
+
+ /** Constants */
+ private final String[] PUBLIC_ENDPOINTS =
+ new String[] {
+ "/oauth/token",
+ "/oauth/google/token",
+ "/oauth/facebook/token",
+ "/oauth/token/public_key",
+ "/oauth/token/verify",
+ "/oauth/ego-token"
+ };
+
+ /** Dependencies */
+ private AuthenticationManager authenticationManager;
+
+ private OAuth2SsoFilter oAuth2SsoFilter;
+
+ @SneakyThrows
+ @Autowired
+ public SecureServerConfig(
+ AuthenticationManager authenticationManager, OAuth2SsoFilter oAuth2SsoFilter) {
+ this.authenticationManager = authenticationManager;
+ this.oAuth2SsoFilter = oAuth2SsoFilter;
+ }
+
+ @Bean
+ @SneakyThrows
+ public JWTAuthorizationFilter authorizationFilter() {
+ return new JWTAuthorizationFilter(authenticationManager, PUBLIC_ENDPOINTS);
+ }
+
+ // Do not register JWTAuthorizationFilter in global scope
+ @Bean
+ public FilterRegistrationBean jwtAuthorizationFilterRegistration(JWTAuthorizationFilter filter) {
+ FilterRegistrationBean registration = new FilterRegistrationBean<>(filter);
+ registration.setEnabled(false);
+ return registration;
+ }
+
+ // Do not register OAuth2SsoFilter in global scope
+ @Bean
+ public FilterRegistrationBean oAuth2SsoFilterRegistration(OAuth2SsoFilter filter) {
+ FilterRegistrationBean registration = new FilterRegistrationBean<>(filter);
+ registration.setEnabled(false);
+ return registration;
+ }
+
+ @Bean
+ public AuthorizationManager authorizationManager() {
+ return new SecureAuthorizationManager();
+ }
+
+ // Register oauth2 filter earlier so it can handle redirects signaled by exceptions in
+ // authentication requests.
+ @Bean
+ public FilterRegistrationBean oauth2ClientFilterRegistration(
+ OAuth2ClientContextFilter filter) {
+ FilterRegistrationBean registration = new FilterRegistrationBean<>();
+ registration.setFilter(filter);
+ registration.setOrder(-100);
+ return registration;
+ }
+
+ // int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
+ @Configuration
+ @Order(SecurityProperties.BASIC_AUTH_ORDER - 3)
+ public class OAuthConfigurerAdapter extends WebSecurityConfigurerAdapter {
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.requestMatchers()
+ .antMatchers("/oauth/login/*", "/oauth/ego-token")
+ .and()
+ .csrf()
+ .disable()
+ .authorizeRequests()
+ .anyRequest()
+ .permitAll()
+ .and()
+ .addFilterAfter(oAuth2SsoFilter, BasicAuthenticationFilter.class);
+ }
+ }
+
+ @Configuration
+ @Order(SecurityProperties.BASIC_AUTH_ORDER + 3)
+ public class AppConfigurerAdapter extends WebSecurityConfigurerAdapter {
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.csrf()
+ .disable()
+ .authorizeRequests()
+ .antMatchers(
+ "/",
+ "/favicon.ico",
+ "/swagger**",
+ "/swagger-resources/**",
+ "/configuration/ui",
+ "/configuration/**",
+ "/v2/api**",
+ "/webjars/**",
+ "/oauth/token/verify",
+ "/oauth/token/public_key")
+ .permitAll()
+ .antMatchers(HttpMethod.OPTIONS, "/**")
+ .permitAll()
+ .anyRequest()
+ .authenticated()
+ .and()
+ .addFilterBefore(authorizationFilter(), BasicAuthenticationFilter.class)
+ .sessionManagement()
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
+ }
+ }
+}
diff --git a/src/main/java/org/overture/ego/config/ServerConfig.java b/src/main/java/bio/overture/ego/config/ServerConfig.java
similarity index 78%
rename from src/main/java/org/overture/ego/config/ServerConfig.java
rename to src/main/java/bio/overture/ego/config/ServerConfig.java
index 1e2be461d..eedf2c803 100644
--- a/src/main/java/org/overture/ego/config/ServerConfig.java
+++ b/src/main/java/bio/overture/ego/config/ServerConfig.java
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package org.overture.ego.config;
+package bio.overture.ego.config;
-import org.overture.ego.security.AuthorizationManager;
-import org.overture.ego.security.DefaultAuthorizationManager;
+import bio.overture.ego.security.AuthorizationManager;
+import bio.overture.ego.security.DefaultAuthorizationManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@@ -38,12 +38,17 @@ public AuthorizationManager authorizationManager() {
@Override
protected void configure(HttpSecurity http) throws Exception {
- http.csrf().disable()
+ http.csrf()
+ .disable()
.authorizeRequests()
- .antMatchers("/**").permitAll()
- .anyRequest().authenticated().and().authorizeRequests()
+ .antMatchers("/**")
+ .permitAll()
+ .anyRequest()
+ .authenticated()
.and()
- .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
+ .authorizeRequests()
+ .and()
+ .sessionManagement()
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
-
}
diff --git a/src/main/java/bio/overture/ego/config/SwaggerConfig.java b/src/main/java/bio/overture/ego/config/SwaggerConfig.java
new file mode 100644
index 000000000..d4939337d
--- /dev/null
+++ b/src/main/java/bio/overture/ego/config/SwaggerConfig.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2017. The Ontario Institute for Cancer Research. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 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.
+ */
+
+package bio.overture.ego.config;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.val;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.paths.RelativePathProvider;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+@EnableSwagger2
+@Configuration
+public class SwaggerConfig {
+
+ @Component
+ @ConfigurationProperties(prefix = "swagger")
+ class SwaggerProperties {
+ /** Specify host if ego is running behind proxy. */
+ @Setter @Getter private String host = "";
+
+ /**
+ * If there is url write rule, you may want to set this variable. This value requires host to be
+ * not empty.
+ */
+ @Setter @Getter private String baseUrl = "";
+ }
+
+ @Bean
+ public Docket productApi(SwaggerProperties properties) {
+ val docket =
+ new Docket(DocumentationType.SWAGGER_2)
+ .select()
+ .apis(RequestHandlerSelectors.basePackage("bio.overture.ego.controller"))
+ .build()
+ .host(properties.host)
+ .pathProvider(
+ new RelativePathProvider(null) {
+ @Override
+ public String getApplicationBasePath() {
+ return properties.getBaseUrl();
+ }
+ })
+ .apiInfo(metaInfo());
+
+ return docket;
+ }
+
+ private ApiInfo metaInfo() {
+
+ return new ApiInfo(
+ "ego Service API",
+ "ego API Documentation",
+ "0.02",
+ "",
+ new Contact("", "", ""),
+ "Apache License Version 2.0",
+ "");
+ }
+}
diff --git a/src/main/java/bio/overture/ego/config/UserDefaultsConfig.java b/src/main/java/bio/overture/ego/config/UserDefaultsConfig.java
new file mode 100644
index 000000000..96358bde1
--- /dev/null
+++ b/src/main/java/bio/overture/ego/config/UserDefaultsConfig.java
@@ -0,0 +1,28 @@
+package bio.overture.ego.config;
+
+import static bio.overture.ego.model.enums.StatusType.PENDING;
+import static bio.overture.ego.model.enums.StatusType.resolveStatusType;
+import static bio.overture.ego.model.enums.UserType.USER;
+import static bio.overture.ego.model.enums.UserType.resolveUserType;
+import static org.springframework.util.StringUtils.isEmpty;
+
+import bio.overture.ego.model.enums.StatusType;
+import bio.overture.ego.model.enums.UserType;
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class UserDefaultsConfig {
+
+ @Getter private final UserType defaultUserType;
+
+ @Getter private final StatusType defaultUserStatus;
+
+ public UserDefaultsConfig(
+ @Value("${default.user.type}") String userType,
+ @Value("${default.user.status}") String userStatus) {
+ this.defaultUserType = isEmpty(userType) ? USER : resolveUserType(userType);
+ this.defaultUserStatus = isEmpty(userStatus) ? PENDING : resolveStatusType(userStatus);
+ }
+}
diff --git a/src/main/java/org/overture/ego/config/WebRequestConfig.java b/src/main/java/bio/overture/ego/config/WebRequestConfig.java
similarity index 82%
rename from src/main/java/org/overture/ego/config/WebRequestConfig.java
rename to src/main/java/bio/overture/ego/config/WebRequestConfig.java
index 546b312a2..e99bcd589 100644
--- a/src/main/java/org/overture/ego/config/WebRequestConfig.java
+++ b/src/main/java/bio/overture/ego/config/WebRequestConfig.java
@@ -14,24 +14,23 @@
* limitations under the License.
*/
-package org.overture.ego.config;
+package bio.overture.ego.config;
-import org.overture.ego.controller.resolver.FilterResolver;
-import org.overture.ego.controller.resolver.PageableResolver;
-import org.overture.ego.model.enums.Fields;
-import org.overture.ego.utils.FieldUtils;
+import bio.overture.ego.controller.resolver.FilterResolver;
+import bio.overture.ego.controller.resolver.PageableResolver;
+import bio.overture.ego.model.enums.Fields;
+import bio.overture.ego.utils.FieldUtils;
+import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
-import java.util.List;
-
@Configuration
public class WebRequestConfig extends WebMvcConfigurerAdapter {
@Bean
- public List fieldValues(){
+ public List fieldValues() {
return FieldUtils.getStaticFieldValueList(Fields.class);
}
diff --git a/src/main/java/bio/overture/ego/controller/ApplicationController.java b/src/main/java/bio/overture/ego/controller/ApplicationController.java
new file mode 100644
index 000000000..42e25bb89
--- /dev/null
+++ b/src/main/java/bio/overture/ego/controller/ApplicationController.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 2017. The Ontario Institute for Cancer Research. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 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.
+ */
+
+package bio.overture.ego.controller;
+
+import static bio.overture.ego.controller.resolver.PageableResolver.LIMIT;
+import static bio.overture.ego.controller.resolver.PageableResolver.OFFSET;
+import static bio.overture.ego.controller.resolver.PageableResolver.SORT;
+import static bio.overture.ego.controller.resolver.PageableResolver.SORTORDER;
+import static org.apache.commons.lang.StringUtils.isEmpty;
+import static org.springframework.http.HttpHeaders.AUTHORIZATION;
+import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
+import static org.springframework.web.bind.annotation.RequestMethod.GET;
+import static org.springframework.web.bind.annotation.RequestMethod.POST;
+import static org.springframework.web.bind.annotation.RequestMethod.PUT;
+
+import bio.overture.ego.model.dto.CreateApplicationRequest;
+import bio.overture.ego.model.dto.PageDTO;
+import bio.overture.ego.model.dto.UpdateApplicationRequest;
+import bio.overture.ego.model.entity.Application;
+import bio.overture.ego.model.entity.Group;
+import bio.overture.ego.model.entity.User;
+import bio.overture.ego.model.enums.Fields;
+import bio.overture.ego.model.search.Filters;
+import bio.overture.ego.model.search.SearchFilter;
+import bio.overture.ego.security.AdminScoped;
+import bio.overture.ego.service.ApplicationService;
+import bio.overture.ego.service.GroupService;
+import bio.overture.ego.service.UserService;
+import bio.overture.ego.view.Views;
+import com.fasterxml.jackson.annotation.JsonView;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import java.util.List;
+import java.util.UUID;
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+import springfox.documentation.annotations.ApiIgnore;
+
+@Slf4j
+@RestController
+@RequestMapping("/applications")
+public class ApplicationController {
+
+ /** Dependencies */
+ private final ApplicationService applicationService;
+
+ private final GroupService groupService;
+ private final UserService userService;
+
+ @Autowired
+ public ApplicationController(
+ @NonNull ApplicationService applicationService,
+ @NonNull GroupService groupService,
+ @NonNull UserService userService) {
+ this.applicationService = applicationService;
+ this.groupService = groupService;
+ this.userService = userService;
+ }
+
+ @AdminScoped
+ @RequestMapping(method = GET, value = "")
+ @ApiImplicitParams({
+ @ApiImplicitParam(
+ name = LIMIT,
+ required = false,
+ dataType = "string",
+ paramType = "query",
+ value = "Number of results to retrieve"),
+ @ApiImplicitParam(
+ name = OFFSET,
+ required = false,
+ dataType = "string",
+ paramType = "query",
+ value = "Index of first result to retrieve"),
+ @ApiImplicitParam(
+ name = SORT,
+ required = false,
+ dataType = "string",
+ paramType = "query",
+ value = "Field to sort on"),
+ @ApiImplicitParam(
+ name = SORTORDER,
+ required = false,
+ dataType = "string",
+ paramType = "query",
+ value = "Sorting order: ASC|DESC. Default order: DESC"),
+ })
+ @ApiResponses(value = {@ApiResponse(code = 200, message = "Page Applications")})
+ @JsonView(Views.REST.class)
+ public @ResponseBody PageDTO findApplications(
+ @RequestHeader(value = AUTHORIZATION, required = true) final String accessToken,
+ @RequestParam(value = "query", required = false) String query,
+ @ApiIgnore @Filters List filters,
+ Pageable pageable) {
+ if (isEmpty(query)) {
+ return new PageDTO<>(applicationService.listApps(filters, pageable));
+ } else {
+ return new PageDTO<>(applicationService.findApps(query, filters, pageable));
+ }
+ }
+
+ @AdminScoped
+ @RequestMapping(method = POST, value = "")
+ @ApiResponses(
+ value = {@ApiResponse(code = 200, message = "New Application", response = Application.class)})
+ public @ResponseBody Application createApplication(
+ @RequestHeader(value = AUTHORIZATION, required = true) final String accessToken,
+ @RequestBody(required = true) CreateApplicationRequest request) {
+ return applicationService.create(request);
+ }
+
+ @AdminScoped
+ @RequestMapping(method = GET, value = "/{id}")
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = 200, message = "Application Details", response = Application.class)
+ })
+ @JsonView(Views.REST.class)
+ public @ResponseBody Application getApplication(
+ @RequestHeader(value = AUTHORIZATION, required = true) final String accessToken,
+ @PathVariable(value = "id", required = true) UUID id) {
+ return applicationService.getById(id);
+ }
+
+ @AdminScoped
+ @RequestMapping(method = PUT, value = "/{id}")
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = 200, message = "Updated application info", response = Application.class)
+ })
+ public @ResponseBody Application updateApplication(
+ @RequestHeader(value = AUTHORIZATION, required = true) final String accessToken,
+ @PathVariable(name = "id", required = true) UUID id,
+ @RequestBody(required = true) UpdateApplicationRequest updateRequest) {
+ return applicationService.partialUpdate(id, updateRequest);
+ }
+
+ @AdminScoped
+ @RequestMapping(method = DELETE, value = "/{id}")
+ @ResponseStatus(value = HttpStatus.OK)
+ public void deleteApplication(
+ @RequestHeader(value = AUTHORIZATION, required = true) final String accessToken,
+ @PathVariable(value = "id", required = true) UUID id) {
+ applicationService.delete(id);
+ }
+
+ @AdminScoped
+ @RequestMapping(method = GET, value = "/{id}/users")
+ @ApiImplicitParams({
+ @ApiImplicitParam(
+ name = LIMIT,
+ required = false,
+ dataType = "string",
+ paramType = "query",
+ value = "Number of results to retrieve"),
+ @ApiImplicitParam(
+ name = OFFSET,
+ required = false,
+ dataType = "string",
+ paramType = "query",
+ value = "Index of first result to retrieve"),
+ @ApiImplicitParam(
+ name = Fields.ID,
+ required = false,
+ dataType = "string",
+ paramType = "query",
+ value = "Search for ids containing this text"),
+ @ApiImplicitParam(
+ name = SORT,
+ required = false,
+ dataType = "string",
+ paramType = "query",
+ value = "Field to sort on"),
+ @ApiImplicitParam(
+ name = SORTORDER,
+ required = false,
+ dataType = "string",
+ paramType = "query",
+ value = "Sorting order: ASC|DESC. Default order: DESC"),
+ })
+ @ApiResponses(value = {@ApiResponse(code = 200, message = "Page Users for an Application")})
+ @JsonView(Views.REST.class)
+ public @ResponseBody PageDTO getUsersForApplication(
+ @RequestHeader(value = AUTHORIZATION, required = true) final String accessToken,
+ @PathVariable(value = "id", required = true) UUID id,
+ @RequestParam(value = "query", required = false) String query,
+ @ApiIgnore @Filters List filters,
+ Pageable pageable) {
+ if (isEmpty(query)) {
+ return new PageDTO<>(userService.findUsersForApplication(id, filters, pageable));
+ } else {
+ return new PageDTO<>(userService.findUsersForApplication(id, query, filters, pageable));
+ }
+ }
+
+ @AdminScoped
+ @RequestMapping(method = GET, value = "/{id}/groups")
+ @ApiImplicitParams({
+ @ApiImplicitParam(
+ name = LIMIT,
+ required = false,
+ dataType = "string",
+ paramType = "query",
+ value = "Number of results to retrieve"),
+ @ApiImplicitParam(
+ name = OFFSET,
+ required = false,
+ dataType = "string",
+ paramType = "query",
+ value = "Index of first result to retrieve"),
+ @ApiImplicitParam(
+ name = Fields.ID,
+ required = false,
+ dataType = "string",
+ paramType = "query",
+ value = "Search for ids containing this text"),
+ @ApiImplicitParam(
+ name = SORT,
+ required = false,
+ dataType = "string",
+ paramType = "query",
+ value = "Field to sort on"),
+ @ApiImplicitParam(
+ name = SORTORDER,
+ required = false,
+ dataType = "string",
+ paramType = "query",
+ value = "Sorting order: ASC|DESC. Default order: DESC"),
+ })
+ @ApiResponses(value = {@ApiResponse(code = 200, message = "Page Groups for an Application")})
+ @JsonView(Views.REST.class)
+ public @ResponseBody PageDTO getGroupsForApplication(
+ @RequestHeader(value = AUTHORIZATION, required = true) final String accessToken,
+ @PathVariable(value = "id", required = true) UUID id,
+ @RequestParam(value = "query", required = false) String query,
+ @ApiIgnore @Filters List filters,
+ Pageable pageable) {
+ if (isEmpty(query)) {
+ return new PageDTO<>(groupService.findGroupsForApplication(id, filters, pageable));
+ } else {
+ return new PageDTO<>(groupService.findGroupsForApplication(id, query, filters, pageable));
+ }
+ }
+}
diff --git a/src/main/java/bio/overture/ego/controller/AuthController.java b/src/main/java/bio/overture/ego/controller/AuthController.java
new file mode 100644
index 000000000..eedefb97e
--- /dev/null
+++ b/src/main/java/bio/overture/ego/controller/AuthController.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2017. The Ontario Institute for Cancer Research. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 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.
+ */
+
+package bio.overture.ego.controller;
+
+import static org.springframework.http.HttpStatus.BAD_REQUEST;
+import static org.springframework.http.HttpStatus.OK;
+import static org.springframework.http.HttpStatus.UNAUTHORIZED;
+import static org.springframework.web.bind.annotation.RequestMethod.GET;
+import static org.springframework.web.bind.annotation.RequestMethod.POST;
+
+import bio.overture.ego.provider.facebook.FacebookTokenService;
+import bio.overture.ego.provider.google.GoogleTokenService;
+import bio.overture.ego.service.TokenService;
+import bio.overture.ego.token.IDToken;
+import bio.overture.ego.token.signer.TokenSigner;
+import lombok.NonNull;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import lombok.val;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
+import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+@Slf4j
+@RestController
+@RequestMapping("/oauth")
+public class AuthController {
+
+ private final TokenService tokenService;
+ private final GoogleTokenService googleTokenService;
+ private final FacebookTokenService facebookTokenService;
+ private final TokenSigner tokenSigner;
+
+ @Autowired
+ public AuthController(
+ @NonNull TokenService tokenService,
+ @NonNull GoogleTokenService googleTokenService,
+ @NonNull FacebookTokenService facebookTokenService,
+ @NonNull TokenSigner tokenSigner) {
+ this.tokenService = tokenService;
+ this.googleTokenService = googleTokenService;
+ this.facebookTokenService = facebookTokenService;
+ this.tokenSigner = tokenSigner;
+ }
+
+ @RequestMapping(method = GET, value = "/google/token")
+ @ResponseStatus(value = OK)
+ @SneakyThrows
+ public @ResponseBody String exchangeGoogleTokenForAuth(
+ @RequestHeader(value = "token") final String idToken) {
+ if (!googleTokenService.validToken(idToken))
+ throw new InvalidTokenException("Invalid user token:" + idToken);
+ val authInfo = googleTokenService.decode(idToken);
+ return tokenService.generateUserToken(authInfo);
+ }
+
+ @RequestMapping(method = GET, value = "/facebook/token")
+ @ResponseStatus(value = OK)
+ @SneakyThrows
+ public @ResponseBody String exchangeFacebookTokenForAuth(
+ @RequestHeader(value = "token") final String idToken) {
+ if (!facebookTokenService.validToken(idToken))
+ throw new InvalidTokenException("Invalid user token:" + idToken);
+ val authInfo = facebookTokenService.getAuthInfo(idToken);
+ if (authInfo.isPresent()) {
+ return tokenService.generateUserToken(authInfo.get());
+ } else {
+ throw new InvalidTokenException("Unable to generate auth token for this user");
+ }
+ }
+
+ @RequestMapping(method = GET, value = "/token/verify")
+ @ResponseStatus(value = OK)
+ @SneakyThrows
+ public @ResponseBody boolean verifyJWToken(@RequestHeader(value = "token") final String token) {
+ if (StringUtils.isEmpty(token)) {
+ throw new InvalidTokenException("ScopedAccessToken is empty");
+ }
+
+ if (!tokenService.isValidToken(token)) {
+ throw new InvalidTokenException("ScopedAccessToken failed validation");
+ }
+ return true;
+ }
+
+ @RequestMapping(method = GET, value = "/token/public_key")
+ @ResponseStatus(value = OK)
+ public @ResponseBody String getPublicKey() {
+ val pubKey = tokenSigner.getEncodedPublicKey();
+ return pubKey.orElse("");
+ }
+
+ @RequestMapping(
+ method = {GET, POST},
+ value = "/ego-token")
+ @SneakyThrows
+ public ResponseEntity user(OAuth2Authentication authentication) {
+ if (authentication == null) return new ResponseEntity<>("Please login", UNAUTHORIZED);
+ String token = tokenService.generateUserToken((IDToken) authentication.getPrincipal());
+ SecurityContextHolder.getContext().setAuthentication(null);
+ return new ResponseEntity<>(token, OK);
+ }
+
+ @ExceptionHandler({InvalidTokenException.class})
+ public ResponseEntity