From 4e10fce7efaaeea4725f635d2b5da9e1ea22ea96 Mon Sep 17 00:00:00 2001 From: Amanda Anganes Date: Wed, 27 Jun 2012 13:24:35 -0400 Subject: [PATCH 01/10] 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; From 676808bdac4ad7664e5ead985cda07734686cf83 Mon Sep 17 00:00:00 2001 From: Amanda Anganes Date: Wed, 27 Jun 2012 15:34:12 -0400 Subject: [PATCH 02/10] got things to deploy - could not reference UserInfo directly in ApprovedSite and WhitelistedSite; needed to reference DefaultUserInfo instead. --- .../org/mitre/openid/connect/model/ApprovedSite.java | 9 +++++---- .../org/mitre/openid/connect/model/WhitelistedSite.java | 6 +++--- .../src/main/resources/META-INF/persistence.xml | 1 + 3 files changed, 9 insertions(+), 7 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 35fd75f6f..324a187af 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 @@ -31,6 +31,7 @@ import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; import javax.persistence.Temporal; +import javax.persistence.Transient; import org.mitre.oauth2.model.ClientDetailsEntity; @@ -47,7 +48,7 @@ public class ApprovedSite { private Long id; // which user made the approval - private UserInfo userInfo; + private DefaultUserInfo userInfo; // which OAuth2 client is this tied to private ClientDetailsEntity clientDetails; @@ -98,14 +99,14 @@ public class ApprovedSite { */ @ManyToOne @JoinColumn(name="userinfo_id") - public UserInfo getUserInfo() { + public DefaultUserInfo getUserInfo() { return userInfo; } /** * @param userInfo the userInfo to set */ - public void setUserInfo(UserInfo userInfo) { + public void setUserInfo(DefaultUserInfo userInfo) { this.userInfo = userInfo; } @@ -192,7 +193,7 @@ public class ApprovedSite { * Does this AP entry correspond to a WS? * @return */ - @Basic + @Transient public Boolean getIsWhitelisted() { return (whitelistedSite != null); } 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 13af65712..021382451 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 @@ -50,7 +50,7 @@ public class WhitelistedSite { private Long id; // Reference to the admin user who created this entry - private UserInfo creator; + private DefaultUserInfo creator; // which OAuth2 client is this tied to private ClientDetailsEntity clientDetails; @@ -115,11 +115,11 @@ public class WhitelistedSite { @ManyToOne @JoinColumn(name="userinfo_id") - public UserInfo getCreator() { + public DefaultUserInfo getCreator() { return creator; } - public void setCreator(UserInfo creator) { + public void setCreator(DefaultUserInfo creator) { this.creator = creator; } } diff --git a/openid-connect-server/src/main/resources/META-INF/persistence.xml b/openid-connect-server/src/main/resources/META-INF/persistence.xml index 6d22a8773..b4f165fcb 100644 --- a/openid-connect-server/src/main/resources/META-INF/persistence.xml +++ b/openid-connect-server/src/main/resources/META-INF/persistence.xml @@ -14,6 +14,7 @@ org.mitre.openid.connect.model.IdToken org.mitre.openid.connect.model.IdTokenClaims org.mitre.openid.connect.model.DefaultUserInfo + org.mitre.openid.connect.model.WhitelistedSite NONE From a223565364802c968092071240430dc85fbecb67 Mon Sep 17 00:00:00 2001 From: Amanda Anganes Date: Fri, 29 Jun 2012 11:45:46 -0400 Subject: [PATCH 03/10] updating user approval handler --- .../connect/token/JdbcUserApprovalHandler.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) 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 c4f6cf656..ae9d142e6 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,11 +15,12 @@ ******************************************************************************/ package org.mitre.openid.connect.token; -import java.util.Date; import java.util.Collection; +import java.util.Date; +import java.util.Set; import org.mitre.openid.connect.model.ApprovedSite; -import org.mitre.openid.connect.model.UserInfo; +import org.mitre.openid.connect.model.DefaultUserInfo; import org.mitre.openid.connect.model.WhitelistedSite; import org.mitre.openid.connect.service.ApprovedSiteService; import org.mitre.openid.connect.service.UserInfoService; @@ -81,7 +82,7 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { ClientDetails client = clientDetailsService.loadClientByClientId(authorizationRequest.getClientId()); //lookup ApprovedSites by userId - UserInfo user = userInfoService.getByUserId(userId); + DefaultUserInfo user = (DefaultUserInfo)userInfoService.getByUserId(userId); Collection approvedSites = approvedSiteService.getByUserInfo(user); @@ -106,6 +107,14 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { return true; } + + if (authorizationRequest.isApproved() && !authorizationRequest.getApprovalParameters().isEmpty()) { + //Make a new AP + ApprovedSite newAP = new ApprovedSite(); + newAP.setAccessDate(new Date()); + //Set allowedScopes = authorizationRequest.getApprovalParameters().get("scope"); + //newAP.setAllowedScopes(allowedScopes); + } return false; } From db415bfa2b2ed39c954b0e10a5d8e1083c6efeeb Mon Sep 17 00:00:00 2001 From: Amanda Anganes Date: Tue, 31 Jul 2012 14:38:09 -0400 Subject: [PATCH 04/10] Working on user approval handler --- .../org/mitre/openid/connect/token/JdbcUserApprovalHandler.java | 1 - 1 file changed, 1 deletion(-) 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 ae9d142e6..717e8c9ac 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 @@ -17,7 +17,6 @@ package org.mitre.openid.connect.token; import java.util.Collection; import java.util.Date; -import java.util.Set; import org.mitre.openid.connect.model.ApprovedSite; import org.mitre.openid.connect.model.DefaultUserInfo; From 845976b8aca0675d320f812de9ee033314e3965a Mon Sep 17 00:00:00 2001 From: Amanda Anganes Date: Fri, 3 Aug 2012 12:49:40 -0400 Subject: [PATCH 05/10] First stages of getting the graylist portion to work. Currently no mechanism for telling the system NOT to remember your decision; that will come later. All approvals will be automatically stored with this code. --- .../openid/connect/model/ApprovedSite.java | 5 ++ .../db/tables/allowed_scopes.sql | 4 ++ .../impl/JpaApprovedSiteRepository.java | 2 +- .../impl/JpaWhitelistedSiteRepository.java | 4 +- .../token/JdbcUserApprovalHandler.java | 68 ++++++++++++++++--- .../webapp/WEB-INF/application-context.xml | 5 +- .../webapp/WEB-INF/views/oauth/approve.jsp | 20 +++--- 7 files changed, 83 insertions(+), 25 deletions(-) create mode 100644 openid-connect-server/db/tables/allowed_scopes.sql 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 324a187af..796cdb1f7 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 @@ -19,6 +19,7 @@ import java.util.Date; import java.util.Set; import javax.persistence.Basic; +import javax.persistence.CollectionTable; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.FetchType; @@ -162,6 +163,10 @@ public class ApprovedSite { * @return the allowedScopes */ @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable( + name="allowed_scopes", + joinColumns=@JoinColumn(name="owner_id") + ) public Set getAllowedScopes() { return allowedScopes; } diff --git a/openid-connect-server/db/tables/allowed_scopes.sql b/openid-connect-server/db/tables/allowed_scopes.sql new file mode 100644 index 000000000..cdb952c32 --- /dev/null +++ b/openid-connect-server/db/tables/allowed_scopes.sql @@ -0,0 +1,4 @@ +create table allowed_scopes( + owner_id VARCHAR(256), + allowedScopes VARCHAR(256) +); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java index 428a56ec8..b0959d851 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java @@ -76,7 +76,7 @@ public class JpaApprovedSiteRepository implements ApprovedSiteRepository { public Collection getByUserInfo(UserInfo userInfo) { TypedQuery query = manager.createNamedQuery( "ApprovedSite.getByUserInfo", ApprovedSite.class); - query.setParameter("approvedSiteUserInfo", userInfo.getUserId()); + query.setParameter("approvedSiteUserInfo", userInfo); List found = query.getResultList(); 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 00a51f3e1..22587d1f0 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 @@ -26,6 +26,7 @@ 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.mitre.util.jpa.JpaUtil; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -88,8 +89,7 @@ public class JpaWhitelistedSiteRepository implements WhitelistedSiteRepository { public WhitelistedSite getByClientDetails(ClientDetails client) { TypedQuery query = manager.createNamedQuery("WhitelistedSite.getByClientDetails", WhitelistedSite.class); query.setParameter("clientDetails", client); - - return query.getSingleResult(); + return JpaUtil.getSingleResult(query.getResultList()); } @Override 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 717e8c9ac..8372bc8a5 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 @@ -17,7 +17,9 @@ package org.mitre.openid.connect.token; import java.util.Collection; import java.util.Date; +import java.util.Set; +import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.openid.connect.model.ApprovedSite; import org.mitre.openid.connect.model.DefaultUserInfo; import org.mitre.openid.connect.model.WhitelistedSite; @@ -31,6 +33,9 @@ import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.approval.UserApprovalHandler; +import com.google.common.base.Splitter; +import com.google.common.collect.Sets; + /** * Custom User Approval Handler implementation which uses a concept of a whitelist, * blacklist, and greylist. @@ -75,19 +80,22 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { @Override public boolean isApproved(AuthorizationRequest authorizationRequest, Authentication userAuthentication) { - //Check database to see if the user identified by the userAuthentication has stored an approval decision + //First, check database to see if the user identified by the userAuthentication has stored an approval decision + String userId = userAuthentication.getName(); - ClientDetails client = clientDetailsService.loadClientByClientId(authorizationRequest.getClientId()); - - //lookup ApprovedSites by userId DefaultUserInfo user = (DefaultUserInfo)userInfoService.getByUserId(userId); + //lookup ApprovedSites by userId Collection approvedSites = approvedSiteService.getByUserInfo(user); for (ApprovedSite ap : approvedSites) { - if (ap.getClientDetails().getClientId() == client.getClientId()) { - //TODO need to test more than just id + if (sitesMatch(ap, authorizationRequest, user)) { + + //We have a match; update the access date on the AP entry and return true. + ap.setAccessDate(new Date()); + approvedSiteService.save(ap); + return true; } } @@ -107,15 +115,57 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { return true; } - if (authorizationRequest.isApproved() && !authorizationRequest.getApprovalParameters().isEmpty()) { + boolean approved = Boolean.parseBoolean(authorizationRequest.getApprovalParameters().get("user_oauth_approval")); + + if (approved && !authorizationRequest.getApprovalParameters().isEmpty()) { + + //TODO: check approval parameters to see if we should store this request or not + //Make a new AP ApprovedSite newAP = new ApprovedSite(); newAP.setAccessDate(new Date()); - //Set allowedScopes = authorizationRequest.getApprovalParameters().get("scope"); - //newAP.setAllowedScopes(allowedScopes); + String scopes = authorizationRequest.getAuthorizationParameters().get("scope"); + Set allowedScopes = Sets.newHashSet(Splitter.on(" ").split(scopes)); + newAP.setAllowedScopes(allowedScopes); + newAP.setClientDetails((ClientDetailsEntity)client); + newAP.setUserInfo((DefaultUserInfo)user); + newAP.setCreationDate(new Date()); + approvedSiteService.save(newAP); + + return true; } return false; } + + /** + * Check if a given ApprovedSite entry matches the information about the current request. + * + * @param ap the ApprovedSite to compare + * @param authReq the AuthorizationRequest for this requst + * @param user the User making the request + * @return true if everything matches, false otherwise + */ + private boolean sitesMatch(ApprovedSite ap, AuthorizationRequest authReq, DefaultUserInfo user) { + + ClientDetails client = clientDetailsService.loadClientByClientId(authReq.getClientId()); + + String scopes = authReq.getAuthorizationParameters().get("scope"); + Set allowedScopes = Sets.newHashSet(Splitter.on(" ").split(scopes)); + + if (!(ap.getClientDetails().getClientId()).equalsIgnoreCase(client.getClientId())) { + return false; + } + if (!(ap.getUserInfo().getUserId()).equalsIgnoreCase(user.getUserId())) { + return false; + } + for (String scope : allowedScopes) { + if (!ap.getAllowedScopes().contains(scope)) { + return false; + } + } + + return true; + } } diff --git a/openid-connect-server/src/main/webapp/WEB-INF/application-context.xml b/openid-connect-server/src/main/webapp/WEB-INF/application-context.xml index 143836cf8..1177e3b22 100644 --- a/openid-connect-server/src/main/webapp/WEB-INF/application-context.xml +++ b/openid-connect-server/src/main/webapp/WEB-INF/application-context.xml @@ -57,7 +57,7 @@ client-details-service-ref="defaultOAuth2ClientDetailsEntityService" authorization-request-factory-ref="authorizationRequestFactory" token-services-ref="defaultOAuth2ProviderTokenService" - user-approval-handler-ref="userApprovalHandler" + user-approval-handler-ref="jdbcUserApprovalHandler" authorization-endpoint-url="/authorize" token-endpoint-url="/token"> @@ -82,8 +82,7 @@ - - + diff --git a/openid-connect-server/src/main/webapp/WEB-INF/views/oauth/approve.jsp b/openid-connect-server/src/main/webapp/WEB-INF/views/oauth/approve.jsp index 8c4bbe13d..2f8b7d074 100644 --- a/openid-connect-server/src/main/webapp/WEB-INF/views/oauth/approve.jsp +++ b/openid-connect-server/src/main/webapp/WEB-INF/views/oauth/approve.jsp @@ -62,16 +62,16 @@
Access to: - - basic profile information - - email address - - address - - phone number - - offline access + + + + + + + + + +
From b87d54b06eeec42ac83c0baee0d54c991898bfb6 Mon Sep 17 00:00:00 2001 From: Amanda Anganes Date: Fri, 3 Aug 2012 13:32:17 -0400 Subject: [PATCH 06/10] Changed UserInfo references to String "userId" references --- .../openid/connect/model/ApprovedSite.java | 17 ++++++++--------- .../repository/ApprovedSiteRepository.java | 5 ++--- .../connect/service/ApprovedSiteService.java | 3 +-- .../db/tables/approvedsite.sql | 2 +- .../impl/JpaApprovedSiteRepository.java | 9 ++++----- .../service/impl/ApprovedSiteServiceImpl.java | 5 ++--- .../connect/token/JdbcUserApprovalHandler.java | 18 ++++++------------ 7 files changed, 24 insertions(+), 35 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 796cdb1f7..56c6f226f 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 @@ -40,8 +40,8 @@ import org.mitre.oauth2.model.ClientDetailsEntity; @Table(name="approvedsite") @NamedQueries({ @NamedQuery(name = "ApprovedSite.getAll", query = "select a from ApprovedSite a"), - @NamedQuery(name = "ApprovedSite.getByUserInfo", query = "select a from ApprovedSite a where a.userInfo = :approvedSiteUserInfo"), - @NamedQuery(name = "ApprovedSite.getByClientDetails", query = "select a from ApprovedSite a where a.clientDetails = :approvedSiteClientDetails") + @NamedQuery(name = "ApprovedSite.getByUserId", query = "select a from ApprovedSite a where a.userId = :userId"), + @NamedQuery(name = "ApprovedSite.getByClientDetails", query = "select a from ApprovedSite a where a.clientDetails = :clientDetails") }) public class ApprovedSite { @@ -49,7 +49,7 @@ public class ApprovedSite { private Long id; // which user made the approval - private DefaultUserInfo userInfo; + private String userId; // which OAuth2 client is this tied to private ClientDetailsEntity clientDetails; @@ -98,17 +98,16 @@ public class ApprovedSite { /** * @return the userInfo */ - @ManyToOne - @JoinColumn(name="userinfo_id") - public DefaultUserInfo getUserInfo() { - return userInfo; + @Basic + public String getUserId() { + return userId; } /** * @param userInfo the userInfo to set */ - public void setUserInfo(DefaultUserInfo userInfo) { - this.userInfo = userInfo; + public void setUserId(String userId) { + this.userId = userId; } /** diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/ApprovedSiteRepository.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/ApprovedSiteRepository.java index 7bd08bea8..c7517ce9e 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/ApprovedSiteRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/ApprovedSiteRepository.java @@ -19,7 +19,6 @@ import java.util.Collection; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.openid.connect.model.ApprovedSite; -import org.mitre.openid.connect.model.UserInfo; /** * ApprovedSite repository interface @@ -57,12 +56,12 @@ public interface ApprovedSiteRepository { /** * Return a collection of ApprovedSite managed by this repository matching the - * provided UserInfo + * provided user id * * @param userId * @return */ - public Collection getByUserInfo(UserInfo userInfo); + public Collection getByUserId(String userId); /** * Removes the given ApprovedSite from the repository diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java index 07370e122..7243fc189 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java @@ -19,7 +19,6 @@ import java.util.Collection; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.openid.connect.model.ApprovedSite; -import org.mitre.openid.connect.model.UserInfo; /** * Interface for ApprovedSite service @@ -53,7 +52,7 @@ public interface ApprovedSiteService { * @param userId * @return */ - public Collection getByUserInfo(UserInfo userInfo); + public Collection getByUserId(String userId); /** * Save an ApprovedSite diff --git a/openid-connect-server/db/tables/approvedsite.sql b/openid-connect-server/db/tables/approvedsite.sql index 32b7fa629..51172f2d5 100644 --- a/openid-connect-server/db/tables/approvedsite.sql +++ b/openid-connect-server/db/tables/approvedsite.sql @@ -1,6 +1,6 @@ CREATE TABLE approvedsite ( id BIGINT AUTO_INCREMENT PRIMARY KEY, - userinfo_id VARCHAR(256), + userId VARCHAR(256), clientdetails_id VARCHAR(256), creationDate DATE, accessDate DATE, diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java index b0959d851..880404412 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java @@ -26,7 +26,6 @@ import javax.persistence.TypedQuery; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.openid.connect.model.ApprovedSite; -import org.mitre.openid.connect.model.UserInfo; import org.mitre.openid.connect.repository.ApprovedSiteRepository; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -58,7 +57,7 @@ public class JpaApprovedSiteRepository implements ApprovedSiteRepository { TypedQuery query = manager.createNamedQuery( "ApprovedSite.getByClientDetails", ApprovedSite.class); - query.setParameter("approvedSiteClientDetails", clientDetails); + query.setParameter("clientDetails", clientDetails); List found = query.getResultList(); @@ -73,10 +72,10 @@ public class JpaApprovedSiteRepository implements ApprovedSiteRepository { @Override @Transactional - public Collection getByUserInfo(UserInfo userInfo) { + public Collection getByUserId(String userId) { TypedQuery query = manager.createNamedQuery( - "ApprovedSite.getByUserInfo", ApprovedSite.class); - query.setParameter("approvedSiteUserInfo", userInfo); + "ApprovedSite.getByUserId", ApprovedSite.class); + query.setParameter("userId", userId); List found = query.getResultList(); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/ApprovedSiteServiceImpl.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/ApprovedSiteServiceImpl.java index 6c7c7134b..6e2634ed4 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/ApprovedSiteServiceImpl.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/ApprovedSiteServiceImpl.java @@ -19,7 +19,6 @@ import java.util.Collection; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.openid.connect.model.ApprovedSite; -import org.mitre.openid.connect.model.UserInfo; import org.mitre.openid.connect.repository.ApprovedSiteRepository; import org.mitre.openid.connect.service.ApprovedSiteService; import org.springframework.beans.factory.annotation.Autowired; @@ -67,8 +66,8 @@ public class ApprovedSiteServiceImpl implements ApprovedSiteService { } @Override - public Collection getByUserInfo(UserInfo userInfo) { - return approvedSiteRepository.getByUserInfo(userInfo); + public Collection getByUserId(String userId) { + return approvedSiteRepository.getByUserId(userId); } @Override 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 8372bc8a5..2a5168dfd 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 @@ -21,10 +21,8 @@ import java.util.Set; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.openid.connect.model.ApprovedSite; -import org.mitre.openid.connect.model.DefaultUserInfo; 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; @@ -52,9 +50,6 @@ import com.google.common.collect.Sets; * */ public class JdbcUserApprovalHandler implements UserApprovalHandler { - - @Autowired - UserInfoService userInfoService; @Autowired ApprovedSiteService approvedSiteService; @@ -84,13 +79,12 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { String userId = userAuthentication.getName(); ClientDetails client = clientDetailsService.loadClientByClientId(authorizationRequest.getClientId()); - DefaultUserInfo user = (DefaultUserInfo)userInfoService.getByUserId(userId); //lookup ApprovedSites by userId - Collection approvedSites = approvedSiteService.getByUserInfo(user); + Collection approvedSites = approvedSiteService.getByUserId(userId); for (ApprovedSite ap : approvedSites) { - if (sitesMatch(ap, authorizationRequest, user)) { + if (sitesMatch(ap, authorizationRequest, userId)) { //We have a match; update the access date on the AP entry and return true. ap.setAccessDate(new Date()); @@ -108,7 +102,7 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { newAP.setWhitelistedSite(ws); newAP.setAllowedScopes(ws.getAllowedScopes()); newAP.setCreationDate(new Date()); - newAP.setUserInfo(user); + newAP.setUserId(userId); //TODO set timeout date? approvedSiteService.save(newAP); @@ -128,7 +122,7 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { Set allowedScopes = Sets.newHashSet(Splitter.on(" ").split(scopes)); newAP.setAllowedScopes(allowedScopes); newAP.setClientDetails((ClientDetailsEntity)client); - newAP.setUserInfo((DefaultUserInfo)user); + newAP.setUserId(userId); newAP.setCreationDate(new Date()); approvedSiteService.save(newAP); @@ -146,7 +140,7 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { * @param user the User making the request * @return true if everything matches, false otherwise */ - private boolean sitesMatch(ApprovedSite ap, AuthorizationRequest authReq, DefaultUserInfo user) { + private boolean sitesMatch(ApprovedSite ap, AuthorizationRequest authReq, String userId) { ClientDetails client = clientDetailsService.loadClientByClientId(authReq.getClientId()); @@ -156,7 +150,7 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { if (!(ap.getClientDetails().getClientId()).equalsIgnoreCase(client.getClientId())) { return false; } - if (!(ap.getUserInfo().getUserId()).equalsIgnoreCase(user.getUserId())) { + if (!(ap.getUserId()).equalsIgnoreCase(userId)) { return false; } for (String scope : allowedScopes) { From 2f28cf33e7793be9cadccf56d2c496389d81d246 Mon Sep 17 00:00:00 2001 From: Amanda Anganes Date: Fri, 3 Aug 2012 16:43:37 -0400 Subject: [PATCH 07/10] Changed UserInfo refs in WhitelistedSite to String ids; updated the user approval handler to check if "remember this decision" is checked and only make a new AP if so, and to pull in the scopes selected on the approval page as the saved allowed scopes for that AP. --- .../openid/connect/model/WhitelistedSite.java | 21 ++++++---- .../repository/WhitelistedSiteRepository.java | 5 +-- .../service/WhitelistedSiteService.java | 5 +-- .../db/tables/whitelistedsite.sql | 2 +- .../impl/JpaWhitelistedSiteRepository.java | 7 ++-- .../impl/WhitelistedSiteServiceImpl.java | 5 +-- .../token/JdbcUserApprovalHandler.java | 39 ++++++++++++------- .../webapp/WEB-INF/views/oauth/approve.jsp | 14 ++++--- 8 files changed, 58 insertions(+), 40 deletions(-) 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 021382451..a845a5f4a 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,6 +17,8 @@ package org.mitre.openid.connect.model; import java.util.Set; +import javax.persistence.Basic; +import javax.persistence.CollectionTable; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.FetchType; @@ -42,7 +44,7 @@ import org.mitre.oauth2.model.ClientDetailsEntity; @NamedQueries({ @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") + @NamedQuery(name = "WhitelistedSite.getByCreatoruserId", query = "select w from WhitelistedSite w where w.creatorUserId = :userId") }) public class WhitelistedSite { @@ -50,7 +52,7 @@ public class WhitelistedSite { private Long id; // Reference to the admin user who created this entry - private DefaultUserInfo creator; + private String creatorUserId; // which OAuth2 client is this tied to private ClientDetailsEntity clientDetails; @@ -102,6 +104,10 @@ public class WhitelistedSite { * @return the allowedScopes */ @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable( + name="allowed_scopes", + joinColumns=@JoinColumn(name="owner_id") + ) public Set getAllowedScopes() { return allowedScopes; } @@ -113,13 +119,12 @@ public class WhitelistedSite { this.allowedScopes = allowedScopes; } - @ManyToOne - @JoinColumn(name="userinfo_id") - public DefaultUserInfo getCreator() { - return creator; + @Basic + public String getCreatorUserId() { + return creatorUserId; } - public void setCreator(DefaultUserInfo creator) { - this.creator = creator; + public void setCreatorUserId(String creatorUserId) { + this.creatorUserId = creatorUserId; } } 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 aa5448ae7..be7f077e5 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,6 @@ 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; @@ -56,10 +55,10 @@ public interface WhitelistedSiteRepository { /** * Return a collection of the WhitelistedSites created by a given user * - * @param creator the UserInfo representing an admin who may have made some WhitelistedSites + * @param creator the id of the admin who may have created some WhitelistedSites * @return the collection of corresponding WhitelistedSites, if any, or null */ - public Collection getByCreator(UserInfo creator); + public Collection getByCreator(String creatorId); /** * Removes the given IdToken from the repository 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 bb4568ae8..9408dffc1 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 @@ -17,7 +17,6 @@ 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; @@ -56,10 +55,10 @@ public interface WhitelistedSiteService { /** * Return a collection of the WhitelistedSites created by a given user * - * @param creator the UserInfo representing an admin who may have made some WhitelistedSites + * @param creator the user id of an admin who may have made some WhitelistedSites * @return the collection of corresponding WhitelistedSites, if any, or null */ - public Collection getByCreator(UserInfo creator); + public Collection getByCreator(String creatorId); /** * Removes the given WhitelistedSite from the repository diff --git a/openid-connect-server/db/tables/whitelistedsite.sql b/openid-connect-server/db/tables/whitelistedsite.sql index d029d9dbb..88044d985 100644 --- a/openid-connect-server/db/tables/whitelistedsite.sql +++ b/openid-connect-server/db/tables/whitelistedsite.sql @@ -1,5 +1,5 @@ CREATE TABLE whitelistedsite ( id BIGINT AUTO_INCREMENT PRIMARY KEY, - userinfo_id VARCHAR(256), + creatorUserId VARCHAR(256), clientdetails_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 22587d1f0..0ced5f0df 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,7 +23,6 @@ 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.mitre.util.jpa.JpaUtil; @@ -94,9 +93,9 @@ public class JpaWhitelistedSiteRepository implements WhitelistedSiteRepository { @Override @Transactional - public Collection getByCreator(UserInfo creator) { - TypedQuery query = manager.createNamedQuery("WhitelistedSite.getByUserInfo", WhitelistedSite.class); - query.setParameter("userInfo", creator); + public Collection getByCreator(String creatorId) { + TypedQuery query = manager.createNamedQuery("WhitelistedSite.getByCreaterUserId", WhitelistedSite.class); + query.setParameter("userId", creatorId); return query.getResultList(); } 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 17a060ea5..4dccb840d 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 @@ -17,7 +17,6 @@ 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; @@ -86,8 +85,8 @@ public class WhitelistedSiteServiceImpl implements WhitelistedSiteService { } @Override - public Collection getByCreator(UserInfo creator) { - return whitelistedSiteRepository.getByCreator(creator); + public Collection getByCreator(String creatorId) { + return whitelistedSiteRepository.getByCreator(creatorId); } } 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 2a5168dfd..dc1874324 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 @@ -17,6 +17,7 @@ package org.mitre.openid.connect.token; import java.util.Collection; import java.util.Date; +import java.util.Map; import java.util.Set; import org.mitre.oauth2.model.ClientDetailsEntity; @@ -113,18 +114,30 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { if (approved && !authorizationRequest.getApprovalParameters().isEmpty()) { - //TODO: check approval parameters to see if we should store this request or not - - //Make a new AP - ApprovedSite newAP = new ApprovedSite(); - newAP.setAccessDate(new Date()); - String scopes = authorizationRequest.getAuthorizationParameters().get("scope"); - Set allowedScopes = Sets.newHashSet(Splitter.on(" ").split(scopes)); - newAP.setAllowedScopes(allowedScopes); - newAP.setClientDetails((ClientDetailsEntity)client); - newAP.setUserId(userId); - newAP.setCreationDate(new Date()); - approvedSiteService.save(newAP); + //Only store an ApprovedSite if the user has checked "remember this decision": + if (authorizationRequest.getApprovalParameters().get("remember") != null) { + + //Make a new AP + ApprovedSite newAP = new ApprovedSite(); + newAP.setAccessDate(new Date()); + + Set allowedScopes = Sets.newHashSet(); + Map approvalParams = authorizationRequest.getApprovalParameters(); + + for (String key : approvalParams.keySet()) { + if (key.contains("scope")) { + //This is a scope parameter from the approval page. The value sent back should + //be the scope string. + allowedScopes.add(approvalParams.get(key)); + } + } + + newAP.setAllowedScopes(allowedScopes); + newAP.setClientDetails((ClientDetailsEntity)client); + newAP.setUserId(userId); + newAP.setCreationDate(new Date()); + approvedSiteService.save(newAP); + } return true; } @@ -143,7 +156,7 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { private boolean sitesMatch(ApprovedSite ap, AuthorizationRequest authReq, String userId) { ClientDetails client = clientDetailsService.loadClientByClientId(authReq.getClientId()); - + String scopes = authReq.getAuthorizationParameters().get("scope"); Set allowedScopes = Sets.newHashSet(Splitter.on(" ").split(scopes)); diff --git a/openid-connect-server/src/main/webapp/WEB-INF/views/oauth/approve.jsp b/openid-connect-server/src/main/webapp/WEB-INF/views/oauth/approve.jsp index 2f8b7d074..94e15d888 100644 --- a/openid-connect-server/src/main/webapp/WEB-INF/views/oauth/approve.jsp +++ b/openid-connect-server/src/main/webapp/WEB-INF/views/oauth/approve.jsp @@ -63,15 +63,19 @@
Access to: - + + - + - + - + - + + + +
From ae44bd5e0c076d0929bd03884e9466f4c2b1ff04 Mon Sep 17 00:00:00 2001 From: Amanda Anganes Date: Mon, 6 Aug 2012 13:40:27 -0400 Subject: [PATCH 08/10] Works; about to do some cleanup --- .../DefaultOAuth2ProviderTokenService.java | 19 +---------- .../token/JdbcUserApprovalHandler.java | 34 +++++++++++++++---- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java index 5851d1834..6b8500ea5 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java @@ -42,8 +42,6 @@ import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import com.google.common.collect.Sets; - /** * @author jricher @@ -82,22 +80,7 @@ public class DefaultOAuth2ProviderTokenService implements OAuth2TokenEntityServi token.setClient(client); // inherit the scope from the auth - // this lets us match which scope is requested - if (client.isScoped()) { - - // restrict granted scopes to a valid subset of those - Set validScopes = Sets.newHashSet(); - - for (String requested : clientAuth.getScope()) { - if (client.getScope().contains(requested)) { - validScopes.add(requested); - } else { - logger.warn("Client " + client.getClientId() + " requested out of permission scope: " + requested); - } - } - - token.setScope(validScopes); - } + token.setScope(clientAuth.getScope()); // make it expire if necessary // TODO: pending upstream updates, check for 0 or -1 value here 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 dc1874324..0542d0c06 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 @@ -27,6 +27,7 @@ import org.mitre.openid.connect.service.ApprovedSiteService; 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.common.exceptions.InvalidScopeException; import org.springframework.security.oauth2.provider.AuthorizationRequest; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.ClientDetailsService; @@ -78,6 +79,7 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { //First, check database to see if the user identified by the userAuthentication has stored an approval decision + //getName may not be filled in? TODO: investigate String userId = userAuthentication.getName(); ClientDetails client = clientDetailsService.loadClientByClientId(authorizationRequest.getClientId()); @@ -96,15 +98,15 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { } WhitelistedSite ws = whitelistedSiteService.getByClientDetails(client); - if (ws != null) { + if (ws != null && scopesMatch(ws, authorizationRequest)) { //Create an approved site ApprovedSite newAP = new ApprovedSite(); + newAP.setClientDetails((ClientDetailsEntity)client); newAP.setAccessDate(new Date()); newAP.setWhitelistedSite(ws); newAP.setAllowedScopes(ws.getAllowedScopes()); newAP.setCreationDate(new Date()); newAP.setUserId(userId); - //TODO set timeout date? approvedSiteService.save(newAP); return true; @@ -117,6 +119,9 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { //Only store an ApprovedSite if the user has checked "remember this decision": if (authorizationRequest.getApprovalParameters().get("remember") != null) { + //TODO: Remember may eventually have an option to remember for a specific amount + //of time; this would set the ApprovedSite.timeout. + //Make a new AP ApprovedSite newAP = new ApprovedSite(); newAP.setAccessDate(new Date()); @@ -158,15 +163,15 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { ClientDetails client = clientDetailsService.loadClientByClientId(authReq.getClientId()); String scopes = authReq.getAuthorizationParameters().get("scope"); - Set allowedScopes = Sets.newHashSet(Splitter.on(" ").split(scopes)); + Set requestedScopes = Sets.newHashSet(Splitter.on(" ").split(scopes)); - if (!(ap.getClientDetails().getClientId()).equalsIgnoreCase(client.getClientId())) { + if (!(ap.getClientDetails().getClientId()).equals(client.getClientId())) { return false; } - if (!(ap.getUserId()).equalsIgnoreCase(userId)) { + if (!(ap.getUserId()).equals(userId)) { return false; } - for (String scope : allowedScopes) { + for (String scope : requestedScopes) { if (!ap.getAllowedScopes().contains(scope)) { return false; } @@ -175,4 +180,21 @@ public class JdbcUserApprovalHandler implements UserApprovalHandler { return true; } + private boolean scopesMatch(WhitelistedSite ws, AuthorizationRequest authorizationRequest) { + + String scopes = authorizationRequest.getAuthorizationParameters().get("scope"); + Set authRequestScopes = Sets.newHashSet(Splitter.on(" ").split(scopes)); + + Set wsScopes = ws.getAllowedScopes(); + + for (String scope : authRequestScopes) { + if (!wsScopes.contains(scope)) { + throw new InvalidScopeException("Invalid scope: " + scope, wsScopes); + } + } + + + return true; + } + } From 5fb67ab7bba309c69596df00a3e8c657a6d22d47 Mon Sep 17 00:00:00 2001 From: Amanda Anganes Date: Mon, 6 Aug 2012 14:33:16 -0400 Subject: [PATCH 09/10] Did a lot of cleanup; untested but compiles --- .../openid/connect/model/ApprovedSite.java | 21 +- .../openid/connect/model/WhitelistedSite.java | 22 +- .../repository/ApprovedSiteRepository.java | 20 +- .../repository/WhitelistedSiteRepository.java | 5 +- .../connect/service/ApprovedSiteService.java | 24 +-- .../service/WhitelistedSiteService.java | 5 +- .../db/tables/approvedsite.sql | 2 +- .../db/tables/whitelistedsite.sql | 2 +- .../impl/JpaApprovedSiteRepository.java | 41 ++-- .../impl/JpaWhitelistedSiteRepository.java | 9 +- .../service/impl/ApprovedSiteServiceImpl.java | 48 +++-- .../impl/WhitelistedSiteServiceImpl.java | 7 +- .../token/JdbcUserApprovalHandler.java | 200 ------------------ .../webapp/WEB-INF/application-context.xml | 2 +- 14 files changed, 92 insertions(+), 316 deletions(-) delete mode 100644 openid-connect-server/src/main/java/org/mitre/openid/connect/token/JdbcUserApprovalHandler.java 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 56c6f226f..8b653bb03 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 @@ -34,14 +34,12 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.Transient; -import org.mitre.oauth2.model.ClientDetailsEntity; - @Entity @Table(name="approvedsite") @NamedQueries({ @NamedQuery(name = "ApprovedSite.getAll", query = "select a from ApprovedSite a"), @NamedQuery(name = "ApprovedSite.getByUserId", query = "select a from ApprovedSite a where a.userId = :userId"), - @NamedQuery(name = "ApprovedSite.getByClientDetails", query = "select a from ApprovedSite a where a.clientDetails = :clientDetails") + @NamedQuery(name = "ApprovedSite.getByClientIdAndUserId", query = "select a from ApprovedSite a where a.clientId = :clientId and a.userId = :userId") }) public class ApprovedSite { @@ -52,7 +50,7 @@ public class ApprovedSite { private String userId; // which OAuth2 client is this tied to - private ClientDetailsEntity clientDetails; + private String clientId; // when was this first approved? private Date creationDate; @@ -111,19 +109,18 @@ public class ApprovedSite { } /** - * @return the clientDetails + * @return the clientId */ - @ManyToOne - @JoinColumn(name="clientdetails_id") - public ClientDetailsEntity getClientDetails() { - return clientDetails; + @Basic + public String getClientId() { + return clientId; } /** - * @param clientDetails the clientDetails to set + * @param clientId the clientId to set */ - public void setClientDetails(ClientDetailsEntity clientDetails) { - this.clientDetails = clientDetails; + public void setClientId(String clientId) { + this.clientId = clientId; } /** 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 a845a5f4a..93198c520 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 @@ -26,13 +26,10 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; -import org.mitre.oauth2.model.ClientDetailsEntity; - /** * Indicator that login to a site should be automatically granted * without user interaction. @@ -43,7 +40,7 @@ import org.mitre.oauth2.model.ClientDetailsEntity; @Table(name="whitelistedsite") @NamedQueries({ @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.getByClientId", query = "select w from WhitelistedSite w where w.clientId = :clientId"), @NamedQuery(name = "WhitelistedSite.getByCreatoruserId", query = "select w from WhitelistedSite w where w.creatorUserId = :userId") }) public class WhitelistedSite { @@ -55,7 +52,7 @@ public class WhitelistedSite { private String creatorUserId; // which OAuth2 client is this tied to - private ClientDetailsEntity clientDetails; + private String clientId; // what scopes be allowed by default // this should include all information for what data to access @@ -85,19 +82,18 @@ public class WhitelistedSite { } /** - * @return the clientDetails + * @return the clientId */ - @ManyToOne - @JoinColumn(name="clientdetails_id") - public ClientDetailsEntity getClientDetails() { - return clientDetails; + @Basic + public String getClientId() { + return clientId; } /** - * @param clientDetails the clientDetails to set + * @param clientId the clientId to set */ - public void setClientDetails(ClientDetailsEntity clientDetails) { - this.clientDetails = clientDetails; + public void setClientId(String clientId) { + this.clientId = clientId; } /** diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/ApprovedSiteRepository.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/ApprovedSiteRepository.java index c7517ce9e..4b8ed2a83 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/ApprovedSiteRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/ApprovedSiteRepository.java @@ -17,13 +17,12 @@ package org.mitre.openid.connect.repository; import java.util.Collection; -import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.openid.connect.model.ApprovedSite; /** * ApprovedSite repository interface * - * @author Michael Joseph Walsh + * @author Michael Joseph Walsh, aanganes * */ public interface ApprovedSiteRepository { @@ -46,22 +45,13 @@ public interface ApprovedSiteRepository { /** * Return a collection of ApprovedSite managed by this repository matching the - * provided ClientDetailsEntity + * provided client ID and user ID * + * @param clientId * @param userId * @return - */ - public Collection getByClientDetails( - ClientDetailsEntity clientDetails); - - /** - * Return a collection of ApprovedSite managed by this repository matching the - * provided user id - * - * @param userId - * @return - */ - public Collection getByUserId(String userId); + */ + public ApprovedSite getByClientIdAndUserId(String clientId, String userId); /** * Removes the given ApprovedSite from the repository 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 be7f077e5..4b5556286 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 @@ -18,12 +18,11 @@ package org.mitre.openid.connect.repository; import java.util.Collection; import org.mitre.openid.connect.model.WhitelistedSite; -import org.springframework.security.oauth2.provider.ClientDetails; /** * WhitelistedSite repository interface * - * @author Michael Joseph Walsh + * @author Michael Joseph Walsh, aanganes * */ public interface WhitelistedSiteRepository { @@ -50,7 +49,7 @@ public interface WhitelistedSiteRepository { * @param client the Relying Party * @return the corresponding WhitelistedSite if one exists for the RP, or null */ - public WhitelistedSite getByClientDetails(ClientDetails client); + public WhitelistedSite getByClientId(String clientId); /** * Return a collection of the WhitelistedSites created by a given user diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java index 7243fc189..08c94b9e4 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java @@ -16,19 +16,23 @@ package org.mitre.openid.connect.service; import java.util.Collection; +import java.util.Date; +import java.util.Set; -import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.openid.connect.model.ApprovedSite; +import org.mitre.openid.connect.model.WhitelistedSite; /** * Interface for ApprovedSite service * - * @author Michael Joseph Walsh + * @author Michael Joseph Walsh, aanganes * */ public interface ApprovedSiteService { + public ApprovedSite createApprovedSite(String clientId, String userId, Date timeoutDate, Set allowedScopes, WhitelistedSite whitelistedSite); + /** * Return a collection of all ApprovedSites * @@ -38,21 +42,13 @@ public interface ApprovedSiteService { /** * Return a collection of ApprovedSite managed by this repository matching the - * provided ClientDetailsEntity + * provided client ID and user ID * + * @param clientId * @param userId * @return */ - public Collection getByClientDetails(ClientDetailsEntity clientDetails); - - /** - * Return a collection of ApprovedSite managed by this repository matching the - * provided UserInfo - * - * @param userId - * @return - */ - public Collection getByUserId(String userId); + public ApprovedSite getByClientIdAndUserId(String clientId, String userId); /** * Save an ApprovedSite @@ -60,7 +56,7 @@ public interface ApprovedSiteService { * @param approvedSite * the ApprovedSite to be saved */ - public void save(ApprovedSite approvedSite); + public ApprovedSite save(ApprovedSite approvedSite); /** * Get ApprovedSite for 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 9408dffc1..377001eb1 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 @@ -18,12 +18,11 @@ package org.mitre.openid.connect.service; import java.util.Collection; import org.mitre.openid.connect.model.WhitelistedSite; -import org.springframework.security.oauth2.provider.ClientDetails; /** * Interface for WhitelistedSite service * - * @author Michael Joseph Walsh + * @author Michael Joseph Walsh, aanganes * */ public interface WhitelistedSiteService { @@ -50,7 +49,7 @@ public interface WhitelistedSiteService { * @param client the Relying Party * @return the corresponding WhitelistedSite if one exists for the RP, or null */ - public WhitelistedSite getByClientDetails(ClientDetails client); + public WhitelistedSite getByClientId(String clientId); /** * Return a collection of the WhitelistedSites created by a given user diff --git a/openid-connect-server/db/tables/approvedsite.sql b/openid-connect-server/db/tables/approvedsite.sql index 51172f2d5..fde183f41 100644 --- a/openid-connect-server/db/tables/approvedsite.sql +++ b/openid-connect-server/db/tables/approvedsite.sql @@ -1,7 +1,7 @@ CREATE TABLE approvedsite ( id BIGINT AUTO_INCREMENT PRIMARY KEY, userId VARCHAR(256), - clientdetails_id VARCHAR(256), + clientId VARCHAR(256), creationDate DATE, accessDate DATE, timeoutDate DATE, diff --git a/openid-connect-server/db/tables/whitelistedsite.sql b/openid-connect-server/db/tables/whitelistedsite.sql index 88044d985..0b3782c33 100644 --- a/openid-connect-server/db/tables/whitelistedsite.sql +++ b/openid-connect-server/db/tables/whitelistedsite.sql @@ -1,5 +1,5 @@ CREATE TABLE whitelistedsite ( id BIGINT AUTO_INCREMENT PRIMARY KEY, creatorUserId VARCHAR(256), - clientdetails_id VARCHAR(256) + clientId VARCHAR(256) ); \ No newline at end of file diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java index 880404412..9cb36cb74 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java @@ -18,22 +18,21 @@ package org.mitre.openid.connect.repository.impl; import static org.mitre.util.jpa.JpaUtil.saveOrUpdate; import java.util.Collection; -import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; -import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.openid.connect.model.ApprovedSite; import org.mitre.openid.connect.repository.ApprovedSiteRepository; +import org.mitre.util.jpa.JpaUtil; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; /** * JPA ApprovedSite repository implementation * - * @author Michael Joseph Walsh + * @author Michael Joseph Walsh, aanganes * */ @Repository @@ -49,20 +48,6 @@ public class JpaApprovedSiteRepository implements ApprovedSiteRepository { "ApprovedSite.getAll", ApprovedSite.class); return query.getResultList(); } - - @Override - @Transactional - public Collection getByClientDetails( - ClientDetailsEntity clientDetails) { - - TypedQuery query = manager.createNamedQuery( - "ApprovedSite.getByClientDetails", ApprovedSite.class); - query.setParameter("clientDetails", clientDetails); - - List found = query.getResultList(); - - return found; - } @Override @Transactional @@ -70,18 +55,6 @@ public class JpaApprovedSiteRepository implements ApprovedSiteRepository { return manager.find(ApprovedSite.class, id); } - @Override - @Transactional - public Collection getByUserId(String userId) { - TypedQuery query = manager.createNamedQuery( - "ApprovedSite.getByUserId", ApprovedSite.class); - query.setParameter("userId", userId); - - List found = query.getResultList(); - - return found; - } - @Override @Transactional public void remove(ApprovedSite approvedSite) { @@ -108,4 +81,14 @@ public class JpaApprovedSiteRepository implements ApprovedSiteRepository { public ApprovedSite save(ApprovedSite approvedSite) { return saveOrUpdate(approvedSite.getId(), manager, approvedSite); } + + @Override + public ApprovedSite getByClientIdAndUserId(String clientId, String userId) { + + TypedQuery query = manager.createNamedQuery("ApprovedSite.getByClientIdAndUserId", ApprovedSite.class); + query.setParameter("userId", userId); + query.setParameter("clientId", clientId); + + return JpaUtil.getSingleResult(query.getResultList()); + } } 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 0ced5f0df..4a47688da 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 @@ -26,14 +26,13 @@ import javax.persistence.TypedQuery; import org.mitre.openid.connect.model.WhitelistedSite; import org.mitre.openid.connect.repository.WhitelistedSiteRepository; import org.mitre.util.jpa.JpaUtil; -import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; /** * JPA WhitelistedSite repository implementation * - * @author Michael Joseph Walsh + * @author Michael Joseph Walsh, aanganes * */ @Repository @@ -85,9 +84,9 @@ public class JpaWhitelistedSiteRepository implements WhitelistedSiteRepository { @Override @Transactional - public WhitelistedSite getByClientDetails(ClientDetails client) { - TypedQuery query = manager.createNamedQuery("WhitelistedSite.getByClientDetails", WhitelistedSite.class); - query.setParameter("clientDetails", client); + public WhitelistedSite getByClientId(String clientId) { + TypedQuery query = manager.createNamedQuery("WhitelistedSite.getByClientId", WhitelistedSite.class); + query.setParameter("clientId", clientId); return JpaUtil.getSingleResult(query.getResultList()); } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/ApprovedSiteServiceImpl.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/ApprovedSiteServiceImpl.java index 6e2634ed4..264da2412 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/ApprovedSiteServiceImpl.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/ApprovedSiteServiceImpl.java @@ -16,9 +16,11 @@ package org.mitre.openid.connect.service.impl; import java.util.Collection; +import java.util.Date; +import java.util.Set; -import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.openid.connect.model.ApprovedSite; +import org.mitre.openid.connect.model.WhitelistedSite; import org.mitre.openid.connect.repository.ApprovedSiteRepository; import org.mitre.openid.connect.service.ApprovedSiteService; import org.springframework.beans.factory.annotation.Autowired; @@ -28,7 +30,7 @@ import org.springframework.transaction.annotation.Transactional; /** * Implementation of the ApprovedSiteService * - * @author Michael Joseph Walsh + * @author Michael Joseph Walsh, aanganes * */ @Service @@ -60,19 +62,8 @@ public class ApprovedSiteServiceImpl implements ApprovedSiteService { } @Override - public Collection getByClientDetails( - ClientDetailsEntity clientDetails) { - return approvedSiteRepository.getByClientDetails(clientDetails); - } - - @Override - public Collection getByUserId(String userId) { - return approvedSiteRepository.getByUserId(userId); - } - - @Override - public void save(ApprovedSite approvedSite) { - approvedSiteRepository.save(approvedSite); + public ApprovedSite save(ApprovedSite approvedSite) { + return approvedSiteRepository.save(approvedSite); } @Override @@ -90,4 +81,31 @@ public class ApprovedSiteServiceImpl implements ApprovedSiteService { approvedSiteRepository.removeById(id); } + @Override + public ApprovedSite createApprovedSite(String clientId, String userId, Date timeoutDate, Set allowedScopes, + WhitelistedSite whitelistedSite) { + + ApprovedSite as = new ApprovedSite(); + + Date now = new Date(); + as.setCreationDate(now); + as.setAccessDate(now); + as.setClientId(clientId); + as.setUserId(userId); + as.setTimeoutDate(timeoutDate); + as.setAllowedScopes(allowedScopes); + as.setWhitelistedSite(whitelistedSite); + + return save(as); + + } + + @Override + public ApprovedSite getByClientIdAndUserId(String clientId, + String userId) { + + return approvedSiteRepository.getByClientIdAndUserId(clientId, userId); + + } + } 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 4dccb840d..ca25855e2 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 @@ -21,14 +21,13 @@ 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; /** * Implementation of the WhitelistedSiteService * - * @author Michael Joseph Walsh + * @author Michael Joseph Walsh, aanganes * */ @Service @@ -80,8 +79,8 @@ public class WhitelistedSiteServiceImpl implements WhitelistedSiteService { } @Override - public WhitelistedSite getByClientDetails(ClientDetails client) { - return whitelistedSiteRepository.getByClientDetails(client); + public WhitelistedSite getByClientId(String clientId) { + return whitelistedSiteRepository.getByClientId(clientId); } @Override 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 deleted file mode 100644 index 0542d0c06..000000000 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/JdbcUserApprovalHandler.java +++ /dev/null @@ -1,200 +0,0 @@ -/******************************************************************************* - * Copyright 2012 The MITRE Corporation - * - * 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.openid.connect.token; - -import java.util.Collection; -import java.util.Date; -import java.util.Map; -import java.util.Set; - -import org.mitre.oauth2.model.ClientDetailsEntity; -import org.mitre.openid.connect.model.ApprovedSite; -import org.mitre.openid.connect.model.WhitelistedSite; -import org.mitre.openid.connect.service.ApprovedSiteService; -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.common.exceptions.InvalidScopeException; -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; - -import com.google.common.base.Splitter; -import com.google.common.collect.Sets; - -/** - * 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 - 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) { - - //First, check database to see if the user identified by the userAuthentication has stored an approval decision - - //getName may not be filled in? TODO: investigate - String userId = userAuthentication.getName(); - ClientDetails client = clientDetailsService.loadClientByClientId(authorizationRequest.getClientId()); - - //lookup ApprovedSites by userId - Collection approvedSites = approvedSiteService.getByUserId(userId); - - for (ApprovedSite ap : approvedSites) { - if (sitesMatch(ap, authorizationRequest, userId)) { - - //We have a match; update the access date on the AP entry and return true. - ap.setAccessDate(new Date()); - approvedSiteService.save(ap); - - return true; - } - } - - WhitelistedSite ws = whitelistedSiteService.getByClientDetails(client); - if (ws != null && scopesMatch(ws, authorizationRequest)) { - //Create an approved site - ApprovedSite newAP = new ApprovedSite(); - newAP.setClientDetails((ClientDetailsEntity)client); - newAP.setAccessDate(new Date()); - newAP.setWhitelistedSite(ws); - newAP.setAllowedScopes(ws.getAllowedScopes()); - newAP.setCreationDate(new Date()); - newAP.setUserId(userId); - approvedSiteService.save(newAP); - - return true; - } - - boolean approved = Boolean.parseBoolean(authorizationRequest.getApprovalParameters().get("user_oauth_approval")); - - if (approved && !authorizationRequest.getApprovalParameters().isEmpty()) { - - //Only store an ApprovedSite if the user has checked "remember this decision": - if (authorizationRequest.getApprovalParameters().get("remember") != null) { - - //TODO: Remember may eventually have an option to remember for a specific amount - //of time; this would set the ApprovedSite.timeout. - - //Make a new AP - ApprovedSite newAP = new ApprovedSite(); - newAP.setAccessDate(new Date()); - - Set allowedScopes = Sets.newHashSet(); - Map approvalParams = authorizationRequest.getApprovalParameters(); - - for (String key : approvalParams.keySet()) { - if (key.contains("scope")) { - //This is a scope parameter from the approval page. The value sent back should - //be the scope string. - allowedScopes.add(approvalParams.get(key)); - } - } - - newAP.setAllowedScopes(allowedScopes); - newAP.setClientDetails((ClientDetailsEntity)client); - newAP.setUserId(userId); - newAP.setCreationDate(new Date()); - approvedSiteService.save(newAP); - } - - return true; - } - - return false; - } - - /** - * Check if a given ApprovedSite entry matches the information about the current request. - * - * @param ap the ApprovedSite to compare - * @param authReq the AuthorizationRequest for this requst - * @param user the User making the request - * @return true if everything matches, false otherwise - */ - private boolean sitesMatch(ApprovedSite ap, AuthorizationRequest authReq, String userId) { - - ClientDetails client = clientDetailsService.loadClientByClientId(authReq.getClientId()); - - String scopes = authReq.getAuthorizationParameters().get("scope"); - Set requestedScopes = Sets.newHashSet(Splitter.on(" ").split(scopes)); - - if (!(ap.getClientDetails().getClientId()).equals(client.getClientId())) { - return false; - } - if (!(ap.getUserId()).equals(userId)) { - return false; - } - for (String scope : requestedScopes) { - if (!ap.getAllowedScopes().contains(scope)) { - return false; - } - } - - return true; - } - - private boolean scopesMatch(WhitelistedSite ws, AuthorizationRequest authorizationRequest) { - - String scopes = authorizationRequest.getAuthorizationParameters().get("scope"); - Set authRequestScopes = Sets.newHashSet(Splitter.on(" ").split(scopes)); - - Set wsScopes = ws.getAllowedScopes(); - - for (String scope : authRequestScopes) { - if (!wsScopes.contains(scope)) { - throw new InvalidScopeException("Invalid scope: " + scope, wsScopes); - } - } - - - return true; - } - -} diff --git a/openid-connect-server/src/main/webapp/WEB-INF/application-context.xml b/openid-connect-server/src/main/webapp/WEB-INF/application-context.xml index 1177e3b22..43036f575 100644 --- a/openid-connect-server/src/main/webapp/WEB-INF/application-context.xml +++ b/openid-connect-server/src/main/webapp/WEB-INF/application-context.xml @@ -82,7 +82,7 @@
- + From 32dc92119f0a98578bcbf6de48a1d76a71d53a57 Mon Sep 17 00:00:00 2001 From: Amanda Anganes Date: Mon, 6 Aug 2012 16:29:22 -0400 Subject: [PATCH 10/10] Cleanup completed, this works for the most part. TODO: need to make an upstream change in order to inject a new set of scopes into the AuthorizationRequest. --- .../token/TofuUserApprovalHandler.java | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 openid-connect-server/src/main/java/org/mitre/openid/connect/token/TofuUserApprovalHandler.java diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/TofuUserApprovalHandler.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/TofuUserApprovalHandler.java new file mode 100644 index 000000000..15e1a9544 --- /dev/null +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/TofuUserApprovalHandler.java @@ -0,0 +1,172 @@ +/******************************************************************************* + * Copyright 2012 The MITRE Corporation + * + * 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.openid.connect.token; + +import java.util.Date; +import java.util.Map; +import java.util.Set; + +import org.mitre.openid.connect.model.ApprovedSite; +import org.mitre.openid.connect.model.WhitelistedSite; +import org.mitre.openid.connect.service.ApprovedSiteService; +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.common.exceptions.InvalidScopeException; +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; + +import com.google.common.base.Splitter; +import com.google.common.collect.Sets; + +/** + * 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 TofuUserApprovalHandler implements UserApprovalHandler { + + @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 approved, false otherwise + */ + @Override + public boolean isApproved(AuthorizationRequest authorizationRequest, Authentication userAuthentication) { + + //First, check database to see if the user identified by the userAuthentication has stored an approval decision + + //getName may not be filled in? TODO: investigate + String userId = userAuthentication.getName(); + String clientId = authorizationRequest.getClientId(); + ClientDetails client = clientDetailsService.loadClientByClientId(clientId); + + String scopes = authorizationRequest.getAuthorizationParameters().get("scope"); + Set authRequestScopes = Sets.newHashSet(Splitter.on(" ").split(scopes)); + + //lookup ApprovedSites by userId and clientId + ApprovedSite ap = approvedSiteService.getByClientIdAndUserId(clientId, userId); + + if (ap != null) { + if (scopesMatch(authRequestScopes, ap.getAllowedScopes())) { + + //We have a match; update the access date on the AP entry and return true. + ap.setAccessDate(new Date()); + approvedSiteService.save(ap); + + return true; + } + } + + WhitelistedSite ws = whitelistedSiteService.getByClientId(clientId); + if (ws != null && scopesMatch(authRequestScopes, ws.getAllowedScopes())) { + + //Create an approved site + approvedSiteService.createApprovedSite(clientId, userId, null, ws.getAllowedScopes(), ws); + + return true; + } + + boolean approved = Boolean.parseBoolean(authorizationRequest.getApprovalParameters().get("user_oauth_approval")); + + if (approved && !authorizationRequest.getApprovalParameters().isEmpty()) { + + //Only store an ApprovedSite if the user has checked "remember this decision": + if (authorizationRequest.getApprovalParameters().get("remember") != null) { + + //TODO: Remember may eventually have an option to remember for a specific amount + //of time; this would set the ApprovedSite.timeout. + + Set allowedScopes = Sets.newHashSet(); + Map approvalParams = authorizationRequest.getApprovalParameters(); + + Set keys = approvalParams.keySet(); + + for (String key : keys) { + if (key.contains("scope")) { + //This is a scope parameter from the approval page. The value sent back should + //be the scope string. Check to make sure it is contained in the client's + //registered allowed scopes. + + String scope = approvalParams.get(key); + + //Make sure this scope is allowed for the given client + if (client.getScope().contains(scope)) { + allowedScopes.add(scope); + } + } + } + + //FIXME: inject the final allowedScopes set into the AuthorizationRequest. The requester may have + //asked for many scopes and the user may have denied some of them. + + approvedSiteService.createApprovedSite(clientId, userId, null, allowedScopes, null); + } + + return true; + } + + return false; + } + + /** + * Check whether the requested scope set is a proper subset of the allowed scopes. + * + * @param requestedScopes + * @param allowedScopes + * @return + */ + private boolean scopesMatch(Set requestedScopes, Set allowedScopes) { + + for (String scope : requestedScopes) { + + if (!allowedScopes.contains(scope)) { + throw new InvalidScopeException("Invalid scope: " + scope, allowedScopes); + } + } + + return true; + } + +}