Commit 9c6d53d2 authored by benjamin.ertl's avatar benjamin.ertl
Browse files

oidc + scim user info update

parent 3bceb177
......@@ -20,16 +20,18 @@ 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.claims.UserInfo;
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;
import edu.kit.scc.scim.ScimUserAttributeMapper;
@Component
public class Harmonizer {
public class IdentityHarmonizer {
private static final Logger log = LoggerFactory.getLogger(Harmonizer.class);
private static final Logger log = LoggerFactory.getLogger(IdentityHarmonizer.class);
@Autowired
private ScimClient scimClient;
......@@ -39,12 +41,11 @@ public class Harmonizer {
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;
UserInfo userInfo = null;
if (tokens != null) {
try {
JWT jwt = tokens.getIDToken();
......@@ -54,9 +55,16 @@ public class Harmonizer {
AccessToken accessToken = tokens.getAccessToken();
userInfo = oidcClient.requestUserInfo(accessToken.getValue());
userInfo = oidcClient.requestUserInfo(accessToken.getValue(), claimsSet);
log.debug("User info {}", userInfo.toString());
if (userInfo != null) {
log.debug("User info {}", userInfo.toJSONObject().toJSONString());
ScimUserAttributeMapper attributeMapper = new ScimUserAttributeMapper();
scimUser = attributeMapper.mapFromUserInfo(userInfo);
scimUser.setSchemas(Arrays.asList(scimUser.USER_SCHEMA));
}
} catch (ParseException e) {
log.error(e.getMessage());
......@@ -65,12 +73,19 @@ public class Harmonizer {
// SCIM
log.debug("Try to get SCIM user information");
JSONObject userJson = scimClient.getUser(subject);
log.debug("SCIM user info {}", userJson.toString());
JSONObject userJson = scimClient.getUser(scimUser.getUserName());
if (userJson != null) {
log.debug("SCIM user info {}", userJson.toString());
// TODO merge with SCIM user
}
// LDAP
// TODO
// REGAPP
// TODO
log.debug("Aggregated SCIM user information {}", scimUser.toString());
return scimUser;
}
......
......@@ -50,7 +50,7 @@ public class RestServiceController {
private OidcClient oidcClient;
@Autowired
private Harmonizer identityHarmonizer;
private IdentityHarmonizer identityHarmonizer;
// expected body e.g.
// password=password
......
......@@ -185,24 +185,24 @@ public class HttpClient {
} catch (IOException e) {
// e.printStackTrace();
log.error(e.getMessage());
log.error("ERROR {}", e.getMessage());
} catch (Exception e) {
// e.printStackTrace();
log.error(e.getMessage());
log.error("ERROR {}", e.getMessage());
} finally {
if (in != null)
try {
in.close();
} catch (IOException e) {
// e.printStackTrace();
log.error(e.getMessage());
log.error("ERROR {}", e.getMessage());
}
if (out != null)
try {
out.close();
} catch (IOException e) {
// e.printStackTrace();
log.error(e.getMessage());
log.error("ERROR {}", e.getMessage());
}
}
return response;
......
......@@ -18,7 +18,7 @@ import org.springframework.ldap.core.AttributesMapper;
import edu.kit.scc.dto.GroupDTO;
public class GroupAttributeMapper implements AttributesMapper<GroupDTO> {
public class LdapGroupAttributeMapper implements AttributesMapper<GroupDTO> {
@Override
public GroupDTO mapFromAttributes(Attributes attributes) throws NamingException {
......
......@@ -45,7 +45,7 @@ public class LdapGroupDAO implements GroupDAO {
@Override
public List<GroupDTO> getAllGroups() {
return ldapTemplate.search(groupBase, "(objectclass=posixGroup)", new GroupAttributeMapper());
return ldapTemplate.search(groupBase, "(objectclass=posixGroup)", new LdapGroupAttributeMapper());
}
......@@ -55,7 +55,7 @@ public class LdapGroupDAO implements GroupDAO {
andFilter.and(new EqualsFilter("objectclass", "posixGroup")).and(new EqualsFilter("cn", commonName));
log.debug("LDAP query {}", andFilter.encode());
return ldapTemplate.search("", andFilter.encode(), new GroupAttributeMapper());
return ldapTemplate.search("", andFilter.encode(), new LdapGroupAttributeMapper());
}
@Override
......
......@@ -16,7 +16,7 @@ import org.springframework.ldap.core.AttributesMapper;
import edu.kit.scc.dto.UserDTO;
public class UserAttributeMapper implements AttributesMapper<UserDTO> {
public class LdapUserAttributeMapper implements AttributesMapper<UserDTO> {
@Override
public UserDTO mapFromAttributes(Attributes attributes) throws NamingException {
......
......@@ -44,7 +44,7 @@ public class LdapUserDAO implements UserDAO {
@Override
public List<UserDTO> getAllUsers() {
return ldapTemplate.search(userBase, "(objectclass=posixAccount)", new UserAttributeMapper());
return ldapTemplate.search(userBase, "(objectclass=posixAccount)", new LdapUserAttributeMapper());
}
@Override
......@@ -53,7 +53,7 @@ public class LdapUserDAO implements UserDAO {
andFilter.and(new EqualsFilter("objectclass", "posixAccount")).and(new EqualsFilter("uid", uid));
log.debug("LDAP query {}", andFilter.encode());
return ldapTemplate.search("", andFilter.encode(), new UserAttributeMapper());
return ldapTemplate.search("", andFilter.encode(), new LdapUserAttributeMapper());
}
@Override
......
......@@ -21,6 +21,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.oauth2.sdk.AuthorizationCode;
import com.nimbusds.oauth2.sdk.AuthorizationCodeGrant;
import com.nimbusds.oauth2.sdk.AuthorizationGrant;
......@@ -40,7 +41,11 @@ import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
import com.nimbusds.oauth2.sdk.token.Tokens;
import com.nimbusds.openid.connect.sdk.OIDCTokenResponse;
import com.nimbusds.openid.connect.sdk.OIDCTokenResponseParser;
import com.nimbusds.openid.connect.sdk.UserInfoErrorResponse;
import com.nimbusds.openid.connect.sdk.UserInfoRequest;
import com.nimbusds.openid.connect.sdk.UserInfoResponse;
import com.nimbusds.openid.connect.sdk.UserInfoSuccessResponse;
import com.nimbusds.openid.connect.sdk.claims.UserInfo;
import com.nimbusds.openid.connect.sdk.token.OIDCTokens;
import edu.kit.scc.http.CustomSSLContext;
......@@ -82,9 +87,8 @@ public class OidcClient {
* @return a {@link JSONObject} with the OIDC user information
*/
@SuppressWarnings("static-access")
public JSONObject requestUserInfo(String accessToken) {
JSONObject userInfoResponse = null;
public UserInfo requestUserInfo(String accessToken, JWTClaimsSet claimsSet) {
UserInfo userInfo = null;
try {
AccessToken token = AccessToken.parse("Bearer " + accessToken);
......@@ -96,38 +100,47 @@ public class OidcClient {
httpRequest.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HTTPResponse response = null;
// DEBUG
logHttpRequest(httpRequest);
response = request.toHTTPRequest().send();
log.debug(response.getContentAsJSONObject().toJSONString());
return new JSONObject(response.getContentAsJSONObject().toJSONString());
// userInfoResponse = UserInfoResponse.parse(response);
//
// if (userInfoResponse instanceof UserInfoErrorResponse) {
// ErrorObject error = ((UserInfoErrorResponse)
// userInfoResponse).getErrorObject();
// System.out.println("ERROR " + error.getDescription());
// return null;
// }
//
// UserInfoSuccessResponse successResponse =
// (UserInfoSuccessResponse) userInfoResponse;
// String claims =
// successResponse.getUserInfo().toJSONObject().toJSONString();
//
// System.out.println(claims);
//
// return successResponse;
// DEBUG
logHttpResponse(response);
net.minidev.json.JSONObject jsonResponse = response.getContentAsJSONObject();
jsonResponse.put("sub", claimsSet.getSubject());
response.setContent(jsonResponse.toJSONString());
UserInfoResponse userInfoResponse = UserInfoResponse.parse(response);
if (userInfoResponse instanceof UserInfoErrorResponse) {
UserInfoErrorResponse errorResponse = (UserInfoErrorResponse) userInfoResponse;
ErrorObject error = ((UserInfoErrorResponse) errorResponse).getErrorObject();
log.warn("ERROR HTTP {} code {}", error.getHTTPStatusCode(), error.getCode());
log.warn("ERROR " + error.getDescription());
return null;
}
UserInfoSuccessResponse successResponse = (UserInfoSuccessResponse) userInfoResponse;
userInfo = successResponse.getUserInfo();
log.debug(userInfo.toJSONObject().toJSONString());
return userInfo;
} catch (ParseException e) {
e.printStackTrace();
log.error("ERROR {}", e.getMessage());
} catch (URISyntaxException e) {
e.printStackTrace();
log.error("ERROR {}", e.getMessage());
} catch (IOException e) {
e.printStackTrace();
log.error("ERROR {}", e.getMessage());
}
return userInfoResponse;
return userInfo;
}
/**
......@@ -169,19 +182,17 @@ public class OidcClient {
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------");
// DEBUG
logHttpRequest(httpRequest);
httpResponse = httpRequest.send();
TokenResponse response = null;
response = OIDCTokenResponseParser.parse(httpResponse);
// DEBUG
logHttpResponse(httpResponse);
if (response instanceof TokenErrorResponse) {
TokenErrorResponse tokenErrorResponse = (TokenErrorResponse) response;
......@@ -198,15 +209,35 @@ public class OidcClient {
tokens = oidcTokenResponse.getOIDCTokens();
log.debug(oidcTokenResponse.getOIDCTokens().toJSONObject().toJSONString());
log.debug(tokens.toJSONObject().toJSONString());
return tokens;
} catch (ParseException e) {
e.printStackTrace();
log.error("ERROR {}", e.getMessage());
} catch (IOException e) {
e.printStackTrace();
log.error("ERROR {}", e.getMessage());
} catch (URISyntaxException e) {
e.printStackTrace();
log.error("ERROR {}", e.getMessage());
}
return tokens;
}
private void logHttpRequest(HTTPRequest httpRequest) {
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------");
}
private void logHttpResponse(HTTPResponse httpResponse) {
log.debug("------HTTP RESPONSE DEBUG------");
for (Entry<String, String> e : httpResponse.getHeaders().entrySet())
log.debug("{} {}", e.getKey(), e.getValue());
log.debug("Status code {}", httpResponse.getStatusCode());
log.debug("Content {}", httpResponse.getContent());
log.debug("------HTTP RESPONSE DEBUG------");
}
}
......@@ -52,7 +52,9 @@ public class ScimClient {
public JSONObject getUser(String name) {
JSONObject json = null;
HttpClient client = new HttpClient();
String url = userEndpoint + "?userNameEq" + name;
String url = userEndpoint.replaceAll("/$", "");
url += "?filter=userNameEq" + name;
HttpResponse response = client.makeHttpsGetRequest(user, password, url);
if (response != null) {
......@@ -72,7 +74,8 @@ public class ScimClient {
public JSONObject getUsers() {
JSONObject json = null;
HttpClient client = new HttpClient();
HttpResponse response = client.makeHttpsGetRequest(user, password, userEndpoint);
String url = userEndpoint.replaceAll("/$", "");
HttpResponse response = client.makeHttpsGetRequest(user, password, url);
if (response != null) {
log.debug(response.toString());
......@@ -91,7 +94,8 @@ public class ScimClient {
public JSONObject getGroups(String user, String password) {
JSONObject json = null;
HttpClient client = new HttpClient();
HttpResponse response = client.makeHttpsGetRequest(user, password, groupEndpoint);
String url = groupEndpoint.replaceAll("/$", "");
HttpResponse response = client.makeHttpsGetRequest(user, password, url);
if (response != null) {
log.debug(response.toString());
......
......@@ -17,6 +17,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ScimUser {
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class Name {
private String formatted, familyName, givenName, middleName, honorificPrefix, honorificSufix;
......@@ -79,6 +80,7 @@ public class ScimUser {
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class Email {
private String value, type;
private boolean primary;
......@@ -114,6 +116,7 @@ public class ScimUser {
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class Address {
private String type, streetAddress, locality, region, postalCode, country, formatted;
private boolean primary;
......@@ -194,6 +197,7 @@ public class ScimUser {
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class PhoneNumber {
private String value, type;
......@@ -220,6 +224,7 @@ public class ScimUser {
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class Ims {
private String value, type;
......@@ -246,6 +251,7 @@ public class ScimUser {
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class Photo {
private String value, type;
......@@ -272,6 +278,7 @@ public class ScimUser {
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class Group {
private String value, $ref, display;
......@@ -306,6 +313,7 @@ public class ScimUser {
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class x509Certificate {
private String value;
......@@ -323,6 +331,7 @@ public class ScimUser {
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class Meta extends HashMap<String, String> {
/**
......@@ -339,6 +348,7 @@ public class ScimUser {
private String id, externalId, userName, displayName, nickName, profileUrl, userType, title, preferredLanguage,
locale, timezone, password;
private boolean active;
private Name name;
private List<Email> emails;
private List<Address> addresses;
private List<PhoneNumber> phoneNumbers;
......@@ -347,6 +357,14 @@ public class ScimUser {
private List<Group> groups;
private Meta meta;
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
public List<String> getSchemas() {
return schemas;
}
......@@ -517,9 +535,8 @@ public class ScimUser {
@Override
public String toString() {
return "ScimUser [" + (USER_SCHEMA != null ? "USER_SCHEMA=" + USER_SCHEMA + ", " : "")
+ (schemas != null ? "schemas=" + schemas + ", " : "") + (id != null ? "id=" + id + ", " : "")
+ (externalId != null ? "externalId=" + externalId + ", " : "")
return "ScimUser [" + (schemas != null ? "schemas=" + schemas + ", " : "")
+ (id != null ? "id=" + id + ", " : "") + (externalId != null ? "externalId=" + externalId + ", " : "")
+ (userName != null ? "userName=" + userName + ", " : "")
+ (displayName != null ? "displayName=" + displayName + ", " : "")
+ (nickName != null ? "nickName=" + nickName + ", " : "")
......@@ -530,7 +547,7 @@ public class ScimUser {
+ (locale != null ? "locale=" + locale + ", " : "")
+ (timezone != null ? "timezone=" + timezone + ", " : "")
+ (password != null ? "password=" + password + ", " : "") + "active=" + active + ", "
+ (emails != null ? "emails=" + emails + ", " : "")
+ (name != null ? "name=" + name + ", " : "") + (emails != null ? "emails=" + emails + ", " : "")
+ (addresses != null ? "addresses=" + addresses + ", " : "")
+ (phoneNumbers != null ? "phoneNumbers=" + phoneNumbers + ", " : "")
+ (ims != null ? "ims=" + ims + ", " : "") + (photos != null ? "photos=" + photos + ", " : "")
......
/* 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.net.URI;
import java.util.ArrayList;
import java.util.Date;
import javax.mail.internet.InternetAddress;
import com.nimbusds.oauth2.sdk.id.Subject;
import com.nimbusds.openid.connect.sdk.claims.UserInfo;
import edu.kit.scc.scim.ScimUser.Address;
import edu.kit.scc.scim.ScimUser.Email;
import edu.kit.scc.scim.ScimUser.Meta;
import edu.kit.scc.scim.ScimUser.Name;
import edu.kit.scc.scim.ScimUser.PhoneNumber;
import edu.kit.scc.scim.ScimUser.Photo;
public class ScimUserAttributeMapper {
public ScimUser mapFromUserInfo(UserInfo userInfo) {
ScimUser scimUser = new ScimUser();
scimUser.setName(new Name());
com.nimbusds.openid.connect.sdk.claims.Address address = userInfo.getAddress();
if (address != null) {
scimUser.setAddresses(new ArrayList<Address>());
Address scimAddress = new Address();
scimAddress.setCountry(address.getCountry());
scimAddress.setLocality(address.getLocality());
scimAddress.setPostalCode(address.getPostalCode());
scimAddress.setRegion(address.getRegion());
scimAddress.setFormatted(address.getFormatted());
scimAddress.setStreetAddress(address.getStreetAddress());
scimUser.getAddresses().add(scimAddress);
}
InternetAddress email = userInfo.getEmail();
if (email != null) {
scimUser.setEmails(new ArrayList<Email>());
Email scimEmail = new Email();
scimEmail.setValue(email.getAddress());
scimUser.getEmails().add(scimEmail);
}
String phoneNumber = userInfo.getPhoneNumber();
if (phoneNumber != null) {
scimUser.setPhoneNumbers(new ArrayList<PhoneNumber>());
PhoneNumber scimPhoneNumber = new PhoneNumber();
scimPhoneNumber.setValue(phoneNumber);
scimUser.getPhoneNumbers().add(scimPhoneNumber);
}
String familyName = userInfo.getFamilyName();
if (familyName != null)
scimUser.getName().setFamilyName(familyName);
String givenName = userInfo.getGivenName();
if (givenName != null)
scimUser.getName().setGivenName(givenName);
String locale = userInfo.getLocale();
if (locale != null)
scimUser.setLocale(locale);
String middleName = userInfo.getMiddleName();
if (middleName != null)
scimUser.getName().setMiddleName(middleName);
String userName = userInfo.getName();
if (userName != null)
scimUser.setUserName(userName);
String nickName = userInfo.getNickname();
if (nickName != null)
scimUser.setNickName(nickName);
URI picture = userInfo.getPicture();
if (picture != null) {
scimUser.setPhotos(new ArrayList<Photo>());
Photo photo = new Photo();
photo.setValue(picture.toString());
scimUser.getPhotos().add(photo);
}
String displayName = userInfo.getPreferredUsername();
if (displayName != null)
scimUser.setDisplayName(displayName);
URI profile = userInfo.getProfile();
if (profile != null)
scimUser.setProfileUrl(profile.toString());
Subject subject = userInfo.getSubject();
if (subject != null)
scimUser.setExternalId(subject.getValue());
Date updateTime = userInfo.getUpdatedTime();
if (updateTime != null) {
if (scimUser.getMeta() == null)
scimUser.setMeta(new Meta());
scimUser.getMeta().put("lastModified", updateTime.toString());
}
URI website = userInfo.getWebsite();
if (website != null) {
if (scimUser.getMeta() == null)