started claims service, added expiration to permissions

pull/708/merge
Justin Richer 2015-03-16 22:52:21 -04:00
parent a57c336e11
commit 2aadb09f49
10 changed files with 396 additions and 3 deletions

View File

@ -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<String> claimTokenFormat;
private Set<String> 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<String> getClaimTokenFormat() {
return claimTokenFormat;
}
/**
* @param claimTokenFormat the claimTokenFormat to set
*/
public void setClaimTokenFormat(Set<String> 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<String> getIssuer() {
return issuer;
}
/**
* @param issuer the issuer to set
*/
public void setIssuer(Set<String> issuer) {
this.issuer = issuer;
}
}

View File

@ -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<String> scopes;
private String ticket;
private Date expiration;
private Collection<Claim> 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<Claim> getClaimsSupplied() {
return claimsSupplied;
}
/**
* @param claimsSupplied the claimsSupplied to set
*/
public void setClaimsSupplied(Collection<Claim> claimsSupplied) {
this.claimsSupplied = claimsSupplied;
}
}

View File

@ -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<Claim> 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<Claim> getClaimsRequired() {
return claimsRequired;
}
/**
* @param claimsRequired the claimsRequired to set
*/
public void setClaimsRequired(Collection<Claim> claimsRequired) {
this.claimsRequired = claimsRequired;
}

View File

@ -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);
}

View File

@ -38,4 +38,13 @@ public interface PermissionService {
*/
public Permission create(ResourceSet resourceSet, Set<String> 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);
}

View File

@ -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)
);

View File

@ -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);

View File

@ -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<Permission> query = em.createNamedQuery(Permission.QUERY_TICKET, Permission.class);
query.setParameter(Permission.PARAM_TICKET, ticket);
return JpaUtil.getSingleResult(query.getResultList());
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}