initial
This commit is contained in:
commit
b991bed464
27 changed files with 2760 additions and 0 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
target/
|
||||||
|
node_modules/
|
||||||
|
*.iml
|
||||||
|
*.idea
|
||||||
|
.settings/
|
||||||
|
.project
|
||||||
|
.classpath
|
18
api-key-ear/Dockerfile
Normal file
18
api-key-ear/Dockerfile
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
FROM java:8-jre-alpine
|
||||||
|
|
||||||
|
ENV KEYCLOAK_VERSION 6.0.1
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates openssl && wget https://downloads.jboss.org/keycloak/${KEYCLOAK_VERSION}/keycloak-${KEYCLOAK_VERSION}.tar.gz
|
||||||
|
|
||||||
|
RUN tar xvf keycloak-${KEYCLOAK_VERSION}.tar.gz && rm keycloak-${KEYCLOAK_VERSION}.tar.gz
|
||||||
|
|
||||||
|
WORKDIR keycloak-${KEYCLOAK_VERSION}
|
||||||
|
|
||||||
|
#add admin user
|
||||||
|
RUN ./bin/add-user-keycloak.sh -u admin -p admin --realm master
|
||||||
|
|
||||||
|
COPY target/api-key-ear-0.1.ear standalone/deployments
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
ENTRYPOINT ["./bin/standalone.sh", "-b", "0.0.0.0"]
|
1650
api-key-ear/import/example-realm.json
Normal file
1650
api-key-ear/import/example-realm.json
Normal file
File diff suppressed because it is too large
Load diff
50
api-key-ear/pom.xml
Normal file
50
api-key-ear/pom.xml
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>com.gwidgets</groupId>
|
||||||
|
<artifactId>api-key-keycloak-example</artifactId>
|
||||||
|
<version>0.1</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>api-key-ear</artifactId>
|
||||||
|
<packaging>ear</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<dockerfile-maven-version>1.4.3</dockerfile-maven-version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.gwidgets</groupId>
|
||||||
|
<artifactId>api-key-module</artifactId>
|
||||||
|
<version>${version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-ear-plugin</artifactId>
|
||||||
|
<version>2.10.1</version>
|
||||||
|
<configuration>
|
||||||
|
<defaultLibBundleDir>lib</defaultLibBundleDir>
|
||||||
|
<modules>
|
||||||
|
<jarModule>
|
||||||
|
<groupId>com.gwidgets</groupId>
|
||||||
|
<artifactId>api-key-module</artifactId>
|
||||||
|
<includeInApplicationXml>true</includeInApplicationXml>
|
||||||
|
<bundleFileName>api-key-module.jar</bundleFileName>
|
||||||
|
<bundleDir>/</bundleDir>
|
||||||
|
</jarModule>
|
||||||
|
</modules>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<jboss-deployment-structure>
|
||||||
|
<deployment>
|
||||||
|
<module-alias name="deployment.api-key"/>
|
||||||
|
</deployment>
|
||||||
|
<sub-deployment name="api-key-module.jar">
|
||||||
|
<dependencies>
|
||||||
|
<module name="javax.persistence.api"/>
|
||||||
|
<module name="javax.servlet.api"/>
|
||||||
|
<module name="javax.transaction.api"/>
|
||||||
|
<module name="javax.ws.rs.api"/>
|
||||||
|
<module name="org.jboss.resteasy.resteasy-jaxb-provider"/>
|
||||||
|
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
|
||||||
|
<module name="org.jboss.resteasy.resteasy-multipart-provider"/>
|
||||||
|
<module name="org.javassist"/>
|
||||||
|
<module name="org.hibernate"/>
|
||||||
|
<module name="org.keycloak.keycloak-core"/>
|
||||||
|
<module name="org.keycloak.keycloak-server-spi"/>
|
||||||
|
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||||
|
<module name="org.keycloak.keycloak-model-jpa"/>
|
||||||
|
<module name="org.keycloak.keycloak-model-infinispan"/>
|
||||||
|
<module name="org.keycloak.keycloak-services"/>
|
||||||
|
</dependencies>
|
||||||
|
</sub-deployment>
|
||||||
|
</jboss-deployment-structure>
|
57
api-key-module/pom.xml
Normal file
57
api-key-module/pom.xml
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>com.gwidgets</groupId>
|
||||||
|
<artifactId>api-key-keycloak-example</artifactId>
|
||||||
|
<version>0.1</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>api-key-module</artifactId>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>8</source>
|
||||||
|
<target>8</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<keyclock.version>6.0.1</keyclock.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-core</artifactId>
|
||||||
|
<version>${keyclock.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-server-spi</artifactId>
|
||||||
|
<version>${keyclock.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-model-jpa</artifactId>
|
||||||
|
<version>${keyclock.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.spec.javax.ws.rs</groupId>
|
||||||
|
<artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
|
||||||
|
<version>1.0.0.Final</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.gwidgets.providers;
|
||||||
|
|
||||||
|
import com.gwidgets.resources.ApiKeyResource;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.services.resource.RealmResourceProvider;
|
||||||
|
|
||||||
|
public class ApiKeyResourceProvider implements RealmResourceProvider {
|
||||||
|
|
||||||
|
|
||||||
|
private KeycloakSession session;
|
||||||
|
|
||||||
|
public ApiKeyResourceProvider(KeycloakSession session) {
|
||||||
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getResource() {
|
||||||
|
return new ApiKeyResource(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.gwidgets.providers;
|
||||||
|
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
import org.keycloak.services.resource.RealmResourceProvider;
|
||||||
|
import org.keycloak.services.resource.RealmResourceProviderFactory;
|
||||||
|
|
||||||
|
public class ApiKeyResourceProviderFactory implements RealmResourceProviderFactory {
|
||||||
|
|
||||||
|
|
||||||
|
public RealmResourceProvider create(KeycloakSession session) {
|
||||||
|
return new ApiKeyResourceProvider(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(Config.Scope config) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return "check";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package com.gwidgets.providers;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import org.keycloak.common.util.RandomString;
|
||||||
|
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||||
|
import org.keycloak.events.Event;
|
||||||
|
import org.keycloak.events.EventListenerProvider;
|
||||||
|
import org.keycloak.events.EventType;
|
||||||
|
import org.keycloak.events.admin.AdminEvent;
|
||||||
|
import org.keycloak.events.admin.OperationType;
|
||||||
|
import org.keycloak.events.admin.ResourceType;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.RealmProvider;
|
||||||
|
import org.keycloak.models.jpa.entities.UserAttributeEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.UserEntity;
|
||||||
|
|
||||||
|
public class RegisterEventListenerProvider implements EventListenerProvider {
|
||||||
|
|
||||||
|
private KeycloakSession session;
|
||||||
|
private RealmProvider model;
|
||||||
|
//keycloak utility to generate random strings, anything can be used e.g UUID,..
|
||||||
|
private RandomString randomString;
|
||||||
|
private EntityManager entityManager;
|
||||||
|
|
||||||
|
public RegisterEventListenerProvider(KeycloakSession session) {
|
||||||
|
this.session = session;
|
||||||
|
this.model = session.realms();
|
||||||
|
this.entityManager = session.getProvider(JpaConnectionProvider.class).getEntityManager();
|
||||||
|
this.randomString = new RandomString(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onEvent(Event event) {
|
||||||
|
//we are only interested in the register event
|
||||||
|
if (event.getType().equals(EventType.REGISTER)) {
|
||||||
|
RealmModel realm = model.getRealm(event.getRealmId());
|
||||||
|
String userId = event.getUserId();
|
||||||
|
addApiKeyAttribute(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onEvent(AdminEvent adminEvent, boolean includeRepresentation) {
|
||||||
|
// in case the user is created from admin or rest api
|
||||||
|
if (Objects.equals(adminEvent.getResourceType(), ResourceType.USER) && Objects.equals(adminEvent.getOperationType(), OperationType.CREATE)) {
|
||||||
|
String userId = adminEvent.getResourcePath().split("/")[1];
|
||||||
|
if (Objects.nonNull(userId)) {
|
||||||
|
addApiKeyAttribute(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void addApiKeyAttribute(String userId) {
|
||||||
|
|
||||||
|
String apiKey = randomString.nextString();
|
||||||
|
UserEntity userEntity = entityManager.find(UserEntity.class, userId);
|
||||||
|
UserAttributeEntity attributeEntity = new UserAttributeEntity();
|
||||||
|
attributeEntity.setName("api-key");
|
||||||
|
attributeEntity.setValue(apiKey);
|
||||||
|
attributeEntity.setUser(userEntity);
|
||||||
|
attributeEntity.setId(UUID.randomUUID().toString());
|
||||||
|
entityManager.persist(attributeEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.gwidgets.providers;
|
||||||
|
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.events.EventListenerProvider;
|
||||||
|
import org.keycloak.events.EventListenerProviderFactory;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
|
||||||
|
public class RegisterEventListenerProviderFactory implements EventListenerProviderFactory {
|
||||||
|
|
||||||
|
public EventListenerProvider create(KeycloakSession keycloakSession) {
|
||||||
|
return new RegisterEventListenerProvider(keycloakSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(Config.Scope scope) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postInit(KeycloakSessionFactory keycloakSessionFactory) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return "api-key-registration-generation";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.gwidgets.resources;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
|
||||||
|
public class ApiKeyResource {
|
||||||
|
|
||||||
|
private KeycloakSession session;
|
||||||
|
|
||||||
|
private final String realmName;
|
||||||
|
|
||||||
|
public ApiKeyResource(KeycloakSession session) {
|
||||||
|
this.session = session;
|
||||||
|
String envRealmName = System.getenv("REALM_NAME");
|
||||||
|
this.realmName = Objects.isNull(envRealmName) || Objects.equals(System.getenv(envRealmName), "")? "example": envRealmName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces("application/json")
|
||||||
|
public Response checkApiKey(@QueryParam("apiKey") String apiKey) {
|
||||||
|
List<UserModel> result = session.userStorageManager().searchForUserByUserAttribute("api-key", apiKey, session.realms().getRealm(realmName));
|
||||||
|
return result.isEmpty() ? Response.status(401).build(): Response.ok().build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
com.gwidgets.providers.RegisterEventListenerProviderFactory
|
|
@ -0,0 +1 @@
|
||||||
|
com.gwidgets.providers.ApiKeyResourceProviderFactory
|
31
dashboard-service/.gitignore
vendored
Normal file
31
dashboard-service/.gitignore
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
HELP.md
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**
|
||||||
|
!**/src/test/**
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
20
dashboard-service/Dockerfile
Normal file
20
dashboard-service/Dockerfile
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
FROM maven:3.6-jdk-8-alpine as builder
|
||||||
|
|
||||||
|
RUN mkdir app
|
||||||
|
|
||||||
|
WORKDIR app
|
||||||
|
|
||||||
|
COPY src src
|
||||||
|
COPY pom.xml .
|
||||||
|
|
||||||
|
RUN mvn clean package
|
||||||
|
|
||||||
|
FROM openjdk:8-jre-alpine
|
||||||
|
|
||||||
|
ARG VERSION=0.1
|
||||||
|
|
||||||
|
COPY --from=builder /app/target/dashboard-service-${VERSION}.jar app.jar
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD ["java", "-jar", "app.jar"]
|
63
dashboard-service/pom.xml
Normal file
63
dashboard-service/pom.xml
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>2.1.0.RELEASE</version>
|
||||||
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
<groupId>com.gwidgets</groupId>
|
||||||
|
<artifactId>dashboard-service</artifactId>
|
||||||
|
<version>0.1</version>
|
||||||
|
<name>dashboard-service</name>
|
||||||
|
<description>Demo project for Spring Boot</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
<keycloak.version>6.0.1</keycloak.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-security-adapter</artifactId>
|
||||||
|
<version>6.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak.bom</groupId>
|
||||||
|
<artifactId>keycloak-adapter-bom</artifactId>
|
||||||
|
<version>${keycloak.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.gwidgets;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import org.keycloak.KeycloakPrincipal;
|
||||||
|
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||||
|
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||||
|
import org.keycloak.representations.AccessToken;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
public class DashboardController {
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping(value = "/user", produces = "application/json")
|
||||||
|
@ResponseBody
|
||||||
|
public AccessToken getUserInfo(HttpServletRequest request) {
|
||||||
|
return ((KeycloakPrincipal<RefreshableKeycloakSecurityContext>)((KeycloakAuthenticationToken)request.getUserPrincipal()).getPrincipal()).getKeycloakSecurityContext().getToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/logout")
|
||||||
|
public @ResponseBody void logout(HttpServletRequest request) throws Exception {
|
||||||
|
request.logout();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.gwidgets;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class DashboardServiceApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(DashboardServiceApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
package com.gwidgets;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
import org.keycloak.KeycloakPrincipal;
|
||||||
|
import org.keycloak.KeycloakSecurityContext;
|
||||||
|
import org.keycloak.adapters.KeycloakConfigResolver;
|
||||||
|
import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
|
||||||
|
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
|
||||||
|
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
|
||||||
|
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Scope;
|
||||||
|
import org.springframework.context.annotation.ScopedProxyMode;
|
||||||
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
|
||||||
|
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||||
|
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
|
||||||
|
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
|
||||||
|
import org.keycloak.adapters.springboot.KeycloakSpringBootProperties;
|
||||||
|
|
||||||
|
|
||||||
|
@EnableConfigurationProperties(KeycloakSpringBootProperties.class)
|
||||||
|
@KeycloakConfiguration
|
||||||
|
public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public KeycloakConfigResolver keycloakConfigResolver() {
|
||||||
|
return new KeycloakSpringBootConfigResolver();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
super.configure(http);
|
||||||
|
http.authorizeRequests().antMatchers("/**").authenticated()
|
||||||
|
.and().anonymous().disable().csrf().disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void configureGlobal(AuthenticationManagerBuilder auth) {
|
||||||
|
|
||||||
|
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
|
||||||
|
|
||||||
|
SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper();
|
||||||
|
grantedAuthorityMapper.setPrefix("ROLE_");
|
||||||
|
grantedAuthorityMapper.setConvertToUpperCase(true);
|
||||||
|
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthorityMapper);
|
||||||
|
auth.authenticationProvider(keycloakAuthenticationProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Override
|
||||||
|
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
|
||||||
|
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||||
|
public KeycloakSecurityContext getKeycloakSecurityContext() {
|
||||||
|
|
||||||
|
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||||
|
Principal principal = attributes.getRequest().getUserPrincipal();
|
||||||
|
if (principal == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (principal instanceof KeycloakAuthenticationToken) {
|
||||||
|
principal = Principal.class.cast(KeycloakAuthenticationToken.class.cast(principal).getPrincipal());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (principal instanceof KeycloakPrincipal) {
|
||||||
|
return KeycloakPrincipal.class.cast(principal).getKeycloakSecurityContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
15
dashboard-service/src/main/resources/application.properties
Normal file
15
dashboard-service/src/main/resources/application.properties
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
spring.jmx.enabled=false
|
||||||
|
server.port=8180
|
||||||
|
|
||||||
|
keycloak.realm=${REALM_NAME:example}
|
||||||
|
keycloak.auth-server-url=http://auth-server:8080/auth
|
||||||
|
keycloak.ssl-required=external
|
||||||
|
keycloak.resource=dashboard-client
|
||||||
|
keycloak.credentials.secret=8556a0a4-7235-4e90-a712-42c0fde60474
|
||||||
|
keycloak.principal-attribute=preferred_username
|
||||||
|
keycloak.use-resource-role-mappings=true
|
||||||
|
keycloak.cors=true
|
||||||
|
|
||||||
|
|
||||||
|
spring.main.allow-bean-definition-overriding=true
|
34
dashboard-service/src/main/resources/static/index.html
Normal file
34
dashboard-service/src/main/resources/static/index.html
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", (e) => {
|
||||||
|
var request = new XMLHttpRequest();
|
||||||
|
request.addEventListener("load", (evt) => {
|
||||||
|
let userInfo = JSON.parse(evt.target.response)
|
||||||
|
document.getElementById("username").innerText = userInfo.name;
|
||||||
|
document.getElementById("api-key").innerText = userInfo["api-key"];
|
||||||
|
});
|
||||||
|
request.open("GET", "/user");
|
||||||
|
request.send();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Welcome to App</h1> <h2 id="username"></h2>
|
||||||
|
|
||||||
|
You can access the rest api by using the api key <h3 id="api-key"> </h3>
|
||||||
|
|
||||||
|
<button id="logout">logout</button>
|
||||||
|
<script>
|
||||||
|
document.getElementById("logout").addEventListener("click", (e) => {
|
||||||
|
var request = new XMLHttpRequest();
|
||||||
|
request.open("POST", "/logout");
|
||||||
|
request.addEventListener("loadend", (e) => {window.location.pathname = "/";});
|
||||||
|
request.send();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
24
docker-compose.yaml
Normal file
24
docker-compose.yaml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
version: '3.3'
|
||||||
|
services:
|
||||||
|
auth-server:
|
||||||
|
build: api-key-ear
|
||||||
|
environment:
|
||||||
|
REALM_NAME: example
|
||||||
|
command: ["-Dkeycloak.migration.action=import", "-Dkeycloak.migration.provider=dir", "-Dkeycloak.migration.dir=/import", "-Dkeycloak.migration.strategy=OVERWRITE_EXISTING"]
|
||||||
|
volumes:
|
||||||
|
- ./api-key-ear/import:/import
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
dashboard-service:
|
||||||
|
build: dashboard-service
|
||||||
|
environment:
|
||||||
|
REALM_NAME: example
|
||||||
|
ports:
|
||||||
|
- "8180:8180"
|
||||||
|
rest-api-service:
|
||||||
|
build: rest-api-service
|
||||||
|
environment:
|
||||||
|
REALM_NAME: example
|
||||||
|
AUTH_SERVER_URL: auth-server:8080
|
||||||
|
ports:
|
||||||
|
- "8280:8280"
|
19
pom.xml
Normal file
19
pom.xml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<groupId>com.gwidgets</groupId>
|
||||||
|
<artifactId>api-key-keycloak-example</artifactId>
|
||||||
|
<version>0.1</version>
|
||||||
|
<modules>
|
||||||
|
<module>api-key-module</module>
|
||||||
|
<module>api-key-ear</module>
|
||||||
|
</modules>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
9
rest-api-service/Dockerfile
Normal file
9
rest-api-service/Dockerfile
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
FROM node:11-alpine
|
||||||
|
|
||||||
|
COPY index.js .
|
||||||
|
COPY package.json .
|
||||||
|
COPY package-lock.json .
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
ENTRYPOINT [ "node", "index.js" ]
|
32
rest-api-service/index.js
Normal file
32
rest-api-service/index.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
var express = require('express')
|
||||||
|
const http = require('http');
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
//always received as lower case by node
|
||||||
|
let apiKey = req.headers["x-api-key"];
|
||||||
|
|
||||||
|
let authServer = process.env.AUTH_SERVER_URL
|
||||||
|
let realmName = process.env.REALM_NAME;
|
||||||
|
|
||||||
|
console.log(`checking api key ${apiKey}, auth server ${authServer}`)
|
||||||
|
|
||||||
|
http.get("http://"+authServer+"/auth/realms/"+realmName+"/check?apiKey="+apiKey, (authResponse) => {
|
||||||
|
if (authResponse.statusCode == 200) {
|
||||||
|
next()
|
||||||
|
} else {
|
||||||
|
res.status(401).send();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
app.get('/', function (req, res) {
|
||||||
|
console.log("returning response")
|
||||||
|
res.status(200).set("Content-Type", "application/json").send('{"forecast" : "weather is cool today"}')
|
||||||
|
})
|
||||||
|
|
||||||
|
var server = app.listen(8280, function () {
|
||||||
|
console.log("service running at http://%s:%s", server.address().address, server.address().port)
|
||||||
|
})
|
379
rest-api-service/package-lock.json
generated
Normal file
379
rest-api-service/package-lock.json
generated
Normal file
|
@ -0,0 +1,379 @@
|
||||||
|
{
|
||||||
|
"name": "rest-api-service",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"accepts": {
|
||||||
|
"version": "1.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||||
|
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
|
||||||
|
"requires": {
|
||||||
|
"mime-types": "2.1.24",
|
||||||
|
"negotiator": "0.6.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"array-flatten": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||||
|
},
|
||||||
|
"body-parser": {
|
||||||
|
"version": "1.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||||
|
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
|
||||||
|
"requires": {
|
||||||
|
"bytes": "3.1.0",
|
||||||
|
"content-type": "1.0.4",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "1.1.2",
|
||||||
|
"http-errors": "1.7.2",
|
||||||
|
"iconv-lite": "0.4.24",
|
||||||
|
"on-finished": "2.3.0",
|
||||||
|
"qs": "6.7.0",
|
||||||
|
"raw-body": "2.4.0",
|
||||||
|
"type-is": "1.6.18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bytes": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
|
||||||
|
},
|
||||||
|
"content-disposition": {
|
||||||
|
"version": "0.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
|
||||||
|
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"content-type": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
|
||||||
|
},
|
||||||
|
"cookie": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
||||||
|
},
|
||||||
|
"cookie-signature": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
|
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"depd": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
|
||||||
|
},
|
||||||
|
"destroy": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
|
||||||
|
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
|
||||||
|
},
|
||||||
|
"ee-first": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||||
|
},
|
||||||
|
"encodeurl": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
||||||
|
},
|
||||||
|
"escape-html": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
|
||||||
|
},
|
||||||
|
"etag": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||||
|
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
||||||
|
},
|
||||||
|
"express": {
|
||||||
|
"version": "4.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
||||||
|
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
|
||||||
|
"requires": {
|
||||||
|
"accepts": "1.3.7",
|
||||||
|
"array-flatten": "1.1.1",
|
||||||
|
"body-parser": "1.19.0",
|
||||||
|
"content-disposition": "0.5.3",
|
||||||
|
"content-type": "1.0.4",
|
||||||
|
"cookie": "0.4.0",
|
||||||
|
"cookie-signature": "1.0.6",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "1.1.2",
|
||||||
|
"encodeurl": "1.0.2",
|
||||||
|
"escape-html": "1.0.3",
|
||||||
|
"etag": "1.8.1",
|
||||||
|
"finalhandler": "1.1.2",
|
||||||
|
"fresh": "0.5.2",
|
||||||
|
"merge-descriptors": "1.0.1",
|
||||||
|
"methods": "1.1.2",
|
||||||
|
"on-finished": "2.3.0",
|
||||||
|
"parseurl": "1.3.3",
|
||||||
|
"path-to-regexp": "0.1.7",
|
||||||
|
"proxy-addr": "2.0.5",
|
||||||
|
"qs": "6.7.0",
|
||||||
|
"range-parser": "1.2.1",
|
||||||
|
"safe-buffer": "5.1.2",
|
||||||
|
"send": "0.17.1",
|
||||||
|
"serve-static": "1.14.1",
|
||||||
|
"setprototypeof": "1.1.1",
|
||||||
|
"statuses": "1.5.0",
|
||||||
|
"type-is": "1.6.18",
|
||||||
|
"utils-merge": "1.0.1",
|
||||||
|
"vary": "1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"finalhandler": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"encodeurl": "1.0.2",
|
||||||
|
"escape-html": "1.0.3",
|
||||||
|
"on-finished": "2.3.0",
|
||||||
|
"parseurl": "1.3.3",
|
||||||
|
"statuses": "1.5.0",
|
||||||
|
"unpipe": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"forwarded": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
|
||||||
|
},
|
||||||
|
"fresh": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||||
|
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
|
||||||
|
},
|
||||||
|
"http": {
|
||||||
|
"version": "0.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/http/-/http-0.0.0.tgz",
|
||||||
|
"integrity": "sha1-huYybSnF0Dnen6xYSkVon5KfT3I="
|
||||||
|
},
|
||||||
|
"http-errors": {
|
||||||
|
"version": "1.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
||||||
|
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
|
||||||
|
"requires": {
|
||||||
|
"depd": "1.1.2",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"setprototypeof": "1.1.1",
|
||||||
|
"statuses": "1.5.0",
|
||||||
|
"toidentifier": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iconv-lite": {
|
||||||
|
"version": "0.4.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
|
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||||
|
"requires": {
|
||||||
|
"safer-buffer": "2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||||
|
},
|
||||||
|
"ipaddr.js": {
|
||||||
|
"version": "1.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
|
||||||
|
"integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
|
||||||
|
},
|
||||||
|
"media-typer": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
|
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
||||||
|
},
|
||||||
|
"merge-descriptors": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
|
||||||
|
},
|
||||||
|
"mime": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
|
||||||
|
},
|
||||||
|
"mime-db": {
|
||||||
|
"version": "1.40.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
|
||||||
|
"integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
|
||||||
|
},
|
||||||
|
"mime-types": {
|
||||||
|
"version": "2.1.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
|
||||||
|
"integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
|
||||||
|
"requires": {
|
||||||
|
"mime-db": "1.40.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
},
|
||||||
|
"negotiator": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||||
|
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
||||||
|
},
|
||||||
|
"on-finished": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||||
|
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
|
||||||
|
"requires": {
|
||||||
|
"ee-first": "1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parseurl": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
|
||||||
|
},
|
||||||
|
"path-to-regexp": {
|
||||||
|
"version": "0.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||||
|
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||||
|
},
|
||||||
|
"proxy-addr": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
|
||||||
|
"requires": {
|
||||||
|
"forwarded": "0.1.2",
|
||||||
|
"ipaddr.js": "1.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"qs": {
|
||||||
|
"version": "6.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||||
|
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
||||||
|
},
|
||||||
|
"range-parser": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
|
||||||
|
},
|
||||||
|
"raw-body": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
|
||||||
|
"requires": {
|
||||||
|
"bytes": "3.1.0",
|
||||||
|
"http-errors": "1.7.2",
|
||||||
|
"iconv-lite": "0.4.24",
|
||||||
|
"unpipe": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
|
},
|
||||||
|
"safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
|
},
|
||||||
|
"send": {
|
||||||
|
"version": "0.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
|
||||||
|
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "1.1.2",
|
||||||
|
"destroy": "1.0.4",
|
||||||
|
"encodeurl": "1.0.2",
|
||||||
|
"escape-html": "1.0.3",
|
||||||
|
"etag": "1.8.1",
|
||||||
|
"fresh": "0.5.2",
|
||||||
|
"http-errors": "1.7.2",
|
||||||
|
"mime": "1.6.0",
|
||||||
|
"ms": "2.1.1",
|
||||||
|
"on-finished": "2.3.0",
|
||||||
|
"range-parser": "1.2.1",
|
||||||
|
"statuses": "1.5.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serve-static": {
|
||||||
|
"version": "1.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
|
||||||
|
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
|
||||||
|
"requires": {
|
||||||
|
"encodeurl": "1.0.2",
|
||||||
|
"escape-html": "1.0.3",
|
||||||
|
"parseurl": "1.3.3",
|
||||||
|
"send": "0.17.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"setprototypeof": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
|
||||||
|
},
|
||||||
|
"statuses": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||||
|
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
|
||||||
|
},
|
||||||
|
"toidentifier": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
||||||
|
},
|
||||||
|
"type-is": {
|
||||||
|
"version": "1.6.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||||
|
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||||
|
"requires": {
|
||||||
|
"media-typer": "0.3.0",
|
||||||
|
"mime-types": "2.1.24"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unpipe": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
|
||||||
|
},
|
||||||
|
"utils-merge": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||||
|
},
|
||||||
|
"vary": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
rest-api-service/package.json
Normal file
16
rest-api-service/package.json
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "rest-api-service",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"http": "0.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue