UserManager.kt 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /*
  2. * Nextcloud Talk application
  3. *
  4. * @author Mario Danic
  5. * @author Andy Scherzinger
  6. * Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
  7. * Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation, either version 3 of the License, or
  12. * at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. package com.nextcloud.talk.users
  23. import android.text.TextUtils
  24. import com.bluelinelabs.logansquare.LoganSquare
  25. import com.nextcloud.talk.data.user.UsersRepository
  26. import com.nextcloud.talk.data.user.model.User
  27. import com.nextcloud.talk.models.ExternalSignalingServer
  28. import com.nextcloud.talk.models.json.capabilities.Capabilities
  29. import com.nextcloud.talk.models.json.push.PushConfigurationState
  30. import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
  31. import io.reactivex.Maybe
  32. import io.reactivex.Single
  33. @Suppress("TooManyFunctions")
  34. class UserManager internal constructor(private val userRepository: UsersRepository) : CurrentUserProviderNew {
  35. val users: Single<List<User>>
  36. get() = userRepository.getUsers()
  37. val usersScheduledForDeletion: Single<List<User>>
  38. get() = userRepository.getUsersScheduledForDeletion()
  39. override val currentUser: Maybe<User>
  40. get() {
  41. return userRepository.getActiveUser()
  42. }
  43. fun deleteUser(internalId: Long): Int {
  44. return userRepository.deleteUser(userRepository.getUserWithId(internalId).blockingGet())
  45. }
  46. fun getUserById(userId: String): Maybe<User> {
  47. return userRepository.getUserWithUserId(userId)
  48. }
  49. fun getUserWithId(id: Long): Maybe<User> {
  50. return userRepository.getUserWithId(id)
  51. }
  52. fun disableAllUsersWithoutId(id: Long): Single<Int> {
  53. val results = userRepository.getUsersWithoutUserId(id)
  54. return results.map { users ->
  55. var count = 0
  56. if (users.isNotEmpty()) {
  57. for (entity in users) {
  58. entity.current = false
  59. userRepository.updateUser(entity)
  60. count++
  61. }
  62. }
  63. count
  64. }
  65. }
  66. fun checkIfUserIsScheduledForDeletion(username: String, server: String): Single<Boolean> {
  67. return userRepository
  68. .getUserWithUsernameAndServer(username, server)
  69. .map { it.scheduledForDeletion }
  70. .switchIfEmpty(Single.just(false))
  71. }
  72. fun getUserWithInternalId(id: Long): Maybe<User> {
  73. return userRepository.getUserWithIdNotScheduledForDeletion(id)
  74. }
  75. fun checkIfUserExists(username: String, server: String): Single<Boolean> {
  76. return userRepository
  77. .getUserWithUsernameAndServer(username, server)
  78. .map { true }
  79. .switchIfEmpty(Single.just(false))
  80. }
  81. /**
  82. * Don't ask
  83. *
  84. * @return `true` if the user was updated **AND** there is another user to set as active, `false` otherwise
  85. */
  86. fun scheduleUserForDeletionWithId(id: Long): Single<Boolean> {
  87. return userRepository.getUserWithId(id)
  88. .map { user ->
  89. user.scheduledForDeletion = true
  90. user.current = false
  91. userRepository.updateUser(user)
  92. }
  93. .flatMap { getAnyUserAndSetAsActive() }
  94. .map { true }
  95. .switchIfEmpty(Single.just(false))
  96. }
  97. private fun getAnyUserAndSetAsActive(): Maybe<User> {
  98. val results = userRepository.getUsersNotScheduledForDeletion()
  99. return results
  100. .flatMapMaybe {
  101. if (it.isNotEmpty()) {
  102. val user = it.first()
  103. user.apply {
  104. current = true
  105. }.also { currentUser ->
  106. userRepository.updateUser(currentUser)
  107. }
  108. Maybe.just(user)
  109. } else {
  110. Maybe.empty()
  111. }
  112. }
  113. }
  114. fun updateExternalSignalingServer(id: Long, externalSignalingServer: ExternalSignalingServer): Single<Int> {
  115. return userRepository.getUserWithId(id).map { user ->
  116. user.externalSignalingServer = externalSignalingServer
  117. userRepository.updateUser(user)
  118. }.toSingle()
  119. }
  120. fun updateOrCreateUser(user: User): Single<Int> {
  121. return Single.fromCallable {
  122. when (user.id) {
  123. null -> userRepository.insertUser(user).toInt()
  124. else -> userRepository.updateUser(user)
  125. }
  126. }
  127. }
  128. fun setUserAsActive(user: User): Single<Boolean> {
  129. return userRepository.setUserAsActiveWithId(user.id!!)
  130. }
  131. fun storeProfile(username: String?, userAttributes: UserAttributes): Maybe<User> {
  132. val userMaybe: Maybe<User> = findUser(null, userAttributes)
  133. return userMaybe
  134. .map { user: User? ->
  135. when (user) {
  136. null -> createUser(
  137. username,
  138. userAttributes
  139. )
  140. else -> {
  141. user.token = userAttributes.token
  142. user.baseUrl = userAttributes.serverUrl
  143. user.current = true
  144. user.userId = userAttributes.userId
  145. user.token = userAttributes.token
  146. user.displayName = userAttributes.displayName
  147. user.clientCertificate = userAttributes.certificateAlias
  148. updateUserData(
  149. user,
  150. userAttributes
  151. )
  152. user
  153. }
  154. }
  155. }
  156. .switchIfEmpty(Maybe.just(createUser(username, userAttributes)))
  157. .map { user ->
  158. userRepository.insertUser(user)
  159. }
  160. .flatMap { id ->
  161. userRepository.getUserWithId(id)
  162. }
  163. }
  164. @Deprecated("Only available for migration, use updateExternalSignalingServer or create new methods")
  165. fun createOrUpdateUser(
  166. username: String?,
  167. userAttributes: UserAttributes
  168. ): Maybe<User> {
  169. val userMaybe: Maybe<User> = findUser(username, userAttributes)
  170. return userMaybe
  171. .map { user: User? ->
  172. when (user) {
  173. null -> createUser(
  174. username,
  175. userAttributes
  176. )
  177. else -> {
  178. updateUserData(
  179. user,
  180. userAttributes
  181. )
  182. user
  183. }
  184. }
  185. }
  186. .switchIfEmpty(Maybe.just(createUser(username, userAttributes)))
  187. .map { user ->
  188. userRepository.insertUser(user)
  189. }
  190. .flatMap { id ->
  191. userRepository.getUserWithId(id)
  192. }
  193. }
  194. private fun findUser(username: String?, userAttributes: UserAttributes): Maybe<User> {
  195. return if (userAttributes.id != null) {
  196. userRepository.getUserWithId(userAttributes.id)
  197. } else if (username != null && userAttributes.serverUrl != null) {
  198. userRepository.getUserWithUsernameAndServer(username, userAttributes.serverUrl)
  199. } else {
  200. Maybe.empty()
  201. }
  202. }
  203. fun getUserWithUsernameAndServer(username: String, server: String): Maybe<User> {
  204. return userRepository.getUserWithUsernameAndServer(username, server)
  205. }
  206. private fun updateUserData(user: User, userAttributes: UserAttributes) {
  207. user.userId = userAttributes.userId
  208. user.token = userAttributes.token
  209. user.displayName = userAttributes.displayName
  210. if (userAttributes.pushConfigurationState != null) {
  211. user.pushConfigurationState = LoganSquare
  212. .parse(userAttributes.pushConfigurationState, PushConfigurationState::class.java)
  213. }
  214. if (userAttributes.capabilities != null) {
  215. user.capabilities = LoganSquare
  216. .parse(userAttributes.capabilities, Capabilities::class.java)
  217. }
  218. user.clientCertificate = userAttributes.certificateAlias
  219. if (userAttributes.externalSignalingServer != null) {
  220. user.externalSignalingServer = LoganSquare
  221. .parse(userAttributes.externalSignalingServer, ExternalSignalingServer::class.java)
  222. }
  223. user.current = userAttributes.currentUser == true
  224. }
  225. private fun createUser(username: String?, userAttributes: UserAttributes): User {
  226. val user = User()
  227. user.baseUrl = userAttributes.serverUrl
  228. user.username = username
  229. user.token = userAttributes.token
  230. if (!TextUtils.isEmpty(userAttributes.displayName)) {
  231. user.displayName = userAttributes.displayName
  232. }
  233. if (userAttributes.pushConfigurationState != null) {
  234. user.pushConfigurationState = LoganSquare
  235. .parse(userAttributes.pushConfigurationState, PushConfigurationState::class.java)
  236. }
  237. if (!TextUtils.isEmpty(userAttributes.userId)) {
  238. user.userId = userAttributes.userId
  239. }
  240. if (!TextUtils.isEmpty(userAttributes.capabilities)) {
  241. user.capabilities = LoganSquare.parse(userAttributes.capabilities, Capabilities::class.java)
  242. }
  243. if (!TextUtils.isEmpty(userAttributes.certificateAlias)) {
  244. user.clientCertificate = userAttributes.certificateAlias
  245. }
  246. if (!TextUtils.isEmpty(userAttributes.externalSignalingServer)) {
  247. user.externalSignalingServer = LoganSquare
  248. .parse(userAttributes.externalSignalingServer, ExternalSignalingServer::class.java)
  249. }
  250. user.current = userAttributes.currentUser == true
  251. return user
  252. }
  253. companion object {
  254. const val TAG = "UserManager"
  255. }
  256. data class UserAttributes(
  257. val id: Long?,
  258. val serverUrl: String?,
  259. val currentUser: Boolean?,
  260. val userId: String?,
  261. val token: String?,
  262. val displayName: String?,
  263. val pushConfigurationState: String?,
  264. val capabilities: String?,
  265. val certificateAlias: String?,
  266. val externalSignalingServer: String?
  267. )
  268. }