Skip to content

Commit

Permalink
Merge pull request #23 from privacyidea/v0.3
Browse files Browse the repository at this point in the history
v0.3
  • Loading branch information
nilsbehlen authored Oct 22, 2019
2 parents f49e384 + b3f676a commit 9a5d15b
Show file tree
Hide file tree
Showing 11 changed files with 753 additions and 539 deletions.
13 changes: 4 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ In this blog entry, you can find an example with Keycloak, privacyIDEA and Drupa
## Installation

* Move the packed jar file into your deployment directory `standalone/deployment`.
* Copy the template privacyIDEA.ftl to `themes/base/login`.
* Move the template privacyIDEA.ftl to `themes/base/login`.

Now you can enable the execution for your auth flow.
If you set the execution as 'required', every user needs to login with a second factor.
Expand All @@ -36,12 +36,7 @@ You can find different preferences in your configuration, which are explained be

## Manual build with source code

You can also build the provider yourself.
***Notice:** This is not a stable release. Do not use it in a productive environment.*
* If the wildfly server is running, the authenticator can directly be deployed with
``mvn clean install wildfly:deploy`` and only the template has to be copied.

* We used the [demo server](https://www.keycloak.org/archive/downloads-4.3.0.html) to build our plugin.
* Clone this repo to `keycloak-demo-4.3.0.Final/examples/providers`
* Build this provider with `mvn clean install wildfly:deploy`
* Pack the content of `target/classes` to privacyidea.jar

Go on with **Installation**.
* Otherwise build with ``mvn clean install`` and go on with **Installation**
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
<description/>
<modelVersion>4.0.0</modelVersion>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
Expand Down Expand Up @@ -86,6 +90,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
Expand Down
68 changes: 34 additions & 34 deletions privacyIDEA.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,23 @@
<div class="${properties.kcInputWrapperClass!}">
<#if tokenType = "push">
${pushMessage}
<#else>
<#else>
${otpMessage}
</#if>
<#-- Show QR code for new token, if one has been enrolled -->
<#if tokenEnrollmentQR != "">
<center>
<div style="text-align: center;">
<img width="256" height="256" src="${tokenEnrollmentQR}">
</center>
</div>
Please scan the QR-Code with an authenticator app like "privacyIDEA Authenticator" or "Google Authenticator"
</#if>
<input id="pi_otp" name="pi_otp" type="hidden" class="${properties.kcInputClass!}" autofocus />
<input id="pi_otp" name="pi_otp" type="hidden" class="${properties.kcInputClass!}" autofocus/>
</div>
</div>

<div class="${properties.kcFormGroupClass!}">
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
<div class="${properties.kcFormOptionsWrapperClass!}">
</div>
<div class="${properties.kcFormOptionsWrapperClass!}"/>
</div>

<#--These inputs will be returned to privacyIDEAAuthenticator-->
Expand All @@ -42,43 +41,44 @@
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
<div class="${properties.kcFormButtonsWrapperClass!}">
<#if tokenType = "push">
<#--The form will be reloaded if push token is enabled to check if it is confirmed.
The interval can be set in the configuration-->
<script>
window.onload=function(){
window.setTimeout(function() {
document.forms["loginform"].submit();
}, parseInt(${pushTokenInterval})*1000);
};
</script>
<#if otpToken>
<#--The token type can be changed if we can use push or otp-->
<input class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="changeTokenTypeButton" id="changeTokenTypeButton" onClick="changeTokenType('otp')" type="button" value="Use OTP"/>
</#if>
<#else>
<#--If token type is not push, an input field and login button is needed-->
<script>
document.getElementById("pi_otp").type="password";
document.getElementById("pi_otp").required=true;
</script>
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
<#if pushToken>
<#--The token type can be changed if we can use push or otp-->
<input class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="changeTokenTypeButton" id="changeTokenTypeButton" onClick="changeTokenType('push')" type="button" value="Use Push Token"/>
</#if>
<#--The form will be reloaded if push token is enabled to check if it is confirmed.
The interval can be set in the configuration-->
<script>
window.onload = function () {
window.setTimeout(function () {
document.forms["loginform"].submit();
}, parseInt(${pushTokenInterval}) * 1000);
};
</script>
<#if otpToken>
<#--The token type can be changed if we can use push or otp-->
<input class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="changeTokenTypeButton" id="changeTokenTypeButton" onClick="changeTokenType('otp')" type="button" value="Use OTP"/>
</#if>
<#else>
<#--If token type is not push, an input field and login button is needed-->
<script>
document.getElementById("pi_otp").type = "password";
document.getElementById("pi_otp").required = true;
</script>
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
<#if pushToken>
<#--The token type can be changed if we can use push or otp-->
<input class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="changeTokenTypeButton" id="changeTokenTypeButton" onClick="changeTokenType('push')" type="button" value="Use Push Token"/>
</#if>
</#if>
<#--If we change the token type, this information must be transmitted to privacyIDEAAuthenticator-->
<script>
function changeTokenType(tokenType) {
document.getElementById("tokenType").value=tokenType;
document.getElementById("tokenTypeChanged").value="true";
document.getElementById("tokenType").value = tokenType;
document.getElementById("tokenTypeChanged").value = "true";
document.forms["loginform"].submit();
}
</script>
<input class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="cancel" id="kc-cancel" type="submit" value="${msg("doCancel")}"/>
<input class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}"
name="cancel" id="kc-cancel" type="submit" value="${msg("doCancel")}"/>
</div>
</div>
</div>
</form>
</#if>
</@layout.registrationLayout>
</@layout.registrationLayout>
95 changes: 95 additions & 0 deletions src/main/java/org/privacyidea/authenticator/Configuration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package org.privacyidea.authenticator;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import static org.privacyidea.authenticator.Const.*;

class Configuration {

private String _serverURL;
private String _realm;
private boolean _doSSLVerify;
private boolean _doTriggerChallenge;
private String _serviceAccountName;
private String _serviceAccountPass;
private List<String> _excludedGroups = new ArrayList<>();
private boolean _doEnrollToken;
private String _enrollingTokenType;
private List<Integer> _pushtokenPollingInterval = new ArrayList<>();

Configuration(Map<String, String> configMap) {
_serverURL = configMap.get(CONFIG_SERVER);
_realm = configMap.get(CONFIG_REALM) == null ? "" : configMap.get(CONFIG_REALM);
_doSSLVerify = configMap.get(CONFIG_VERIFYSSL) != null && configMap.get(CONFIG_VERIFYSSL).equals(TRUE);
_doTriggerChallenge = configMap.get(CONFIG_DOTRIGGERCHALLENGE) != null && configMap.get(CONFIG_DOTRIGGERCHALLENGE).equals(TRUE);
_serviceAccountName = configMap.get(CONFIG_SERVICEACCOUNT) == null ? "" : configMap.get(CONFIG_SERVICEACCOUNT);
_serviceAccountPass = (configMap.get(CONFIG_SERVICEPASS) == null) ? "" : configMap.get(CONFIG_SERVICEPASS);
_doEnrollToken = configMap.get(CONFIG_ENROLLTOKEN) != null && configMap.get(CONFIG_ENROLLTOKEN).equals(TRUE);
_enrollingTokenType = configMap.get(CONFIG_ENROLLTOKENTYPE) == null ? "" : configMap.get(CONFIG_ENROLLTOKENTYPE);

String excludedGroupsStr = configMap.get(CONFIG_EXCLUDEGROUPS);
if (excludedGroupsStr != null) {
_excludedGroups.addAll(Arrays.asList(excludedGroupsStr.split(",")));
}

// Set default, overwrite if configured
_pushtokenPollingInterval.addAll(DEFAULT_POLLING_ARRAY);
String s = configMap.get(CONFIG_PUSHTOKENINTERVAL);
if (s != null) {
List<String> strPollingIntervals = Arrays.asList(s.split(","));
if (!strPollingIntervals.isEmpty()) {
_pushtokenPollingInterval.clear();
for (String str : strPollingIntervals) {
try {
_pushtokenPollingInterval.add(Integer.parseInt(str));
} catch (NumberFormatException e) {
_pushtokenPollingInterval.add(DEFAULT_POLLING_INTERVAL);
}
}
}
}
}

String getServerURL() {
return _serverURL;
}

String getRealm() {
return _realm;
}

boolean doSSLVerify() {
return _doSSLVerify;
}

boolean doTriggerChallenge() {
return _doTriggerChallenge;
}

String getServiceAccountName() {
return _serviceAccountName;
}

String getServiceAccountPass() {
return _serviceAccountPass;
}

List<String> getExcludedGroups() {
return _excludedGroups;
}

boolean doEnrollToken() {
return _doEnrollToken;
}

String getEnrollingTokenType() {
return _enrollingTokenType;
}

List<Integer> getPushtokenPollingInterval() {
return _pushtokenPollingInterval;
}
}
81 changes: 81 additions & 0 deletions src/main/java/org/privacyidea/authenticator/Const.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.privacyidea.authenticator;

import java.util.Arrays;
import java.util.List;

final class Const {
private Const() {
}

static final String PROVIDER_ID = "privacyidea-authenticator";

static final String GET = "GET";
static final String POST = "POST";
static final String TRUE = "true";

static final String ENDPOINT_AUTH = "/auth";
static final String ENDPOINT_TOKEN_INIT = "/token/init";
static final String ENDPOINT_TRIGGERCHALLENGE = "/validate/triggerchallenge";
static final String ENDPOINT_TOKEN_CHALLENGES = "/token/challenges";
static final String ENDPOINT_VALIDATE_CHECK = "/validate/check";
static final String ENDPOINT_TOKEN = "/token";

static final String DEFAULT_PUSH_MESSAGE = "Please confirm the authentication on your mobile device";
static final String DEFAULT_OTP_MESSAGE = "Please enter the OTP";

static final int DEFAULT_POLLING_INTERVAL = 2; // Will be used if single value from config cannot be parsed
static final List<Integer> DEFAULT_POLLING_ARRAY = Arrays.asList(5, 1, 1, 1, 2, 3); // Will be used if no intervals are specified

static final String FORM_PUSHTOKEN_INTERVAL = "pushTokenInterval";
static final String FORM_TOKEN_ENROLLMENT_QR = "tokenEnrollmentQR";
static final String FORM_TOKENTYPE = "tokenType";
static final String FORM_PUSHTOKEN = "pushToken";
static final String FORM_OTPTOKEN = "otpToken";
static final String FORM_PUSH_MESSAGE = "pushMessage";
static final String FORM_OTP_MESSAGE = "otpMessage";
static final String FORM_FILE_NAME = "privacyIDEA.ftl";
static final String FORM_TOKENTYPE_CHANGED = "tokenTypeChanged";
static final String FORM_PI_OTP = "pi_otp";

static final String PARAM_KEY_USERNAME = "username";
static final String PARAM_KEY_USER = "user";
static final String PARAM_KEY_PASSWORD = "password";
static final String PARAM_KEY_PASS = "pass";
static final String PARAM_KEY_TYPE = "type";
static final String PARAM_KEY_GENKEY = "genkey";
static final String PARAM_KEY_TRANSACTION_ID = "transaction_id";
static final String PARAM_KEY_REALM = "realm";

static final String TOKEN_TYPE_PUSH = "push";
static final String TOKEN_TYPE_OTP = "otp"; // Classic OTPs like HOTP/TOTP

static final String AUTH_NOTE_TRANSACTION_ID = "pi.transaction_id";
static final String AUTH_NOTE_AUTH_COUNTER = "authCounter";

static final String JSON_KEY_DETAIL = "detail";
static final String JSON_KEY_RESULT = "result";
static final String JSON_KEY_VALUE = "value";
static final String JSON_KEY_MESSAGE = "message";
static final String JSON_KEY_MULTI_CHALLENGE = "multi_challenge";
static final String JSON_KEY_TYPE = "type";
static final String JSON_KEY_TOKEN = "token";
static final String JSON_KEY_GOOGLEURL = "googleurl";
static final String JSON_KEY_IMG = "img";
static final String JSON_KEY_CHALLENGES = "challenges";
static final String JSON_KEY_OTP_VALID = "otp_valid";
static final String JSON_KEY_TRANSACTION_ID = "transaction_id";
static final String JSON_KEY_MESSAGES = "messages";
static final String JSON_KEY_TRANSACTION_IDS = "transaction_ids";
static final String JSON_KEY_TOKENS = "tokens";

static final String CONFIG_PUSHTOKENINTERVAL = "pipushtokeninterval";
static final String CONFIG_EXCLUDEGROUPS = "piexcludegroups";
static final String CONFIG_ENROLLTOKENTYPE = "pienrolltokentype";
static final String CONFIG_ENROLLTOKEN = "pienrolltoken";
static final String CONFIG_SERVICEPASS = "piservicepass";
static final String CONFIG_SERVICEACCOUNT = "piserviceaccount";
static final String CONFIG_DOTRIGGERCHALLENGE = "pidotriggerchallenge";
static final String CONFIG_VERIFYSSL = "piverifyssl";
static final String CONFIG_REALM = "pirealm";
static final String CONFIG_SERVER = "piserver";
}
Loading

0 comments on commit 9a5d15b

Please sign in to comment.