From 98e414b6df60a7780f43a96a8b05674bdd06e292 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 21 May 2015 21:56:21 -0400 Subject: [PATCH] broke out authentication holder class into parts, no more serializable pieces in the database, closes #696 --- .../model/AuthenticationHolderEntity.java | 255 +++++++++++++++++- .../oauth2/model/ClientDetailsEntity.java | 6 + .../oauth2/model/SavedUserAuthentication.java | 174 ++++++++++++ .../JWEAlgorithmStringConverter.java | 2 +- .../JWEEncryptionMethodStringConverter.java | 2 +- .../JWSAlgorithmStringConverter.java | 2 +- .../convert/SerializableStringConverter.java | 65 +++++ ...SimpleGrantedAuthorityStringConverter.java | 50 ++++ .../db/tables/hsql_database_tables.sql | 55 +++- 9 files changed, 599 insertions(+), 12 deletions(-) create mode 100644 openid-connect-common/src/main/java/org/mitre/oauth2/model/SavedUserAuthentication.java rename openid-connect-common/src/main/java/org/mitre/oauth2/model/{ => convert}/JWEAlgorithmStringConverter.java (97%) rename openid-connect-common/src/main/java/org/mitre/oauth2/model/{ => convert}/JWEEncryptionMethodStringConverter.java (97%) rename openid-connect-common/src/main/java/org/mitre/oauth2/model/{ => convert}/JWSAlgorithmStringConverter.java (97%) create mode 100644 openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/SerializableStringConverter.java create mode 100644 openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/SimpleGrantedAuthorityStringConverter.java diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java index 3d3151bd8..0c51da57c 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java @@ -16,19 +16,33 @@ *******************************************************************************/ package org.mitre.oauth2.model; +import java.io.Serializable; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + import javax.persistence.Basic; +import javax.persistence.CollectionTable; import javax.persistence.Column; +import javax.persistence.Convert; +import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; -import javax.persistence.Lob; +import javax.persistence.JoinColumn; +import javax.persistence.MapKeyColumn; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; +import javax.persistence.Transient; +import org.mitre.oauth2.model.convert.SerializableStringConverter; +import org.mitre.oauth2.model.convert.SimpleGrantedAuthorityStringConverter; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; @Entity @Table(name = "authentication_holder") @@ -46,8 +60,26 @@ public class AuthenticationHolderEntity { private Long id; - private OAuth2Authentication authentication; - + private SavedUserAuthentication userAuth; + + private Collection authorities; + + private Set resourceIds; + + private boolean approved; + + private String redirectUri; + + private Set responseTypes; + + private Map extensions; + + private String clientId; + + private Set scope; + + private Map requestParameters; + public AuthenticationHolderEntity() { } @@ -63,15 +95,222 @@ public class AuthenticationHolderEntity { this.id = id; } - @Lob - @Basic(fetch=FetchType.LAZY) - @Column(name = "authentication") + @Transient public OAuth2Authentication getAuthentication() { - return authentication; + // TODO: memoize this + return new OAuth2Authentication(createOAuth2Request(), getUserAuth()); + } + + /** + * @return + */ + private OAuth2Request createOAuth2Request() { + return new OAuth2Request(requestParameters, clientId, authorities, approved, scope, scope, redirectUri, responseTypes, extensions); } public void setAuthentication(OAuth2Authentication authentication) { - this.authentication = authentication; + + // pull apart the request and save its bits + OAuth2Request o2Request = authentication.getOAuth2Request(); + setAuthorities(o2Request.getAuthorities()); + setClientId(o2Request.getClientId()); + setExtensions(o2Request.getExtensions()); + setRedirectUri(o2Request.getRedirectUri()); + setRequestParameters(o2Request.getRequestParameters()); + setResourceIds(o2Request.getResourceIds()); + setResponseTypes(o2Request.getResponseTypes()); + setScope(o2Request.getScope()); + setApproved(o2Request.isApproved()); + + this.userAuth = new SavedUserAuthentication(authentication.getUserAuthentication()); + } + + /** + * @return the userAuth + */ + @Basic + @JoinColumn(name = "user_auth_id") + public SavedUserAuthentication getUserAuth() { + return userAuth; + } + + /** + * @param userAuth the userAuth to set + */ + public void setUserAuth(SavedUserAuthentication userAuth) { + this.userAuth = userAuth; + } + + /** + * @return the authorities + */ + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable( + name="authentication_holder_authority", + joinColumns=@JoinColumn(name="owner_id") + ) + @Convert(converter = SimpleGrantedAuthorityStringConverter.class) + @Column(name="authority") + public Collection getAuthorities() { + return authorities; + } + + /** + * @param authorities the authorities to set + */ + public void setAuthorities(Collection authorities) { + this.authorities = authorities; + } + + /** + * @return the resourceIds + */ + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable( + name="authentication_holder_resource_id", + joinColumns=@JoinColumn(name="owner_id") + ) + @Column(name="resource_id") + public Set getResourceIds() { + return resourceIds; + } + + /** + * @param resourceIds the resourceIds to set + */ + public void setResourceIds(Set resourceIds) { + this.resourceIds = resourceIds; + } + + /** + * @return the approved + */ + @Basic + @Column(name="approved") + public boolean isApproved() { + return approved; + } + + /** + * @param approved the approved to set + */ + public void setApproved(boolean approved) { + this.approved = approved; + } + + /** + * @return the redirectUri + */ + @Basic + @Column(name="redirect_uri") + public String getRedirectUri() { + return redirectUri; + } + + /** + * @param redirectUri the redirectUri to set + */ + public void setRedirectUri(String redirectUri) { + this.redirectUri = redirectUri; + } + + /** + * @return the responseTypes + */ + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable( + name="authentication_holder_response_type", + joinColumns=@JoinColumn(name="owner_id") + ) + @Column(name="response_type") + public Set getResponseTypes() { + return responseTypes; + } + + /** + * @param responseTypes the responseTypes to set + */ + public void setResponseTypes(Set responseTypes) { + this.responseTypes = responseTypes; + } + + /** + * @return the extensions + */ + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable( + name="authentication_holder_extension", + joinColumns=@JoinColumn(name="owner_id") + ) + @Column(name="val") + @MapKeyColumn(name="extension") + @Convert(converter=SerializableStringConverter.class) + public Map getExtensions() { + return extensions; + } + + /** + * @param extensions the extensions to set + */ + public void setExtensions(Map extensions) { + this.extensions = extensions; + } + + /** + * @return the clientId + */ + @Basic + @Column(name="client_id") + public String getClientId() { + return clientId; + } + + /** + * @param clientId the clientId to set + */ + public void setClientId(String clientId) { + this.clientId = clientId; + } + + /** + * @return the scope + */ + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable( + name="authentication_holder_scope", + joinColumns=@JoinColumn(name="owner_id") + ) + @Column(name="scope") + public Set getScope() { + return scope; + } + + /** + * @param scope the scope to set + */ + public void setScope(Set scope) { + this.scope = scope; + } + + /** + * @return the requestParameters + */ + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable( + name="authentication_holder_request_parameter", + joinColumns=@JoinColumn(name="owner_id") + ) + @Column(name="val") + @MapKeyColumn(name="param") + public Map getRequestParameters() { + return requestParameters; + } + + /** + * @param requestParameters the requestParameters to set + */ + public void setRequestParameters(Map requestParameters) { + this.requestParameters = requestParameters; } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java index f0a0e95ef..2a70139b3 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java @@ -47,7 +47,12 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; +import org.mitre.oauth2.model.convert.JWEAlgorithmStringConverter; +import org.mitre.oauth2.model.convert.JWEEncryptionMethodStringConverter; +import org.mitre.oauth2.model.convert.JWSAlgorithmStringConverter; +import org.mitre.oauth2.model.convert.SimpleGrantedAuthorityStringConverter; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.provider.ClientDetails; import com.nimbusds.jose.EncryptionMethod; @@ -461,6 +466,7 @@ public class ClientDetailsEntity implements ClientDetails { joinColumns=@JoinColumn(name="owner_id") ) @Override + @Convert(converter = SimpleGrantedAuthorityStringConverter.class) @Column(name="authority") public Set getAuthorities() { return authorities; diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/SavedUserAuthentication.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/SavedUserAuthentication.java new file mode 100644 index 000000000..91e98b2cc --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/SavedUserAuthentication.java @@ -0,0 +1,174 @@ +/******************************************************************************* + * Copyright 2015 The MITRE Corporation + * and the MIT Kerberos and Internet Trust Consortium + * + * 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ + +package org.mitre.oauth2.model; + +import java.util.Collection; + +import javax.persistence.Basic; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.Convert; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.Transient; + +import org.mitre.oauth2.model.convert.SimpleGrantedAuthorityStringConverter; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; + +/** + * This class stands in for an original Authentication object. + * + * @author jricher + * + */ +@Entity +public class SavedUserAuthentication implements Authentication { + + private static final long serialVersionUID = -1804249963940323488L; + + private Long id; + + private String name; + + private Collection authorities; + + private boolean authenticated; + + private String sourceClass; + + /** + * Create a Saved Auth from an existing Auth token + */ + public SavedUserAuthentication(Authentication src) { + setName(src.getName()); + setAuthorities(src.getAuthorities()); + setAuthenticated(src.isAuthenticated()); + setSourceClass(src.getClass().getName()); + } + + /** + * Create an empty saved auth + */ + public SavedUserAuthentication() { + + } + + /** + * @return the id + */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + public Long getId() { + return id; + } + + /** + * @param id the id to set + */ + public void setId(Long id) { + this.id = id; + } + + @Override + @Basic + @Column(name="name") + public String getName() { + return name; + } + + @Override + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable( + name="saved_user_auth_authority", + joinColumns=@JoinColumn(name="owner_id") + ) + @Convert(converter = SimpleGrantedAuthorityStringConverter.class) + @Column(name="authority") + public Collection getAuthorities() { + return authorities; + } + + @Override + @Transient + public Object getCredentials() { + return ""; + } + + @Override + @Transient + public Object getDetails() { + return null; + } + + @Override + @Transient + public Object getPrincipal() { + return getName(); + } + + @Override + @Basic + @Column(name="authenticated") + public boolean isAuthenticated() { + return authenticated; + } + + @Override + public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { + this.authenticated = isAuthenticated; + } + + /** + * @return the sourceClass + */ + @Basic + @Column(name="source_class") + public String getSourceClass() { + return sourceClass; + } + + /** + * @param sourceClass the sourceClass to set + */ + public void setSourceClass(String sourceClass) { + this.sourceClass = sourceClass; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @param authorities the authorities to set + */ + public void setAuthorities(Collection authorities) { + this.authorities = authorities; + } + + +} diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/JWEAlgorithmStringConverter.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/JWEAlgorithmStringConverter.java similarity index 97% rename from openid-connect-common/src/main/java/org/mitre/oauth2/model/JWEAlgorithmStringConverter.java rename to openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/JWEAlgorithmStringConverter.java index a56fe21c4..1711c839b 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/JWEAlgorithmStringConverter.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/JWEAlgorithmStringConverter.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ -package org.mitre.oauth2.model; +package org.mitre.oauth2.model.convert; import javax.persistence.AttributeConverter; import javax.persistence.Converter; diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/JWEEncryptionMethodStringConverter.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/JWEEncryptionMethodStringConverter.java similarity index 97% rename from openid-connect-common/src/main/java/org/mitre/oauth2/model/JWEEncryptionMethodStringConverter.java rename to openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/JWEEncryptionMethodStringConverter.java index 2cdd09d24..80eafecdf 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/JWEEncryptionMethodStringConverter.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/JWEEncryptionMethodStringConverter.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ -package org.mitre.oauth2.model; +package org.mitre.oauth2.model.convert; import javax.persistence.AttributeConverter; import javax.persistence.Converter; diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/JWSAlgorithmStringConverter.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/JWSAlgorithmStringConverter.java similarity index 97% rename from openid-connect-common/src/main/java/org/mitre/oauth2/model/JWSAlgorithmStringConverter.java rename to openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/JWSAlgorithmStringConverter.java index 51cc48e24..9d40edee6 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/JWSAlgorithmStringConverter.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/JWSAlgorithmStringConverter.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ -package org.mitre.oauth2.model; +package org.mitre.oauth2.model.convert; import javax.persistence.AttributeConverter; import javax.persistence.Converter; diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/SerializableStringConverter.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/SerializableStringConverter.java new file mode 100644 index 000000000..d62e54fb4 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/SerializableStringConverter.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright 2015 The MITRE Corporation + * and the MIT Kerberos and Internet Trust Consortium + * + * 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ + +package org.mitre.oauth2.model.convert; + +import java.io.Serializable; +import java.util.Date; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Translates a Serializable object of certain primitive types + * into a String for storage in the database, for use with the + * OAuth2Request extensions map. + * + * This class does allow some extension data to be lost. + * + * @author jricher + * + */ +@Converter +public class SerializableStringConverter implements AttributeConverter { + + private static Logger logger = LoggerFactory.getLogger(SerializableStringConverter.class); + + @Override + public String convertToDatabaseColumn(Serializable attribute) { + if (attribute == null) { + return null; + } else if (attribute instanceof String) { + return (String) attribute; + } else if (attribute instanceof Long) { + return attribute.toString(); + } else if (attribute instanceof Date) { + return Long.toString(((Date)attribute).getTime()); + } else { + logger.warn("Dropping data from request: " + attribute + " :: " + attribute.getClass()); + return null; + } + } + + @Override + public Serializable convertToEntityAttribute(String dbData) { + return dbData; + } + +} diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/SimpleGrantedAuthorityStringConverter.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/SimpleGrantedAuthorityStringConverter.java new file mode 100644 index 000000000..dec3e4b86 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/SimpleGrantedAuthorityStringConverter.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright 2015 The MITRE Corporation + * and the MIT Kerberos and Internet Trust Consortium + * + * 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ + +package org.mitre.oauth2.model.convert; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +/** + * @author jricher + * + */ +@Converter +public class SimpleGrantedAuthorityStringConverter implements AttributeConverter { + + @Override + public String convertToDatabaseColumn(SimpleGrantedAuthority attribute) { + if (attribute != null) { + return attribute.getAuthority(); + } else { + return null; + } + } + + @Override + public SimpleGrantedAuthority convertToEntityAttribute(String dbData) { + if (dbData != null) { + return new SimpleGrantedAuthority(dbData); + } else { + return null; + } + } + +} diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/hsql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/tables/hsql_database_tables.sql index eb40b2573..0fa147dd0 100644 --- a/openid-connect-server-webapp/src/main/resources/db/tables/hsql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/tables/hsql_database_tables.sql @@ -44,11 +44,64 @@ CREATE TABLE IF NOT EXISTS approved_site_scope ( scope VARCHAR(256) ); + + + CREATE TABLE IF NOT EXISTS authentication_holder ( id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, - authentication LONGVARBINARY + user_auth_id BIGINT, + approved BOOLEAN, + redirect_uri VARCHAR(2048), + client_id VARCHAR(256), + ); +CREATE TABLE IF NOT EXISTS authentication_holder_authority ( + owner_id BIGINT, + authority VARCHAR(256) +); + +CREATE TABLE IF NOT EXISTS authentication_holder_resource_id ( + owner_id BIGINT, + resource_id VARCHAR(2048) +); + +CREATE TABLE IF NOT EXISTS authentication_holder_response_type ( + owner_id BIGINT, + response_type VARCHAR(2048) +); + +CREATE TABLE IF NOT EXISTS authentication_holder_extension ( + owner_id BIGINT, + extension VARCHAR(2048), + val VARCHAR(2048) +); + +CREATE TABLE IF NOT EXISTS authentication_holder_scope ( + owner_id BIGINT, + scope VARCHAR(2048) +); + +CREATE TABLE IF NOT EXISTS authentication_holder_request_parameter ( + owner_id BIGINT, + param VARCHAR(2048), + val VARCHAR(2048) +); + +CREATE TABLE IF NOT EXISTS saved_user_auth ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, + name VARCHAR(1024), + authenticated BOOLEAN, + source_class VARCHAR(2048) +); + +CREATE TABLE IF NOT EXISTS saved_user_auth_authority ( + owner_id BIGINT, + authority VARCHAR(256) +); + + + CREATE TABLE IF NOT EXISTS client_authority ( owner_id BIGINT, authority VARCHAR(256)