Commit 3bceb177 authored by benjamin.ertl's avatar benjamin.ertl
Browse files

refactoring

parent 21a12365
/* Copyright 2016 Karlsruhe Institute of Technology (KIT)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
*/
package edu.kit.scc;
import java.text.ParseException;
import java.util.Arrays;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.oauth2.sdk.token.AccessToken;
import com.nimbusds.openid.connect.sdk.token.OIDCTokens;
import edu.kit.scc.oidc.OidcClient;
import edu.kit.scc.scim.ScimClient;
import edu.kit.scc.scim.ScimUser;
@Component
public class Harmonizer {
private static final Logger log = LoggerFactory.getLogger(Harmonizer.class);
@Autowired
private ScimClient scimClient;
@Autowired
private OidcClient oidcClient;
public ScimUser harmonizeIdentities(String subject, OIDCTokens tokens) {
ScimUser scimUser = new ScimUser();
scimUser.setSchemas(Arrays.asList(scimUser.USER_SCHEMA));
scimUser.setUserName(subject);
// OIDC
log.debug("Try to get OIDC user information");
JSONObject userInfo = null;
if (tokens != null) {
try {
JWT jwt = tokens.getIDToken();
JWTClaimsSet claimsSet = jwt.getJWTClaimsSet();
log.debug("Claims set {}", claimsSet.toJSONObject().toJSONString());
AccessToken accessToken = tokens.getAccessToken();
userInfo = oidcClient.requestUserInfo(accessToken.getValue());
log.debug("User info {}", userInfo.toString());
} catch (ParseException e) {
log.error(e.getMessage());
}
}
// SCIM
log.debug("Try to get SCIM user information");
JSONObject userJson = scimClient.getUser(subject);
log.debug("SCIM user info {}", userJson.toString());
// LDAP
// TODO
log.debug("Aggregated SCIM user information {}", scimUser.toString());
return scimUser;
}
}
...@@ -8,12 +8,10 @@ ...@@ -8,12 +8,10 @@
*/ */
package edu.kit.scc; package edu.kit.scc;
import java.text.ParseException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
...@@ -27,18 +25,11 @@ import org.springframework.web.bind.annotation.RequestMethod; ...@@ -27,18 +25,11 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.oauth2.sdk.token.AccessToken;
import com.nimbusds.openid.connect.sdk.token.OIDCTokens; import com.nimbusds.openid.connect.sdk.token.OIDCTokens;
import edu.kit.scc.dto.GroupDTO;
import edu.kit.scc.dto.UserDTO;
import edu.kit.scc.http.HttpClient;
import edu.kit.scc.http.HttpResponse;
import edu.kit.scc.ldap.LdapClient;
import edu.kit.scc.oidc.OidcClient; import edu.kit.scc.oidc.OidcClient;
import edu.kit.scc.scim.ScimClient; import edu.kit.scc.regapp.RegAppClient;
import edu.kit.scc.scim.ScimUser;
@RestController @RestController
@RequestMapping("/rest") @RequestMapping("/rest")
...@@ -52,20 +43,14 @@ public class RestServiceController { ...@@ -52,20 +43,14 @@ public class RestServiceController {
@Value("${regapp.servicePassword}") @Value("${regapp.servicePassword}")
private String restPassword; private String restPassword;
@Value("${regapp.serviceUrl}")
private String serviceUrl;
@Autowired @Autowired
private HttpClient httpClient; private RegAppClient regAppClient;
@Autowired @Autowired
private OidcClient oidcClient; private OidcClient oidcClient;
@Autowired @Autowired
private ScimClient scimClient; private Harmonizer identityHarmonizer;
@Autowired
private LdapClient ldapClient;
// expected body e.g. // expected body e.g.
// password=password // password=password
...@@ -73,108 +58,58 @@ public class RestServiceController { ...@@ -73,108 +58,58 @@ public class RestServiceController {
// password=3D49806e48a5cd2941604eb9dfe321c3bc // password=3D49806e48a5cd2941604eb9dfe321c3bc
@RequestMapping(path = "/ecp/regid/{regId}", method = RequestMethod.POST) @RequestMapping(path = "/ecp/regid/{regId}", method = RequestMethod.POST)
public void ecpAuthentication(@PathVariable String regId, @RequestHeader("Authorization") String basicAuthorization, public ScimUser ecpAuthentication(@PathVariable String regId,
@RequestBody String body) { @RequestHeader("Authorization") String basicAuthorization, @RequestBody String body) {
String encodedCredentials = basicAuthorization.split(" ")[1];
String[] credentials = new String(Base64.decodeBase64(encodedCredentials)).split(":");
if (!credentials[0].equals(restUser) || !credentials[1].equals(restPassword)) { verifyAuthorization(basicAuthorization);
log.error("Wrong credentials {} {}", credentials[0], credentials[1]);
throw new UnauthorizedException();
}
log.debug("Request body {}", body); log.debug("Request body {}", body);
// REG-APP // REG-APP
log.debug("Try reg-app authentication"); log.debug("Try reg-app authentication");
String regAppUrl = serviceUrl.replaceAll("/$", ""); boolean regAppSuccess = regAppClient.authenticate(regId, body);
regAppUrl += "/" + regId; log.debug("Reg-app success {}", regAppSuccess);
HttpResponse response = httpClient.makeHttpPostRequest(restUser, restPassword, body, regAppUrl);
if (response != null && response.statusCode == 200) {
log.debug("Reg-app authentication success");
// TODO harmonize
// harmonizeIdentities(userName);
return;
}
// OIDC // OIDC
log.debug("Try OIDC authentication"); boolean oidcSuccess = false;
OIDCTokens tokens = null; OIDCTokens tokens = null;
try { if (!regAppSuccess) {
String token = body.split("=")[1]; log.debug("Try OIDC authentication");
// oidcJson = oidcClient.requestUserInfo(token);
tokens = oidcClient.requestTokens(token);
} catch (ArrayIndexOutOfBoundsException e) {
log.error(e.getMessage());
throw new UnauthorizedException();
}
if (tokens != null) {
try { try {
JWT jwt = tokens.getIDToken(); String token = body.split("=")[1];
JWTClaimsSet claimsSet = jwt.getJWTClaimsSet(); log.debug("Got token {}", token);
log.debug(claimsSet.toJSONObject().toJSONString()); token = URLDecoder.decode(token, "UTF-8");
tokens = oidcClient.requestTokens(token);
AccessToken accessToken = tokens.getAccessToken();
oidcClient.requestUserInfo(accessToken.getValue()); if (tokens != null) {
log.debug("OIDC authentication success");
String subject = claimsSet.getSubject(); oidcSuccess = true;
log.debug("OIDC authentication success"); }
// TODO harmonize } catch (ArrayIndexOutOfBoundsException e) {
harmonizeIdentities(subject); log.error(e.getMessage());
return; throw new UnauthorizedException();
} catch (ParseException e) { } catch (UnsupportedEncodingException e) {
log.error(e.getMessage()); log.error(e.getMessage());
throw new UnauthorizedException(); throw new UnauthorizedException();
} }
} }
log.debug("OIDC success {}", oidcSuccess);
if (regAppSuccess || oidcSuccess) {
return identityHarmonizer.harmonizeIdentities(regId, tokens);
}
// if nothing succeeded, fail ... gracefully // if nothing succeeded, fail ... gracefully
throw new UnauthorizedException(); throw new UnauthorizedException();
} }
private void harmonizeIdentities(String subject) { private void verifyAuthorization(String basicAuthorization) {
// SCIM String encodedCredentials = basicAuthorization.split(" ")[1];
// we are looking for groups in the SCIM response String[] credentials = new String(Base64.decodeBase64(encodedCredentials)).split(":");
log.debug("Try to get SCIM user information");
JSONObject userJson = scimClient.getUser(subject);
if (userJson != null) {
try {
JSONArray resources = userJson.getJSONArray("Resources");
JSONObject userResource = resources.getJSONObject(0);
String userName = userResource.getString("userName");
JSONObject names = userResource.getJSONObject("name");
UserDTO existingUser = ldapClient.getLdapUser(userName);
// there should always be an existing user in the LDAP tree
if (existingUser != null)
log.debug(existingUser.toString());
else {
throw new UnauthorizedException("no existing LDAP user");
}
JSONArray roles = userResource.getJSONArray("groups"); if (!credentials[0].equals(restUser) || !credentials[1].equals(restPassword)) {
for (int i = 0; i < roles.length(); i++) { log.error("Wrong credentials {} {}", credentials[0], credentials[1]);
JSONObject role = roles.getJSONObject(i); throw new UnauthorizedException();
String cn = role.getString("display");
GroupDTO group = ldapClient.getLdapGroup(cn);
if (group != null) {
// check/add user
if (group.getMemberUids() != null && !group.getMemberUids().contains(userName))
ldapClient.addGroupMember(cn, userName);
} else {
// create new group and add user
ldapClient.createGroup(cn, ldapClient.generateGroupId());
ldapClient.addGroupMember(cn, userName);
}
}
} catch (JSONException e) {
// no additional user information
log.error(e.getMessage());
}
} }
} }
......
/* Copyright 2016 Karlsruhe Institute of Technology (KIT)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
*/
package edu.kit.scc.dto;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UserInfo {
private List<String> userIdentities;
private List<String> userGroups;
public List<String> getUserIdentities() {
return userIdentities;
}
public UserInfo setUserIdentities(List<String> userIdentities) {
this.userIdentities = userIdentities;
return this;
}
public List<String> getUserGroups() {
return userGroups;
}
public UserInfo setUserGroups(List<String> userGroups) {
this.userGroups = userGroups;
return this;
}
@Override
public String toString() {
return "UserInfo [" + (userIdentities != null ? "userIdentities=" + userIdentities + ", " : "")
+ (userGroups != null ? "userGroups=" + userGroups : "") + "]";
}
}
...@@ -11,6 +11,7 @@ package edu.kit.scc.oidc; ...@@ -11,6 +11,7 @@ package edu.kit.scc.oidc;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.Map.Entry;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
...@@ -80,6 +81,7 @@ public class OidcClient { ...@@ -80,6 +81,7 @@ public class OidcClient {
* the OAuth2 access token * the OAuth2 access token
* @return a {@link JSONObject} with the OIDC user information * @return a {@link JSONObject} with the OIDC user information
*/ */
@SuppressWarnings("static-access")
public JSONObject requestUserInfo(String accessToken) { public JSONObject requestUserInfo(String accessToken) {
JSONObject userInfoResponse = null; JSONObject userInfoResponse = null;
...@@ -135,6 +137,7 @@ public class OidcClient { ...@@ -135,6 +137,7 @@ public class OidcClient {
* the OAuth2 authorization code * the OAuth2 authorization code
* @return a {@link Tokens} bundle with all OIDC tokens * @return a {@link Tokens} bundle with all OIDC tokens
*/ */
@SuppressWarnings("static-access")
public OIDCTokens requestTokens(String authorizationCode) { public OIDCTokens requestTokens(String authorizationCode) {
AuthorizationCode code = new AuthorizationCode(authorizationCode); AuthorizationCode code = new AuthorizationCode(authorizationCode);
...@@ -150,22 +153,44 @@ public class OidcClient { ...@@ -150,22 +153,44 @@ public class OidcClient {
ClientAuthentication clientAuthentication = new ClientSecretBasic(clientID, clientSecret); ClientAuthentication clientAuthentication = new ClientSecretBasic(clientID, clientSecret);
AuthorizationGrant codeGrant = new AuthorizationCodeGrant(code, redirectUri); AuthorizationGrant codeGrant = new AuthorizationCodeGrant(code, redirectUri);
// codeGrant.toParameters().put("client_id", this.clientId);
// codeGrant.toParameters().put("client_secret", this.clientSecret);
// for (Entry<String, String> entry :
// codeGrant.toParameters().entrySet())
// log.debug("{} {}", entry.getKey(), entry.getValue());
TokenRequest request = new TokenRequest(tokenEndpoint, clientAuthentication, codeGrant); TokenRequest request = new TokenRequest(tokenEndpoint, clientAuthentication, codeGrant);
HTTPResponse httpResponse = null; HTTPResponse httpResponse = null;
HTTPRequest httpRequest = request.toHTTPRequest(); HTTPRequest httpRequest = request.toHTTPRequest();
// httpRequest.setAccept("*/*");
httpRequest.setDefaultHostnameVerifier(new NullHostNameVerifier()); httpRequest.setDefaultHostnameVerifier(new NullHostNameVerifier());
httpRequest.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); httpRequest.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
log.debug("------HTTP REQUEST DEBUG------");
for (Entry<String, String> e : httpRequest.getHeaders().entrySet())
log.debug("{} {}", e.getKey(), e.getValue());
log.debug("Method {}", httpRequest.getMethod());
log.debug("Query {}", httpRequest.getQuery());
log.debug("Url {}", httpRequest.getURL());
log.debug("------HTTP REQUEST DEBUG------");
httpResponse = httpRequest.send(); httpResponse = httpRequest.send();
TokenResponse response = null; TokenResponse response = null;
response = OIDCTokenResponseParser.parse(httpResponse); response = OIDCTokenResponseParser.parse(httpResponse);
if (response instanceof TokenErrorResponse) { if (response instanceof TokenErrorResponse) {
TokenErrorResponse tokenErrorResponse = (TokenErrorResponse) response;
log.warn("ERROR {}", tokenErrorResponse.toJSONObject().toJSONString());
ErrorObject error = ((TokenErrorResponse) response).getErrorObject(); ErrorObject error = ((TokenErrorResponse) response).getErrorObject();
log.debug("ERROR " + error.getDescription()); log.warn("ERROR HTTP {} code {}", error.getHTTPStatusCode(), error.getCode());
log.warn("ERROR " + error.getDescription());
return null; return null;
} }
......
package edu.kit.scc.regapp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import edu.kit.scc.http.HttpClient;
import edu.kit.scc.http.HttpResponse;
@Component
public class RegAppClient {
private static final Logger log = LoggerFactory.getLogger(RegAppClient.class);
@Value("${regapp.serviceUsername}")
private String restUser;
@Value("${regapp.servicePassword}")
private String restPassword;
@Value("${regapp.serviceUrl}")
private String serviceUrl;
@Autowired
private HttpClient httpClient;
public boolean authenticate(String regId, String credentials) {
String regAppUrl = serviceUrl.replaceAll("/$", "");
regAppUrl += "/" + regId;
HttpResponse response = httpClient.makeHttpPostRequest(restUser, restPassword, credentials, regAppUrl);
if (response != null && response.statusCode == 200) {
log.debug("Reg-app authentication success");
return true;
}
return false;
}
}
...@@ -34,25 +34,20 @@ import javax.xml.transform.TransformerFactory; ...@@ -34,25 +34,20 @@ import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource; import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamResult;
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.auth.AuthScope;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.opensaml.DefaultBootstrap; import org.opensaml.DefaultBootstrap;
import org.opensaml.common.SAMLVersion; import org.opensaml.common.SAMLVersion;
import org.opensaml.common.impl.SecureRandomIdentifierGenerator; import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.core.Assertion; import org.opensaml.saml2.core.Assertion;
import org.opensaml.saml2.core.Attribute; import org.opensaml.saml2.core.Attribute;
import org.opensaml.saml2.core.AttributeQuery; import org.opensaml.saml2.core.AttributeQuery;
import org.opensaml.saml2.core.AttributeStatement; import org.opensaml.saml2.core.AttributeStatement;
import org.opensaml.saml2.core.AttributeValue; import org.opensaml.saml2.core.AttributeValue;
import org.opensaml.saml2.core.AuthnRequest;
import org.opensaml.saml2.core.Conditions; import org.opensaml.saml2.core.Conditions;
import org.opensaml.saml2.core.Issuer; import org.opensaml.saml2.core.Issuer;
import org.opensaml.saml2.core.NameID; import org.opensaml.saml2.core.NameID;
import org.opensaml.saml2.core.NameIDPolicy;
import org.opensaml.saml2.core.Response; import org.opensaml.saml2.core.Response;
import org.opensaml.saml2.core.Subject; import org.opensaml.saml2.core.Subject;
import org.opensaml.ws.soap.client.BasicSOAPMessageContext; import org.opensaml.ws.soap.client.BasicSOAPMessageContext;
...@@ -107,6 +102,7 @@ public class SamlClient { ...@@ -107,6 +102,7 @@ public class SamlClient {
} }
} }
@SuppressWarnings("unused")
private String printXml(Element element) { private String printXml(Element element) {
String returnString = ""; String returnString = "";
TransformerFactory tfactory = TransformerFactory.newInstance(); TransformerFactory tfactory = TransformerFactory.newInstance();
......
/* Copyright 2016 Karlsruhe Institute of Technology (KIT)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
*/
package edu.kit.scc.scim;
import java.util.HashMap;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ScimUser {
public static class Name {
private String formatted, familyName, givenName, middleName, honorificPrefix, honorificSufix;
public String getFormatted() {
return formatted;
}
public void setFormatted(String formatted) {
this.formatted = formatted;
}
public String getFamilyName() {
return familyName;
}
public void setFamilyName(String familyName) {
this.familyName = familyName;
}
public String getGivenName() {
return givenName;
}