text: /*
   * Copyright © 2016-2019 The Thingsboard Authors
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  import thingsboardApiLogin  from './login.service';
  import angularStorage from 'angular-storage';
  
  export default angular.module('thingsboard.api.user', [thingsboardApiLogin,
      angularStorage])
      .factory('userService', UserService)
      .name;
  
  /*@ngInject*/
  function UserService($http, $q, $rootScope, adminService, dashboardService, timeService, loginService, toast, store, jwtHelper, $translate, $state, $location) {
      var currentUser = null,
          currentUserDetails = null,
          lastPublicDashboardId = null,
          allowedDashboardIds = [],
          redirectParams = null,
          userTokenAccessEnabled = false,
          userLoaded = false;
  
      var refreshTokenQueue = [];
  
      var service = {
          deleteUser: deleteUser,
          getAuthority: getAuthority,
          isPublic: isPublic,
          getPublicId: getPublicId,
          parsePublicId: parsePublicId,
          isAuthenticated: isAuthenticated,
          getCurrentUser: getCurrentUser,
          getCustomerUsers: getCustomerUsers,
          getUser: getUser,
          getTenantAdmins: getTenantAdmins,
          isUserLoaded: isUserLoaded,
          saveUser: saveUser,
          sendActivationEmail: sendActivationEmail,
          getActivationLink: getActivationLink,
          setUserFromJwtToken: setUserFromJwtToken,
          getJwtToken: getJwtToken,
          clearJwtToken: clearJwtToken,
          isJwtTokenValid : isJwtTokenValid,
          validateJwtToken: validateJwtToken,
          refreshJwtToken: refreshJwtToken,
          refreshTokenPending: refreshTokenPending,
          updateAuthorizationHeader: updateAuthorizationHeader,
          setAuthorizationRequestHeader: setAuthorizationRequestHeader,
          setRedirectParams: setRedirectParams,
          gotoDefaultPlace: gotoDefaultPlace,
          forceDefaultPlace: forceDefaultPlace,
          updateLastPublicDashboardId: updateLastPublicDashboardId,
          logout: logout,
          reloadUser: reloadUser,
          isUserTokenAccessEnabled: isUserTokenAccessEnabled,
          loginAsUser: loginAsUser
      }
  
      reloadUser();
  
      return service;
  
      function reloadUser() {
          userLoaded = false;
          loadUser(true).then(function success() {
              notifyUserLoaded();
          }, function fail() {
              notifyUserLoaded();
          });
      }
  
      function updateAndValidateToken(token, prefix, notify) {
          var valid = false;
          var tokenData = jwtHelper.decodeToken(token);
          var issuedAt = tokenData.iat;
          var expTime = tokenData.exp;
          if (issuedAt && expTime) {
              var ttl = expTime - issuedAt;
              if (ttl > 0) {
                  var clientExpiration = new Date().valueOf() + ttl*1000;
                  store.set(prefix, token);
                  store.set(prefix + '_expiration', clientExpiration);
                  valid = true;
              }
          }
          if (!valid && notify) {
              $rootScope.$broadcast('unauthenticated');
          }
      }
  
      function clearTokenData() {
          store.remove('jwt_token');
          store.remove('jwt_token_expiration');
          store.remove('refresh_token');
          store.remove('refresh_token_expiration');
      }
  
      function setUserFromJwtToken(jwtToken, refreshToken, notify, doLogout) {
          currentUser = null;
          currentUserDetails = null;
          lastPublicDashboardId = null;
          userTokenAccessEnabled = false;
          allowedDashboardIds = [];
          if (!jwtToken) {
              clearTokenData();
              if (notify) {
                  $rootScope.$broadcast('unauthenticated', doLogout);
              }
          } else {
              updateAndValidateToken(jwtToken, 'jwt_token', true);
              updateAndValidateToken(refreshToken, 'refresh_token', true);
              if (notify) {
                  loadUser(false).then(function success() {
                      $rootScope.$broadcast('authenticated');
                  }, function fail() {
                      $rootScope.$broadcast('unauthenticated');
                  });
              } else {
                  loadUser(false);
              }
          }
      }
  
      function isAuthenticated() {
          return store.get('jwt_token');
      }
  
      function getJwtToken() {
          return store.get('jwt_token');
      }
  
      function logout() {
          clearJwtToken(true);
      }
  
      function clearJwtToken(doLogout) {
          setUserFromJwtToken(null, null, true, doLogout);
      }
  
      function isJwtTokenValid() {
          return isTokenValid('jwt_token');
      }
  
      function isTokenValid(prefix) {
          var clientExpiration = store.get(prefix + '_expiration');
          return clientExpiration && clientExpiration > new Date().valueOf();
      }
  
      function validateJwtToken(doRefresh) {
          var deferred = $q.defer();
          if (!isTokenValid('jwt_token')) {
              if (doRefresh) {
                  refreshJwtToken().then(function success() {
                      deferred.resolve();
                  }, function fail() {
                      deferred.reject();
                  });
              } else {
                  clearJwtToken(false);
                  deferred.reject();
              }
          } else {
              deferred.resolve();
          }
          return deferred.promise;
      }
  
      function resolveRefreshTokenQueue(data) {
          for (var q=0; q < refreshTokenQueue.length;q++) {
              refreshTokenQueue[q].resolve(data);
          }
          refreshTokenQueue = [];
      }
  
      function rejectRefreshTokenQueue(message) {
          for (var q=0;q<refreshTokenQueue.length;q++) {
              refreshTokenQueue[q].reject(message);
          }
          refreshTokenQueue = [];
      }
  
      function refreshTokenPending() {
          return refreshTokenQueue.length > 0;
      }
  
      function refreshJwtToken() {
          var deferred = $q.defer();
          refreshTokenQueue.push(deferred);
          if (refreshTokenQueue.length === 1) {
              var refreshToken = store.get('refresh_token');
              var refreshTokenValid = isTokenValid('refresh_token');
              setUserFromJwtToken(null, null, false, false);
              if (!refreshTokenValid) {
                  rejectRefreshTokenQueue($translate.instant('access.refresh-token-expired'));
              } else {
                  var refreshTokenRequest = {
                      refreshToken: refreshToken
                  };
                  $http.post('/api/auth/token', refreshTokenRequest).then(function success(response) {
                      var token = response.data.token;
                      var refreshToken = response.data.refreshToken;
                      setUserFromJwtToken(token, refreshToken, false);
                      resolveRefreshTokenQueue(response.data);
                  }, function fail() {
                      clearJwtToken(false);
                      rejectRefreshTokenQueue($translate.instant('access.refresh-token-failed'));
                  });
              }
          }
          return deferred.promise;
      }
  
      function getCurrentUser() {
          return currentUser;
      }
  
      function getAuthority() {
          if (currentUser) {
              return currentUser.authority;
          } else {
              return '';
          }
      }
  
      function isPublic() {
          if (currentUser) {
              return currentUser.isPublic;
          } else {
              return false;
          }
      }
  
      function getPublicId() {
          if (isPublic()) {
              return currentUser.sub;
          } else {
              return null;
          }
      }
  
      function parsePublicId() {
          var token = getJwtToken();
          if (token) {
              var tokenData = jwtHelper.decodeToken(token);
              if (tokenData && tokenData.isPublic) {
                  return tokenData.sub;
              }
          }
          return null;
      }
  
      function isUserLoaded() {
          return userLoaded;
      }
  
      function loadUser(doTokenRefresh) {
  
          var deferred = $q.defer();
  
          function fetchAllowedDashboardIds() {
              var pageLink = {limit: 100};
              var fetchDashboardsPromise;
              if (currentUser.authority === 'TENANT_ADMIN') {
                  fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink);
              } else {
                  fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink);
              }
              fetchDashboardsPromise.then(
                  function success(result) {
                      var dashboards = result.data;
                      for (var d=0;d<dashboards.length;d++) {
                          allowedDashboardIds.push(dashboards[d].id.id);
                      }
                      deferred.resolve();
                  },
                  function fail() {
                      deferred.reject();
                  }
              );
          }
  
          function updateUserLang() {
              if (currentUserDetails.additionalInfo && currentUserDetails.additionalInfo.lang) {
                  $translate.use(currentUserDetails.additionalInfo.lang);
              }
          }
  
          function procceedJwtTokenValidate() {
              validateJwtToken(doTokenRefresh).then(function success() {
                  var jwtToken = store.get('jwt_token');
                  currentUser = jwtHelper.decodeToken(jwtToken);
                  if (currentUser && currentUser.scopes && currentUser.scopes.length > 0) {
                      currentUser.authority = currentUser.scopes[0];
                  } else if (currentUser) {
                      currentUser.authority = "ANONYMOUS";
                  }
                  var sysParamsPromise = loadSystemParams();
                  if (currentUser.isPublic) {
                      $rootScope.forceFullscreen = true;
                      sysParamsPromise.then(
                          () => { fetchAllowedDashboardIds(); },
                          () => { deferred.reject(); }
                      );
                  } else if (currentUser.userId) {
                      getUser(currentUser.userId, true).then(
                          function success(user) {
                              sysParamsPromise.then(
                                  () => {
                                      currentUserDetails = user;
                                      updateUserLang();
                                      $rootScope.forceFullscreen = false;
                                      if (userForceFullscreen()) {
                                          $rootScope.forceFullscreen = true;
                                      }
                                      if ($rootScope.forceFullscreen && (currentUser.authority === 'TENANT_ADMIN' ||
                                          currentUser.authority === 'CUSTOMER_USER')) {
                                          fetchAllowedDashboardIds();
                                      } else {
                                          deferred.resolve();
                                      }
                                  },
                                  () => {
                                      deferred.reject();
                                      logout();
                                  }
                              );
                          },
                          function fail() {
                              deferred.reject();
                              logout();
                          }
                      )
                  } else {
                      deferred.reject();
                  }
              }, function fail() {
                  deferred.reject();
              });
          }
  
          if (!currentUser) {
              var locationSearch = $location.search();
              if (locationSearch.publicId) {
                  loginService.publicLogin(locationSearch.publicId).then(function success(response) {
                      var token = response.data.token;
                      var refreshToken = response.data.refreshToken;
                      updateAndValidateToken(token, 'jwt_token', false);
                      updateAndValidateToken(refreshToken, 'refresh_token', false);
                      procceedJwtTokenValidate();
                  }, function fail() {
                      $location.search('publicId', null);
                      deferred.reject();
                  });
              } else {
                  procceedJwtTokenValidate();
              }
          } else {
              deferred.resolve();
          }
          return deferred.promise;
      }
  
      function loadIsUserTokenAccessEnabled() {
          var deferred = $q.defer();
          if (currentUser.authority === 'SYS_ADMIN' || currentUser.authority === 'TENANT_ADMIN') {
              var url = '/api/user/tokenAccessEnabled';
              $http.get(url).then(function success(response) {
                  userTokenAccessEnabled = response.data;
                  deferred.resolve(response.data);
              }, function fail() {
                  userTokenAccessEnabled = false;
                  deferred.reject();
              });
          } else {
              userTokenAccessEnabled = false;
              deferred.resolve(false);
          }
          return deferred.promise;
      }
  
      function loadSystemParams() {
          var promises = [];
          promises.push(loadIsUserTokenAccessEnabled());
          promises.push(timeService.loadMaxDatapointsLimit());
          return $q.all(promises);
      }
  
      function notifyUserLoaded() {
          if (!userLoaded) {
              userLoaded = true;
              $rootScope.$broadcast('userLoaded');
          }
      }
  
      function updateAuthorizationHeader(headers) {
          var jwtToken = store.get('jwt_token');
          if (jwtToken) {
              headers['X-Authorization'] = 'Bearer ' + jwtToken;
          }
          return jwtToken;
      }
  
      function setAuthorizationRequestHeader(request) {
          var jwtToken = store.get('jwt_token');
          if (jwtToken) {
              request.setRequestHeader('X-Authorization', 'Bearer ' + jwtToken);
          }
          return jwtToken;
      }
  
      function getTenantAdmins(tenantId, pageLink) {
          var deferred = $q.defer();
          var url = '/api/tenant/' + tenantId + '/users?limit=' + pageLink.limit;
          if (angular.isDefined(pageLink.textSearch)) {
              url += '&textSearch=' + pageLink.textSearch;
          }
          if (angular.isDefined(pageLink.idOffset)) {
              url += '&idOffset=' + pageLink.idOffset;
          }
          if (angular.isDefined(pageLink.textOffset)) {
              url += '&textOffset=' + pageLink.textOffset;
          }
          $http.get(url, null).then(function success(response) {
              deferred.resolve(response.data);
          }, function fail() {
              deferred.reject();
          });
          return deferred.promise;
      }
  
      function getCustomerUsers(customerId, pageLink) {
          var deferred = $q.defer();
          var url = '/api/customer/' + customerId + '/users?limit=' + pageLink.limit;
          if (angular.isDefined(pageLink.textSearch)) {
              url += '&textSearch=' + pageLink.textSearch;
          }
          if (angular.isDefined(pageLink.idOffset)) {
              url += '&idOffset=' + pageLink.idOffset;
          }
          if (angular.isDefined(pageLink.textOffset)) {
              url += '&textOffset=' + pageLink.textOffset;
          }
          $http.get(url, null).then(function success(response) {
              deferred.resolve(response.data);
          }, function fail() {
              deferred.reject();
          });
          return deferred.promise;
      }
  
      function saveUser(user, sendActivationMail) {
          var deferred = $q.defer();
          var url = '/api/user';
          if (angular.isDefined(sendActivationMail)) {
              url += '?sendActivationMail=' + sendActivationMail;
          }
          $http.post(url, user).then(function success(response) {
              deferred.resolve(response.data);
          }, function fail() {
              deferred.reject();
          });
          return deferred.promise;
      }
  
      function getUser(userId, ignoreErrors, config) {
          var deferred = $q.defer();
          var url = '/api/user/' + userId;
          if (!config) {
              config = {};
          }
          config = Object.assign(config, { ignoreErrors: ignoreErrors });
          $http.get(url, config).then(function success(response) {
              deferred.resolve(response.data);
          }, function fail() {
              deferred.reject();
          });
          return deferred.promise;
      }
  
      function deleteUser(userId) {
          var deferred = $q.defer();
          var url = '/api/user/' + userId;
          $http.delete(url).then(function success() {
              deferred.resolve();
          }, function fail() {
              deferred.reject();
          });
          return deferred.promise;
      }
  
      function sendActivationEmail(email) {
          var deferred = $q.defer();
          var url = '/api/user/sendActivationMail?email=' + email;
          $http.post(url, null).then(function success(response) {
              deferred.resolve(response);
          }, function fail() {
              deferred.reject();
          });
          return deferred.promise;
      }
  
      function getActivationLink(userId) {
          var deferred = $q.defer();
          var url = `/api/user/${userId}/activationLink`
          $http.get(url).then(function success(response) {
              deferred.resolve(response.data);
          }, function fail() {
              deferred.reject();
          });
          return deferred.promise;
      }
  
      function forceDefaultPlace(to, params) {
          if (currentUser && isAuthenticated()) {
              if (currentUser.authority === 'TENANT_ADMIN' || currentUser.authority === 'CUSTOMER_USER') {
                  if ((userHasDefaultDashboard() && $rootScope.forceFullscreen) || isPublic()) {
                      if (to.name === 'home.profile') {
                          if (userHasProfile()) {
                              return false;
                          } else {
                              return true;
                          }
                      } else if ((to.name === 'home.dashboards.dashboard' || to.name === 'dashboard')
                          && allowedDashboardIds.indexOf(params.dashboardId) > -1) {
                          return false;
                      } else {
                          return true;
                      }
                  }
              }
          }
          return false;
      }
  
      function setRedirectParams(params) {
          redirectParams = params;
      }
  
      function gotoDefaultPlace(params) {
          if (currentUser && isAuthenticated()) {
              var place = redirectParams ? redirectParams.toName : 'home.links';
              params = redirectParams ? redirectParams.params : params;
              redirectParams = null;
              if (currentUser.authority === 'TENANT_ADMIN' || currentUser.authority === 'CUSTOMER_USER') {
                  if (userHasDefaultDashboard()) {
                      place = $rootScope.forceFullscreen ? 'dashboard' : 'home.dashboards.dashboard';
                      params = {dashboardId: currentUserDetails.additionalInfo.defaultDashboardId};
                  } else if (isPublic()) {
                      place = 'dashboard';
                      params = {dashboardId: lastPublicDashboardId};
                  }
              } else if (currentUser.authority === 'SYS_ADMIN') {
                  adminService.checkUpdates().then(
                      function (updateMessage) {
                          if (updateMessage && updateMessage.updateAvailable) {
                              toast.showInfo(updateMessage.message, 0, null, 'bottom right');
                          }
                      }
                  );
              }
              $state.go(place, params, {reload: true});
          } else {
              $state.go('login', params);
          }
      }
  
      function userHasDefaultDashboard() {
          return currentUserDetails &&
                 currentUserDetails.additionalInfo &&
                 currentUserDetails.additionalInfo.defaultDashboardId;
      }
  
      function userForceFullscreen() {
          return (currentUser && currentUser.isPublic) ||
                 (currentUserDetails.additionalInfo &&
                  currentUserDetails.additionalInfo.defaultDashboardFullscreen &&
                  currentUserDetails.additionalInfo.defaultDashboardFullscreen === true);
      }
  
      function userHasProfile() {
          return currentUser && !currentUser.isPublic;
      }
  
      function updateLastPublicDashboardId(dashboardId) {
          if (isPublic()) {
              lastPublicDashboardId = dashboardId;
          }
      }
  
      function isUserTokenAccessEnabled() {
          return userTokenAccessEnabled;
      }
  
      function loginAsUser(userId) {
          var url = '/api/user/' + userId + '/token';
          $http.get(url).then(function success(response) {
              var token = response.data.token;
              var refreshToken = response.data.refreshToken;
              setUserFromJwtToken(token, refreshToken, true);
          }, function fail() {
          });
      }
  
  }
  