Merge "Merge remote-tracking branch 'origin/master' into notification-service" into...
[platform/upstream/iotivity.git] / cloud / account / src / main / java / org / iotivity / cloud / accountserver / resources / account / AccountManager.java
1 /*
2  * //******************************************************************
3  * //
4  * // Copyright 2016 Samsung Electronics All Rights Reserved.
5  * //
6  * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
7  * //
8  * // Licensed under the Apache License, Version 2.0 (the "License");
9  * // you may not use this file except in compliance with the License.
10  * // You may obtain a copy of the License at
11  * //
12  * //      http://www.apache.org/licenses/LICENSE-2.0
13  * //
14  * // Unless required by applicable law or agreed to in writing, software
15  * // distributed under the License is distributed on an "AS IS" BASIS,
16  * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * // See the License for the specific language governing permissions and
18  * // limitations under the License.
19  * //
20  * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
21  */
22 package org.iotivity.cloud.accountserver.resources.account;
23
24 import java.io.IOException;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.nio.file.Paths;
28 import java.text.DateFormat;
29 import java.text.ParseException;
30 import java.text.SimpleDateFormat;
31 import java.util.ArrayList;
32 import java.util.Date;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.UUID;
36
37 import org.iotivity.cloud.accountserver.Constants;
38 import org.iotivity.cloud.accountserver.db.AccountDBManager;
39 import org.iotivity.cloud.accountserver.db.TokenTable;
40 import org.iotivity.cloud.accountserver.db.UserTable;
41 import org.iotivity.cloud.accountserver.oauth.OAuthProviderFactory;
42 import org.iotivity.cloud.accountserver.resources.acl.group.GroupResource;
43 import org.iotivity.cloud.accountserver.util.TypeCastingManager;
44 import org.iotivity.cloud.base.exception.ServerException.BadRequestException;
45 import org.iotivity.cloud.base.exception.ServerException.InternalServerErrorException;
46 import org.iotivity.cloud.base.exception.ServerException.NotFoundException;
47 import org.iotivity.cloud.base.exception.ServerException.UnAuthorizedException;
48 import org.iotivity.cloud.util.Log;
49
50 /**
51  *
52  * This class provides a set of APIs to handle requests about account
53  * information of authorized user.
54  *
55  */
56 public class AccountManager {
57
58     private OAuthProviderFactory           mFactory                  = null;
59     private TypeCastingManager<UserTable>  mUserTableCastingManager  = new TypeCastingManager<>();
60     private TypeCastingManager<TokenTable> mTokenTableCastingManager = new TypeCastingManager<>();
61
62     public HashMap<String, Object> signUp(String did, String authCode,
63             String authProvider, Object options) {
64
65         boolean res = false;
66         authProvider = checkAuthProviderName(authProvider);
67         res = loadAuthProviderLibrary(authProvider);
68
69         if (!res) {
70             throw new InternalServerErrorException(
71                     authProvider + " library is not loaded");
72         }
73         String userUuid = null;
74         // set token data
75         TokenTable tokenInfo = requestAccessToken(authCode, options);
76         tokenInfo.setDid(did);
77         tokenInfo.setProvider(authProvider);
78         Date currentTime = new Date();
79         DateFormat transFormat = new SimpleDateFormat("yyyyMMddkkmm");
80         tokenInfo.setIssuedtime(transFormat.format(currentTime));
81
82         // set user data
83         UserTable userInfo = requestUserInfo(tokenInfo.getAccesstoken(),
84                 options);
85         userInfo.setProvider(authProvider);
86
87         // check uuid
88         userUuid = findUuid(userInfo.getUserid(), authProvider);
89
90         storeUserTokenInfo(userUuid, userInfo, tokenInfo);
91         // make response
92         HashMap<String, Object> response = makeSignUpResponse(tokenInfo);
93
94         return response;
95     }
96
97     private void storeUserTokenInfo(String userUuid, UserTable userInfo,
98             TokenTable tokenInfo) {
99         // store db
100         if (userUuid == null) {
101             userUuid = generateUuid();
102             userInfo.setUuid(userUuid);
103
104             AccountDBManager.getInstance().insertRecord(Constants.USER_TABLE,
105                     castUserTableToMap(userInfo));
106
107             // make my private group
108             GroupResource.getInstance().createGroup(userInfo.getUuid(),
109                     Constants.REQ_GTYPE_PRIVATE);
110         }
111         tokenInfo.setUuid(userUuid);
112         AccountDBManager.getInstance().insertAndReplaceRecord(
113                 Constants.TOKEN_TABLE, castTokenTableToMap(tokenInfo));
114     }
115
116     private String checkAuthProviderName(String authProvider) {
117
118         String authProviderName = null;
119
120         if (authProvider.equalsIgnoreCase(Constants.GITHUB)) {
121             authProviderName = Constants.GITHUB;
122         } else if (authProvider.equalsIgnoreCase(Constants.SAMSUNG)) {
123             authProviderName = Constants.SAMSUNG;
124         }
125
126         return authProviderName;
127     }
128
129     private String findUuid(String userId, String authProvider) {
130         String uuid = null;
131
132         HashMap<String, Object> condition = new HashMap<>();
133         condition.put(Constants.KEYFIELD_USERID, userId);
134
135         ArrayList<HashMap<String, Object>> recordList = AccountDBManager
136                 .getInstance().selectRecord(Constants.USER_TABLE, condition);
137
138         for (HashMap<String, Object> record : recordList) {
139             String foundProvider = record.get(Constants.KEYFIELD_PROVIDER)
140                     .toString();
141             if (foundProvider != null
142                     && foundProvider.equalsIgnoreCase(authProvider)) {
143                 return record.get(Constants.KEYFIELD_UUID).toString();
144             }
145         }
146         return uuid;
147     }
148
149     private HashMap<String, Object> castUserTableToMap(UserTable userInfo) {
150
151         return mUserTableCastingManager.convertObjectToMap(userInfo);
152     }
153
154     private HashMap<String, Object> castTokenTableToMap(TokenTable tokenInfo) {
155
156         return mTokenTableCastingManager.convertObjectToMap(tokenInfo);
157     }
158
159     private TokenTable castMapToTokenTable(HashMap<String, Object> record) {
160         TokenTable tokenInfo = new TokenTable();
161         return mTokenTableCastingManager.convertMaptoObject(record, tokenInfo);
162     }
163
164     private HashMap<String, Object> makeSignUpResponse(TokenTable tokenInfo) {
165
166         HashMap<String, Object> response = new HashMap<>();
167
168         response.put(Constants.RESP_ACCESS_TOKEN, tokenInfo.getAccesstoken());
169         response.put(Constants.RESP_REFRESH_TOKEN, tokenInfo.getRefreshtoken());
170         response.put(Constants.RESP_TOKEN_TYPE, Constants.TOKEN_TYPE_BEARER);
171         response.put(Constants.RESP_EXPIRES_IN, tokenInfo.getExpiredtime());
172         response.put(Constants.RESP_UUID, tokenInfo.getUuid());
173
174         // It will be modified.
175         response.put(Constants.RESP_REDIRECT_URI, getRegionCIUrl());
176         response.put(Constants.RESP_CERTIFICATE, getRootCert());
177         response.put(Constants.RESP_SERVER_ID, Constants.CLOUD_UUID);
178
179         return response;
180     }
181
182     private String getRegionCIUrl() {
183
184         // TODO: add region management
185         return "coap+tcp://127.0.0.1:5683";
186     }
187
188     private byte[] getRootCert() {
189
190         byte[] byteRootCert = null;
191
192         Path path = Paths.get(Constants.ROOT_CERT_FILE);
193
194         try {
195
196             byteRootCert = Files.readAllBytes(path);
197
198         } catch (IOException e) {
199
200             e.printStackTrace();
201             // throw new InternalServerErrorException(
202             // "root cert file read failed!");
203         }
204
205         return byteRootCert;
206     }
207
208     private Boolean loadAuthProviderLibrary(String authProvider) {
209         mFactory = new OAuthProviderFactory();
210
211         return mFactory.load(authProvider);
212     }
213
214     private TokenTable requestAccessToken(String authCode, Object options) {
215         TokenTable tokenInfo = mFactory.requestAccessTokenInfo(authCode,
216                 options);
217         Log.d("access token : " + tokenInfo.getAccesstoken());
218         Log.d("refresh token : " + tokenInfo.getRefreshtoken());
219         Log.d("expired time" + tokenInfo.getExpiredtime());
220
221         return tokenInfo;
222     }
223
224     private UserTable requestUserInfo(String accessToken, Object options) {
225         UserTable userInfo = mFactory.requestGetUserInfo(accessToken, options);
226         Log.d("user id  : " + userInfo.getUserid());
227
228         return userInfo;
229     }
230
231     private String generateUuid() {
232         UUID uuid = UUID.randomUUID();
233         String userUuid = uuid.toString();
234         Log.d("generated uuid : " + userUuid);
235         return userUuid;
236     }
237
238     public HashMap<String, Object> signInOut(String uuid, String did,
239             String accessToken) {
240
241         // find record about uuid and did
242         HashMap<String, Object> condition = new HashMap<>();
243         condition.put(Constants.KEYFIELD_UUID, uuid);
244
245         ArrayList<HashMap<String, Object>> recordList = findRecord(
246                 AccountDBManager.getInstance()
247                         .selectRecord(Constants.TOKEN_TABLE, condition),
248                 Constants.KEYFIELD_DID, did);
249
250         if (recordList.isEmpty()) {
251             throw new UnAuthorizedException("access token doesn't exist");
252         }
253
254         HashMap<String, Object> record = recordList.get(0);
255
256         TokenTable tokenInfo = castMapToTokenTable(record);
257
258         if (verifyToken(tokenInfo, accessToken)) {
259             long remainedSeconds = getRemainedSeconds(
260                     tokenInfo.getExpiredtime(), tokenInfo.getIssuedtime());
261
262             return makeSignInResponse(remainedSeconds);
263         } else {
264             throw new UnAuthorizedException("AccessToken is unauthorized");
265         }
266     }
267
268     private ArrayList<HashMap<String, Object>> findRecord(
269             ArrayList<HashMap<String, Object>> recordList, String fieldName,
270             String value) {
271         ArrayList<HashMap<String, Object>> foundRecord = new ArrayList<>();
272
273         for (HashMap<String, Object> record : recordList) {
274             Object obj = record.get(fieldName);
275             if (obj != null && obj.equals(value)) {
276                 foundRecord.add(record);
277             }
278         }
279         return foundRecord;
280     }
281
282     private HashMap<String, Object> makeSignInResponse(long remainedSeconds) {
283         HashMap<String, Object> response = new HashMap<>();
284         response.put(Constants.RESP_EXPIRES_IN, remainedSeconds);
285
286         return response;
287     }
288
289     private long getRemainedSeconds(long expiredTime, String issuedTime) {
290         if (expiredTime == Constants.TOKEN_INFINITE) {
291             return Constants.TOKEN_INFINITE;
292         } else {
293             return expiredTime - getElaspedSeconds(issuedTime);
294         }
295     }
296
297     private boolean verifyToken(TokenTable tokenInfo, String accessToken) {
298
299         if (checkTokenInDB(tokenInfo, accessToken)) {
300             if (tokenInfo.getExpiredtime() == Constants.TOKEN_INFINITE) {
301                 return true;
302             }
303             if (checkExpiredTime(tokenInfo)) {
304                 return true;
305             }
306         }
307         return false;
308     }
309
310     private boolean checkTokenInDB(TokenTable tokenInfo, String token) {
311
312         if (tokenInfo.getAccesstoken() == null) {
313             Log.w("token doesn't exist");
314             return false;
315         } else if (!tokenInfo.getAccesstoken().equals(token)) {
316             Log.w("token is not correct");
317             return false;
318         }
319         return true;
320     }
321
322     private boolean checkExpiredTime(TokenTable tokenInfo) {
323
324         String issuedTime = tokenInfo.getIssuedtime();
325         long expiredTime = tokenInfo.getExpiredtime();
326
327         long remainTime = getElaspedSeconds(issuedTime);
328
329         if (remainTime > expiredTime) {
330             Log.w("access token is expired");
331             return false;
332         }
333         return true;
334     }
335
336     private long getElaspedSeconds(String issuedTime) {
337
338         DateFormat format = new SimpleDateFormat("yyyyMMddkkmm");
339         Date currentTime = new Date();
340         Date issuedTimeDate = null;
341
342         try {
343             issuedTimeDate = format.parse(issuedTime);
344         } catch (ParseException e) {
345             e.printStackTrace();
346         }
347
348         long difference = currentTime.getTime() - issuedTimeDate.getTime();
349         long elaspedSeconds = difference / 1000;
350         Log.d("accessToken elasped time: " + elaspedSeconds + "s");
351
352         return elaspedSeconds;
353     }
354
355     public HashMap<String, Object> refreshToken(String uuid, String did,
356             String grantType, String refreshToken) {
357
358         // find record about uuid and did
359         HashMap<String, Object> condition = new HashMap<>();
360         condition.put(Constants.KEYFIELD_UUID, uuid);
361
362         ArrayList<HashMap<String, Object>> recordList = findRecord(
363                 AccountDBManager.getInstance()
364                         .selectRecord(Constants.TOKEN_TABLE, condition),
365                 Constants.KEYFIELD_DID, did);
366
367         if (recordList.isEmpty()) {
368             throw new NotFoundException("refresh token doesn't exist");
369         }
370
371         HashMap<String, Object> record = recordList.get(0);
372
373         TokenTable oldTokenInfo = castMapToTokenTable(record);
374
375         if (!checkTokenInDB(oldTokenInfo, refreshToken)) {
376             throw new NotFoundException("refresh token is not correct");
377         }
378         // call 3rd party refresh token method
379         TokenTable newTokenInfo = requestRefreshToken(refreshToken);
380
381         // record change
382         oldTokenInfo.setAccesstoken(newTokenInfo.getAccesstoken());
383         oldTokenInfo.setRefreshtoken(newTokenInfo.getRefreshtoken());
384
385         // insert record
386         AccountDBManager.getInstance().insertAndReplaceRecord(
387                 Constants.TOKEN_TABLE, castTokenTableToMap(oldTokenInfo));
388
389         // make response
390         HashMap<String, Object> response = makeRefreshTokenResponse(
391                 oldTokenInfo);
392
393         return response;
394     }
395
396     private HashMap<String, Object> makeRefreshTokenResponse(
397             TokenTable tokenInfo) {
398         HashMap<String, Object> response = new HashMap<>();
399         response.put(Constants.RESP_ACCESS_TOKEN, tokenInfo.getAccesstoken());
400         response.put(Constants.RESP_REFRESH_TOKEN, tokenInfo.getRefreshtoken());
401         response.put(Constants.RESP_TOKEN_TYPE, Constants.TOKEN_TYPE_BEARER);
402         response.put(Constants.RESP_EXPIRES_IN, tokenInfo.getExpiredtime());
403
404         return response;
405     }
406
407     private TokenTable requestRefreshToken(String refreshToken) {
408
409         TokenTable tokenInfo = mFactory.requestRefreshTokenInfo(refreshToken);
410
411         Log.d("access token : " + tokenInfo.getAccesstoken());
412         Log.d("refresh token : " + tokenInfo.getRefreshtoken());
413         Log.d("expired time : " + tokenInfo.getExpiredtime());
414
415         return tokenInfo;
416     }
417
418     public HashMap<String, Object> searchUserAboutUuid(String uuid) {
419         // search user info about uuid
420         HashMap<String, Object> condition = new HashMap<>();
421         condition.put(Constants.KEYFIELD_UUID, uuid);
422
423         ArrayList<HashMap<String, Object>> recordList = AccountDBManager
424                 .getInstance().selectRecord(Constants.USER_TABLE, condition);
425         HashMap<String, Object> response = makeSearchUserResponse(recordList);
426
427         return response;
428     }
429
430     private HashMap<String, Object> makeSearchUserResponse(
431             ArrayList<HashMap<String, Object>> recordList) {
432         HashMap<String, Object> response = new HashMap<>();
433         ArrayList<HashMap<String, Object>> ulist = new ArrayList<>();
434
435         for (HashMap<String, Object> record : recordList) {
436             HashMap<String, Object> uInfo = new HashMap<>();
437             String uid = record.get(Constants.KEYFIELD_UUID).toString();
438             uInfo.put(Constants.RESP_UUID, uid);
439             record.remove(Constants.KEYFIELD_UUID);
440             uInfo.put(Constants.RESP_USER_INFO, record);
441             ulist.add(uInfo);
442         }
443
444         response.put(Constants.RESP_USER_LIST, ulist);
445         Log.d("User List " + response.toString());
446
447         return response;
448     }
449
450     // TODO: It will be changed
451     public HashMap<String, Object> searchUserAboutCriteria(String criteria) {
452         // parse criteria
453         String[] searchType = getSearchType(criteria);
454
455         // search user info about criteria
456         HashMap<String, Object> condition = new HashMap<>();
457         condition.put(searchType[0], searchType[1]);
458
459         ArrayList<HashMap<String, Object>> recordList = AccountDBManager
460                 .getInstance().selectRecord(Constants.USER_TABLE, condition);
461         HashMap<String, Object> response = makeSearchUserResponse(recordList);
462         return response;
463     }
464
465     // TODO: It will be changed
466     private String[] getSearchType(String criteria) {
467         String[] searchType = criteria.split(":");
468         String searchKey = searchType[0];
469         String searchValue = searchType[1];
470
471         if (searchKey == null || searchValue == null) {
472             throw new BadRequestException("search key or value is null");
473         }
474
475         return searchType;
476     }
477
478     public void deleteDevice(String uid, String di) {
479
480         HashSet<String> diSet = new HashSet<String>();
481         diSet.add(di);
482
483         // the group that gid is uid is my group.
484         GroupResource.getInstance().removeGroupDevice(uid, diSet);
485     }
486 }