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; package org.mitre.uma.model;
import java.util.Collection;
import java.util.Date;
import java.util.Set; import java.util.Set;
import javax.persistence.Basic; import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.CollectionTable; import javax.persistence.CollectionTable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.ElementCollection; import javax.persistence.ElementCollection;
@ -30,7 +33,12 @@ import javax.persistence.GenerationType;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
/** /**
* *
@ -41,12 +49,21 @@ import javax.persistence.Table;
*/ */
@Entity @Entity
@Table(name = "permission") @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 class Permission {
public static final String QUERY_TICKET = "Permission.queryByTicket";
public static final String PARAM_TICKET = "ticket";
private Long id; private Long id;
private ResourceSet resourceSet; private ResourceSet resourceSet;
private Set<String> scopes; private Set<String> scopes;
private String ticket; private String ticket;
private Date expiration;
private Collection<Claim> claimsSupplied;
/** /**
* @return the id * @return the id
@ -117,5 +134,38 @@ public class Permission {
this.ticket = 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; package org.mitre.uma.model;
import java.util.Collection;
import java.util.Set; import java.util.Set;
import javax.persistence.Basic; import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.CollectionTable; import javax.persistence.CollectionTable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.ElementCollection; import javax.persistence.ElementCollection;
@ -30,6 +32,7 @@ import javax.persistence.Id;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.NamedQueries; import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery; import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table; import javax.persistence.Table;
@Entity @Entity
@ -46,7 +49,6 @@ public class ResourceSet {
public static final String PARAM_OWNER = "owner"; public static final String PARAM_OWNER = "owner";
public static final String PARAM_CLIENTID = "clientId"; public static final String PARAM_CLIENTID = "clientId";
private Long id; private Long id;
private String name; private String name;
private String uri; 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 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 String clientId; // client id of the protected resource that registered this resource set via OAuth token
private Collection<Claim> claimsRequired;
/** /**
* @return the id * @return the id
*/ */
@ -190,6 +194,22 @@ public class ResourceSet {
this.clientId = 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); 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); 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 ( CREATE TABLE IF NOT EXISTS permission (
id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY,
resource_set_id BIGINT NOT NULL, 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 ( CREATE TABLE IF NOT EXISTS permission_scope (
owner_id BIGINT NOT NULL, owner_id BIGINT NOT NULL,
scope VARCHAR(256) 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.service.UserInfoService;
import org.mitre.openid.connect.view.HttpCodeView; import org.mitre.openid.connect.view.HttpCodeView;
import org.mitre.openid.connect.view.JsonEntityView; import org.mitre.openid.connect.view.JsonEntityView;
import org.mitre.uma.web.AuthorizationRequestEndpoint;
import org.mitre.uma.web.PermissionRegistrationEndpoint; import org.mitre.uma.web.PermissionRegistrationEndpoint;
import org.mitre.uma.web.ResourceSetRegistrationEndpoint; import org.mitre.uma.web.ResourceSetRegistrationEndpoint;
import org.mitre.openid.connect.web.DynamicClientRegistrationEndpoint; import org.mitre.openid.connect.web.DynamicClientRegistrationEndpoint;
@ -376,7 +377,7 @@ public class DiscoveryEndpoint {
m.put("introspection_endpoint", issuer + "introspect"); m.put("introspection_endpoint", issuer + "introspect");
m.put("resource_set_registration_endpoint", issuer + ResourceSetRegistrationEndpoint.URL); m.put("resource_set_registration_endpoint", issuer + ResourceSetRegistrationEndpoint.URL);
m.put("permission_registration_endpoint", issuer + PermissionRegistrationEndpoint.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.EntityManager;
import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import org.mitre.uma.model.Permission; import org.mitre.uma.model.Permission;
import org.mitre.uma.repository.PermissionRepository; import org.mitre.uma.repository.PermissionRepository;
@ -42,4 +43,14 @@ public class JpaPermissionRepository implements PermissionRepository {
return JpaUtil.saveOrUpdate(p.getId(), em, p); 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; package org.mitre.uma.service.impl;
import java.sql.Date;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@ -42,6 +43,8 @@ public class DefaultPermissionService implements PermissionService {
@Autowired @Autowired
private SystemScopeService scopeService; private SystemScopeService scopeService;
private Long permissionExpirationSeconds = 60L * 60L; // 1 hr
/* (non-Javadoc) /* (non-Javadoc)
* @see org.mitre.uma.service.PermissionService#create(org.mitre.uma.model.ResourceSet, java.util.Set) * @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.setResourceSet(resourceSet);
p.setScopes(scopes); p.setScopes(scopes);
p.setTicket(UUID.randomUUID().toString()); p.setTicket(UUID.randomUUID().toString());
p.setExpiration(new Date(System.currentTimeMillis() + permissionExpirationSeconds * 1000L));
return repository.save(p); 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;
}
}
}