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.GroupBrokerManager;
43 import org.iotivity.cloud.accountserver.resources.acl.group.GroupManager;
44 import org.iotivity.cloud.accountserver.resources.acl.id.AclResource;
45 import org.iotivity.cloud.accountserver.util.TypeCastingManager;
46 import org.iotivity.cloud.base.exception.ServerException.BadRequestException;
47 import org.iotivity.cloud.base.exception.ServerException.InternalServerErrorException;
48 import org.iotivity.cloud.base.exception.ServerException.NotFoundException;
49 import org.iotivity.cloud.base.exception.ServerException.UnAuthorizedException;
50 import org.iotivity.cloud.util.Log;
54 * This class provides a set of APIs to handle requests about account
55 * information of authorized user.
58 public class AccountManager {
60 private OAuthProviderFactory mFactory = null;
61 private TypeCastingManager<UserTable> mUserTableCastingManager = new TypeCastingManager<>();
62 private TypeCastingManager<TokenTable> mTokenTableCastingManager = new TypeCastingManager<>();
65 * API to return a sign-up response payload
68 * Device id registered under user account
70 * Unique identifier of the resource which is obtained from an
71 * auth provider or a single sign-on (SSO) client
73 * Provider name user for authentication (e.g., "Github")
75 * Optional field (e.g., region authserver url, apiserver url)
77 * @return Sign-up response payload
80 public HashMap<String, Object> signUp(String did, String authCode,
81 String authProvider, Object options) {
84 // check auth provider name not to be case-sensitive
85 authProvider = checkAuthProviderName(authProvider);
86 res = loadAuthProviderLibrary(authProvider);
89 throw new InternalServerErrorException(
90 authProvider + " library is not loaded");
92 String userUuid = null;
94 TokenTable tokenInfo = requestAccessToken(authCode, options);
95 tokenInfo.setDid(did);
96 tokenInfo.setProvider(authProvider);
97 Date currentTime = new Date();
98 DateFormat transFormat = new SimpleDateFormat("yyyyMMddkkmm");
99 tokenInfo.setIssuedtime(transFormat.format(currentTime));
102 UserTable userInfo = requestUserInfo(tokenInfo.getAccesstoken(),
104 userInfo.setProvider(authProvider);
107 userUuid = findUuid(userInfo.getUserid(), authProvider);
109 // store token information and user information to the DB
110 // private group creation and store group information to the DB
111 userUuid = storeUserTokenInfo(userUuid, userInfo, tokenInfo, did);
113 AclResource.getInstance().createAcl(userUuid, did);
116 HashMap<String, Object> response = makeSignUpResponse(tokenInfo);
122 * API to return a sign-in or sign-out response payload
125 * User id which is provided by Sign-up process
127 * Device id registered under user account
129 * Access token used for communication with cloud
130 * @return Sign-in or sign-out response payload
132 public HashMap<String, Object> signInOut(String uuid, String did,
133 String accessToken) {
135 // find token information corresponding to the uuid and did
136 HashMap<String, Object> condition = new HashMap<>();
137 condition.put(Constants.KEYFIELD_UUID, uuid);
139 ArrayList<HashMap<String, Object>> recordList = findRecord(
140 AccountDBManager.getInstance()
141 .selectRecord(Constants.TOKEN_TABLE, condition),
142 Constants.KEYFIELD_DID, did);
144 if (recordList.isEmpty()) {
145 throw new UnAuthorizedException("access token doesn't exist");
148 HashMap<String, Object> record = recordList.get(0);
150 TokenTable tokenInfo = castMapToTokenTable(record);
152 // token verification to check if the accesstoken is expired
153 if (verifyToken(tokenInfo, accessToken)) {
154 long remainedSeconds = getRemainedSeconds(
155 tokenInfo.getExpiredtime(), tokenInfo.getIssuedtime());
157 return makeSignInResponse(remainedSeconds);
159 throw new UnAuthorizedException("AccessToken is unauthorized");
164 * API to return a token refresh response payload
167 * user id which is provided by Sign-up process
169 * device id registered under user account
171 * token type to be granted
172 * @param refreshToken
173 * Refresh token used to refresh the access token in cloud before
175 * @return Token refresh response payload
178 public HashMap<String, Object> refreshToken(String uuid, String did,
179 String grantType, String refreshToken) {
181 // find record about uuid and did
182 HashMap<String, Object> condition = new HashMap<>();
183 condition.put(Constants.KEYFIELD_UUID, uuid);
184 condition.put(Constants.KEYFIELD_DID, did);
186 ArrayList<HashMap<String, Object>> recordList = findRecord(
187 AccountDBManager.getInstance()
188 .selectRecord(Constants.TOKEN_TABLE, condition),
189 Constants.KEYFIELD_DID, did);
191 if (recordList.isEmpty()) {
192 throw new NotFoundException("refresh token doesn't exist");
195 HashMap<String, Object> record = recordList.get(0);
197 TokenTable oldTokenInfo = castMapToTokenTable(record);
198 String provider = oldTokenInfo.getProvider();
200 if (!checkRefreshTokenInDB(oldTokenInfo, refreshToken)) {
201 throw new NotFoundException("refresh token is not correct");
203 // call 3rd party refresh token method
204 TokenTable newTokenInfo = requestRefreshToken(refreshToken, provider);
207 oldTokenInfo.setAccesstoken(newTokenInfo.getAccesstoken());
208 oldTokenInfo.setRefreshtoken(newTokenInfo.getRefreshtoken());
211 AccountDBManager.getInstance().insertAndReplaceRecord(
212 Constants.TOKEN_TABLE, castTokenTableToMap(oldTokenInfo));
215 HashMap<String, Object> response = makeRefreshTokenResponse(
221 private String storeUserTokenInfo(String userUuid, UserTable userInfo,
222 TokenTable tokenInfo, String did) {
224 // the user table is created
225 if (userUuid == null) {
226 userUuid = generateUuid();
227 userInfo.setUuid(userUuid);
229 AccountDBManager.getInstance().insertRecord(Constants.USER_TABLE,
230 castUserTableToMap(userInfo));
232 // make my private group
233 GroupBrokerManager.getInstance().createGroup(userInfo.getUuid(),
234 userInfo.getUuid(), null, null);
236 tokenInfo.setUuid(userUuid);
237 AccountDBManager.getInstance().insertAndReplaceRecord(
238 Constants.TOKEN_TABLE, castTokenTableToMap(tokenInfo));
242 private String checkAuthProviderName(String authProvider) {
244 String authProviderName = null;
246 if (authProvider.equalsIgnoreCase(Constants.GITHUB)) {
247 authProviderName = Constants.GITHUB;
248 } else if (authProvider.equalsIgnoreCase(Constants.SAMSUNG)) {
249 authProviderName = Constants.SAMSUNG;
250 } else if (authProvider.equalsIgnoreCase(Constants.GOOGLE))
251 authProviderName = Constants.GOOGLE;
253 Log.w("Unsupported oauth provider : " + authProvider);
256 return authProviderName;
259 private String findUuid(String userId, String authProvider) {
262 HashMap<String, Object> condition = new HashMap<>();
263 condition.put(Constants.KEYFIELD_USERID, userId);
265 ArrayList<HashMap<String, Object>> recordList = AccountDBManager
266 .getInstance().selectRecord(Constants.USER_TABLE, condition);
268 for (HashMap<String, Object> record : recordList) {
269 String foundProvider = record.get(Constants.KEYFIELD_PROVIDER)
271 if (foundProvider != null
272 && foundProvider.equalsIgnoreCase(authProvider)) {
273 return record.get(Constants.KEYFIELD_UUID).toString();
279 private HashMap<String, Object> castUserTableToMap(UserTable userInfo) {
281 return mUserTableCastingManager.convertObjectToMap(userInfo);
284 private HashMap<String, Object> castTokenTableToMap(TokenTable tokenInfo) {
286 return mTokenTableCastingManager.convertObjectToMap(tokenInfo);
289 private TokenTable castMapToTokenTable(HashMap<String, Object> record) {
290 TokenTable tokenInfo = new TokenTable();
291 return mTokenTableCastingManager.convertMaptoObject(record, tokenInfo);
294 private HashMap<String, Object> makeSignUpResponse(TokenTable tokenInfo) {
296 HashMap<String, Object> response = new HashMap<>();
298 response.put(Constants.RESP_ACCESS_TOKEN, tokenInfo.getAccesstoken());
299 response.put(Constants.RESP_REFRESH_TOKEN, tokenInfo.getRefreshtoken());
300 response.put(Constants.RESP_TOKEN_TYPE, Constants.TOKEN_TYPE_BEARER);
301 response.put(Constants.RESP_EXPIRES_IN, tokenInfo.getExpiredtime());
302 response.put(Constants.RESP_UUID, tokenInfo.getUuid());
304 // It will be modified.
305 response.put(Constants.RESP_REDIRECT_URI, getRegionCIUrl());
306 response.put(Constants.RESP_CERTIFICATE, getRootCert());
307 response.put(Constants.RESP_SERVER_ID, Constants.CLOUD_UUID);
312 private String getRegionCIUrl() {
314 // TODO: add region management
315 return "coap+tcp://127.0.0.1:5683";
318 private byte[] getRootCert() {
320 byte[] byteRootCert = null;
322 Path path = Paths.get(Constants.ROOT_CERT_FILE);
326 byteRootCert = Files.readAllBytes(path);
328 } catch (IOException e) {
331 // throw new InternalServerErrorException(
332 // "root cert file read failed!");
338 private Boolean loadAuthProviderLibrary(String authProvider) {
339 mFactory = new OAuthProviderFactory();
341 return mFactory.load(authProvider);
344 private TokenTable requestAccessToken(String authCode, Object options) {
345 TokenTable tokenInfo = mFactory.requestAccessTokenInfo(authCode,
347 Log.d("access token : " + tokenInfo.getAccesstoken());
348 Log.d("refresh token : " + tokenInfo.getRefreshtoken());
349 Log.d("expired time : " + tokenInfo.getExpiredtime());
354 private UserTable requestUserInfo(String accessToken, Object options) {
355 UserTable userInfo = mFactory.requestGetUserInfo(accessToken, options);
356 Log.d("user id : " + userInfo.getUserid());
361 private String generateUuid() {
362 UUID uuid = UUID.randomUUID();
363 String userUuid = uuid.toString();
364 Log.d("generated uuid : " + userUuid);
368 private ArrayList<HashMap<String, Object>> findRecord(
369 ArrayList<HashMap<String, Object>> recordList, String fieldName,
371 ArrayList<HashMap<String, Object>> foundRecord = new ArrayList<>();
373 for (HashMap<String, Object> record : recordList) {
374 Object obj = record.get(fieldName);
375 if (obj != null && obj.equals(value)) {
376 foundRecord.add(record);
382 private HashMap<String, Object> makeSignInResponse(long remainedSeconds) {
383 HashMap<String, Object> response = new HashMap<>();
384 response.put(Constants.RESP_EXPIRES_IN, remainedSeconds);
389 private long getRemainedSeconds(long expiredTime, String issuedTime) {
390 if (expiredTime == Constants.TOKEN_INFINITE) {
391 return Constants.TOKEN_INFINITE;
393 return expiredTime - getElaspedSeconds(issuedTime);
397 private boolean verifyToken(TokenTable tokenInfo, String accessToken) {
399 if (!checkAccessTokenInDB(tokenInfo, accessToken)) {
403 if (tokenInfo.getExpiredtime() != Constants.TOKEN_INFINITE
404 && !checkExpiredTime(tokenInfo)) {
411 private boolean checkRefreshTokenInDB(TokenTable tokenInfo, String token) {
412 if (tokenInfo.getRefreshtoken() == null) {
413 Log.w("Refreshtoken doesn't exist");
415 } else if (!tokenInfo.getRefreshtoken().equals(token)) {
416 Log.w("Refreshtoken is not correct");
422 private boolean checkAccessTokenInDB(TokenTable tokenInfo, String token) {
423 if (tokenInfo.getAccesstoken() == null) {
424 Log.w("AccessToken doesn't exist");
426 } else if (!tokenInfo.getAccesstoken().equals(token)) {
427 Log.w("AccessToken is not correct");
433 private boolean checkExpiredTime(TokenTable tokenInfo) {
435 String issuedTime = tokenInfo.getIssuedtime();
436 long expiredTime = tokenInfo.getExpiredtime();
438 long remainTime = getElaspedSeconds(issuedTime);
440 if (remainTime > expiredTime) {
441 Log.w("access token is expired");
447 private long getElaspedSeconds(String issuedTime) {
449 DateFormat format = new SimpleDateFormat("yyyyMMddkkmm");
450 Date currentTime = new Date();
451 Date issuedTimeDate = null;
454 issuedTimeDate = format.parse(issuedTime);
455 } catch (ParseException e) {
459 long difference = currentTime.getTime() - issuedTimeDate.getTime();
460 long elaspedSeconds = difference / 1000;
461 Log.d("accessToken elasped time: " + elaspedSeconds + "s");
463 return elaspedSeconds;
466 private HashMap<String, Object> makeRefreshTokenResponse(
467 TokenTable tokenInfo) {
468 HashMap<String, Object> response = new HashMap<>();
469 response.put(Constants.RESP_ACCESS_TOKEN, tokenInfo.getAccesstoken());
470 response.put(Constants.RESP_REFRESH_TOKEN, tokenInfo.getRefreshtoken());
471 response.put(Constants.RESP_TOKEN_TYPE, Constants.TOKEN_TYPE_BEARER);
472 response.put(Constants.RESP_EXPIRES_IN, tokenInfo.getExpiredtime());
477 private TokenTable requestRefreshToken(String refreshToken,
480 if (mFactory == null) {
483 String authProvider = checkAuthProviderName(provider);
484 res = loadAuthProviderLibrary(authProvider);
487 throw new InternalServerErrorException(
488 authProvider + " library is not loaded");
492 TokenTable tokenInfo = mFactory.requestRefreshTokenInfo(refreshToken);
494 Log.d("access token : " + tokenInfo.getAccesstoken());
495 Log.d("refresh token : " + tokenInfo.getRefreshtoken());
496 Log.d("expired time : " + tokenInfo.getExpiredtime());
501 public HashMap<String, Object> searchUserAboutUuid(String uuid) {
502 // search user info about uuid
503 HashMap<String, Object> condition = new HashMap<>();
504 condition.put(Constants.KEYFIELD_UUID, uuid);
506 ArrayList<HashMap<String, Object>> recordList = AccountDBManager
507 .getInstance().selectRecord(Constants.USER_TABLE, condition);
508 HashMap<String, Object> response = makeSearchUserResponse(recordList);
513 private HashMap<String, Object> makeSearchUserResponse(
514 ArrayList<HashMap<String, Object>> recordList) {
515 HashMap<String, Object> response = new HashMap<>();
516 ArrayList<HashMap<String, Object>> ulist = new ArrayList<>();
518 for (HashMap<String, Object> record : recordList) {
519 HashMap<String, Object> uInfo = new HashMap<>();
520 String uid = record.get(Constants.KEYFIELD_UUID).toString();
521 uInfo.put(Constants.RESP_UUID, uid);
522 record.remove(Constants.KEYFIELD_UUID);
523 uInfo.put(Constants.RESP_USER_INFO, record);
527 response.put(Constants.RESP_USER_LIST, ulist);
528 Log.d("User List " + response.toString());
533 // TODO: It will be changed
534 public HashMap<String, Object> searchUserAboutCriteria(String criteria) {
536 String[] searchType = getSearchType(criteria);
538 // search user info about criteria
539 HashMap<String, Object> condition = new HashMap<>();
540 condition.put(searchType[0], searchType[1]);
542 ArrayList<HashMap<String, Object>> recordList = AccountDBManager
543 .getInstance().selectRecord(Constants.USER_TABLE, condition);
544 HashMap<String, Object> response = makeSearchUserResponse(recordList);
548 // TODO: It will be changed
549 private String[] getSearchType(String criteria) {
550 String[] searchType = criteria.split(":");
551 String searchKey = searchType[0];
552 String searchValue = searchType[1];
554 if (searchKey == null || searchValue == null) {
555 throw new BadRequestException("search key or value is null");
561 public void deleteDevice(String uid, String di) {
563 HashSet<String> diSet = new HashSet<String>();
566 // token table search criteria
567 HashMap<String, Object> condition = new HashMap<>();
568 condition.put(Constants.KEYFIELD_UUID, uid);
569 condition.put(Constants.KEYFIELD_DID, di);
571 // delete Token information from the DB
572 AccountDBManager.getInstance().deleteRecord(Constants.TOKEN_TABLE,
574 // delete device ID from all groups in the DB
575 GroupManager.getInstance().deleteDevicesFromAllGroup(di);