0917018213e8b939f2768552036eaf2672b6c3fd
[moon.git] /
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. 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
9 package org.opendaylight.aaa.authn.mdsal.store;
10
11 import com.google.common.base.Optional;
12 import com.google.common.util.concurrent.CheckedFuture;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.Futures;
15 import java.math.BigInteger;
16 import java.util.concurrent.ExecutorService;
17 import java.util.concurrent.Executors;
18 import org.opendaylight.aaa.api.Authentication;
19 import org.opendaylight.aaa.api.TokenStore;
20 import org.opendaylight.aaa.authn.mdsal.store.util.AuthNStoreUtil;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
23 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
26 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
27 import org.opendaylight.yang.gen.v1.urn.aaa.yang.authn.claims.rev141029.TokenCacheTimes;
28 import org.opendaylight.yang.gen.v1.urn.aaa.yang.authn.claims.rev141029.token_cache_times.TokenList;
29 import org.opendaylight.yang.gen.v1.urn.aaa.yang.authn.claims.rev141029.token_cache_times.TokenListKey;
30 import org.opendaylight.yang.gen.v1.urn.aaa.yang.authn.claims.rev141029.token_cache_times.token_list.UserTokens;
31 import org.opendaylight.yang.gen.v1.urn.aaa.yang.authn.claims.rev141029.tokencache.Claims;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 public class AuthNStore implements AutoCloseable, TokenStore {
37
38     private static final Logger LOG = LoggerFactory.getLogger(AuthNStore.class);
39     private DataBroker broker;
40     private static BigInteger timeToLive;
41     private static Integer timeToWait;
42     private final ExecutorService deleteExpiredTokenThread = Executors.newFixedThreadPool(1);
43     private final DataEncrypter dataEncrypter;
44
45     public AuthNStore(final DataBroker dataBroker, final String config_key) {
46         this.broker = dataBroker;
47         this.dataEncrypter = new DataEncrypter(config_key);
48         LOG.info("Created MD-SAL AAA Token Cache Service...");
49     }
50
51     @Override
52     public void close() throws Exception {
53         deleteExpiredTokenThread.shutdown();
54         LOG.info("MD-SAL AAA Token Cache closed...");
55
56     }
57
58     @Override
59     public void put(String token, Authentication auth) {
60         token = dataEncrypter.encrypt(token);
61         Claims claims = AuthNStoreUtil.createClaimsRecord(token, auth);
62
63         // create and insert parallel struct
64         UserTokens userTokens = AuthNStoreUtil.createUserTokens(token, timeToLive.longValue());
65         TokenList tokenlist = AuthNStoreUtil.createTokenList(userTokens, auth.userId());
66
67         writeClaimAndTokenToStore(claims, userTokens, tokenlist);
68         deleteExpiredTokenThread.execute(deleteOldTokens(claims));
69     }
70
71     @Override
72     public Authentication get(String token) {
73         token = dataEncrypter.encrypt(token);
74         Authentication authentication = null;
75         Claims claims = readClaims(token);
76         if (claims != null) {
77             UserTokens userToken = readUserTokensFromDS(claims.getToken(), claims.getUserId());
78             authentication = AuthNStoreUtil.convertClaimToAuthentication(claims,
79                     userToken.getExpiration());
80         }
81         deleteExpiredTokenThread.execute(deleteOldTokens(claims));
82         return authentication;
83     }
84
85     @Override
86     public boolean delete(String token) {
87         token = dataEncrypter.encrypt(token);
88         boolean result = false;
89         Claims claims = readClaims(token);
90         result = deleteClaims(token);
91         if (result) {
92             deleteUserTokenFromDS(token, claims.getUserId());
93         }
94         deleteExpiredTokenThread.execute(deleteOldTokens(claims));
95         return result;
96     }
97
98     @Override
99     public long tokenExpiration() {
100         return timeToLive.longValue();
101     }
102
103     public void setTimeToLive(BigInteger timeToLive) {
104         this.timeToLive = timeToLive;
105     }
106
107     public void setTimeToWait(Integer timeToWait) {
108         this.timeToWait = timeToWait;
109     }
110
111     private void writeClaimAndTokenToStore(final Claims claims, UserTokens usertokens,
112             final TokenList tokenlist) {
113
114         final InstanceIdentifier<Claims> claims_iid = AuthNStoreUtil.createInstIdentifierForTokencache(claims.getToken());
115         WriteTransaction tx = broker.newWriteOnlyTransaction();
116         tx.put(LogicalDatastoreType.OPERATIONAL, claims_iid, claims, true);
117
118         final InstanceIdentifier<UserTokens> userTokens_iid = AuthNStoreUtil.createInstIdentifierUserTokens(
119                 tokenlist.getUserId(), usertokens.getTokenid());
120         tx.put(LogicalDatastoreType.OPERATIONAL, userTokens_iid, usertokens, true);
121
122         CheckedFuture<Void, TransactionCommitFailedException> commitFuture = tx.submit();
123         Futures.addCallback(commitFuture, new FutureCallback<Void>() {
124
125             @Override
126             public void onSuccess(Void result) {
127                 LOG.trace("Token {} was written to datastore.", claims.getToken());
128                 LOG.trace("Tokenlist for userId {} was written to datastore.",
129                         tokenlist.getUserId());
130             }
131
132             @Override
133             public void onFailure(Throwable t) {
134                 LOG.error("Inserting token {} to datastore failed.", claims.getToken());
135                 LOG.trace("Inserting for userId {} tokenlist to datastore failed.",
136                         tokenlist.getUserId());
137             }
138
139         });
140     }
141
142     private Claims readClaims(String token) {
143         final InstanceIdentifier<Claims> claims_iid = AuthNStoreUtil.createInstIdentifierForTokencache(token);
144         Claims claims = null;
145         ReadTransaction rt = broker.newReadOnlyTransaction();
146         CheckedFuture<Optional<Claims>, ReadFailedException> claimsFuture = rt.read(
147                 LogicalDatastoreType.OPERATIONAL, claims_iid);
148         try {
149             Optional<Claims> maybeClaims = claimsFuture.checkedGet();
150             if (maybeClaims.isPresent()) {
151                 claims = maybeClaims.get();
152             }
153         } catch (ReadFailedException e) {
154             LOG.error(
155                     "Something wrong happened in DataStore. Getting Claim for token {} failed.",
156                     token, e);
157         }
158         return claims;
159     }
160
161     private TokenList readTokenListFromDS(String userId) {
162         InstanceIdentifier<TokenList> tokenList_iid = InstanceIdentifier.builder(
163                 TokenCacheTimes.class).child(TokenList.class, new TokenListKey(userId)).build();
164         TokenList tokenList = null;
165         ReadTransaction rt = broker.newReadOnlyTransaction();
166         CheckedFuture<Optional<TokenList>, ReadFailedException> userTokenListFuture = rt.read(
167                 LogicalDatastoreType.OPERATIONAL, tokenList_iid);
168         try {
169             Optional<TokenList> maybeTokenList = userTokenListFuture.checkedGet();
170             if (maybeTokenList.isPresent()) {
171                 tokenList = maybeTokenList.get();
172             }
173         } catch (ReadFailedException e) {
174             LOG.error(
175                     "Something wrong happened in DataStore. Getting TokenList for userId {} failed.",
176                     userId, e);
177         }
178         return tokenList;
179     }
180
181     private UserTokens readUserTokensFromDS(String token, String userId) {
182         final InstanceIdentifier<UserTokens> userTokens_iid = AuthNStoreUtil.createInstIdentifierUserTokens(
183                 userId, token);
184         UserTokens userTokens = null;
185
186         ReadTransaction rt = broker.newReadOnlyTransaction();
187         CheckedFuture<Optional<UserTokens>, ReadFailedException> userTokensFuture = rt.read(
188                 LogicalDatastoreType.OPERATIONAL, userTokens_iid);
189
190         try {
191             Optional<UserTokens> maybeUserTokens = userTokensFuture.checkedGet();
192             if (maybeUserTokens.isPresent()) {
193                 userTokens = maybeUserTokens.get();
194             }
195         } catch (ReadFailedException e) {
196             LOG.error(
197                     "Something wrong happened in DataStore. Getting UserTokens for token {} failed.",
198                     token, e);
199         }
200
201         return userTokens;
202     }
203
204     private boolean deleteClaims(String token) {
205         final InstanceIdentifier<Claims> claims_iid = AuthNStoreUtil.createInstIdentifierForTokencache(token);
206         boolean result = false;
207         WriteTransaction tx = broker.newWriteOnlyTransaction();
208         tx.delete(LogicalDatastoreType.OPERATIONAL, claims_iid);
209         CheckedFuture<Void, TransactionCommitFailedException> commitFuture = tx.submit();
210
211         try {
212             commitFuture.checkedGet();
213             result = true;
214         } catch (TransactionCommitFailedException e) {
215             LOG.error("Something wrong happened in DataStore. Claim "
216                     + "deletion for token {} from DataStore failed.", token, e);
217         }
218         return result;
219     }
220
221     private void deleteUserTokenFromDS(String token, String userId) {
222         final InstanceIdentifier<UserTokens> userTokens_iid = AuthNStoreUtil.createInstIdentifierUserTokens(
223                 userId, token);
224
225         WriteTransaction tx = broker.newWriteOnlyTransaction();
226         tx.delete(LogicalDatastoreType.OPERATIONAL, userTokens_iid);
227         CheckedFuture<Void, TransactionCommitFailedException> commitFuture = tx.submit();
228         try {
229             commitFuture.checkedGet();
230         } catch (TransactionCommitFailedException e) {
231             LOG.error("Something wrong happened in DataStore. UserToken "
232                     + "deletion for token {} from DataStore failed.", token, e);
233         }
234     }
235
236     private Runnable deleteOldTokens(final Claims claims) {
237         return new Runnable() {
238
239             @Override
240             public void run() {
241                 TokenList tokenList = null;
242                 if (claims != null) {
243                     tokenList = readTokenListFromDS(claims.getUserId());
244                 }
245                 if (tokenList != null) {
246                     for (UserTokens currUserToken : tokenList.getUserTokens()) {
247                         long diff = System.currentTimeMillis()
248                                 - currUserToken.getTimestamp().longValue();
249                         if (diff > currUserToken.getExpiration()
250                                 && currUserToken.getExpiration() != 0) {
251                             if (deleteClaims(currUserToken.getTokenid())) {
252                                 deleteUserTokenFromDS(currUserToken.getTokenid(),
253                                         claims.getUserId());
254                                 LOG.trace("Expired tokens for UserId {} deleted.",
255                                         claims.getUserId());
256                             }
257                         }
258                     }
259                 }
260             }
261         };
262     }
263 }