From 2aadb09f4903159448e47e490827196ba1a4c774 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 16 Mar 2015 22:52:21 -0400 Subject: [PATCH] started claims service, added expiration to permissions --- .../main/java/org/mitre/uma/model/Claim.java | 147 ++++++++++++++++++ .../java/org/mitre/uma/model/Permission.java | 50 ++++++ .../java/org/mitre/uma/model/ResourceSet.java | 22 ++- .../uma/repository/PermissionRepository.java | 8 + .../mitre/uma/service/PermissionService.java | 9 ++ .../db/tables/hsql_database_tables.sql | 22 ++- .../discovery/web/DiscoveryEndpoint.java | 3 +- .../impl/JpaPermissionRepository.java | 11 ++ .../impl/DefaultPermissionService.java | 12 ++ .../uma/web/AuthorizationRequestEndpoint.java | 115 ++++++++++++++ 10 files changed, 396 insertions(+), 3 deletions(-) create mode 100644 openid-connect-common/src/main/java/org/mitre/uma/model/Claim.java create mode 100644 openid-connect-server/src/main/java/org/mitre/uma/web/AuthorizationRequestEndpoint.java diff --git a/openid-connect-common/src/main/java/org/mitre/uma/model/Claim.java b/openid-connect-common/src/main/java/org/mitre/uma/model/Claim.java new file mode 100644 index 000000000..ab36908c2 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/uma/model/Claim.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright 2015 The MITRE Corporation + * and the MIT Kerberos and Internet Trust Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ + +package org.mitre.uma.model; + +import java.util.Set; + +import javax.persistence.Basic; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.Table; + +/** + * @author jricher + * + */ +@Entity +@Table(name = "claim") +public class Claim { + + private Long id; + private String name; + private String friendlyName; + private String claimType; + private Set claimTokenFormat; + private Set issuer; + + /** + * @return the id + */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + public Long getId() { + return id; + } + /** + * @param id the id to set + */ + public void setId(Long id) { + this.id = id; + } + /** + * @return the name + */ + @Basic + @Column(name = "name") + public String getName() { + return name; + } + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the friendlyName + */ + @Basic + @Column(name = "friendly_name") + public String getFriendlyName() { + return friendlyName; + } + /** + * @param friendlyName the friendlyName to set + */ + public void setFriendlyName(String friendlyName) { + this.friendlyName = friendlyName; + } + + /** + * @return the claimType + */ + @Basic + @Column(name = "claim_type") + public String getClaimType() { + return claimType; + } + /** + * @param claimType the claimType to set + */ + public void setClaimType(String claimType) { + this.claimType = claimType; + } + + /** + * @return the claimTokenFormat + */ + @ElementCollection(fetch = FetchType.EAGER) + @Column(name = "claim_token_format") + @CollectionTable( + name = "claim_token_format", + joinColumns = @JoinColumn(name = "owner_id") + ) + public Set getClaimTokenFormat() { + return claimTokenFormat; + } + /** + * @param claimTokenFormat the claimTokenFormat to set + */ + public void setClaimTokenFormat(Set claimTokenFormat) { + this.claimTokenFormat = claimTokenFormat; + } + + /** + * @return the issuer + */ + @ElementCollection(fetch = FetchType.EAGER) + @Column(name = "claim_issuer") + @CollectionTable( + name = "issuer", + joinColumns = @JoinColumn(name = "owner_id") + ) + public Set getIssuer() { + return issuer; + } + /** + * @param issuer the issuer to set + */ + public void setIssuer(Set issuer) { + this.issuer = issuer; + } + +} diff --git a/openid-connect-common/src/main/java/org/mitre/uma/model/Permission.java b/openid-connect-common/src/main/java/org/mitre/uma/model/Permission.java index 43f321e46..1904a4d0d 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/model/Permission.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/model/Permission.java @@ -17,9 +17,12 @@ package org.mitre.uma.model; +import java.util.Collection; +import java.util.Date; import java.util.Set; import javax.persistence.Basic; +import javax.persistence.CascadeType; import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.ElementCollection; @@ -30,7 +33,12 @@ 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.OneToMany; import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; /** * @@ -41,12 +49,21 @@ import javax.persistence.Table; */ @Entity @Table(name = "permission") +@NamedQueries({ + @NamedQuery(name = Permission.QUERY_TICKET, query = "select p from Permission p where p.ticket = :" + Permission.PARAM_TICKET) +}) public class Permission { + public static final String QUERY_TICKET = "Permission.queryByTicket"; + public static final String PARAM_TICKET = "ticket"; + + private Long id; private ResourceSet resourceSet; private Set scopes; private String ticket; + private Date expiration; + private Collection claimsSupplied; /** * @return the id @@ -116,6 +133,39 @@ public class Permission { public void setTicket(String ticket) { this.ticket = ticket; } + + /** + * @return the expiration + */ + @Basic + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "expiration") + public Date getExpiration() { + return expiration; + } + + /** + * @param expiration the expiration to set + */ + public void setExpiration(Date expiration) { + this.expiration = expiration; + } + + /** + * @return the claimsSupplied + */ + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "permission_id") + public Collection getClaimsSupplied() { + return claimsSupplied; + } + + /** + * @param claimsSupplied the claimsSupplied to set + */ + public void setClaimsSupplied(Collection claimsSupplied) { + this.claimsSupplied = claimsSupplied; + } } diff --git a/openid-connect-common/src/main/java/org/mitre/uma/model/ResourceSet.java b/openid-connect-common/src/main/java/org/mitre/uma/model/ResourceSet.java index 467b9877a..f577e0fe6 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/model/ResourceSet.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/model/ResourceSet.java @@ -16,9 +16,11 @@ *******************************************************************************/ package org.mitre.uma.model; +import java.util.Collection; import java.util.Set; import javax.persistence.Basic; +import javax.persistence.CascadeType; import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.ElementCollection; @@ -30,6 +32,7 @@ import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; +import javax.persistence.OneToMany; import javax.persistence.Table; @Entity @@ -46,7 +49,6 @@ public class ResourceSet { public static final String PARAM_OWNER = "owner"; public static final String PARAM_CLIENTID = "clientId"; - private Long id; private String name; private String uri; @@ -57,6 +59,8 @@ public class ResourceSet { private String owner; // username of the person responsible for the registration (either directly or via OAuth token) private String clientId; // client id of the protected resource that registered this resource set via OAuth token + private Collection claimsRequired; + /** * @return the id */ @@ -189,6 +193,22 @@ public class ResourceSet { public void setClientId(String clientId) { this.clientId = clientId; } + + /** + * @return the claimsRequired + */ + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "resource_set_id") + public Collection getClaimsRequired() { + return claimsRequired; + } + + /** + * @param claimsRequired the claimsRequired to set + */ + public void setClaimsRequired(Collection claimsRequired) { + this.claimsRequired = claimsRequired; + } diff --git a/openid-connect-common/src/main/java/org/mitre/uma/repository/PermissionRepository.java b/openid-connect-common/src/main/java/org/mitre/uma/repository/PermissionRepository.java index f7ff56609..84cb7a1c9 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/repository/PermissionRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/repository/PermissionRepository.java @@ -31,4 +31,12 @@ public interface PermissionRepository { */ public Permission save(Permission p); + /** + * Get the permission indicated by its ticket value. + * + * @param ticket + * @return + */ + public Permission getByTicket(String ticket); + } diff --git a/openid-connect-common/src/main/java/org/mitre/uma/service/PermissionService.java b/openid-connect-common/src/main/java/org/mitre/uma/service/PermissionService.java index 1f0b9ac4b..d2634b6ea 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/service/PermissionService.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/service/PermissionService.java @@ -38,4 +38,13 @@ public interface PermissionService { */ public Permission create(ResourceSet resourceSet, Set scopes); + /** + * + * Read the permission associated with the given ticket. + * + * @param the ticket value to search on + * @return the permission object, or null if none is found + */ + public Permission getByTicket(String ticket); + } diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/hsql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/tables/hsql_database_tables.sql index 5743b54b9..9be43707d 100644 --- a/openid-connect-server-webapp/src/main/resources/db/tables/hsql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/tables/hsql_database_tables.sql @@ -238,10 +238,30 @@ CREATE TABLE IF NOT EXISTS resource_set_scope ( CREATE TABLE IF NOT EXISTS permission ( id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, resource_set_id BIGINT NOT NULL, - ticket VARCHAR(256) NOT NULL + ticket VARCHAR(256) NOT NULL, + expiration TIMESTAMP ); CREATE TABLE IF NOT EXISTS permission_scope ( owner_id BIGINT NOT NULL, scope VARCHAR(256) NOT NULL ); + +CREATE TABLE IF NOT EXISTS claim ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, + name VARCHAR(256), + friendly_name VARCHAR(1024), + claim_type VARCHAR(1024), + resource_set_id BIGINT, + permission_id BIGINT +); + +CREATE TABLE IF NOT EXISTS claim_token_format ( + owner_id BIGINT NOT NULL, + claim_token_format VARCHAR(1024) +); + +CREATE TABLE IF NOT EXISTS claim_issuer ( + owner_id BIGINT NOT NULL, + issuer VARCHAR(1024) +); diff --git a/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java b/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java index 8e4f3274d..10ce60f20 100644 --- a/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java @@ -32,6 +32,7 @@ import org.mitre.openid.connect.model.UserInfo; import org.mitre.openid.connect.service.UserInfoService; import org.mitre.openid.connect.view.HttpCodeView; import org.mitre.openid.connect.view.JsonEntityView; +import org.mitre.uma.web.AuthorizationRequestEndpoint; import org.mitre.uma.web.PermissionRegistrationEndpoint; import org.mitre.uma.web.ResourceSetRegistrationEndpoint; import org.mitre.openid.connect.web.DynamicClientRegistrationEndpoint; @@ -376,7 +377,7 @@ public class DiscoveryEndpoint { m.put("introspection_endpoint", issuer + "introspect"); m.put("resource_set_registration_endpoint", issuer + ResourceSetRegistrationEndpoint.URL); m.put("permission_registration_endpoint", issuer + PermissionRegistrationEndpoint.URL); -// m.put("rpt_endpoint", issuer + RPT_ENDPOINT); + m.put("rpt_endpoint", issuer + issuer + AuthorizationRequestEndpoint.URL); diff --git a/openid-connect-server/src/main/java/org/mitre/uma/repository/impl/JpaPermissionRepository.java b/openid-connect-server/src/main/java/org/mitre/uma/repository/impl/JpaPermissionRepository.java index b2912f95b..7d8e0066f 100644 --- a/openid-connect-server/src/main/java/org/mitre/uma/repository/impl/JpaPermissionRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/uma/repository/impl/JpaPermissionRepository.java @@ -19,6 +19,7 @@ package org.mitre.uma.repository.impl; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; +import javax.persistence.TypedQuery; import org.mitre.uma.model.Permission; import org.mitre.uma.repository.PermissionRepository; @@ -42,4 +43,14 @@ public class JpaPermissionRepository implements PermissionRepository { return JpaUtil.saveOrUpdate(p.getId(), em, p); } + /* (non-Javadoc) + * @see org.mitre.uma.repository.PermissionRepository#getByTicket(java.lang.String) + */ + @Override + public Permission getByTicket(String ticket) { + TypedQuery query = em.createNamedQuery(Permission.QUERY_TICKET, Permission.class); + query.setParameter(Permission.PARAM_TICKET, ticket); + return JpaUtil.getSingleResult(query.getResultList()); + } + } diff --git a/openid-connect-server/src/main/java/org/mitre/uma/service/impl/DefaultPermissionService.java b/openid-connect-server/src/main/java/org/mitre/uma/service/impl/DefaultPermissionService.java index 0fd2fc7ac..728687392 100644 --- a/openid-connect-server/src/main/java/org/mitre/uma/service/impl/DefaultPermissionService.java +++ b/openid-connect-server/src/main/java/org/mitre/uma/service/impl/DefaultPermissionService.java @@ -17,6 +17,7 @@ package org.mitre.uma.service.impl; +import java.sql.Date; import java.util.Set; import java.util.UUID; @@ -42,6 +43,8 @@ public class DefaultPermissionService implements PermissionService { @Autowired private SystemScopeService scopeService; + private Long permissionExpirationSeconds = 60L * 60L; // 1 hr + /* (non-Javadoc) * @see org.mitre.uma.service.PermissionService#create(org.mitre.uma.model.ResourceSet, java.util.Set) */ @@ -58,9 +61,18 @@ public class DefaultPermissionService implements PermissionService { p.setResourceSet(resourceSet); p.setScopes(scopes); p.setTicket(UUID.randomUUID().toString()); + p.setExpiration(new Date(System.currentTimeMillis() + permissionExpirationSeconds * 1000L)); return repository.save(p); } + /* (non-Javadoc) + * @see org.mitre.uma.service.PermissionService#getByTicket(java.lang.String) + */ + @Override + public Permission getByTicket(String ticket) { + return repository.getByTicket(ticket); + } + } diff --git a/openid-connect-server/src/main/java/org/mitre/uma/web/AuthorizationRequestEndpoint.java b/openid-connect-server/src/main/java/org/mitre/uma/web/AuthorizationRequestEndpoint.java new file mode 100644 index 000000000..a401ec736 --- /dev/null +++ b/openid-connect-server/src/main/java/org/mitre/uma/web/AuthorizationRequestEndpoint.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright 2015 The MITRE Corporation + * and the MIT Kerberos and Internet Trust Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ + +package org.mitre.uma.web; + +import org.mitre.oauth2.model.OAuth2AccessTokenEntity; +import org.mitre.oauth2.service.OAuth2TokenEntityService; +import org.mitre.oauth2.service.SystemScopeService; +import org.mitre.oauth2.web.AuthenticationUtilities; +import org.mitre.openid.connect.view.HttpCodeView; +import org.mitre.openid.connect.view.JsonErrorView; +import org.mitre.uma.model.Permission; +import org.mitre.uma.model.ResourceSet; +import org.mitre.uma.service.PermissionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.util.MimeTypeUtils; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +/** + * @author jricher + * + */ +@Controller +@RequestMapping("/" + AuthorizationRequestEndpoint.URL) +public class AuthorizationRequestEndpoint { + + public static final String RPT = "rpt"; + public static final String TICKET = "ticket"; + public static final String URL = "authz_request"; + + @Autowired + private PermissionService permissionService; + + @Autowired + private OAuth2TokenEntityService tokenService; + + @RequestMapping(method = RequestMethod.POST, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE) + public String authorizationRequest(@RequestBody String jsonString, Model m, Authentication auth) { + + AuthenticationUtilities.ensureOAuthScope(auth, SystemScopeService.UMA_AUTHORIZATION_SCOPE); + + JsonParser parser = new JsonParser(); + JsonElement e = parser.parse(jsonString); + + if (e.isJsonObject()) { + JsonObject o = e.getAsJsonObject(); + + if (o.has(TICKET)) { + + OAuth2AccessTokenEntity rpt = null; + if (o.has(RPT)) { + String rptValue = o.get(RPT).getAsString(); + rpt = tokenService.readAccessToken(rptValue); + } + + String ticketValue = o.get(TICKET).getAsString(); + + Permission perm = permissionService.getByTicket(ticketValue); + + if (perm != null) { + // found the ticket, see if it's any good + + ResourceSet rs = perm.getResourceSet(); + + + + + } else { + // ticket wasn't found, return an error + m.addAttribute(HttpStatus.BAD_REQUEST); + m.addAttribute(JsonErrorView.ERROR, "invalid_ticket"); + return JsonErrorView.VIEWNAME; + } + + } else { + m.addAttribute(HttpCodeView.CODE, HttpStatus.BAD_REQUEST); + m.addAttribute(JsonErrorView.ERROR_MESSAGE, "Missing JSON elements."); + return JsonErrorView.VIEWNAME; + } + + + } else { + m.addAttribute(HttpCodeView.CODE, HttpStatus.BAD_REQUEST); + m.addAttribute(JsonErrorView.ERROR_MESSAGE, "Malformed JSON request."); + return JsonErrorView.VIEWNAME; + } + + } + + +}