Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
benjamin.ertl
aai-identity-harmonization
Commits
3bceb177
Commit
3bceb177
authored
Jan 28, 2016
by
benjamin.ertl
Browse files
refactoring
parent
21a12365
Changes
7
Show whitespace changes
Inline
Side-by-side
src/main/java/edu/kit/scc/Harmonizer.java
0 → 100644
View file @
3bceb177
/* 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
;
}
}
src/main/java/edu/kit/scc/RestServiceController.java
View file @
3bceb177
...
...
@@ -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
Htt
pClient
htt
pClient
;
private
RegAp
pClient
regAp
pClient
;
@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
;
if
(!
regAppSuccess
)
{
log
.
debug
(
"Try OIDC authentication"
);
try
{
String
token
=
body
.
split
(
"="
)[
1
];
// oidcJson = oidcClient.requestUserInfo(token);
log
.
debug
(
"Got token {}"
,
token
);
token
=
URLDecoder
.
decode
(
token
,
"UTF-8"
);
tokens
=
oidcClient
.
requestTokens
(
token
);
}
catch
(
ArrayIndexOutOfBoundsException
e
)
{
log
.
error
(
e
.
getMessage
());
throw
new
UnauthorizedException
();
}
if
(
tokens
!=
null
)
{
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
)
{
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
();
}
}
...
...
src/main/java/edu/kit/scc/dto/UserInfo.java
0 → 100644
View file @
3bceb177
/* 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
:
""
)
+
"]"
;
}
}
src/main/java/edu/kit/scc/oidc/OidcClient.java
View file @
3bceb177
...
...
@@ -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
;
}
...
...
src/main/java/edu/kit/scc/regapp/RegAppClient.java
0 → 100644
View file @
3bceb177
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
;
}
}
src/main/java/edu/kit/scc/saml/SamlClient.java
View file @
3bceb177
...
...
@@ -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
();
...
...
src/main/java/edu/kit/scc/scim/ScimUser.java
0 → 100644
View file @
3bceb177
/* 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
;
}
public
String
getLocality
()
{
return
locality
;
}