new cas auth module
This commit is contained in:
commit
49f0308c4f
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
/bin
|
||||
/build
|
||||
/target
|
||||
/.classpath
|
||||
/.project
|
||||
/.settings/
|
||||
/cas-overlay/target/
|
||||
/cas-overlay/build/
|
||||
/cas-overlay/.classpath
|
||||
/cas-overlay/.project
|
||||
/cas-overlay/.settings/
|
||||
/hostsharing-auth/target/
|
||||
/hostsharing-auth/build/
|
||||
/hostsharing-auth/.classpath
|
||||
/hostsharing-auth/.project
|
||||
/hostsharing-auth/.settings/
|
8
README.md
Normal file
8
README.md
Normal file
@ -0,0 +1,8 @@
|
||||
CAS Module für Hostsharing
|
||||
==========================
|
||||
|
||||
Dieses Modul authentifiziert Username und Passwort gegen die HSAdmin API.
|
||||
|
||||
Dieses Modul wird gebaut und in das lokale Maven-Repository des aktuellen Users instaliert mit dem Befehl
|
||||
|
||||
mvn clean install
|
69
pom.xml
Normal file
69
pom.xml
Normal file
@ -0,0 +1,69 @@
|
||||
<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/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>net.hostsharing.cas</groupId>
|
||||
<artifactId>casauthhsadmin</artifactId>
|
||||
<version>1.0.4</version>
|
||||
<name>CAS Auth HSAdmin</name>
|
||||
<properties>
|
||||
<maven.compiler.release>11</maven.compiler.release>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<cas.version>6.4.4.2</cas.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apereo.cas</groupId>
|
||||
<artifactId>cas-server-core</artifactId>
|
||||
<version>${cas.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apereo.cas</groupId>
|
||||
<artifactId>cas-server-core-authentication-api</artifactId>
|
||||
<version>${cas.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apereo.cas</groupId>
|
||||
<artifactId>cas-server-core-util-api</artifactId>
|
||||
<version>${cas.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apereo.cas</groupId>
|
||||
<artifactId>cas-server-core-configuration-api</artifactId>
|
||||
<version>${cas.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.xmlrpc</groupId>
|
||||
<artifactId>xmlrpc-client</artifactId>
|
||||
<version>3.1.3</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>xml-apis</groupId>
|
||||
<artifactId>xml-apis</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<compilerArgument></compilerArgument>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<finalName>casauthhsadmin</finalName>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,46 @@
|
||||
package net.hostsharing.cas.auth;
|
||||
|
||||
import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
|
||||
import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer;
|
||||
import org.apereo.cas.authentication.AuthenticationHandler;
|
||||
import org.apereo.cas.authentication.principal.PrincipalFactory;
|
||||
import org.apereo.cas.authentication.principal.PrincipalFactoryUtils;
|
||||
import org.apereo.cas.authentication.principal.PrincipalResolver;
|
||||
import org.apereo.cas.services.ServicesManager;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
public class HostsharingAuthEventExecutionPlanConfiguration implements AuthenticationEventExecutionPlanConfigurer {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("servicesManager")
|
||||
private ObjectProvider<ServicesManager> servicesManager;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("defaultPrincipalResolver")
|
||||
private ObjectProvider<PrincipalResolver> defaultPrincipalResolver;
|
||||
|
||||
@ConditionalOnMissingBean(name = "hostsharingAuthenticationPrincipalFactory")
|
||||
@Bean
|
||||
@RefreshScope
|
||||
public PrincipalFactory hostsharingAuthenticationPrincipalFactory() {
|
||||
return PrincipalFactoryUtils.newPrincipalFactory();
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public AuthenticationHandler myAuthenticationHandler() {
|
||||
final String name = "Hostsharing Authentication";
|
||||
return new HostsharingAuthenticationHandler(name, servicesManager.getObject(), hostsharingAuthenticationPrincipalFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureAuthenticationExecutionPlan(AuthenticationEventExecutionPlan plan) {
|
||||
plan.registerAuthenticationHandler(myAuthenticationHandler());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
package net.hostsharing.cas.auth;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.URL;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.apache.xmlrpc.XmlRpcException;
|
||||
import org.apache.xmlrpc.client.XmlRpcClient;
|
||||
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
|
||||
import org.apereo.cas.authentication.AuthenticationHandlerExecutionResult;
|
||||
import org.apereo.cas.authentication.CoreAuthenticationUtils;
|
||||
import org.apereo.cas.authentication.PreventedException;
|
||||
import org.apereo.cas.authentication.credential.UsernamePasswordCredential;
|
||||
import org.apereo.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
|
||||
import org.apereo.cas.authentication.principal.Principal;
|
||||
import org.apereo.cas.authentication.principal.PrincipalFactory;
|
||||
import org.apereo.cas.services.ServicesManager;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
public class HostsharingAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler {
|
||||
|
||||
public HostsharingAuthenticationHandler(String name, ServicesManager servicesManager, PrincipalFactory principalFactory) {
|
||||
super(name, servicesManager, principalFactory, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(UsernamePasswordCredential credential, String originalPassword)
|
||||
throws GeneralSecurityException, PreventedException {
|
||||
|
||||
final String username = credential.getUsername();
|
||||
final String password = credential.getPassword();
|
||||
|
||||
try {
|
||||
|
||||
final Map<String, List<Object>> attributes = validateCredentials(username, password);
|
||||
final Principal principal = this.principalFactory.createPrincipal(username, attributes);
|
||||
return createHandlerResult(credential, principal);
|
||||
|
||||
} catch (PasswordValidationException | IOException | XmlRpcException | ParserConfigurationException | SAXException e) {
|
||||
throw new GeneralSecurityException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, List<Object>> validateCredentials(final String login, final String password)
|
||||
throws PasswordValidationException, XmlRpcException, GeneralSecurityException, IOException, ParserConfigurationException, SAXException {
|
||||
|
||||
if (!login.contains("@")) {
|
||||
throw new GeneralSecurityException("expect email address");
|
||||
}
|
||||
final String emailDomain = login.split("@")[1];
|
||||
final URL url = new URL("http://" + emailDomain + "/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=" + login);
|
||||
final InputStream autoconfigStream = url.openConnection().getInputStream();
|
||||
final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||
final Document document = documentBuilder.parse(autoconfigStream);
|
||||
final NodeList inServersNodes = document.getElementsByTagName("username");
|
||||
if (inServersNodes.getLength() != 2) {
|
||||
throw new GeneralSecurityException("expect email address");
|
||||
}
|
||||
|
||||
final String username = inServersNodes.item(0).getTextContent();
|
||||
|
||||
|
||||
final TicketService ticketService = new TicketService(username, password);
|
||||
final String grantingTicket = ticketService.getGrantingTicket();
|
||||
final String ticket = ticketService.getServiceTicket(grantingTicket);
|
||||
|
||||
final XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
|
||||
config.setServerURL(new URL("https://config.hostsharing.net:443/hsar/xmlrpc/hsadmin"));
|
||||
config.setEnabledForExtensions(true);
|
||||
final XmlRpcClient client = new XmlRpcClient();
|
||||
client.setConfig(config);
|
||||
|
||||
final List<Serializable> xmlRpcParamsList = new ArrayList<Serializable>();
|
||||
xmlRpcParamsList.add(username);
|
||||
xmlRpcParamsList.add(ticket);
|
||||
final HashMap<String, Serializable> whereParamsMap = new HashMap<String, Serializable>();
|
||||
whereParamsMap.put("name", username);
|
||||
xmlRpcParamsList.add(whereParamsMap);
|
||||
final Object[] rpcResult = (Object[]) client.execute("user.search", xmlRpcParamsList);
|
||||
if (rpcResult.length != 1) {
|
||||
throw new GeneralSecurityException("unknown username");
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<String, Serializable> userData = (Map<String, Serializable>) rpcResult[0];
|
||||
final String comment = (String) userData.get("comment");
|
||||
int firstCommaIndex = comment.indexOf(',');
|
||||
String displayName = comment;
|
||||
String[] groups = new String[0];
|
||||
if (firstCommaIndex > 0) {
|
||||
displayName = comment.substring(0, firstCommaIndex).trim();
|
||||
final String[] splitStrings = comment.substring(firstCommaIndex + 1).split(",");
|
||||
groups = new String[splitStrings.length];
|
||||
for (int idx=0; idx<splitStrings.length; idx++) {
|
||||
groups[idx] = splitStrings[idx].trim();
|
||||
}
|
||||
}
|
||||
final Map<String, Object> attribsMap = new HashMap<String, Object>();
|
||||
attribsMap.put("groups", groups);
|
||||
attribsMap.put("displayName", displayName);
|
||||
attribsMap.put("mail", login);
|
||||
final Map<String, List<Object>> attributes = CoreAuthenticationUtils.convertAttributeValuesToMultiValuedObjects(attribsMap);
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
Map<String, List<Object>> map = validateCredentials(args[0], args[1]);
|
||||
for (String key : map.keySet()) {
|
||||
System.out.println(key + ": " + map.get(key));
|
||||
}
|
||||
} catch (IOException | PasswordValidationException | XmlRpcException | GeneralSecurityException | ParserConfigurationException | SAXException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package net.hostsharing.cas.auth;
|
||||
|
||||
public class PasswordValidationException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PasswordValidationException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PasswordValidationException(final Throwable excption) {
|
||||
super(excption);
|
||||
}
|
||||
|
||||
public PasswordValidationException(final String message, final Throwable exception) {
|
||||
super(message, exception);
|
||||
}
|
||||
|
||||
}
|
85
src/main/java/net/hostsharing/cas/auth/TicketService.java
Normal file
85
src/main/java/net/hostsharing/cas/auth/TicketService.java
Normal file
@ -0,0 +1,85 @@
|
||||
package net.hostsharing.cas.auth;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
/**
|
||||
* Helper for service tickets.
|
||||
* Hostsharing uses the CAS authentication service to authenticate
|
||||
* users of hostsharing services. This class is used to create a
|
||||
* "ticket granting ticket" for a session and service ticket for
|
||||
* individual service calls.
|
||||
*/
|
||||
public class TicketService {
|
||||
final String user;
|
||||
final String password;
|
||||
|
||||
public TicketService(final String user, final String password) {
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getGrantingTicket() throws PasswordValidationException {
|
||||
String ticket = null;
|
||||
try {
|
||||
String userParam = "username=" + URLEncoder.encode(user, "UTF-8");
|
||||
String passwordParam = "password=" + URLEncoder.encode(password, "UTF-8");
|
||||
String encodedData = userParam + "&" + passwordParam;
|
||||
URL url = new URL("https://login.hostsharing.net/cas/v1/tickets");
|
||||
|
||||
final HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||
connection.setDoInput(true);
|
||||
connection.setDoOutput(true);
|
||||
connection.setUseCaches(false);
|
||||
connection.setAllowUserInteraction(false);
|
||||
final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream()));
|
||||
writer.write(encodedData);
|
||||
writer.close();
|
||||
connection.connect();
|
||||
ticket = connection.getHeaderField("Location");
|
||||
} catch (Exception e) {
|
||||
throw new PasswordValidationException(e);
|
||||
}
|
||||
|
||||
return ticket;
|
||||
}
|
||||
|
||||
public String getServiceTicket(String grantingTicket) throws PasswordValidationException {
|
||||
String ticket = null;
|
||||
try {
|
||||
String serviceParam = "service=" + URLEncoder.encode("https://config.hostsharing.net:443/hsar/backend", "UTF-8");
|
||||
URL url = new URL(grantingTicket);
|
||||
|
||||
final HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||
connection.setDoInput(true);
|
||||
connection.setDoOutput(true);
|
||||
connection.setUseCaches(false);
|
||||
connection.setAllowUserInteraction(false);
|
||||
final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream()));
|
||||
writer.write(serviceParam);
|
||||
writer.close();
|
||||
connection.connect();
|
||||
final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
ticket = reader.readLine();
|
||||
String readLine = reader.readLine();
|
||||
do {
|
||||
readLine = reader.readLine();
|
||||
} while (readLine != null);
|
||||
} catch (Exception e) {
|
||||
throw new PasswordValidationException(e);
|
||||
}
|
||||
|
||||
return ticket;
|
||||
}
|
||||
|
||||
}
|
1
src/main/resources/META-INF/spring.factories
Normal file
1
src/main/resources/META-INF/spring.factories
Normal file
@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=net.hostsharing.cas.auth.HostsharingAuthEventExecutionPlanConfiguration
|
Loading…
Reference in New Issue
Block a user