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

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 @@
*/
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.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -27,18 +25,11 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
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 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.scim.ScimClient;
import edu.kit.scc.regapp.RegAppClient;
import edu.kit.scc.scim.ScimUser;
@RestController
@RequestMapping("/rest")
......@@ -52,20 +43,14 @@ public class RestServiceController {
@Value("${regapp.servicePassword}")
private String restPassword;
@Value("${regapp.serviceUrl}")
private String serviceUrl;
@Autowired
private HttpClient httpClient;
private RegAppClient regAppClient;
@Autowired
private OidcClient oidcClient;
@Autowired
private ScimClient scimClient;
@Autowired
private LdapClient ldapClient;
private Harmonizer identityHarmonizer;
// expected body e.g.
// password=password
......@@ -73,108 +58,58 @@ public class RestServiceController {
// password=3D49806e48a5cd2941604eb9dfe321c3bc
@RequestMapping(path = "/ecp/regid/{regId}", method = RequestMethod.POST)
public void ecpAuthentication(@PathVariable String regId, @RequestHeader("Authorization") String basicAuthorization,
@RequestBody String body) {
String encodedCredentials = basicAuthorization.split(" ")[1];
String[] credentials = new String(Base64.decodeBase64(encodedCredentials)).split(":");
public ScimUser ecpAuthentication(@PathVariable String regId,
@RequestHeader("Authorization") String basicAuthorization, @RequestBody String body) {
if (!credentials[0].equals(restUser) || !credentials[1].equals(restPassword)) {
log.error("Wrong credentials {} {}", credentials[0], credentials[1]);
throw new UnauthorizedException();
}
verifyAuthorization(basicAuthorization);
log.debug("Request body {}", body);
// REG-APP
log.debug("Try reg-app authentication");
String regAppUrl = serviceUrl.replaceAll("/$", "");
regAppUrl += "/" + regId;
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;
}
boolean regAppSuccess = regAppClient.authenticate(regId, body);
log.debug("Reg-app success {}", regAppSuccess);
// OIDC
log.debug("Try OIDC authentication");
boolean oidcSuccess = false;
OIDCTokens tokens = null;
try {
String token = body.split("=")[1];
// oidcJson = oidcClient.requestUserInfo(token);
tokens = oidcClient.requestTokens(token);
} catch (ArrayIndexOutOfBoundsException e) {
log.error(e.getMessage());
throw new UnauthorizedException();
}
if (tokens != null) {
if (!regAppSuccess) {
log.debug("Try OIDC authentication");
try {
JWT jwt = tokens.getIDToken();
JWTClaimsSet claimsSet = jwt.getJWTClaimsSet();
log.debug(claimsSet.toJSONObject().toJSONString());
AccessToken accessToken = tokens.getAccessToken();
oidcClient.requestUserInfo(accessToken.getValue());
String subject = claimsSet.getSubject();
log.debug("OIDC authentication success");
// TODO harmonize
harmonizeIdentities(subject);
return;
} catch (ParseException e) {
String token = body.split("=")[1];
log.debug("Got token {}", token);
token = URLDecoder.decode(token, "UTF-8");
tokens = oidcClient.requestTokens(token);
if (tokens != null) {
log.debug("OIDC authentication success");
oidcSuccess = true;
}
} catch (ArrayIndexOutOfBoundsException e) {
log.error(e.getMessage());
throw new UnauthorizedException();
} catch (UnsupportedEncodingException e) {
log.error(e.getMessage());
throw new UnauthorizedException();
}
}
log.debug("OIDC success {}", oidcSuccess);
if (regAppSuccess || oidcSuccess) {
return identityHarmonizer.harmonizeIdentities(regId, tokens);
}
// if nothing succeeded, fail ... gracefully
throw new UnauthorizedException();
}
private void harmonizeIdentities(String subject) {
// SCIM
// we are looking for groups in the SCIM response
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");
}
private void verifyAuthorization(String basicAuthorization) {
String encodedCredentials = basicAuthorization.split(" ")[1];
String[] credentials = new String(Base64.decodeBase64(encodedCredentials)).split(":");
JSONArray roles = userResource.getJSONArray("groups");
for (int i = 0; i < roles.length(); i++) {
JSONObject role = roles.getJSONObject(i);
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());
}
if (!credentials[0].equals(restUser) || !credentials[1].equals(restPassword)) {
log.error("Wrong credentials {} {}", credentials[0], credentials[1]);
throw new UnauthorizedException();
}
}
......
/* 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;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map.Entry;
import javax.net.ssl.SSLContext;
......@@ -80,6 +81,7 @@ public class OidcClient {
* the OAuth2 access token
* @return a {@link JSONObject} with the OIDC user information
*/
@SuppressWarnings("static-access")
public JSONObject requestUserInfo(String accessToken) {
JSONObject userInfoResponse = null;
......@@ -135,6 +137,7 @@ public class OidcClient {
* the OAuth2 authorization code
* @return a {@link Tokens} bundle with all OIDC tokens
*/
@SuppressWarnings("static-access")
public OIDCTokens requestTokens(String authorizationCode) {
AuthorizationCode code = new AuthorizationCode(authorizationCode);
......@@ -150,22 +153,44 @@ public class OidcClient {
ClientAuthentication clientAuthentication = new ClientSecretBasic(clientID, clientSecret);
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);
HTTPResponse httpResponse = null;
HTTPRequest httpRequest = request.toHTTPRequest();
// httpRequest.setAccept("*/*");
httpRequest.setDefaultHostnameVerifier(new NullHostNameVerifier());
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();
TokenResponse response = null;
response = OIDCTokenResponseParser.parse(httpResponse);
if (response instanceof TokenErrorResponse) {
TokenErrorResponse tokenErrorResponse = (TokenErrorResponse) response;
log.warn("ERROR {}", tokenErrorResponse.toJSONObject().toJSONString());
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;
}
......
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;
import javax.xml.transform.dom.DOMSource;
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.auth.AuthScope;
import org.joda.time.DateTime;
import org.opensaml.DefaultBootstrap;
import org.opensaml.common.SAMLVersion;
import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.core.Assertion;
import org.opensaml.saml2.core.Attribute;
import org.opensaml.saml2.core.AttributeQuery;
import org.opensaml.saml2.core.AttributeStatement;
import org.opensaml.saml2.core.AttributeValue;
import org.opensaml.saml2.core.AuthnRequest;
import org.opensaml.saml2.core.Conditions;
import org.opensaml.saml2.core.Issuer;
import org.opensaml.saml2.core.NameID;
import org.opensaml.saml2.core.NameIDPolicy;
import org.opensaml.saml2.core.Response;
import org.opensaml.saml2.core.Subject;
import org.opensaml.ws.soap.client.BasicSOAPMessageContext;
......@@ -107,6 +102,7 @@ public class SamlClient {
}
}
@SuppressWarnings("unused")
private String printXml(Element element) {
String returnString = "";
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;
}
public void setGivenName(String givenName) {
this.givenName = givenName;
}
public String getMiddleName() {
return middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getHonorificPrefix() {
return honorificPrefix;
}
public void setHonorificPrefix(String honorificPrefix) {
this.honorificPrefix = honorificPrefix;
}
public String getHonorificSufix() {
return honorificSufix;
}
public void setHonorificSufix(String honorificSufix) {
this.honorificSufix = honorificSufix;
}
@Override
public String toString() {
return "Name [" + (formatted != null ? "formatted=" + formatted + ", " : "")
+ (familyName != null ? "familyName=" + familyName + ", " : "")
+ (givenName != null ? "givenName=" + givenName + ", " : "")
+ (middleName != null ? "middleName=" + middleName + ", " : "")
+ (honorificPrefix != null ? "honorificPrefix=" + honorificPrefix + ", " : "")
+ (honorificSufix != null ? "honorificSufix=" + honorificSufix : "") + "]";
}
}
public static class Email {
private String value, type;
private boolean primary;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isPrimary() {
return primary;
}
public void setPrimary(boolean primary) {
this.primary = primary;
}
@Override
public String toString() {
return "Email [" + (value != null ? "value=" + value + ", " : "")
+ (type != null ? "type=" + type + ", " : "") + "primary=" + primary + "]";
}
}
public static class Address {
private String type, streetAddress, locality, region, postalCode, country, formatted;
private boolean primary;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getStreetAddress() {
return streetAddress;
}
public void setStreetAddress(String streetAddress) {
this.streetAddress = streetAddress;