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