ugrade to Keycloak 19.0.1
This commit is contained in:
parent
2ec76fe137
commit
bf9a740c2b
8 changed files with 50 additions and 55 deletions
|
@ -1,3 +1,10 @@
|
|||
# Keycloak extension for API key authentication
|
||||
|
||||
The extension contains providers for supporting API key authentication, and also other non related providers like a custom `EmailSenderProvider` (for demo purposes).
|
||||
|
||||
It also contains a customization of the account console (the user info page provided by Keycloak) showing the API key. The account console is accessible at `/auth/realms/{realm_name}/account` and requires the user to be already authenticated.
|
||||
|
||||
The master branch uses the new Keycloak distribution powered by Quarkus. For Legacy keycloak (versions < 17.0.0), you can switch to the `legacy` branch.
|
||||
## How to run
|
||||
|
||||
you can run the project by running the following from a terminal: `mvn -f api-key-module package && mvn -f dashboard-service package && docker-compose up`
|
||||
|
@ -8,6 +15,6 @@ Note: You need to add `auth-server` to your hosts file (`/etc/hosts` for linux)
|
|||
|
||||
1. Navigate to localhost:8180 in a browser, you will redirected to keycloak for authentication
|
||||
2. you need register a new user, after which you will be redirected to the main dashboard page which will show your API key
|
||||
3. copy the API key and use it to call the API: `curl -v -H "x-api-key: $THE_API_KEY" localhost:8200`, if you omit the API key, you will get 401 status
|
||||
3. copy the API key and use it to call the API: `curl -v -H "x-api-key: $THE_API_KEY" localhost:8280`, if you omit the API key, you will get 401 status
|
||||
|
||||
More explanations can be found in this blog [post](http://www.zakariaamine.com/2019-06-14/extending-keycloak)
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
<keycloak.version>15.0.2</keycloak.version>
|
||||
<keycloak.version>19.0.1</keycloak.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
|
|
@ -4,7 +4,7 @@ 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.common.util.SecretGenerator;
|
||||
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventListenerProvider;
|
||||
|
@ -23,14 +23,14 @@ 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 SecretGenerator secretGenerator;
|
||||
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);
|
||||
this.secretGenerator = SecretGenerator.getInstance();
|
||||
}
|
||||
|
||||
public void onEvent(Event event) {
|
||||
|
@ -55,8 +55,7 @@ public class RegisterEventListenerProvider implements EventListenerProvider {
|
|||
|
||||
|
||||
public void addApiKeyAttribute(String userId) {
|
||||
|
||||
String apiKey = randomString.nextString();
|
||||
String apiKey = secretGenerator.randomString(50);
|
||||
UserEntity userEntity = entityManager.find(UserEntity.class, userId);
|
||||
UserAttributeEntity attributeEntity = new UserAttributeEntity();
|
||||
attributeEntity.setName("api-key");
|
||||
|
|
|
@ -8,6 +8,7 @@ import com.amazonaws.services.simpleemail.model.Message;
|
|||
import com.amazonaws.services.simpleemail.model.SendEmailRequest;
|
||||
import java.util.Map;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.email.EmailException;
|
||||
import org.keycloak.email.EmailSenderProvider;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
|
@ -24,9 +25,14 @@ public class SESEmailSenderProvider implements EmailSenderProvider {
|
|||
|
||||
@Override
|
||||
public void send(Map<String, String> config, UserModel user, String subject, String textBody,
|
||||
String htmlBody) {
|
||||
String htmlBody) throws EmailException {
|
||||
this.send(config, user.getEmail(), subject, textBody, htmlBody);
|
||||
}
|
||||
|
||||
log.info("attempting to send email using aws ses for " + user.getEmail());
|
||||
@Override
|
||||
public void send(Map<String, String> config, String address, String subject, String textBody, String htmlBody) throws EmailException {
|
||||
|
||||
log.info("attempting to send email using aws ses for " + address);
|
||||
|
||||
Message message = new Message().withSubject(new Content().withData(subject))
|
||||
.withBody(new Body().withHtml(new Content().withData(htmlBody))
|
||||
|
@ -34,10 +40,10 @@ public class SESEmailSenderProvider implements EmailSenderProvider {
|
|||
|
||||
SendEmailRequest sendEmailRequest = new SendEmailRequest()
|
||||
.withSource("example<" + config.get("from") + ">")
|
||||
.withMessage(message).withDestination(new Destination().withToAddresses(user.getEmail()));
|
||||
.withMessage(message).withDestination(new Destination().withToAddresses(address));
|
||||
|
||||
sesClient.sendEmail(sendEmailRequest);
|
||||
log.info("email sent to " + user.getEmail() + " successfully");
|
||||
log.info("email sent to " + address + " successfully");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
package com.gwidgets.resources;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ApiKeyResource {
|
||||
|
||||
|
@ -25,7 +26,7 @@ public class ApiKeyResource {
|
|||
@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).type(MediaType.APPLICATION_JSON).build(): Response.ok().type(MediaType.APPLICATION_JSON).build();
|
||||
Stream<UserModel> result = session.users().searchForUserByUserAttributeStream(session.realms().getRealm(realmName), "api-key", apiKey);
|
||||
return result.count() > 0 ? Response.ok().type(MediaType.APPLICATION_JSON).build(): Response.status(401).type(MediaType.APPLICATION_JSON).build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
<keycloak.version>15.0.2</keycloak.version>
|
||||
<keycloak.version>19.0.1</keycloak.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
version: '3.7'
|
||||
version: '3.8'
|
||||
services:
|
||||
auth-server:
|
||||
image: jboss/keycloak:15.0.2
|
||||
image: quay.io/keycloak/keycloak:19.0.1
|
||||
environment:
|
||||
KEYCLOAK_USER: admin
|
||||
KEYCLOAK_PASSWORD: admin
|
||||
JAVA_OPTS_APPEND: "-Dkeycloak.migration.action=import -Dkeycloak.migration.provider=dir -Dkeycloak.migration.dir=/import -Dkeycloak.migration.strategy=IGNORE_EXISTING -Dkeycloak.profile.feature.upload_scripts=enabled"
|
||||
KEYCLOAK_ADMIN: admin
|
||||
KEYCLOAK_ADMIN_PASSWORD: admin
|
||||
KC_HTTP_ENABLED: "true"
|
||||
KC_HOSTNAME: auth-server
|
||||
#to keep compatible with other services that are expecting /auth
|
||||
KC_HTTP_RELATIVE_PATH: /auth
|
||||
KC_HOSTNAME_STRICT_HTTPS: "false"
|
||||
JAVA_OPTS_APPEND: "-Dkeycloak.migration.action=import -Dkeycloak.migration.provider=dir -Dkeycloak.migration.dir=/import -Dkeycloak.migration.strategy=IGNORE_EXISTING"
|
||||
volumes:
|
||||
- ./import:/import
|
||||
- ./api-key-module/target/deploy:/opt/jboss/keycloak/standalone/deployments/
|
||||
- ./api-key-module/target/deploy:/opt/keycloak/providers/
|
||||
ports:
|
||||
- "8080:8080"
|
||||
command: ["start"]
|
||||
dashboard-service:
|
||||
build: dashboard-service
|
||||
environment:
|
||||
|
|
|
@ -192,31 +192,7 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"policies": [
|
||||
{
|
||||
"id": "6fc4ef68-6935-4bc1-b1fe-9b7109e43fe1",
|
||||
"name": "Default Policy",
|
||||
"description": "A policy that grants access only for users within this realm",
|
||||
"type": "js",
|
||||
"logic": "POSITIVE",
|
||||
"decisionStrategy": "AFFIRMATIVE",
|
||||
"config": {
|
||||
"code": "// by default, grants any permission associated with this policy\n$evaluation.grant();\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "54f768bd-be40-4b44-acb5-9218d5fd3e2b",
|
||||
"name": "Default Permission",
|
||||
"description": "A permission that applies to the default resource type",
|
||||
"type": "resource",
|
||||
"logic": "POSITIVE",
|
||||
"decisionStrategy": "UNANIMOUS",
|
||||
"config": {
|
||||
"defaultResourceType": "urn:dashboard-client:resources:default",
|
||||
"applyPolicies": "[\"Default Policy\"]"
|
||||
}
|
||||
}
|
||||
],
|
||||
"policies": [],
|
||||
"scopes": []
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Add table
Reference in a new issue