2 * //******************************************************************
4 * // Copyright 2016 Samsung Electronics All Rights Reserved.
6 * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
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
12 * // http://www.apache.org/licenses/LICENSE-2.0
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.
20 * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
22 package org.iotivity.cloud.accountserver.resources.account;
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;
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;
52 * This class provides a set of APIs to handle requests about account
53 * information of authorized user.
56 public class AccountManager {
58 private OAuthProviderFactory mFactory = null;
59 private TypeCastingManager<UserTable> mUserTableCastingManager = new TypeCastingManager<>();
60 private TypeCastingManager<TokenTable> mTokenTableCastingManager = new TypeCastingManager<>();
63 * API to return a sign-up response payload
66 * Device id registered under user account
68 * Unique identifier of the resource which is obtained from an
69 * auth provider or a single sign-on (SSO) client
71 * Provider name user for authentication (e.g., "Github")
73 * Optional field (e.g., region authserver url, apiserver url)
75 * @return Sign-up response payload
78 public HashMap<String, Object> signUp(String did, String authCode,
79 String authProvider, Object options) {
82 // check auth provider name not to be case-sensitive
83 authProvider = checkAuthProviderName(authProvider);
84 res = loadAuthProviderLibrary(authProvider);
87 throw new InternalServerErrorException(
88 authProvider + " library is not loaded");
90 String userUuid = null;
92 TokenTable tokenInfo = requestAccessToken(authCode, options);
93 tokenInfo.setDid(did);
94 tokenInfo.setProvider(authProvider);
95 Date currentTime = new Date();
96 DateFormat transFormat = new SimpleDateFormat("yyyyMMddkkmm");
97 tokenInfo.setIssuedtime(transFormat.format(currentTime));
100 UserTable userInfo = requestUserInfo(tokenInfo.getAccesstoken(),
102 userInfo.setProvider(authProvider);
105 userUuid = findUuid(userInfo.getUserid(), authProvider);
107 // store token information and user information to the DB
108 // private group creation and store group information to the DB
109 storeUserTokenInfo(userUuid, userInfo, tokenInfo, did);
112 HashMap<String, Object> response = makeSignUpResponse(tokenInfo);
118 * API to return a sign-in or sign-out response payload
121 * User id which is provided by Sign-up process
123 * Device id registered under user account
125 * Access token used for communication with cloud
126 * @return Sign-in or sign-out response payload
128 public HashMap<String, Object> signInOut(String uuid, String did,
129 String accessToken) {
131 // find token information corresponding to the uuid and did
132 HashMap<String, Object> condition = new HashMap<>();
133 condition.put(Constants.KEYFIELD_UUID, uuid);
135 ArrayList<HashMap<String, Object>> recordList = findRecord(
136 AccountDBManager.getInstance()
137 .selectRecord(Constants.TOKEN_TABLE, condition),
138 Constants.KEYFIELD_DID, did);
140 if (recordList.isEmpty()) {
141 throw new UnAuthorizedException("access token doesn't exist");
144 HashMap<String, Object> record = recordList.get(0);
146 TokenTable tokenInfo = castMapToTokenTable(record);
148 // token verification to check if the accesstoken is expired
149 if (verifyToken(tokenInfo, accessToken)) {
150 long remainedSeconds = getRemainedSeconds(
151 tokenInfo.getExpiredtime(), tokenInfo.getIssuedtime());
153 return makeSignInResponse(remainedSeconds);
155 throw new UnAuthorizedException("AccessToken is unauthorized");
160 * API to return a token refresh response payload
163 * user id which is provided by Sign-up process
165 * device id registered under user account
167 * token type to be granted
168 * @param refreshToken
169 * Refresh token used to refresh the access token in cloud before
171 * @return Token refresh response payload
174 public HashMap<String, Object> refreshToken(String uuid, String did,
175 String grantType, String refreshToken) {
177 // find record about uuid and did
178 HashMap<String, Object> condition = new HashMap<>();
179 condition.put(Constants.KEYFIELD_UUID, uuid);
180 condition.put(Constants.KEYFIELD_DID, did);
182 ArrayList<HashMap<String, Object>> recordList = findRecord(
183 AccountDBManager.getInstance()
184 .selectRecord(Constants.TOKEN_TABLE, condition),
185 Constants.KEYFIELD_DID, did);
187 if (recordList.isEmpty()) {
188 throw new NotFoundException("refresh token doesn't exist");
191 HashMap<String, Object> record = recordList.get(0);
193 TokenTable oldTokenInfo = castMapToTokenTable(record);
194 String provider = oldTokenInfo.getProvider();
196 if (!checkRefreshTokenInDB(oldTokenInfo, refreshToken)) {
197 throw new NotFoundException("refresh token is not correct");
199 // call 3rd party refresh token method
200 TokenTable newTokenInfo = requestRefreshToken(refreshToken, provider);
203 oldTokenInfo.setAccesstoken(newTokenInfo.getAccesstoken());
204 oldTokenInfo.setRefreshtoken(newTokenInfo.getRefreshtoken());
207 AccountDBManager.getInstance().insertAndReplaceRecord(
208 Constants.TOKEN_TABLE, castTokenTableToMap(oldTokenInfo));
211 HashMap<String, Object> response = makeRefreshTokenResponse(
217 private void storeUserTokenInfo(String userUuid, UserTable userInfo,
218 TokenTable tokenInfo, String did) {
220 // the user table is created
221 if (userUuid == null) {
222 userUuid = generateUuid();
223 userInfo.setUuid(userUuid);
225 AccountDBManager.getInstance().insertRecord(Constants.USER_TABLE,
226 castUserTableToMap(userInfo));
228 // make my private group
229 GroupResource.getInstance().createGroup(userInfo.getUuid(),
230 Constants.REQ_GTYPE_PRIVATE);
232 tokenInfo.setUuid(userUuid);
233 AccountDBManager.getInstance().insertAndReplaceRecord(
234 Constants.TOKEN_TABLE, castTokenTableToMap(tokenInfo));
237 private String checkAuthProviderName(String authProvider) {
239 String authProviderName = null;
241 if (authProvider.equalsIgnoreCase(Constants.GITHUB)) {
242 authProviderName = Constants.GITHUB;
243 } else if (authProvider.equalsIgnoreCase(Constants.SAMSUNG)) {
244 authProviderName = Constants.SAMSUNG;
245 } else if (authProvider.equalsIgnoreCase(Constants.GOOGLE))
246 authProviderName = Constants.GOOGLE;
248 Log.w("Unsupported oauth provider : " + authProvider);
251 return authProviderName;
254 private String findUuid(String userId, String authProvider) {
257 HashMap<String, Object> condition = new HashMap<>();
258 condition.put(Constants.KEYFIELD_USERID, userId);
260 ArrayList<HashMap<String, Object>> recordList = AccountDBManager
261 .getInstance().selectRecord(Constants.USER_TABLE, condition);
263 for (HashMap<String, Object> record : recordList) {
264 String foundProvider = record.get(Constants.KEYFIELD_PROVIDER)
266 if (foundProvider != null
267 && foundProvider.equalsIgnoreCase(authProvider)) {
268 return record.get(Constants.KEYFIELD_UUID).toString();
274 private HashMap<String, Object> castUserTableToMap(UserTable userInfo) {
276 return mUserTableCastingManager.convertObjectToMap(userInfo);
279 private HashMap<String, Object> castTokenTableToMap(TokenTable tokenInfo) {
281 return mTokenTableCastingManager.convertObjectToMap(tokenInfo);
284 private TokenTable castMapToTokenTable(HashMap<String, Object> record) {
285 TokenTable tokenInfo = new TokenTable();
286 return mTokenTableCastingManager.convertMaptoObject(record, tokenInfo);
289 private HashMap<String, Object> makeSignUpResponse(TokenTable tokenInfo) {
291 HashMap<String, Object> response = new HashMap<>();
293 response.put(Constants.RESP_ACCESS_TOKEN, tokenInfo.getAccesstoken());
294 response.put(Constants.RESP_REFRESH_TOKEN, tokenInfo.getRefreshtoken());
295 response.put(Constants.RESP_TOKEN_TYPE, Constants.TOKEN_TYPE_BEARER);
296 response.put(Constants.RESP_EXPIRES_IN, tokenInfo.getExpiredtime());
297 response.put(Constants.RESP_UUID, tokenInfo.getUuid());
299 // It will be modified.
300 response.put(Constants.RESP_REDIRECT_URI, getRegionCIUrl());
301 response.put(Constants.RESP_CERTIFICATE, getRootCert());
302 response.put(Constants.RESP_SERVER_ID, Constants.CLOUD_UUID);
307 private String getRegionCIUrl() {
309 // TODO: add region management
310 return "coap+tcp://127.0.0.1:5683";
313 private byte[] getRootCert() {
315 byte[] byteRootCert = null;
317 Path path = Paths.get(Constants.ROOT_CERT_FILE);
321 byteRootCert = Files.readAllBytes(path);
323 } catch (IOException e) {
326 // throw new InternalServerErrorException(
327 // "root cert file read failed!");
333 private Boolean loadAuthProviderLibrary(String authProvider) {
334 mFactory = new OAuthProviderFactory();
336 return mFactory.load(authProvider);
339 private TokenTable requestAccessToken(String authCode, Object options) {
340 TokenTable tokenInfo = mFactory.requestAccessTokenInfo(authCode,
342 Log.d("access token : " + tokenInfo.getAccesstoken());
343 Log.d("refresh token : " + tokenInfo.getRefreshtoken());
344 Log.d("expired time : " + tokenInfo.getExpiredtime());
349 private UserTable requestUserInfo(String accessToken, Object options) {
350 UserTable userInfo = mFactory.requestGetUserInfo(accessToken, options);
351 Log.d("user id : " + userInfo.getUserid());
356 private String generateUuid() {
357 UUID uuid = UUID.randomUUID();
358 String userUuid = uuid.toString();
359 Log.d("generated uuid : " + userUuid);
363 private ArrayList<HashMap<String, Object>> findRecord(
364 ArrayList<HashMap<String, Object>> recordList, String fieldName,
366 ArrayList<HashMap<String, Object>> foundRecord = new ArrayList<>();
368 for (HashMap<String, Object> record : recordList) {
369 Object obj = record.get(fieldName);
370 if (obj != null && obj.equals(value)) {
371 foundRecord.add(record);
377 private HashMap<String, Object> makeSignInResponse(long remainedSeconds) {
378 HashMap<String, Object> response = new HashMap<>();
379 response.put(Constants.RESP_EXPIRES_IN, remainedSeconds);
384 private long getRemainedSeconds(long expiredTime, String issuedTime) {
385 if (expiredTime == Constants.TOKEN_INFINITE) {
386 return Constants.TOKEN_INFINITE;
388 return expiredTime - getElaspedSeconds(issuedTime);
392 private boolean verifyToken(TokenTable tokenInfo, String accessToken) {
394 if (!checkAccessTokenInDB(tokenInfo, accessToken)) {
398 if (tokenInfo.getExpiredtime() != Constants.TOKEN_INFINITE
399 && !checkExpiredTime(tokenInfo)) {
406 private boolean checkRefreshTokenInDB(TokenTable tokenInfo, String token) {
407 if (tokenInfo.getRefreshtoken() == null) {
408 Log.w("Refreshtoken doesn't exist");
410 } else if (!tokenInfo.getRefreshtoken().equals(token)) {
411 Log.w("Refreshtoken is not correct");
417 private boolean checkAccessTokenInDB(TokenTable tokenInfo, String token) {
418 if (tokenInfo.getAccesstoken() == null) {
419 Log.w("AccessToken doesn't exist");
421 } else if (!tokenInfo.getAccesstoken().equals(token)) {
422 Log.w("AccessToken is not correct");
428 private boolean checkExpiredTime(TokenTable tokenInfo) {
430 String issuedTime = tokenInfo.getIssuedtime();
431 long expiredTime = tokenInfo.getExpiredtime();
433 long remainTime = getElaspedSeconds(issuedTime);
435 if (remainTime > expiredTime) {
436 Log.w("access token is expired");
442 private long getElaspedSeconds(String issuedTime) {
444 DateFormat format = new SimpleDateFormat("yyyyMMddkkmm");
445 Date currentTime = new Date();
446 Date issuedTimeDate = null;
449 issuedTimeDate = format.parse(issuedTime);
450 } catch (ParseException e) {
454 long difference = currentTime.getTime() - issuedTimeDate.getTime();
455 long elaspedSeconds = difference / 1000;
456 Log.d("accessToken elasped time: " + elaspedSeconds + "s");
458 return elaspedSeconds;
461 private HashMap<String, Object> makeRefreshTokenResponse(
462 TokenTable tokenInfo) {
463 HashMap<String, Object> response = new HashMap<>();
464 response.put(Constants.RESP_ACCESS_TOKEN, tokenInfo.getAccesstoken());
465 response.put(Constants.RESP_REFRESH_TOKEN, tokenInfo.getRefreshtoken());
466 response.put(Constants.RESP_TOKEN_TYPE, Constants.TOKEN_TYPE_BEARER);
467 response.put(Constants.RESP_EXPIRES_IN, tokenInfo.getExpiredtime());
472 private TokenTable requestRefreshToken(String refreshToken,
475 if (mFactory == null) {
478 String authProvider = checkAuthProviderName(provider);
479 res = loadAuthProviderLibrary(authProvider);
482 throw new InternalServerErrorException(
483 authProvider + " library is not loaded");
487 TokenTable tokenInfo = mFactory.requestRefreshTokenInfo(refreshToken);
489 Log.d("access token : " + tokenInfo.getAccesstoken());
490 Log.d("refresh token : " + tokenInfo.getRefreshtoken());
491 Log.d("expired time : " + tokenInfo.getExpiredtime());
496 public HashMap<String, Object> searchUserAboutUuid(String uuid) {
497 // search user info about uuid
498 HashMap<String, Object> condition = new HashMap<>();
499 condition.put(Constants.KEYFIELD_UUID, uuid);
501 ArrayList<HashMap<String, Object>> recordList = AccountDBManager
502 .getInstance().selectRecord(Constants.USER_TABLE, condition);
503 HashMap<String, Object> response = makeSearchUserResponse(recordList);
508 private HashMap<String, Object> makeSearchUserResponse(
509 ArrayList<HashMap<String, Object>> recordList) {
510 HashMap<String, Object> response = new HashMap<>();
511 ArrayList<HashMap<String, Object>> ulist = new ArrayList<>();
513 for (HashMap<String, Object> record : recordList) {
514 HashMap<String, Object> uInfo = new HashMap<>();
515 String uid = record.get(Constants.KEYFIELD_UUID).toString();
516 uInfo.put(Constants.RESP_UUID, uid);
517 record.remove(Constants.KEYFIELD_UUID);
518 uInfo.put(Constants.RESP_USER_INFO, record);
522 response.put(Constants.RESP_USER_LIST, ulist);
523 Log.d("User List " + response.toString());
528 // TODO: It will be changed
529 public HashMap<String, Object> searchUserAboutCriteria(String criteria) {
531 String[] searchType = getSearchType(criteria);
533 // search user info about criteria
534 HashMap<String, Object> condition = new HashMap<>();
535 condition.put(searchType[0], searchType[1]);
537 ArrayList<HashMap<String, Object>> recordList = AccountDBManager
538 .getInstance().selectRecord(Constants.USER_TABLE, condition);
539 HashMap<String, Object> response = makeSearchUserResponse(recordList);
543 // TODO: It will be changed
544 private String[] getSearchType(String criteria) {
545 String[] searchType = criteria.split(":");
546 String searchKey = searchType[0];
547 String searchValue = searchType[1];
549 if (searchKey == null || searchValue == null) {
550 throw new BadRequestException("search key or value is null");
556 public void deleteDevice(String uid, String di) {
558 HashSet<String> diSet = new HashSet<String>();
561 // token table search criteria
562 HashMap<String, Object> condition = new HashMap<>();
563 condition.put(Constants.KEYFIELD_UUID, uid);
564 condition.put(Constants.KEYFIELD_DID, di);
566 // delete Token information from the DB
567 AccountDBManager.getInstance().deleteRecord(Constants.TOKEN_TABLE,
569 // delete device ID from all groups in the DB
570 GroupResource.getInstance().removeGroupDeviceinEveryGroup(uid, di);
572 // TODO remove device record from the ACL table