/* * Copyright 2012 SURFnet bv, The Netherlands * * 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.surfnet.oaaas.model; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.persistence.CascadeType; import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.MapKeyColumn; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.validation.ConstraintValidatorContext; import javax.validation.constraints.NotNull; import javax.xml.bind.annotation.XmlRootElement; import org.codehaus.jackson.annotate.JsonIgnore; /** * Represents a Client as defined by the OAuth 2 specification: * * <pre> * An application making protected resource requests on behalf of the resource owner and with its * authorization. The term client does not imply any particular implementation characteristics (e.g. whether * the application executes on a server, a desktop, or other devices). * </pre> */ @SuppressWarnings("serial") @Entity @XmlRootElement @Table(name = "client") @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public class Client extends AbstractEntity { @Column(name = "clientName") @NotNull private String name; @Column(unique = true) private String clientId; @Column private String secret; @Column private String description; @Column private String contactName; @Column private String contactEmail; @ElementCollection(fetch= FetchType.EAGER) private List<String> scopes; @ManyToOne(optional = false) @JsonIgnore @JoinColumn(name = "resourceserver_id", nullable = false, updatable = false) private ResourceServer resourceServer; @ElementCollection(fetch= FetchType.EAGER) @MapKeyColumn(name = "attribute_name") @Column(name = "attribute_value") @CollectionTable(name = "client_attributes", joinColumns = @JoinColumn(name = "client_id")) private Map<String, String> attributes = new HashMap<String, String>(); @Column private String thumbNailUrl; @ElementCollection(fetch= FetchType.EAGER) private List<String> redirectUris = new ArrayList<String>(); @Column private boolean skipConsent; @Column private boolean includePrincipal; /* * Seconds for expire of the access token that is granted for users of this * client */ @Column private long expireDuration; @Column private boolean useRefreshTokens; @Column private boolean allowedImplicitGrant; @Column private boolean allowedClientCredentials; // Listed here so Cascade will work. @OneToMany(mappedBy ="client", cascade = CascadeType.ALL) private List<AccessToken> accessTokens; // Listed here so Cascade will work. @OneToMany(mappedBy ="client", cascade = CascadeType.ALL) private List<AuthorizationRequest> authorizationRequests; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getContactName() { return contactName; } public void setContactName(String contactName) { this.contactName = contactName; } public String getContactEmail() { return contactEmail; } public void setContactEmail(String contactEmail) { this.contactEmail = contactEmail; } /** * @return the scopes */ public List<String> getScopes() { return scopes; } /** * @param scopes * the scopes to set */ public void setScopes(List<String> scopes) { this.scopes = scopes; } /** * @return the resourceServer */ public ResourceServer getResourceServer() { return resourceServer; } /** * @param resourceServer * the resourceServer to set */ public void setResourceServer(ResourceServer resourceServer) { this.resourceServer = resourceServer; } /** * @return the thumbNailUrl */ public String getThumbNailUrl() { return thumbNailUrl; } /** * @param thumbNailUrl * the thumbNailUrl to set */ public void setThumbNailUrl(String thumbNailUrl) { this.thumbNailUrl = thumbNailUrl; } /** * @return the skipConsent */ public boolean isSkipConsent() { return skipConsent; } /** * @param skipConsent * the skipConsent to set */ public void setSkipConsent(boolean skipConsent) { this.skipConsent = skipConsent; } /** * @return the clientId */ public String getClientId() { return clientId; } /** * @param clientId * the clientId to set */ public void setClientId(String clientId) { this.clientId = clientId; } /** * @return the secret */ public String getSecret() { return secret; } /** * @param secret * the secret to set */ public void setSecret(String secret) { this.secret = secret; } /** * Get the redirectUris * @return List of String */ public List<String> getRedirectUris() { return redirectUris; } /** * Set the redirectUris * @param redirectUris the redirectUris to use. */ public void setRedirectUris(List<String> redirectUris) { this.redirectUris = redirectUris; } /** * @return the useRefreshTokens */ public boolean isUseRefreshTokens() { return useRefreshTokens; } /** * @param useRefreshTokens * the useRefreshTokens to set */ public void setUseRefreshTokens(boolean useRefreshTokens) { this.useRefreshTokens = useRefreshTokens; } /** * @return the expireDuration */ public long getExpireDuration() { return expireDuration; } /** * @param expireDuration * the expireDuration to set */ public void setExpireDuration(long expireDuration) { this.expireDuration = expireDuration; } /** * Confirm that we know the client's secret. * * @param clientSecret * @return {@code true} if we know the secret */ public boolean verifySecret(String clientSecret) { return (secret == null && clientSecret == null) || (secret != null && secret.equals(clientSecret)); } /** * @return the attributes */ public Map<String, String> getAttributes() { return attributes; } /** * @param attributes * the attributes to set */ public void setAttributes(Map<String, String> attributes) { this.attributes = attributes; } /** * @return the AllowedImplicitGrant */ public boolean isAllowedImplicitGrant() { return allowedImplicitGrant; } /** * @param AllowedImplicitGrant * the AllowedImplicitGrant to set */ public void setAllowedImplicitGrant(boolean allowedImplicitGrant) { this.allowedImplicitGrant = allowedImplicitGrant; } public boolean isIncludePrincipal() { return includePrincipal; } public void setIncludePrincipal(boolean includePrincipal) { this.includePrincipal = includePrincipal; } public boolean isAllowedClientCredentials() { return allowedClientCredentials; } public void setAllowedClientCredentials(boolean allowedClientCredentials) { this.allowedClientCredentials = allowedClientCredentials; } /* * (non-Javadoc) * * @see org.surfnet.oaaas.model.AbstractEntity#validate() */ @Override public boolean validate(ConstraintValidatorContext context) { boolean isValid = true; if (isUseRefreshTokens() && getExpireDuration() == 0L) { violation(context, "If refresh tokens are to be used then the expiry duration must be greater then 0"); isValid = false; } if (isAllowedClientCredentials() && isAllowedImplicitGrant()) { violation(context, "A Client can not be issued the client credentials grant AND the implicit grant as client credentials requires a secret."); isValid = false; } if (scopes != null && !resourceServer.getScopes().containsAll(scopes)) { String message = "Client should only contain scopes that its resource server defines. " + "Client scopes: " + scopes + ". Resource server scopes: " + resourceServer.getScopes(); violation(context, message); isValid = false; } for (String redirectUri : redirectUris) { try { URI uri = new URI(redirectUri); if (uri.getScheme() == null) { isValid = false; } } catch (URISyntaxException e) { violation(context, "redirectUri '" + redirectUri + "' is not a valid URI"); isValid = false; } } return isValid; } }