Split out datastore implementation from aaa-shiro
[aaa.git] / aaa-shiro / impl / src / main / java / org / opendaylight / aaa / shiro / idm / IdmLightProxy.java
1 /*
2  * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.aaa.shiro.idm;
9
10 import static java.util.Objects.requireNonNull;
11
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.concurrent.ConcurrentHashMap;
16 import javax.inject.Inject;
17 import javax.inject.Singleton;
18 import org.opendaylight.aaa.api.AuthenticationException;
19 import org.opendaylight.aaa.api.Claim;
20 import org.opendaylight.aaa.api.ClaimCache;
21 import org.opendaylight.aaa.api.IDMStoreException;
22 import org.opendaylight.aaa.api.IIDMStore;
23 import org.opendaylight.aaa.api.IdMService;
24 import org.opendaylight.aaa.api.IdMServiceImpl;
25 import org.opendaylight.aaa.api.PasswordCredentialAuth;
26 import org.opendaylight.aaa.api.PasswordCredentials;
27 import org.opendaylight.aaa.api.model.Domain;
28 import org.opendaylight.aaa.api.model.Grant;
29 import org.opendaylight.aaa.api.model.Grants;
30 import org.opendaylight.aaa.api.model.Role;
31 import org.opendaylight.aaa.api.model.User;
32 import org.opendaylight.aaa.api.model.Users;
33 import org.opendaylight.aaa.api.password.service.PasswordHashService;
34 import org.opendaylight.aaa.tokenauthrealm.auth.ClaimBuilder;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 /**
39  * An OSGi proxy for the IdmLight server.
40  */
41 @Singleton
42 public class IdmLightProxy implements PasswordCredentialAuth, IdMService, ClaimCache {
43     private static final Logger LOG = LoggerFactory.getLogger(IdmLightProxy.class);
44
45     /**
46      * Responsible for storing the active claims per domain. The outer map is keyed by domain, and the inner map is
47      * keyed by <code>PasswordCredentials</code>.
48      */
49     private final Map<String, Map<PasswordCredentials, Claim>> claimCache = new ConcurrentHashMap<>();
50
51     private final IIDMStore idmStore;
52     private final PasswordHashService passwordService;
53
54     @Inject
55     public IdmLightProxy(final IIDMStore idmStore, final PasswordHashService passwordService) {
56         this.idmStore = idmStore;
57         this.passwordService = requireNonNull(passwordService);
58     }
59
60     @Override
61     public Claim authenticate(final PasswordCredentials creds) {
62         requireNonNull(creds);
63         requireNonNull(creds.username());
64         requireNonNull(creds.password());
65         String domain = creds.domain() == null ? IIDMStore.DEFAULT_DOMAIN : creds.domain();
66
67         // FIXME: Add cache invalidation
68         return claimCache.computeIfAbsent(domain, k -> new ConcurrentHashMap<>())
69                 .computeIfAbsent(creds, this::dbAuthenticate);
70     }
71
72     /**
73      * Clears the cache of any active claims.
74      */
75     @Override
76     public void clear() {
77         LOG.info("Clearing the claim cache");
78         claimCache.clear();
79     }
80
81     private Claim dbAuthenticate(final PasswordCredentials creds) {
82         Domain domain = null;
83         User user = null;
84         String credsDomain = creds.domain() == null ? IIDMStore.DEFAULT_DOMAIN : creds.domain();
85         // check to see domain exists
86         // TODO: ensure domain names are unique change to 'getDomain'
87         LOG.debug("get domain");
88         try {
89             domain = idmStore.readDomain(credsDomain);
90             if (domain == null) {
91                 throw new AuthenticationException("Domain :" + credsDomain + " does not exist");
92             }
93         } catch (IDMStoreException e) {
94             throw new AuthenticationException("Error while fetching domain", e);
95         }
96
97         // check to see user exists and passes cred check
98         try {
99             LOG.debug("check user / pwd");
100             Users users = idmStore.getUsers(creds.username(), credsDomain);
101             List<User> userList = users.getUsers();
102             if (userList.size() == 0) {
103                 throw new AuthenticationException("User :" + creds.username()
104                         + " does not exist in domain " + credsDomain);
105             }
106             user = userList.get(0);
107             if (!passwordService.passwordsMatch(creds.password(), user.getPassword(), user.getSalt())) {
108                 throw new AuthenticationException("UserName / Password not found");
109             }
110             if (!user.isEnabled()) {
111                 throw new AuthenticationException("Account is disabled");
112             }
113
114             // get all grants & roles for this domain and user
115             LOG.debug("get grants");
116             List<String> roles = new ArrayList<>();
117             Grants grants = idmStore.getGrants(domain.getDomainid(), user.getUserid());
118             for (Grant grant : grants.getGrants()) {
119                 Role role = idmStore.readRole(grant.getRoleid());
120                 if (role != null) {
121                     roles.add(role.getName());
122                 }
123             }
124
125             // build up the claim
126             LOG.debug("build a claim");
127             ClaimBuilder claim = new ClaimBuilder();
128             claim.setUserId(user.getUserid());
129             claim.setUser(creds.username());
130             claim.setDomain(credsDomain);
131             for (String role : roles) {
132                 claim.addRole(role);
133             }
134             return claim.build();
135         } catch (IDMStoreException se) {
136             throw new AuthenticationException("idm data store exception :" + se.toString() + se);
137         }
138     }
139
140     @Override
141     public List<String> listDomains(final String userId) {
142         return new IdMServiceImpl(idmStore).listDomains(userId);
143     }
144
145     @Override
146     public List<String> listRoles(final String userId, final String domainName) {
147         return new IdMServiceImpl(idmStore).listRoles(userId, domainName);
148     }
149
150     @Override
151     public List<String> listUserIDs() throws IDMStoreException {
152         return new IdMServiceImpl(idmStore).listUserIDs();
153     }
154 }