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