/* * oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. * * Copyright (c) 2014, Gluu */ package org.xdi.oxauth.service; import com.google.common.collect.Sets; import com.unboundid.ldap.sdk.Filter; import org.codehaus.jettison.json.JSONArray; import org.gluu.site.ldap.persistence.BatchOperation; import org.gluu.site.ldap.persistence.LdapEntryManager; import org.gluu.site.ldap.persistence.exception.EntryPersistenceException; import org.python.jline.internal.Preconditions; import org.slf4j.Logger; import org.xdi.ldap.model.CustomAttribute; import org.xdi.ldap.model.CustomEntry; import org.xdi.ldap.model.SearchScope; import org.xdi.oxauth.model.common.Scope; import org.xdi.oxauth.model.config.StaticConfiguration; import org.xdi.oxauth.model.configuration.AppConfiguration; import org.xdi.oxauth.model.exception.InvalidClaimException; import org.xdi.oxauth.model.registration.Client; import org.xdi.service.CacheService; import org.xdi.util.StringHelper; import org.xdi.util.security.StringEncrypter; import org.xdi.util.security.StringEncrypter.EncryptionException; import javax.ejb.Stateless; import javax.inject.Inject; import javax.inject.Named; import java.util.*; /** * Provides operations with clients. * * @author Javier Rojas Blum * @author Yuriy Movchan Date: 04/15/2014 * @version October 22, 2016 */ @Stateless @Named public class ClientService { public static final String[] CLIENT_OBJECT_CLASSES = new String[] { "oxAuthClient" }; private static final String CACHE_CLIENT_NAME = "ClientCache"; private static final String CACHE_CLIENT_FILTER_NAME = "ClientFilterCache"; @Inject private Logger log; @Inject private LdapEntryManager ldapEntryManager; @Inject private CacheService cacheService; @Inject private ScopeService scopeService; @Inject private ClientFilterService clientFilterService; @Inject private EncryptionService encryptionService; @Inject private AppConfiguration appConfiguration; @Inject private StaticConfiguration staticConfiguration; private static String getClientIdCacheKey(String clientId) { return "client_id_" + StringHelper.toLowerCase(clientId); } private static String getClientDnCacheKey(String dn) { return "client_dn_" + StringHelper.toLowerCase(dn); } public void persist(Client client) { ldapEntryManager.persist(client); } public void merge(Client client) { ldapEntryManager.merge(client); removeFromCache(client); } /** * Authenticate client. * * @param clientId Client inum. * @param password Client password. * @return <code>true</code> if success, otherwise <code>false</code>. */ public boolean authenticate(String clientId, String password) { log.debug("Authenticating Client with LDAP: clientId = {}", clientId); boolean authenticated = false; try { Client client = getClient(clientId); String decryptedClientSecret = decryptSecret(client.getClientSecret()); authenticated = client != null && decryptedClientSecret != null && decryptedClientSecret.equals(password); } catch (StringEncrypter.EncryptionException e) { log.error(e.getMessage(), e); } return authenticated; } public Set<Client> getClient(Collection<String> clientIds, boolean silent) { Set<Client> set = Sets.newHashSet(); if (clientIds == null) { return set; } for (String clientId : clientIds) { try { set.add(getClient(clientId)); } catch (RuntimeException e) { if (!silent) { throw e; } } } return set; } public Client getClient(String clientId) { if (clientId != null && !clientId.isEmpty()) { Client result = getClientByDn(buildClientDn(clientId)); log.debug("Found {} entries for client id = {}", result != null ? 1 : 0, clientId); return result; } return null; } public Client getClient(String clientId, String registrationAccessToken) { String baseDN = staticConfiguration.getBaseDn().getClients(); Filter filterInum = Filter.createEqualityFilter("inum", clientId); Filter registrationAccessTokenInum = Filter.createEqualityFilter("oxAuthRegistrationAccessToken", registrationAccessToken); Filter filter = Filter.createANDFilter(filterInum, registrationAccessTokenInum); List<Client> clients = ldapEntryManager.findEntries(baseDN, Client.class, filter, null, 1, 1); if (clients != null && clients.size() > 0) { return clients.get(0); } return null; } public Set<Client> getClientsByDns(Collection<String> dnList) { return getClientsByDns(dnList, true); } public Set<Client> getClientsByDns(Collection<String> dnList, boolean silently) { Preconditions.checkNotNull(dnList); final Set<Client> result = Sets.newHashSet(); for (String clientDn : dnList) { try { result.add(getClientByDn(clientDn)); } catch (RuntimeException e) { if (!silently) { throw e; } } } return result; } /** * Returns client by DN. * * @param dn dn of client * @return Client */ public Client getClientByDn(String dn) { Client client = fromCache(dn); if (client == null) { try { client = ldapEntryManager.find(Client.class, dn); putInCache(client); } catch (Exception ex) { log.debug(ex.getMessage()); } } else { log.trace("Get client from cache by Dn '{}'", dn); } return client; } private void putInCache(Client client) { if (client == null) { return; } try { cacheService.put(CACHE_CLIENT_FILTER_NAME, getClientIdCacheKey(client.getClientId()), client); cacheService.put(CACHE_CLIENT_NAME, getClientDnCacheKey(client.getDn()), client); } catch (Exception e) { log.error("Failed to put client in cache, client:" + client, e); } } private Client fromCache(String dn) { try { String key = getClientDnCacheKey(dn); return (Client) cacheService.get(CACHE_CLIENT_NAME, key); } catch (Exception e) { log.error("Failed to fetch client from cache, dn: " + dn, e); return null; } } public org.xdi.ldap.model.CustomAttribute getCustomAttribute(Client client, String attributeName) { for (org.xdi.ldap.model.CustomAttribute customAttribute : client.getCustomAttributes()) { if (StringHelper.equalsIgnoreCase(attributeName, customAttribute.getName())) { return customAttribute; } } return null; } public void setCustomAttribute(Client client, String attributeName, String attributeValue) { org.xdi.ldap.model.CustomAttribute customAttribute = getCustomAttribute(client, attributeName); if (customAttribute == null) { customAttribute = new org.xdi.ldap.model.CustomAttribute(attributeName); client.getCustomAttributes().add(customAttribute); } customAttribute.setValue(attributeValue); } public List<Client> getAllClients(String[] returnAttributes) { String baseDn = staticConfiguration.getBaseDn().getClients(); List<Client> result = ldapEntryManager.findEntries(baseDn, Client.class, returnAttributes, null); return result; } public List<Client> getAllClients(String[] returnAttributes, int size) { String baseDn = staticConfiguration.getBaseDn().getClients(); List<Client> result = ldapEntryManager.findEntries(baseDn, Client.class, null, returnAttributes, size, size); return result; } public List<Client> getClientsWithExpirationDate(BatchOperation<Client> batchOperation, int searchLimit, int sizeLimit){ String baseDN = staticConfiguration.getBaseDn().getClients(); Filter filter = Filter.createPresenceFilter("oxAuthClientSecretExpiresAt"); return ldapEntryManager.findEntries(baseDN, Client.class, filter, SearchScope.SUB, null, batchOperation, 0, searchLimit, sizeLimit); } public String buildClientDn(String p_clientId) { final StringBuilder dn = new StringBuilder(); dn.append(String.format("inum=%s,", p_clientId)); dn.append(staticConfiguration.getBaseDn().getClients()); // ou=clients,o=@!1111,o=gluu return dn.toString(); } public void remove(Client client) { if (client != null) { removeFromCache(client); String clientDn = client.getDn(); ldapEntryManager.removeWithSubtree(clientDn); } } private void removeFromCache(Client client) { try { String clientId = client.getClientId(); String clientDn = client.getDn(); cacheService.remove(CACHE_CLIENT_FILTER_NAME, getClientIdCacheKey(clientId)); cacheService.remove(CACHE_CLIENT_NAME, getClientDnCacheKey(clientDn)); } catch (Exception e) { log.error("Failed to remove client from cache.", e); } } public void updatAccessTime(Client client, boolean isUpdateLogonTime) { if (!appConfiguration.getUpdateClientAccessTime()) { return; } String clientDn = client.getDn(); CustomEntry customEntry = new CustomEntry(); customEntry.setDn(clientDn); customEntry.setCustomObjectClasses(CLIENT_OBJECT_CLASSES); Date now = new GregorianCalendar(TimeZone.getTimeZone("UTC")).getTime(); CustomAttribute customAttributeLastAccessTime = new CustomAttribute("oxLastAccessTime", now); customEntry.getCustomAttributes().add(customAttributeLastAccessTime); if (isUpdateLogonTime) { CustomAttribute customAttributeLastLogonTime = new CustomAttribute("oxLastLogonTime", now); customEntry.getCustomAttributes().add(customAttributeLastLogonTime); } try { ldapEntryManager.merge(customEntry); } catch (EntryPersistenceException epe) { log.error("Failed to update oxLastAccessTime and oxLastLoginTime of client '{}'", clientDn); } removeFromCache(client); } public Object getAttribute(Client client, String clientAttribute) throws InvalidClaimException { Object attribute = null; if (clientAttribute != null) { if (clientAttribute.equals("displayName")) { attribute = client.getClientName(); } else if (clientAttribute.equals("inum")) { attribute = client.getClientId(); } else if (clientAttribute.equals("oxAuthAppType")) { attribute = client.getApplicationType(); } else if (clientAttribute.equals("oxAuthIdTokenSignedResponseAlg")) { attribute = client.getIdTokenSignedResponseAlg(); } else if (clientAttribute.equals("oxAuthRedirectURI") && client.getRedirectUris() != null) { JSONArray array = new JSONArray(); for (String redirectUri : client.getRedirectUris()) { array.put(redirectUri); } attribute = array; } else if (clientAttribute.equals("oxAuthScope") && client.getScopes() != null) { JSONArray array = new JSONArray(); for (String scopeDN : client.getScopes()) { Scope s = scopeService.getScopeByDn(scopeDN); if (s != null) { String scopeName = s.getDisplayName(); array.put(scopeName); } } attribute = array; } else { for (CustomAttribute customAttribute : client.getCustomAttributes()) { if (customAttribute.getName().equals(clientAttribute)) { List<String> values = customAttribute.getValues(); if (values != null) { if (values.size() == 1) { attribute = values.get(0); } else { JSONArray array = new JSONArray(); for (String v : values) { array.put(v); } attribute = array; } } break; } } } } return attribute; } public String decryptSecret(String encryptedClientSecret) throws EncryptionException { return encryptionService.decrypt(encryptedClientSecret); } public String encryptSecret(String clientSecret) throws EncryptionException { return encryptionService.encrypt(clientSecret); } }