001/* 002 * (C) Copyright 2014-2018 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Arnaud Kervern 018 */ 019package org.nuxeo.ecm.platform.oauth2.clients; 020 021import static org.nuxeo.ecm.platform.oauth2.clients.OAuth2ClientService.OAUTH2CLIENT_SCHEMA; 022 023import java.util.Arrays; 024import java.util.Collections; 025import java.util.List; 026import java.util.Optional; 027import java.util.regex.Pattern; 028 029import org.apache.commons.lang3.StringUtils; 030import org.nuxeo.ecm.core.api.DocumentModel; 031 032/** 033 * @author <a href="mailto:[email protected]">Arnaud Kervern</a> 034 * @since 5.9.2 035 */ 036public class OAuth2Client { 037 038 protected static final Pattern LOCALHOST_PATTERN = Pattern.compile("http://localhost(:\\d+)?(/.*)?"); 039 040 protected String name; 041 042 protected String id; 043 044 protected String secret; 045 046 /** 047 * @since 9.2 048 */ 049 protected List<String> redirectURIs; 050 051 /** 052 * @since 9.10 053 */ 054 protected boolean autoGrant; 055 056 protected boolean enabled; 057 058 /** 059 * @since 9.10 060 */ 061 protected OAuth2Client(String name, String id, String secret, List<String> redirectURIs, boolean autoGrant, 062 boolean enabled) { 063 this.name = name; 064 this.id = id; 065 this.secret = secret; 066 this.redirectURIs = redirectURIs; 067 this.autoGrant = autoGrant; 068 this.enabled = enabled; 069 } 070 071 public String getName() { 072 return name; 073 } 074 075 public String getId() { 076 return id; 077 } 078 079 /** 080 * @since 9.2 081 */ 082 public List<String> getRedirectURIs() { 083 return redirectURIs; 084 } 085 086 /** 087 * @since 9.10 088 */ 089 public boolean isAutoGrant() { 090 return autoGrant; 091 } 092 093 public boolean isEnabled() { 094 return enabled; 095 } 096 097 public static OAuth2Client fromDocumentModel(DocumentModel doc) { 098 String name = (String) doc.getPropertyValue(OAUTH2CLIENT_SCHEMA + ":name"); 099 String id = (String) doc.getPropertyValue(OAUTH2CLIENT_SCHEMA + ":clientId"); 100 String secret = (String) doc.getPropertyValue(OAUTH2CLIENT_SCHEMA + ":clientSecret"); 101 List<String> redirectURIs; 102 String redirectURIsProperty = (String) doc.getPropertyValue(OAUTH2CLIENT_SCHEMA + ":redirectURIs"); 103 if (StringUtils.isEmpty(redirectURIsProperty)) { 104 redirectURIs = Collections.emptyList(); 105 } else { 106 redirectURIs = Arrays.asList(redirectURIsProperty.split(",")); 107 } 108 boolean autoGrant = (Boolean) Optional.ofNullable(doc.getPropertyValue(OAUTH2CLIENT_SCHEMA + ":autoGrant")) 109 .orElse(false); 110 boolean enabled = (Boolean) doc.getPropertyValue(OAUTH2CLIENT_SCHEMA + ":enabled"); 111 112 return new OAuth2Client(name, id, secret, redirectURIs, autoGrant, enabled); 113 } 114 115 /** 116 * A redirect URI is considered as valid if and only if: 117 * <ul> 118 * <li>It is not empty</li> 119 * <li>It starts with https, e.g. https://my.redirect.uri</li> 120 * <li>It doesn't start with http, e.g. nuxeo://authorize</li> 121 * <li>It starts with http://localhost with localhost not part of the domain name, e.g. http://localhost:8080/nuxeo, 122 * a counter-example being http://localhost.somecompany.com</li> 123 * </ul> 124 * 125 * @since 9.2 126 */ 127 public static boolean isRedirectURIValid(String redirectURI) { 128 String trimmed = redirectURI.trim(); 129 return !trimmed.isEmpty() && (trimmed.startsWith("https") || !trimmed.startsWith("http") 130 || LOCALHOST_PATTERN.matcher(trimmed).matches()); 131 } 132 133 public boolean isValidWith(String clientId, String clientSecret) { 134 // Related to RFC 6749 2.3.1 clientSecret is omitted if empty 135 return enabled && id.equals(clientId) && (StringUtils.isEmpty(secret) || secret.equals(clientSecret)); 136 } 137 138 /** 139 * @since 9.2 140 */ 141 @Override 142 public String toString() { 143 return String.format("%s(name=%s, id=%s, redirectURIs=%s, autoGrant=%b, enabled=%b)", 144 getClass().getSimpleName(), name, id, redirectURIs, autoGrant, enabled); 145 } 146}