diff --git a/hs-plugin-keycloak-ejb/pom.xml b/hs-plugin-keycloak-ejb/pom.xml
index 2f956f0..b747a46 100755
--- a/hs-plugin-keycloak-ejb/pom.xml
+++ b/hs-plugin-keycloak-ejb/pom.xml
@@ -79,25 +79,52 @@
json
20180130
-
org.codehaus.jackson
jackson-mapper-asl
1.5.0
-
net.glxn.qrgen
qrgen-parent
2.0
pom
-
-
+
com.google.zxing
core
2.0
+
+ org.powermock
+ powermock-module-junit4
+ 1.6.3
+ test
+
+
+ org.powermock
+ powermock-api-mockito
+ 1.6.3
+ test
+
+
+ org.mockito
+ mockito-core
+ 2.4.3
+ test
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+ io.jsonwebtoken
+ jjwt
+ 0.9.1
+
+
diff --git a/hs-plugin-keycloak-ejb/src/main/java/io/hypermine/hypersign/api/HSResourceProvider.java b/hs-plugin-keycloak-ejb/src/main/java/io/hypermine/hypersign/api/HSResourceProvider.java
index 30df23b..fe82649 100644
--- a/hs-plugin-keycloak-ejb/src/main/java/io/hypermine/hypersign/api/HSResourceProvider.java
+++ b/hs-plugin-keycloak-ejb/src/main/java/io/hypermine/hypersign/api/HSResourceProvider.java
@@ -20,12 +20,14 @@
import org.keycloak.services.resource.RealmResourceProvider;
import org.keycloak.services.ServicesLogger;
import org.keycloak.models.*;
-import java.util.HashMap;
+
+import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+
import org.json.JSONObject;
import org.keycloak.wildfly.adduser.*;
import org.codehaus.jackson.map.ObjectMapper;
@@ -37,6 +39,7 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FileInputStream;
+
/**
* @author Stian Thorgersen
*/
@@ -46,23 +49,27 @@ class HSUserModel {
String challange;
String userId;
boolean hasLoggedIn;
- public HSUserModel(String challange, String userId, boolean hasLoggedIn){
+
+ public HSUserModel(String challange, String userId, boolean hasLoggedIn) {
this.challange = challange;
this.userId = userId;
this.hasLoggedIn = hasLoggedIn;
}
}
- class FResponse{
+ class FResponse {
public String status;
public String data;
- public FResponse(){}
- public FResponse(String status, String data){
+
+ public FResponse() {
+ }
+
+ public FResponse(String status, String data) {
this.status = status;
this.data = data;
}
}
-
+
enum Status {
SUCCESS,
FAIL
@@ -72,37 +79,37 @@ enum Status {
private static HashMap userSessionMap = new HashMap<>();// this is temporary
private KeycloakSession session;
private static String hsAuthServerEp = "";
-
- public HSResourceProvider(KeycloakSession session) {
+
+ public HSResourceProvider(KeycloakSession session) {
this.session = session;
}
- private String getHsEP() throws IOException{
+ private String getHsEP() throws IOException {
String hsAuthServerEp = "";
FileInputStream fis = null;
- try{
+ try {
logger.info("Inside the getHsEP constructor");
String fileName = System.getProperty("jboss.server.config.dir") + "/hypersign.properties";
logger.info(fileName);
- Properties properties = new Properties();
+ Properties properties = new Properties();
fis = new FileInputStream(fileName);
properties.load(fis);
hsAuthServerEp = properties.getProperty("auth-server-endpoint");
- if(!hsAuthServerEp.isEmpty()){
+ if (!hsAuthServerEp.isEmpty()) {
//add / if not there
- if(hsAuthServerEp.charAt(hsAuthServerEp.length() -1) != '/'){
+ if (hsAuthServerEp.charAt(hsAuthServerEp.length() - 1) != '/') {
hsAuthServerEp = hsAuthServerEp + "/";
}
logger.info("HS auth server endpoint configured.");
logger.info(hsAuthServerEp);
- }else{
+ } else {
logger.info("HS auth server endpoint not configured. Setting it to default");
hsAuthServerEp = "http://localhost:3000/";
logger.info(hsAuthServerEp);
}
- }catch(IOException e){
+ } catch (IOException e) {
logger.info(e.toString());
- }finally{
+ } finally {
fis.close();
}
logger.info("Inside the getHsEP constructor ends");
@@ -134,7 +141,7 @@ public Response get() {
if (name == null) {
name = session.getContext().getRealm().getName();
}
- return this.formattedReponse(Status.SUCCESS, "Hi, there! " + name);
+ return this.formattedReponse(Status.SUCCESS, "Hi, there! " + name);
}
@POST
@@ -143,58 +150,49 @@ public Response get() {
@Produces(MediaType.APPLICATION_JSON)
public Response register(String body) {
logger.info("Register api called!");
- JSONObject json = null;
- String publicKey = "";
+ JSONObject json = new JSONObject();
+ String userDID = "";
String emaiid = "";
String username = "";
String companyid = "";
UserModel newuser = null;
- try{
- if(!body.isEmpty()){
+ try {
+ if (!body.isEmpty()) {
json = new JSONObject(body);
- if(json != null){
- publicKey = json.getString("publickey");
+ if (json != null) {
+ userDID = json.getString("did");
emaiid = json.getString("email");
username = json.getString("username");
companyid = json.getString("companyid");
- if(!publicKey.isEmpty() || !emaiid.isEmpty()) {
- //saving the user in hs-auth-server
- String url = this.getHsEP() + "register";
- String responseFromAuthServer = AuthServerCaller.postApiCall(url,body);
- json = new JSONObject(responseFromAuthServer);
- if(json.getInt("status") == 0){
- throw new Exception(json.getString("message"));
- }else{
- // I had to trim the publickey to accomodate it in ID field (which is of size 36) in db
- publicKey = publicKey.substring(0, publicKey.length() - 6);
- // saving the user in keycloak
- UserRepresentation userRep = new UserRepresentation();
- userRep.setUsername(username);
- userRep.setId(publicKey);
- userRep.setEmail(emaiid);
- userRep.setEnabled(true);
- userRep.setEmailVerified(false);
- newuser = this.session != null && this.session.getContext() != null
- ? RepresentationToModel.createUser(this.session, this.session.getContext().getRealm(), userRep)
- : null;
- if(newuser != null){
- return this.formattedReponse(Status.SUCCESS, json.getString("message"));
- }else{
- throw new Exception("Could not create the user");
- }
+ if (!userDID.isEmpty() || !emaiid.isEmpty()) {
+ // saving the user in keycloak
+ UserRepresentation userRep = new UserRepresentation();
+ userRep.setUsername(username);
+ userRep.setId(userDID);
+ userRep.setEmail(emaiid);
+ userRep.setEnabled(true);
+ userRep.setEmailVerified(false);
+ newuser = this.session != null && this.session.getContext() != null
+ ? RepresentationToModel.createUser(this.session, this.session.getContext().getRealm(), userRep)
+ : null;
+ if (newuser != null) {
+ return this.formattedReponse(Status.SUCCESS, json.getString("message"));
+ } else {
+ throw new Exception("Could not create the user");
}
- }else {
+
+ } else {
throw new Exception("Publickey or emailId is null");
- }
- }else{
+ }
+ } else {
throw new Exception("Could not parse the body");
}
- }else {
+ } else {
throw new Exception("Request body is null");
}
- }catch(Exception e){
- return this.formattedReponse(Status.FAIL,e.toString());
- }
+ } catch (Exception e) {
+ return this.formattedReponse(Status.FAIL, e.toString());
+ }
}
@POST
@@ -211,41 +209,44 @@ public Response sign(String body) {
String companyId = "";
String rawMessage = "";
HSUserModel user = null;
- try{
- if(!body.isEmpty()){
+ try {
+ if (!body.isEmpty()) {
bodyObj = new JSONObject(body);
- if(bodyObj != null){
- publickey = bodyObj.getString("publicKey"); sessionId = bodyObj.getString("ksSessionId");
- challange = bodyObj.getString("challenge"); signature = bodyObj.getString("signedRsv");
- companyId = bodyObj.getString("companyId"); rawMessage = bodyObj.getString("rawMsg");
- if(!publickey.isEmpty() ||
- !sessionId.isEmpty() ||
- !signature.isEmpty() ||
- !challange.isEmpty() ||
- !companyId.isEmpty()) {
- if(isSignatureValid(body)){
- if (userSessionMap.containsKey(sessionId)){
- user = new HSUserModel(challange, publickey, true);
- userSessionMap.put(sessionId, user);
- return this.formattedReponse(Status.SUCCESS, "User authenticated");
- }else{
- throw new Exception("Invalid session");
- }
- }else{
- throw new Exception("Invalid signature");
- }
- }else {
+ if (bodyObj != null) {
+ publickey = bodyObj.getString("publicKey");
+ sessionId = bodyObj.getString("ksSessionId");
+ challange = bodyObj.getString("challenge");
+ signature = bodyObj.getString("signedRsv");
+ companyId = bodyObj.getString("companyId");
+ rawMessage = bodyObj.getString("rawMsg");
+ if (!publickey.isEmpty() ||
+ !sessionId.isEmpty() ||
+ !signature.isEmpty() ||
+ !challange.isEmpty() ||
+ !companyId.isEmpty()) {
+ if (isSignatureValid(body)) {
+ if (userSessionMap.containsKey(sessionId)) {
+ user = new HSUserModel(challange, publickey, true);
+ userSessionMap.put(sessionId, user);
+ return this.formattedReponse(Status.SUCCESS, "User authenticated");
+ } else {
+ throw new Exception("Invalid session");
+ }
+ } else {
+ throw new Exception("Invalid signature");
+ }
+ } else {
throw new Exception("Publickey, sessionId or signature is null");
- }
- }else{
+ }
+ } else {
throw new Exception("Could not parse the body");
}
- }else {
+ } else {
throw new Exception("Request body is null");
}
- }catch(Exception e){
- return this.formattedReponse(Status.FAIL,e.toString());
- }
+ } catch (Exception e) {
+ return this.formattedReponse(Status.FAIL, e.toString());
+ }
}
@POST
@@ -254,37 +255,37 @@ public Response sign(String body) {
public String getNewSession(String body) {
logger.info("session api called!");
JSONObject json = null;
- try{
- if(!body.isEmpty()){
- if(userSessionMap != null){
+ try {
+ if (!body.isEmpty()) {
+ if (userSessionMap != null) {
json = new JSONObject(body);
String ksSessionId = json.getString("kcSessionId");
String companyId = json.getString("companyId");
- if(json != null && !ksSessionId.isEmpty()){
+ if (json != null && !ksSessionId.isEmpty()) {
// call auth-server for session/challenge
String url = this.getHsEP() + "challenge";
- String reqstBody = "{\"kcSessionId\" : \""+ksSessionId+"\", \"companyId\":\""+companyId+"\"}";
+ String reqstBody = "{\"kcSessionId\" : \"" + ksSessionId + "\", \"companyId\":\"" + companyId + "\"}";
String response = AuthServerCaller.postApiCall(url, reqstBody);
json = new JSONObject(response);
- if(json != null && json.getInt("status") == 1){
+ if (json != null && json.getInt("status") == 1) {
// json = new JSONObject(json.get("data"));
userSessionMap.put(ksSessionId, null); // this is kc session
return json.getString("data"); //this.formattedReponse(Status.SUCCESS, sessionId);
- }else{
+ } else {
throw new Exception(json.getString("message"));
}
- }else{
+ } else {
throw new Exception("Keycloak sessionid can not be null");
}
-
- }else{
+
+ } else {
throw new Exception("userSessionMap is null");
}
- }else{
+ } else {
throw new Exception("Request body can not be null");
}
-
- }catch(Exception e){
+
+ } catch (Exception e) {
return e.toString();//this.formattedReponse(Status.FAIL,e.toString());
}
}
@@ -294,92 +295,91 @@ public String getNewSession(String body) {
@Produces(MediaType.APPLICATION_JSON)
public Response listenSuccess(@PathParam("sessionId") String sessionId) {
logger.info("listen/success api called!");
- try{
- if(userSessionMap != null){
- if (userSessionMap.containsKey(sessionId) && userSessionMap.get(sessionId) != null){
+ try {
+ if (userSessionMap != null) {
+ if (userSessionMap.containsKey(sessionId) && userSessionMap.get(sessionId) != null) {
HSUserModel user = userSessionMap.get(sessionId);
- if (user != null && user.hasLoggedIn){
- return this.formattedReponse(Status.SUCCESS, user.userId);
- }else{
- throw new Exception("User not found or not validated");
+ if (user != null && user.hasLoggedIn) {
+ return this.formattedReponse(Status.SUCCESS, user.userId);
+ } else {
+ throw new Exception("User not found or not validated");
}
- }else{
- throw new Exception("Invalid session or user has not yet validated");
+ } else {
+ throw new Exception("Invalid session or user has not yet validated");
}
- }else{
+ } else {
throw new Exception("userSessionMap is null");
}
- }catch(Exception e){
- return this.formattedReponse(Status.FAIL,e.toString());
- }
+ } catch (Exception e) {
+ return this.formattedReponse(Status.FAIL, e.toString());
+ }
}
@GET
@Path("listen/fail/{sessionId}")
@Produces(MediaType.APPLICATION_JSON)
- public Response listenFail(@PathParam("sessionId") String sessionId) {
+ public Response listenFail(@PathParam("sessionId") String sessionId) {
logger.info("listen/fail api called!");
- try{
- if(userSessionMap != null){
- if (userSessionMap.containsKey(sessionId)){
+ try {
+ if (userSessionMap != null) {
+ if (userSessionMap.containsKey(sessionId)) {
userSessionMap.remove(sessionId);
- return this.formattedReponse(Status.SUCCESS, "Session deleted");
- }else{
- throw new Exception("Invalid session");
+ return this.formattedReponse(Status.SUCCESS, "Session deleted");
+ } else {
+ throw new Exception("Invalid session");
}
- }else{
+ } else {
throw new Exception("userSessionMap is null");
}
- }catch(Exception e){
- return this.formattedReponse(Status.FAIL,e.toString());
- }
+ } catch (Exception e) {
+ return this.formattedReponse(Status.FAIL, e.toString());
+ }
}
- private Response formattedReponse(Status status, String data){
+ private Response formattedReponse(Status status, String data) {
String respStr = "";
- try{
- FResponse response = new FResponse();
+ try {
+ FResponse response = new FResponse();
response.status = status.name();
response.data = data;
ObjectMapper Obj = new ObjectMapper();
// JSONObject bodyObj = new JSONObject(response);
respStr = Obj.writeValueAsString(response);
return Response
- .status(Response.Status.OK)
- .header("Access-Control-Allow-Origin", "*")
- .header("Access-Control-Allow-Credentials", "true")
- .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
- .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
- .entity(respStr)
- .build();
- }
- catch(Exception e){
+ .status(Response.Status.OK)
+ .header("Access-Control-Allow-Origin", "*")
+ .header("Access-Control-Allow-Credentials", "true")
+ .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
+ .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
+ .entity(respStr)
+ .build();
+ } catch (Exception e) {
respStr = e.toString();
- return Response
- .status(Response.Status.BAD_REQUEST)
- .header("Access-Control-Allow-Origin", "*")
- .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
- .header("Access-Control-Allow-Credentials", "true")
- .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
- .header("Access-Control-Max-Age", "1209600")
- .entity(respStr)
- .build();
- }
+ return Response
+ .status(Response.Status.BAD_REQUEST)
+ .header("Access-Control-Allow-Origin", "*")
+ .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
+ .header("Access-Control-Allow-Credentials", "true")
+ .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
+ .header("Access-Control-Max-Age", "1209600")
+ .entity(respStr)
+ .build();
+ }
// return respStr;
}
-
- private Boolean isSignatureValid(String body){
- try{
+
+ private Boolean isSignatureValid(String body) {
+ try {
// call auth-server to validate this user.
String url = this.getHsEP() + "verify";
- String responseFromAuthServer = AuthServerCaller.postApiCall(url,body);
+ String responseFromAuthServer = AuthServerCaller.postApiCall(url, body);
JSONObject json = new JSONObject(responseFromAuthServer);
- if(json != null && json.getInt("status") == 1){
+ if (json != null && json.getInt("status") == 1) {
return true;
- }else{
+ } else {
return false;
}
- }catch(Exception e){
+ } catch (Exception e) {
return false;
}
}
diff --git a/hs-plugin-keycloak-ejb/src/main/java/io/hypermine/hypersign/authenticator/HyperSignAuthenticator.java b/hs-plugin-keycloak-ejb/src/main/java/io/hypermine/hypersign/authenticator/HyperSignAuthenticator.java
index 356c481..1b04d44 100644
--- a/hs-plugin-keycloak-ejb/src/main/java/io/hypermine/hypersign/authenticator/HyperSignAuthenticator.java
+++ b/hs-plugin-keycloak-ejb/src/main/java/io/hypermine/hypersign/authenticator/HyperSignAuthenticator.java
@@ -17,6 +17,8 @@
package io.hypermine.hypersign.authenticator;
+import io.hypermine.hypersign.jwt.JWTManager;
+import org.hibernate.id.GUIDGenerator;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.authentication.AuthenticationFlowContext;
@@ -35,6 +37,8 @@
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.net.URI;
+import java.util.UUID;
+
import org.json.JSONObject;
import io.hypermine.hypersign.service.AuthServerCaller;
@@ -48,7 +52,7 @@ public class HyperSignAuthenticator implements Authenticator {
public static final String CREDENTIAL_TYPE = "hypersign_qrcode";
private static ServicesLogger logger = ServicesLogger.LOGGER;
-
+
protected boolean hasCookie(AuthenticationFlowContext context) {
Cookie cookie = context.getHttpRequest().getHttpHeaders().getCookies().get("HYPERSIGN_QRCODE_SOLVED");
boolean result = cookie != null;
@@ -58,7 +62,7 @@ protected boolean hasCookie(AuthenticationFlowContext context) {
/**********************************************************************************
* This will check if browser has already tried solving the challenge before, if that
* is the case then it wont again give the same challenge, rather it will bypass it
- *
+ *
* ********************************************************************************/
@Override
public void authenticate(AuthenticationFlowContext context) {
@@ -73,38 +77,20 @@ public void authenticate(AuthenticationFlowContext context) {
logger.info("HyperSignAuthenticator :: authenticate : ends");
}
- protected Response getChallenge(AuthenticationFlowContext context){
+ protected Response getChallenge(AuthenticationFlowContext context) {
+ int jwtTimeToLive = 0;
+ UUID uuid = UUID.randomUUID();
logger.info("HyperSignAuthenticator :: getChallenge : starts");
- String url = getFormattedUrl(context, "session"); // baseUrl + "/auth/realms/"+ relam +"/hypersign/session";
- // blocking call to get new session
- System.out.println("************************");
- System.out.println(context.getUriInfo().getQueryParameters().getFirst("state"));
- String executionId = context.getUriInfo().getQueryParameters().getFirst("state");
- String newHSsession = "";
- String relamName = context.getRealm().getName();
- Response challenge = null;
- if(executionId != null && !executionId.isEmpty()){
- String body = "{\"kcSessionId\":\""+executionId+"\", \"companyId\" : \""+relamName+"\"}";
- newHSsession = callAPi(url, body);
- System.out.println("************************");
- System.out.println(newHSsession);
- logger.info("HyperSignAuthenticator :: getChallenge : after");
- String qrJson = "{\"kcSessionId\":\""+executionId+"\", \"companyId\" : \""+relamName+"\", \"hsSessionId\" : \""+newHSsession+"\"}";
- String hsQr = QRCodeGenerator.createORLoginPage(qrJson);
- challenge = context.form()
- .setAttribute("loginMethod", "UAF")
- .setAttribute("hsSession",newHSsession)
- .setAttribute("ksSessionId",executionId)
- .setAttribute("hsQr",hsQr)
- .createForm("hypersign-new.ftl");
- }
- return challenge;
+ String jwtToken = JWTManager.createJWT(jwtTimeToLive, uuid.toString());
+ String hsQr = QRCodeGenerator.createORLoginPage(jwtToken);
+ return context.form().setAttribute("hsQr", hsQr).createForm("hypersign-new.ftl");
+
}
/**********************************************************************************
* This is the main method that will get called once user solves the challenge and
* click on the submit button.
- *
+ *
* ********************************************************************************/
@Override
public void action(AuthenticationFlowContext context) {
@@ -125,72 +111,72 @@ public void action(AuthenticationFlowContext context) {
logger.info("HyperSignAuthenticator :: action : ends");
}
- private String callAPi(String url, String body){
- try{
+ private String callAPi(String url, String body) {
+ try {
logger.info("HyperSignAuthenticator :: callApi : start");
logger.info("HyperSignAuthenticator :: callApi : url :");
logger.info(url);
- if(body != null && !body.isEmpty()){
+ if (body != null && !body.isEmpty()) {
return AuthServerCaller.postApiCall(url, body);
- }else{
+ } else {
return AuthServerCaller.getApiCall(url);
}
- }catch(Exception e){
+ } catch (Exception e) {
logger.error(e);
return "";
}
}
-
+
private String getFormattedUrl(AuthenticationFlowContext context, String endpoint) {
- String baseUrl = "";
- String relam = "";
- String url="";
- if(context != null) {
- baseUrl = (context.getUriInfo() !=null && context.getUriInfo().getBaseUri() != null)
- ? context.getUriInfo().getBaseUri().toString()
- : "http://localhost:8080/auth/";
- relam = (context.getRealm() != null && context.getRealm().getName() != null && !context.getRealm().getName().isEmpty())
- ? context.getRealm().getName()
- : "master";
- url = baseUrl + "realms/"+ relam +"/hypersign/" + endpoint;
- }
- return url;
- }
+ String baseUrl = "";
+ String relam = "";
+ String url = "";
+ if (context != null) {
+ baseUrl = (context.getUriInfo() != null && context.getUriInfo().getBaseUri() != null)
+ ? context.getUriInfo().getBaseUri().toString()
+ : "http://localhost:8080/auth/";
+ relam = (context.getRealm() != null && context.getRealm().getName() != null && !context.getRealm().getName().isEmpty())
+ ? context.getRealm().getName()
+ : "master";
+ url = baseUrl + "realms/" + relam + "/hypersign/" + endpoint;
+ }
+ return url;
+ }
private boolean validateUser(AuthenticationFlowContext context) {
- Boolean isValid = false;
+ Boolean isValid = false;
String url = "";
MultivaluedMap formData = null;
String userIdFromForm = "";
String sessionId = "";
try {
- logger.info("HyperSignAuthenticator :: validateUser : starts ");
+ logger.info("HyperSignAuthenticator :: validateUser : starts ");
formData = context.getHttpRequest().getDecodedFormParameters();
-
+
sessionId = formData.getFirst("ksSessionId");
logger.info("HyperSignAuthenticator :: validateUser : sessionId :");
logger.info(sessionId);
-
+
userIdFromForm = formData.getFirst("userId");
logger.info("HyperSignAuthenticator :: validateUser : userId in form :");
logger.info(userIdFromForm);
-
+
url = getFormattedUrl(context, "listen/success/" + sessionId);
-
+
// check if this user is correct from api call;
// blocking api call
- String resp = callAPi(url,"");
- JSONObject json = new JSONObject(resp);
+ String resp = callAPi(url, "");
+ JSONObject json = new JSONObject(resp);
String userIdFromAPi = "";
- if(json != null){
+ if (json != null) {
userIdFromAPi = json.getString("data");
logger.info("HyperSignAuthenticator :: validateUser : userId in api :");
logger.info(userIdFromAPi);
- }else{
+ } else {
logger.info("User authentication failed from hs-auth-server");
}
-
- if(userIdFromAPi !=null && !userIdFromAPi.isEmpty() && userIdFromAPi.equals(userIdFromForm)){
+
+ if (userIdFromAPi != null && !userIdFromAPi.isEmpty() && userIdFromAPi.equals(userIdFromForm)) {
logger.info("HyperSignAuthenticator :: validateUser : User is valid");
UserCredentialModel input = new UserCredentialModel();
// input.setType(SecretQuestionCredentialProvider.QR_CODE);
@@ -202,27 +188,27 @@ private boolean validateUser(AuthenticationFlowContext context) {
setCookie(context);
context.success();
isValid = true;
- }else{
+ } else {
logger.info("HyperSignAuthenticator :: validateUser : User is not valid");
logger.info("HyperSignAuthenticator :: validateUser : Clear session of this user");
// clear session in case of failure
url = getFormattedUrl(context, "listen/fail/" + sessionId);
- callAPi(url,"");
+ callAPi(url, "");
context.cancelLogin();
- }
- }catch (Exception e) {
+ }
+ } catch (Exception e) {
logger.error(e.toString());
- // TODO: handle exception
- }
+ // TODO: handle exception
+ }
logger.info("HyperSignAuthenticator :: validateUser : ends ");
return isValid;
}
-
+
/**********************************************************************************
* Set the Hypersign cookie for 30 days.
- *
+ *
* ********************************************************************************/
-
+
protected void setCookie(AuthenticationFlowContext context) {
AuthenticatorConfigModel config = context.getAuthenticatorConfig();
int maxCookieAge = 60 * 60 * 24 * 30; // 30 days
@@ -248,7 +234,7 @@ public static void addCookie(String name, String value, String path, String doma
/**********************************************************************************
* We are setting this as false since we dont need the user information as of now.
- *
+ *
* ********************************************************************************/
@Override
public boolean requiresUser() {
@@ -257,12 +243,12 @@ public boolean requiresUser() {
/**********************************************************************************
* Setting this as false sine we do not want to store any information
- *
+ *
* ********************************************************************************/
@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
//return false;
- return session.userCredentialManager().isConfiguredFor(realm, user, HyperSignCredentialProvider.QR_CODE);
+ return session.userCredentialManager().isConfiguredFor(realm, user, HyperSignCredentialProvider.QR_CODE);
}
@Override
diff --git a/hs-plugin-keycloak-ejb/src/main/java/io/hypermine/hypersign/jwt/JWTManager.java b/hs-plugin-keycloak-ejb/src/main/java/io/hypermine/hypersign/jwt/JWTManager.java
new file mode 100644
index 0000000..b6fd283
--- /dev/null
+++ b/hs-plugin-keycloak-ejb/src/main/java/io/hypermine/hypersign/jwt/JWTManager.java
@@ -0,0 +1,44 @@
+package io.hypermine.hypersign.jwt;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.JwtBuilder;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+
+import javax.crypto.spec.SecretKeySpec;
+import javax.xml.bind.DatatypeConverter;
+import java.security.Key;
+import java.util.Date;
+
+public class JWTManager {
+
+ private static String SECRET_KEY = "oeRaYY7Wo24sDqKSX3IM9ASGmdGPmkTd9jo1QTy4b7P9Ze5_9hKolVX8xNrQDcNRfVEdTZNOuOyqEGhXEbdJI-ZQ19k_o9MI0y3eZN2lp9jow55FfXMiINEdt1XR85VipRLSOkT6kSpzs2x-jbLDiz9iFVzkd81YKxMgPA7VfZeQUm4n-mOmnWMaVX30zGFU4L3oPBctYKkl4dYfqYWqRNfrgPJVi5DGFjywgxx0ASEiJHtV72paI3fDR2XwlSkyhhmY-ICjCRmsJN4fX1pdoL8a18-aQrvyu4j0Os6dVPYIoPvvY0SAZtWYKHfM15g7A3HD4cVREf9cUsprCRK93w";
+
+ public static String createJWT(long ttlMillis , String payLoad) {
+
+ SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
+
+ long nowMillis = System.currentTimeMillis();
+ Date now = new Date(nowMillis);
+ byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET_KEY);
+ Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
+ JwtBuilder builder = Jwts.builder().claim("GUID" ,payLoad)
+ .signWith(signatureAlgorithm, signingKey);
+
+ if (ttlMillis >= 0) {
+ long expMillis = nowMillis + ttlMillis;
+ Date exp = new Date(expMillis);
+ builder.setExpiration(exp);
+ }
+
+ return builder.compact();
+ }
+
+ public static Claims decodeJWT(String jwt) {
+
+ Claims claims = Jwts.parser()
+ .setSigningKey(DatatypeConverter.parseBase64Binary(SECRET_KEY))
+ .parseClaimsJws(jwt).getBody();
+ return claims;
+ }
+}
diff --git a/hs-plugin-keycloak-ejb/src/test/java/io/hypermine/hypersign/api/HSResourceProviderTest.java b/hs-plugin-keycloak-ejb/src/test/java/io/hypermine/hypersign/api/HSResourceProviderTest.java
new file mode 100644
index 0000000..60f5d44
--- /dev/null
+++ b/hs-plugin-keycloak-ejb/src/test/java/io/hypermine/hypersign/api/HSResourceProviderTest.java
@@ -0,0 +1,36 @@
+package io.hypermine.hypersign.api;
+
+
+import org.junit.runner.RunWith;
+import org.keycloak.models.KeycloakSession;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import static org.junit.Assert.assertEquals;
+
+@RunWith(MockitoJUnitRunner.class)
+public class HSResourceProviderTest {
+ @Mock
+ private KeycloakSession keycloakSession;
+
+
+ @org.junit.Test
+ public void register() {
+ String body = "{\n" +
+ " \"did\": \"12321321323d\",\n" +
+ " \"email\": \"dev@hypermine.com\",\n" +
+ " \"username\": \"dev gurung\",\n" +
+ " \"companyid\": \"hypermine\"\n" +
+ "}";
+ HSResourceProvider hsResourceProvider = new HSResourceProvider(keycloakSession);
+ assertEquals("{\"status\":\"FAIL\",\"data\":\"java.lang.Exception: Could not create the user\"}", hsResourceProvider.register(body).getEntity());
+ }
+
+ @org.junit.Test
+ public void sign() {
+ }
+}
\ No newline at end of file
diff --git a/hs-plugin-keycloak-ejb/src/test/java/io/hypermine/hypersign/authenticator/HyperSignAuthenticatorTest.java b/hs-plugin-keycloak-ejb/src/test/java/io/hypermine/hypersign/authenticator/HyperSignAuthenticatorTest.java
new file mode 100644
index 0000000..8e86d1b
--- /dev/null
+++ b/hs-plugin-keycloak-ejb/src/test/java/io/hypermine/hypersign/authenticator/HyperSignAuthenticatorTest.java
@@ -0,0 +1,32 @@
+package io.hypermine.hypersign.authenticator;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.keycloak.authentication.AuthenticationFlowContext;
+import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.forms.login.LoginFormsProvider;
+import org.keycloak.forms.login.freemarker.FreeMarkerLoginFormsProvider;
+import org.keycloak.models.KeycloakSession;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(PowerMockRunner.class)
+public class HyperSignAuthenticatorTest {
+
+ @Mock
+ private AuthenticationFlowContext authenticationFlowContext;
+ @Test
+ public void getChallenge() {
+ PowerMockito.mock(AuthenticationFlowContext.class);
+ when(authenticationFlowContext.form()).thenReturn(new MockLoginFormProvider());
+ HyperSignAuthenticator hyperSignAuthenticator = new HyperSignAuthenticator();
+ assertNotNull (hyperSignAuthenticator.getChallenge(authenticationFlowContext));
+
+ }
+}
\ No newline at end of file
diff --git a/hs-plugin-keycloak-ejb/src/test/java/io/hypermine/hypersign/authenticator/MockLoginFormProvider.java b/hs-plugin-keycloak-ejb/src/test/java/io/hypermine/hypersign/authenticator/MockLoginFormProvider.java
new file mode 100644
index 0000000..4494bd6
--- /dev/null
+++ b/hs-plugin-keycloak-ejb/src/test/java/io/hypermine/hypersign/authenticator/MockLoginFormProvider.java
@@ -0,0 +1,325 @@
+package io.hypermine.hypersign.authenticator;
+
+import org.keycloak.forms.login.LoginFormsProvider;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.FormMessage;
+import org.keycloak.sessions.AuthenticationSessionModel;
+
+import javax.ws.rs.core.*;
+import java.lang.annotation.Annotation;
+import java.net.URI;
+import java.util.*;
+
+/*This is to be used while mocking the response from the getChallenge method*/
+public class MockLoginFormProvider implements LoginFormsProvider {
+ @Override
+ public void addScript(String scriptUrl) {
+
+ }
+
+ @Override
+ public Response createResponse(UserModel.RequiredAction action) {
+ return null;
+ }
+
+ @Override
+ public Response createForm(String form) {
+ return new Response() {
+ @Override
+ public int getStatus() {
+ return 0;
+ }
+
+ @Override
+ public StatusType getStatusInfo() {
+ return null;
+ }
+
+ @Override
+ public Object getEntity() {
+ return null;
+ }
+
+ @Override
+ public T readEntity(Class entityType) {
+ return null;
+ }
+
+ @Override
+ public T readEntity(GenericType entityType) {
+ return null;
+ }
+
+ @Override
+ public T readEntity(Class entityType, Annotation[] annotations) {
+ return null;
+ }
+
+ @Override
+ public T readEntity(GenericType entityType, Annotation[] annotations) {
+ return null;
+ }
+
+ @Override
+ public boolean hasEntity() {
+ return false;
+ }
+
+ @Override
+ public boolean bufferEntity() {
+ return false;
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public MediaType getMediaType() {
+ return null;
+ }
+
+ @Override
+ public Locale getLanguage() {
+ return null;
+ }
+
+ @Override
+ public int getLength() {
+ return 0;
+ }
+
+ @Override
+ public Set getAllowedMethods() {
+ return null;
+ }
+
+ @Override
+ public Map getCookies() {
+ return null;
+ }
+
+ @Override
+ public EntityTag getEntityTag() {
+ return null;
+ }
+
+ @Override
+ public Date getDate() {
+ return null;
+ }
+
+ @Override
+ public Date getLastModified() {
+ return null;
+ }
+
+ @Override
+ public URI getLocation() {
+ return null;
+ }
+
+ @Override
+ public Set getLinks() {
+ return null;
+ }
+
+ @Override
+ public boolean hasLink(String relation) {
+ return false;
+ }
+
+ @Override
+ public Link getLink(String relation) {
+ return null;
+ }
+
+ @Override
+ public Link.Builder getLinkBuilder(String relation) {
+ return null;
+ }
+
+ @Override
+ public MultivaluedMap getMetadata() {
+ return null;
+ }
+
+ @Override
+ public MultivaluedMap getStringHeaders() {
+ return null;
+ }
+
+ @Override
+ public String getHeaderString(String name) {
+ return null;
+ }
+ };
+ }
+
+ @Override
+ public String getMessage(String message) {
+ return null;
+ }
+
+ @Override
+ public String getMessage(String message, String... parameters) {
+ return null;
+ }
+
+ @Override
+ public Response createLogin() {
+ return null;
+ }
+
+ @Override
+ public Response createPasswordReset() {
+ return null;
+ }
+
+ @Override
+ public Response createLoginTotp() {
+ return null;
+ }
+
+ @Override
+ public Response createRegistration() {
+ return null;
+ }
+
+ @Override
+ public Response createInfoPage() {
+ return null;
+ }
+
+ @Override
+ public Response createUpdateProfilePage() {
+ return null;
+ }
+
+ @Override
+ public Response createIdpLinkConfirmLinkPage() {
+ return null;
+ }
+
+ @Override
+ public Response createIdpLinkEmailPage() {
+ return null;
+ }
+
+ @Override
+ public Response createLoginExpiredPage() {
+ return null;
+ }
+
+ @Override
+ public Response createErrorPage(Response.Status status) {
+ return null;
+ }
+
+ @Override
+ public Response createOAuthGrant() {
+ return null;
+ }
+
+ @Override
+ public Response createCode() {
+ return null;
+ }
+
+ @Override
+ public Response createX509ConfirmPage() {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider setAuthenticationSession(AuthenticationSessionModel authenticationSession) {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider setClientSessionCode(String accessCode) {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider setAccessRequest(List clientScopesRequested) {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider setError(String message, Object... parameters) {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider setErrors(List messages) {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider addError(FormMessage errorMessage) {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider addSuccess(FormMessage errorMessage) {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider setSuccess(String message, Object... parameters) {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider setInfo(String message, Object... parameters) {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider setUser(UserModel user) {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider setResponseHeader(String headerName, String headerValue) {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider setFormData(MultivaluedMap formData) {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider setAttribute(String name, Object value) {
+ return new MockLoginFormProvider();
+ }
+
+ @Override
+ public LoginFormsProvider setStatus(Response.Status status) {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider setMediaType(MediaType type) {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider setActionUri(URI requestUri) {
+ return null;
+ }
+
+ @Override
+ public LoginFormsProvider setExecution(String execution) {
+ return null;
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/hs-plugin-keycloak-ejb/src/test/java/io/hypermine/hypersign/jwt/JWTManagerTest.java b/hs-plugin-keycloak-ejb/src/test/java/io/hypermine/hypersign/jwt/JWTManagerTest.java
new file mode 100644
index 0000000..f308a0b
--- /dev/null
+++ b/hs-plugin-keycloak-ejb/src/test/java/io/hypermine/hypersign/jwt/JWTManagerTest.java
@@ -0,0 +1,33 @@
+package io.hypermine.hypersign.jwt;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.MalformedJwtException;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class JWTManagerTest {
+
+ @Test
+ public void createJWT() {
+ String payLoad = "1234-3434-34343";
+ int jwtTimeToLive = 800000;
+
+ String jwt = JWTManager.createJWT(
+ jwtTimeToLive,
+ payLoad
+ );
+ Claims claims = JWTManager.decodeJWT(jwt);
+ assertEquals("1234-3434-34343", claims.get("GUID"));
+ }
+
+
+ @Test(expected = MalformedJwtException.class)
+ public void decodeShouldFail() {
+
+ String notAJwt = "This is not a JWT";
+
+ Claims claims = JWTManager.decodeJWT(notAJwt);
+
+ }
+}
\ No newline at end of file