Dateien nach "sourcecode/src/main/java/de/gmp/keycloak" hochladen
This commit is contained in:
parent
706ac46d59
commit
8bf3b758d7
150
sourcecode/src/main/java/de/gmp/keycloak/IpAuthenticator.java
Normal file
150
sourcecode/src/main/java/de/gmp/keycloak/IpAuthenticator.java
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
package de.gmp.keycloak;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.core.MultivaluedMap;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||||
|
import org.keycloak.authentication.Authenticator;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.apache.commons.net.util.SubnetUtils;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.keycloak.authentication.AuthenticationFlowError;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
|
||||||
|
// import org.keycloak.sessions.AuthenticationSessionModel;
|
||||||
|
// import org.keycloak.storage.UserStorageManager;
|
||||||
|
// import org.keycloak.credential.CredentialModel;
|
||||||
|
// import org.keycloak.storage.UserStorageManager;
|
||||||
|
// import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
// import org.keycloak.models.UserCredentialModel;
|
||||||
|
// import org.keycloak.credential.UserCredentialStore;
|
||||||
|
// import org.keycloak.authentication.AuthenticationProcessor;
|
||||||
|
// import org.keycloak.authentication.AuthenticationFlowError;
|
||||||
|
// import org.keycloak.authentication.CredentialValidator;
|
||||||
|
// import org.keycloak.credential.CredentialProvider;
|
||||||
|
// import org.keycloak.models.KeycloakSession;
|
||||||
|
// import org.keycloak.models.RealmModel;
|
||||||
|
// import org.keycloak.models.UserCredentialModel;
|
||||||
|
// import org.keycloak.models.UserModel;
|
||||||
|
// import org.keycloak.models.utils.FormMessage;
|
||||||
|
// import org.keycloak.sessions.AuthenticationSessionModel;
|
||||||
|
// import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public class IpAuthenticator implements Authenticator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void authenticate(AuthenticationFlowContext context) {
|
||||||
|
// SCHRITT 1: Prüfen, ob der Benutzer den IP-Login überspringen will.
|
||||||
|
String skip = context.getHttpRequest().getUri().getQueryParameters().getFirst("skip_ip_login");
|
||||||
|
if ("true".equals(skip)) {
|
||||||
|
context.attempted();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCHRITT 2: IP-Prüfung wie gehabt
|
||||||
|
UserModel user = findUserByIp(context);
|
||||||
|
if (user != null) {
|
||||||
|
// Benutzer gefunden -> Zeige unsere spezielle Login-Seite an.
|
||||||
|
Response challenge = context.form()
|
||||||
|
.setAttribute("user", user)
|
||||||
|
.createForm("ip-login-page.ftl");
|
||||||
|
context.challenge(challenge);
|
||||||
|
} else {
|
||||||
|
// Kein passender Benutzer gefunden -> Fahre mit dem normalen Flow fort.
|
||||||
|
context.attempted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void action(AuthenticationFlowContext context) {
|
||||||
|
UserModel user = findUserByIp(context);
|
||||||
|
if (user != null) {
|
||||||
|
context.setUser(user);
|
||||||
|
context.success();
|
||||||
|
} else {
|
||||||
|
// Sicherheitshalber, falls die IP sich zwischenzeitlich ändert.
|
||||||
|
context.failure(AuthenticationFlowError.UNKNOWN_USER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserModel findUserByIp(AuthenticationFlowContext context) {
|
||||||
|
RealmModel realm = context.getRealm();
|
||||||
|
KeycloakSession session = context.getSession();
|
||||||
|
String clientIp = context.getConnection().getRemoteAddr();
|
||||||
|
|
||||||
|
try (Stream<UserModel> userStream = session.users().searchForUserByUserAttributeStream(realm, "ipLogin", "1")) {
|
||||||
|
Optional<UserModel> matchingUser = userStream.filter(user -> {
|
||||||
|
Optional<String> ipChecker = user.getAttributeStream("ipAddresses").filter(ipBlock -> {
|
||||||
|
String[] ipBlocks = ipBlock.split(",");
|
||||||
|
for (String ipOrCidr : ipBlocks) {
|
||||||
|
//System.out.println(ipOrCidr);
|
||||||
|
if (isIpMatch(clientIp, ipOrCidr.trim())) {
|
||||||
|
//System.out.println(clientIp + " match!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}).findFirst();
|
||||||
|
if (ipChecker.isPresent()) {
|
||||||
|
//System.out.println("Check: "+ipChecker);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}).findFirst();
|
||||||
|
|
||||||
|
if (matchingUser.isPresent()) {
|
||||||
|
// Speichere den gefundenen Benutzer für den nächsten Schritt (action)
|
||||||
|
UserModel user = matchingUser.get();
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isIpMatch(String ip, String ipOrCidr) {
|
||||||
|
try {
|
||||||
|
if (ipOrCidr.contains("-")) { // Mehrere IPs nach Schreibweise 192.168.1.1-192.168.1.5
|
||||||
|
String[] parts = ipOrCidr.split("-");
|
||||||
|
String startIp = parts[0].trim();
|
||||||
|
String endIp = parts[1].trim();
|
||||||
|
try {
|
||||||
|
long ipToCheckLong = ipToLong(ip);
|
||||||
|
long startIpLong = ipToLong(startIp);
|
||||||
|
long endIpLong = ipToLong(endIp);
|
||||||
|
return ipToCheckLong >= startIpLong && ipToCheckLong <= endIpLong;
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else { // Davon ausgehend, dass es sich nur um eine Adresse handelt
|
||||||
|
return ipOrCidr.equals(ip);
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long ipToLong(String ip) throws UnknownHostException {
|
||||||
|
InetAddress inetAddress = InetAddress.getByName(ip);
|
||||||
|
byte[] ipBytes = inetAddress.getAddress();
|
||||||
|
long result = 0;
|
||||||
|
for (byte b : ipBytes) {
|
||||||
|
result = (result << 8) | (b & 0xFF);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leere Methoden
|
||||||
|
@Override public boolean requiresUser() { return false; }
|
||||||
|
@Override public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) { return true; }
|
||||||
|
@Override public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {}
|
||||||
|
@Override public void close() {}
|
||||||
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
package de.gmp.keycloak;
|
||||||
|
|
||||||
|
import org.keycloak.authentication.Authenticator;
|
||||||
|
import org.keycloak.authentication.AuthenticatorFactory;
|
||||||
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
// import org.keycloak.models.UserModel;
|
||||||
|
// import org.keycloak.models.RealmModel;
|
||||||
|
|
||||||
|
public class IpAuthenticatorFactory implements AuthenticatorFactory {
|
||||||
|
|
||||||
|
public static final String PROVIDER_ID = "ip-authenticator";
|
||||||
|
|
||||||
|
private static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
|
||||||
|
AuthenticationExecutionModel.Requirement.ALTERNATIVE,
|
||||||
|
AuthenticationExecutionModel.Requirement.DISABLED
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayType() {
|
||||||
|
return "Login per IP";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return PROVIDER_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Authenticator create(KeycloakSession session) {
|
||||||
|
return new IpAuthenticator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Config.Scope config) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postInit(KeycloakSessionFactory factory) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConfigurable() { return false; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUserSetupAllowed() { return false; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
|
||||||
|
return new AuthenticationExecutionModel.Requirement[]{
|
||||||
|
AuthenticationExecutionModel.Requirement.ALTERNATIVE,
|
||||||
|
AuthenticationExecutionModel.Requirement.DISABLED
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getReferenceCategory() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProviderConfigProperty> getConfigProperties() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHelpText() {
|
||||||
|
return "Fügt eine 'Login mit 1 Klick'-Option hinzu, wenn die IP-Adresse bei den Attributen eines Benutzers gefunden wurde.";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user