From 4e10fce7efaaeea4725f635d2b5da9e1ea22ea96 Mon Sep 17 00:00:00 2001 From: Amanda Anganes Date: Wed, 27 Jun 2012 13:24:35 -0400 Subject: [PATCH] Implementing user approval handler; made some modifications to ApprovedSite and WhitelistedSite models, repositories, and service layers. --- .../openid/connect/model/ApprovedSite.java | 34 ++++++-- .../openid/connect/model/WhitelistedSite.java | 38 ++++----- .../repository/WhitelistedSiteRepository.java | 18 ++++ .../connect/service/UserInfoService.java | 3 +- .../service/WhitelistedSiteService.java | 27 ++++++ .../db/tables/approvedsite.sql | 3 +- .../impl/JpaWhitelistedSiteRepository.java | 20 +++++ .../service/impl/UserInfoServiceImpl.java | 3 +- .../impl/WhitelistedSiteServiceImpl.java | 19 +++++ .../token/JdbcUserApprovalHandler.java | 84 ++++++++++++++++++- .../openid/connect/web/UserInfoEndpoint.java | 2 - 11 files changed, 214 insertions(+), 37 deletions(-) diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ApprovedSite.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ApprovedSite.java index 7aab88860..35fd75f6f 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ApprovedSite.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ApprovedSite.java @@ -47,7 +47,7 @@ public class ApprovedSite { private Long id; // which user made the approval - private String userInfo; + private UserInfo userInfo; // which OAuth2 client is this tied to private ClientDetailsEntity clientDetails; @@ -65,6 +65,9 @@ public class ApprovedSite { // this should include all information for what data to access private Set allowedScopes; + // If this AP is a WS, link to the WS + private WhitelistedSite whitelistedSite; + // TODO: should we store the OAuth2 tokens and IdTokens here? /** @@ -93,15 +96,16 @@ public class ApprovedSite { /** * @return the userInfo */ - @Basic - public String getUserInfo() { + @ManyToOne + @JoinColumn(name="userinfo_id") + public UserInfo getUserInfo() { return userInfo; } /** * @param userInfo the userInfo to set */ - public void setUserInfo(String userInfo) { + public void setUserInfo(UserInfo userInfo) { this.userInfo = userInfo; } @@ -183,7 +187,25 @@ public class ApprovedSite { public void setTimeoutDate(Date timeoutDate) { this.timeoutDate = timeoutDate; } - - + + /** + * Does this AP entry correspond to a WS? + * @return + */ + @Basic + public Boolean getIsWhitelisted() { + return (whitelistedSite != null); + } + + + @ManyToOne + @JoinColumn(name="whitelistedsite_id") + public WhitelistedSite getWhitelistedSite() { + return whitelistedSite; + } + + public void setWhitelistedSite(WhitelistedSite whitelistedSite) { + this.whitelistedSite = whitelistedSite; + } } diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/WhitelistedSite.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/WhitelistedSite.java index ce0b3564d..13af65712 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/WhitelistedSite.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/WhitelistedSite.java @@ -17,7 +17,6 @@ package org.mitre.openid.connect.model; import java.util.Set; -import javax.persistence.Basic; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.FetchType; @@ -35,22 +34,24 @@ import org.mitre.oauth2.model.ClientDetailsEntity; /** * Indicator that login to a site should be automatically granted * without user interaction. - * @author jricher + * @author jricher, aanganes * */ @Entity @Table(name="whitelistedsite") @NamedQueries({ - @NamedQuery(name = "WhitelistedSite.getAll", query = "select w from WhitelistedSite w") + @NamedQuery(name = "WhitelistedSite.getAll", query = "select w from WhitelistedSite w"), + @NamedQuery(name = "WhitelistedSite.getByClientDetails", query = "select w from WhitelistedSite w where w.clientDetails = :clientDetails"), + @NamedQuery(name = "WhitelistedSite.getByUserInfo", query = "select w from WhitelistedSite w where w.creator = :userInfo") }) public class WhitelistedSite { // unique id private Long id; - // who added this site to the whitelist (should be an admin) - private String userInfo; - + // Reference to the admin user who created this entry + private UserInfo creator; + // which OAuth2 client is this tied to private ClientDetailsEntity clientDetails; @@ -81,21 +82,6 @@ public class WhitelistedSite { this.id = id; } - /** - * @return the userInfo - */ - @Basic - public String getUserInfo() { - return userInfo; - } - - /** - * @param userInfo the userInfo to set - */ - public void setUserInfo(String userInfo) { - this.userInfo = userInfo; - } - /** * @return the clientDetails */ @@ -126,4 +112,14 @@ public class WhitelistedSite { public void setAllowedScopes(Set allowedScopes) { this.allowedScopes = allowedScopes; } + + @ManyToOne + @JoinColumn(name="userinfo_id") + public UserInfo getCreator() { + return creator; + } + + public void setCreator(UserInfo creator) { + this.creator = creator; + } } diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/WhitelistedSiteRepository.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/WhitelistedSiteRepository.java index 908cd7795..aa5448ae7 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/WhitelistedSiteRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/WhitelistedSiteRepository.java @@ -17,7 +17,9 @@ package org.mitre.openid.connect.repository; import java.util.Collection; +import org.mitre.openid.connect.model.UserInfo; import org.mitre.openid.connect.model.WhitelistedSite; +import org.springframework.security.oauth2.provider.ClientDetails; /** * WhitelistedSite repository interface @@ -42,6 +44,22 @@ public interface WhitelistedSiteRepository { * @return a valid WhitelistedSite if it exists, null otherwise */ public WhitelistedSite getById(Long id); + + /** + * Find a WhitelistedSite by its associated ClientDetails reference + * + * @param client the Relying Party + * @return the corresponding WhitelistedSite if one exists for the RP, or null + */ + public WhitelistedSite getByClientDetails(ClientDetails client); + + /** + * Return a collection of the WhitelistedSites created by a given user + * + * @param creator the UserInfo representing an admin who may have made some WhitelistedSites + * @return the collection of corresponding WhitelistedSites, if any, or null + */ + public Collection getByCreator(UserInfo creator); /** * Removes the given IdToken from the repository diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/UserInfoService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/UserInfoService.java index ca8e766fe..365cfa9c0 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/UserInfoService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/UserInfoService.java @@ -15,7 +15,6 @@ ******************************************************************************/ package org.mitre.openid.connect.service; -import org.mitre.openid.connect.model.DefaultUserInfo; import org.mitre.openid.connect.model.UserInfo; /** @@ -32,7 +31,7 @@ public interface UserInfoService { * @param userInfo * the UserInfo to be saved */ - public void save(DefaultUserInfo userInfo); + public void save(UserInfo userInfo); /** * Get UserInfo for user id diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/WhitelistedSiteService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/WhitelistedSiteService.java index bb0d67a0d..bb4568ae8 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/WhitelistedSiteService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/WhitelistedSiteService.java @@ -15,7 +15,11 @@ ******************************************************************************/ package org.mitre.openid.connect.service; +import java.util.Collection; + +import org.mitre.openid.connect.model.UserInfo; import org.mitre.openid.connect.model.WhitelistedSite; +import org.springframework.security.oauth2.provider.ClientDetails; /** * Interface for WhitelistedSite service @@ -25,6 +29,13 @@ import org.mitre.openid.connect.model.WhitelistedSite; */ public interface WhitelistedSiteService { + /** + * Return a collection of all WhitelistedSite managed by this service + * + * @return the WhitelistedSite collection, or null + */ + public Collection getAll(); + /** * Returns the WhitelistedSite for the given id * @@ -34,6 +45,22 @@ public interface WhitelistedSiteService { */ public WhitelistedSite getById(Long id); + /** + * Find a WhitelistedSite by its associated ClientDetails reference + * + * @param client the Relying Party + * @return the corresponding WhitelistedSite if one exists for the RP, or null + */ + public WhitelistedSite getByClientDetails(ClientDetails client); + + /** + * Return a collection of the WhitelistedSites created by a given user + * + * @param creator the UserInfo representing an admin who may have made some WhitelistedSites + * @return the collection of corresponding WhitelistedSites, if any, or null + */ + public Collection getByCreator(UserInfo creator); + /** * Removes the given WhitelistedSite from the repository * diff --git a/openid-connect-server/db/tables/approvedsite.sql b/openid-connect-server/db/tables/approvedsite.sql index 7b7897371..32b7fa629 100644 --- a/openid-connect-server/db/tables/approvedsite.sql +++ b/openid-connect-server/db/tables/approvedsite.sql @@ -4,5 +4,6 @@ CREATE TABLE approvedsite ( clientdetails_id VARCHAR(256), creationDate DATE, accessDate DATE, - timeoutDate DATE + timeoutDate DATE, + whitelistedsite_id VARCHAR(256) ); \ No newline at end of file diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaWhitelistedSiteRepository.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaWhitelistedSiteRepository.java index 5880622b3..00a51f3e1 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaWhitelistedSiteRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaWhitelistedSiteRepository.java @@ -23,8 +23,10 @@ import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; +import org.mitre.openid.connect.model.UserInfo; import org.mitre.openid.connect.model.WhitelistedSite; import org.mitre.openid.connect.repository.WhitelistedSiteRepository; +import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -80,4 +82,22 @@ public class JpaWhitelistedSiteRepository implements WhitelistedSiteRepository { public WhitelistedSite save(WhitelistedSite whiteListedSite) { return saveOrUpdate(whiteListedSite.getId(), manager, whiteListedSite); } + + @Override + @Transactional + public WhitelistedSite getByClientDetails(ClientDetails client) { + TypedQuery query = manager.createNamedQuery("WhitelistedSite.getByClientDetails", WhitelistedSite.class); + query.setParameter("clientDetails", client); + + return query.getSingleResult(); + } + + @Override + @Transactional + public Collection getByCreator(UserInfo creator) { + TypedQuery query = manager.createNamedQuery("WhitelistedSite.getByUserInfo", WhitelistedSite.class); + query.setParameter("userInfo", creator); + + return query.getResultList(); + } } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/UserInfoServiceImpl.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/UserInfoServiceImpl.java index 6f7801af6..45c31fd60 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/UserInfoServiceImpl.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/UserInfoServiceImpl.java @@ -15,7 +15,6 @@ ******************************************************************************/ package org.mitre.openid.connect.service.impl; -import org.mitre.openid.connect.model.DefaultUserInfo; import org.mitre.openid.connect.model.UserInfo; import org.mitre.openid.connect.repository.UserInfoRepository; import org.mitre.openid.connect.service.UserInfoService; @@ -53,7 +52,7 @@ public class UserInfoServiceImpl implements UserInfoService { } @Override - public void save(DefaultUserInfo userInfo) { + public void save(UserInfo userInfo) { userInfoRepository.save(userInfo); } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/WhitelistedSiteServiceImpl.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/WhitelistedSiteServiceImpl.java index fb20cb05b..17a060ea5 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/WhitelistedSiteServiceImpl.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/WhitelistedSiteServiceImpl.java @@ -15,10 +15,14 @@ ******************************************************************************/ package org.mitre.openid.connect.service.impl; +import java.util.Collection; + +import org.mitre.openid.connect.model.UserInfo; import org.mitre.openid.connect.model.WhitelistedSite; import org.mitre.openid.connect.repository.WhitelistedSiteRepository; import org.mitre.openid.connect.service.WhitelistedSiteService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -71,4 +75,19 @@ public class WhitelistedSiteServiceImpl implements WhitelistedSiteService { return whitelistedSiteRepository.save(whitelistedSite); } + @Override + public Collection getAll() { + return whitelistedSiteRepository.getAll(); + } + + @Override + public WhitelistedSite getByClientDetails(ClientDetails client) { + return whitelistedSiteRepository.getByClientDetails(client); + } + + @Override + public Collection getByCreator(UserInfo creator) { + return whitelistedSiteRepository.getByCreator(creator); + } + } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/JdbcUserApprovalHandler.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/JdbcUserApprovalHandler.java index d3597dca1..c4f6cf656 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/JdbcUserApprovalHandler.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/JdbcUserApprovalHandler.java @@ -15,20 +15,98 @@ ******************************************************************************/ package org.mitre.openid.connect.token; +import java.util.Date; +import java.util.Collection; + +import org.mitre.openid.connect.model.ApprovedSite; +import org.mitre.openid.connect.model.UserInfo; +import org.mitre.openid.connect.model.WhitelistedSite; +import org.mitre.openid.connect.service.ApprovedSiteService; +import org.mitre.openid.connect.service.UserInfoService; +import org.mitre.openid.connect.service.WhitelistedSiteService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.provider.AuthorizationRequest; +import org.springframework.security.oauth2.provider.ClientDetails; +import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.approval.UserApprovalHandler; +/** + * Custom User Approval Handler implementation which uses a concept of a whitelist, + * blacklist, and greylist. + * + * Blacklisted sites will be caught and handled before this + * point. + * + * Whitelisted sites will be automatically approved, and an ApprovedSite entry will + * be created for the site the first time a given user access it. + * + * All other sites fall into the greylist - the user will be presented with the user + * approval page upon their first visit + * @author aanganes + * + */ public class JdbcUserApprovalHandler implements UserApprovalHandler { + @Autowired + UserInfoService userInfoService; + + @Autowired + ApprovedSiteService approvedSiteService; + + @Autowired + WhitelistedSiteService whitelistedSiteService; + + @Autowired + ClientDetailsService clientDetailsService; + + + /** + * Check if the user has already stored a positive approval decision for this site; or if the + * site is whitelisted, approve it automatically. + * + * Otherwise, return false so that the user will see the approval page and can make their own decision. + * + * @param authorizationRequest the incoming authorization request + * @param userAuthentication the Principal representing the currently-logged-in user + * + * @return true if the site is pre-approved, false otherwise + */ @Override - public boolean isApproved(AuthorizationRequest authorizationRequest, - Authentication userAuthentication) { + public boolean isApproved(AuthorizationRequest authorizationRequest, Authentication userAuthentication) { //Check database to see if the user identified by the userAuthentication has stored an approval decision - userAuthentication.getPrincipal(); + String userId = userAuthentication.getName(); + ClientDetails client = clientDetailsService.loadClientByClientId(authorizationRequest.getClientId()); + //lookup ApprovedSites by userId + UserInfo user = userInfoService.getByUserId(userId); + + Collection approvedSites = approvedSiteService.getByUserInfo(user); + + for (ApprovedSite ap : approvedSites) { + if (ap.getClientDetails().getClientId() == client.getClientId()) { + //TODO need to test more than just id + return true; + } + } + + WhitelistedSite ws = whitelistedSiteService.getByClientDetails(client); + if (ws != null) { + //Create an approved site + ApprovedSite newAP = new ApprovedSite(); + newAP.setAccessDate(new Date()); + newAP.setWhitelistedSite(ws); + newAP.setAllowedScopes(ws.getAllowedScopes()); + newAP.setCreationDate(new Date()); + newAP.setUserInfo(user); + //TODO set timeout date? + approvedSiteService.save(newAP); + + return true; + } + return false; } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java index 3c5f1ff4c..19ff70b90 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java @@ -18,8 +18,6 @@ package org.mitre.openid.connect.web; import java.security.Principal; import java.util.Set; -import org.mitre.oauth2.model.ClientDetailsEntity; -import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.oauth2.service.OAuth2TokenEntityService; import org.mitre.openid.connect.exception.UnknownUserInfoSchemaException; import org.mitre.openid.connect.model.DefaultUserInfo;