From 72b33248217cadfc6289a8d352eec97102e49bb5 Mon Sep 17 00:00:00 2001 From: Denis Oleynik Date: Wed, 19 Feb 2025 17:45:05 +0300 Subject: [PATCH] Add full Basic authorization and sync with backend-server --- .../java/ru/myitschool/work/data/dto/LoginDto.kt | 4 ++-- .../java/ru/myitschool/work/data/dto/Role.kt | 5 +++++ .../ru/myitschool/work/data/dto/UserInfoDto.kt | 16 ++++++++++++---- .../work/data/repo/AccountRepositoryImpl.kt | 8 ++++---- .../data/repo/AuthorizationRepositoryImpl.kt | 11 +++++++---- .../ru/myitschool/work/data/source/AccountApi.kt | 9 +++++---- .../work/data/source/AccountNetworkDataSource.kt | 8 ++++---- .../work/data/source/AuthorizationApi.kt | 11 +++-------- .../source/AuthorizationNetworkDataSource.kt | 4 ++-- .../source/AuthorizationStorageDataSource.kt | 14 +++++++------- .../work/domain/profile/GetUserInfoUseCase.kt | 2 +- .../work/domain/profile/OpenByQrUseCase.kt | 2 +- .../domain/profile/repo/UserInfoRepository.kt | 4 ++-- .../ru/myitschool/work/ui/login/LoginFragment.kt | 5 +++++ .../myitschool/work/ui/login/LoginViewModel.kt | 8 ++++---- 15 files changed, 64 insertions(+), 47 deletions(-) create mode 100644 app/src/main/java/ru/myitschool/work/data/dto/Role.kt diff --git a/app/src/main/java/ru/myitschool/work/data/dto/LoginDto.kt b/app/src/main/java/ru/myitschool/work/data/dto/LoginDto.kt index 0168aa0..3256c0f 100644 --- a/app/src/main/java/ru/myitschool/work/data/dto/LoginDto.kt +++ b/app/src/main/java/ru/myitschool/work/data/dto/LoginDto.kt @@ -4,7 +4,7 @@ import com.google.gson.annotations.SerializedName class LoginDto( @SerializedName("username") - val username: String?, + val username: String, @SerializedName("password") - val password: String? + val password: String ) \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/dto/Role.kt b/app/src/main/java/ru/myitschool/work/data/dto/Role.kt new file mode 100644 index 0000000..9697dc6 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/data/dto/Role.kt @@ -0,0 +1,5 @@ +package ru.myitschool.work.data.dto + +enum class Role { + ADMIN, USER +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/dto/UserInfoDto.kt b/app/src/main/java/ru/myitschool/work/data/dto/UserInfoDto.kt index 0ffc7db..285c720 100644 --- a/app/src/main/java/ru/myitschool/work/data/dto/UserInfoDto.kt +++ b/app/src/main/java/ru/myitschool/work/data/dto/UserInfoDto.kt @@ -3,12 +3,20 @@ package ru.myitschool.work.data.dto import com.google.gson.annotations.SerializedName class UserInfoDto( + @SerializedName("id") + val id: Int, @SerializedName("name") - val fullname: String?, + val fullname: String, + @SerializedName("login") + val login: String, + @SerializedName("role") + val role: Role, + @SerializedName("blocked") + val blocked: Boolean, @SerializedName("photo") - val imageUrl: String?, + val imageUrl: String, @SerializedName("position") - val position: String?, + val position: String, @SerializedName("lastVisit") - val lastEntry: String?, + val lastEntry: String, ) \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/repo/AccountRepositoryImpl.kt b/app/src/main/java/ru/myitschool/work/data/repo/AccountRepositoryImpl.kt index 51b9997..dabe3ea 100644 --- a/app/src/main/java/ru/myitschool/work/data/repo/AccountRepositoryImpl.kt +++ b/app/src/main/java/ru/myitschool/work/data/repo/AccountRepositoryImpl.kt @@ -15,18 +15,18 @@ class AccountRepositoryImpl @Inject constructor( private val accountNetworkDataSource: AccountNetworkDataSource, private val userInfoMapper: Lazy, ): UserInfoRepository { - override suspend fun getInfo(username: String): Result { + override suspend fun getInfo(basicAuth: String): Result { return withContext(Dispatchers.IO) { - accountNetworkDataSource.getInfo(username).fold( + accountNetworkDataSource.getInfo(basicAuth).fold( onSuccess = { value -> userInfoMapper.get().invoke(value) }, onFailure = { error -> Result.failure(error) } ) } } - override suspend fun openByQr(username: String, content: String): Result { + override suspend fun openByQr(basicAuth: String, content: String): Result { return withContext(Dispatchers.IO) { - accountNetworkDataSource.openByQr(username, content) + accountNetworkDataSource.openByQr(basicAuth, content) } } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/repo/AuthorizationRepositoryImpl.kt b/app/src/main/java/ru/myitschool/work/data/repo/AuthorizationRepositoryImpl.kt index b874e98..46ce7dc 100644 --- a/app/src/main/java/ru/myitschool/work/data/repo/AuthorizationRepositoryImpl.kt +++ b/app/src/main/java/ru/myitschool/work/data/repo/AuthorizationRepositoryImpl.kt @@ -1,5 +1,6 @@ package ru.myitschool.work.data.repo +import android.util.Base64 import dagger.Reusable import ru.myitschool.work.data.source.AuthorizationNetworkDataSource import ru.myitschool.work.data.source.AuthorizationStorageDataSource @@ -19,19 +20,21 @@ class AuthorizationRepositoryImpl @Inject constructor( override suspend fun login(data: LoginDto): Result { return withContext(Dispatchers.IO) { - authorizationNetworkDataSource.get().checkLogin(data.username!!) + val basicAuthToken = Base64.encodeToString("${data.username}:${data.password}".toByteArray(), Base64.NO_WRAP) + + authorizationNetworkDataSource.get().checkAuthCredentials(basicAuthToken) .onSuccess { - authorizationStorageDataSource.get().updateLogin(data.username) + authorizationStorageDataSource.get().updateBasicAuth(basicAuthToken) } } } override suspend fun logout() { - authorizationStorageDataSource.get().updateLogin(null) + authorizationStorageDataSource.get().updateBasicAuth(null) } override suspend fun getLogin(): Result { - val result = authorizationStorageDataSource.get().login.firstOrNull() + val result = authorizationStorageDataSource.get().basicAuthToken.firstOrNull() return if (result == null) { Result.failure(Exception("Not authorize")) } else { diff --git a/app/src/main/java/ru/myitschool/work/data/source/AccountApi.kt b/app/src/main/java/ru/myitschool/work/data/source/AccountApi.kt index 2a5101c..8bc149b 100644 --- a/app/src/main/java/ru/myitschool/work/data/source/AccountApi.kt +++ b/app/src/main/java/ru/myitschool/work/data/source/AccountApi.kt @@ -2,20 +2,21 @@ package ru.myitschool.work.data.source import retrofit2.http.Body import retrofit2.http.GET +import retrofit2.http.Header import retrofit2.http.PATCH import retrofit2.http.Path import ru.myitschool.work.data.dto.OpenQrDto import ru.myitschool.work.data.dto.UserInfoDto interface AccountApi { - @GET("api/{username}/info") + @GET("api/employee/info") suspend fun getInfo( - @Path("username") username: String + @Header("Authorization") basicAuth: String ) : UserInfoDto - @PATCH("api/{username}/open") + @PATCH("api/employee/open") suspend fun openByQr( - @Path("username") username: String, + @Header("Authorization") basicAuth: String, @Body content: OpenQrDto ) } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/source/AccountNetworkDataSource.kt b/app/src/main/java/ru/myitschool/work/data/source/AccountNetworkDataSource.kt index 4010778..166faf4 100644 --- a/app/src/main/java/ru/myitschool/work/data/source/AccountNetworkDataSource.kt +++ b/app/src/main/java/ru/myitschool/work/data/source/AccountNetworkDataSource.kt @@ -14,14 +14,14 @@ class AccountNetworkDataSource @Inject constructor( retrofit.create(AccountApi::class.java) } - suspend fun getInfo(username: String): Result { - return kotlin.runCatching { api.getInfo(username = username) } + suspend fun getInfo(basicAuth: String): Result { + return kotlin.runCatching { api.getInfo(basicAuth = basicAuth) } } - suspend fun openByQr(username: String, content: String): Result { + suspend fun openByQr(basicAuth: String, content: String): Result { return kotlin.runCatching { api.openByQr( - username = username, + basicAuth = basicAuth, content = OpenQrDto(value = content) ) } diff --git a/app/src/main/java/ru/myitschool/work/data/source/AuthorizationApi.kt b/app/src/main/java/ru/myitschool/work/data/source/AuthorizationApi.kt index 182edff..c1f4a0c 100644 --- a/app/src/main/java/ru/myitschool/work/data/source/AuthorizationApi.kt +++ b/app/src/main/java/ru/myitschool/work/data/source/AuthorizationApi.kt @@ -1,19 +1,14 @@ package ru.myitschool.work.data.source import retrofit2.Response -import retrofit2.http.Body import retrofit2.http.GET +import retrofit2.http.Header import retrofit2.http.POST import retrofit2.http.Path interface AuthorizationApi { - @GET("api/{username}/auth") - suspend fun checkLogin( - @Path("username") username: String - ) : Response - - @GET("api/login") + @POST("api/login") suspend fun login( - @Body content: AccountApi + @Header("Authorization") basicAuthorization: String, ): Response } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/source/AuthorizationNetworkDataSource.kt b/app/src/main/java/ru/myitschool/work/data/source/AuthorizationNetworkDataSource.kt index a728203..ad05a86 100644 --- a/app/src/main/java/ru/myitschool/work/data/source/AuthorizationNetworkDataSource.kt +++ b/app/src/main/java/ru/myitschool/work/data/source/AuthorizationNetworkDataSource.kt @@ -13,8 +13,8 @@ class AuthorizationNetworkDataSource @Inject constructor( retrofit.create(AuthorizationApi::class.java) } - suspend fun checkLogin(username: String): Result { - return kotlin.runCatching { api.checkLogin(username = username) }.fold( + suspend fun checkAuthCredentials(basicAuth: String): Result { + return kotlin.runCatching { api.login("Basic $basicAuth") }.fold( onSuccess = { response -> when (response.code()) { 200 -> Result.success(Unit) diff --git a/app/src/main/java/ru/myitschool/work/data/source/AuthorizationStorageDataSource.kt b/app/src/main/java/ru/myitschool/work/data/source/AuthorizationStorageDataSource.kt index d4ce936..9184cf2 100644 --- a/app/src/main/java/ru/myitschool/work/data/source/AuthorizationStorageDataSource.kt +++ b/app/src/main/java/ru/myitschool/work/data/source/AuthorizationStorageDataSource.kt @@ -17,22 +17,22 @@ class AuthorizationStorageDataSource @Inject constructor( @ApplicationContext private val context: Context, ) { private val Context.storage: DataStore by preferencesDataStore(name = NAME) - val login: Flow = context.storage.data.map { preferences -> - preferences[LOGIN_KEY] + val basicAuthToken: Flow = context.storage.data.map { preferences -> + preferences[TOKEN_KEY] } - suspend fun updateLogin(username: String?) { + suspend fun updateBasicAuth(token: String?) { context.storage.edit { settings -> - if (username != null) { - settings[LOGIN_KEY] = username + if (token != null) { + settings[TOKEN_KEY] = "Basic $token" } else { - settings.remove(LOGIN_KEY) + settings.remove(TOKEN_KEY) } } } private companion object { const val NAME = "auth_data" - val LOGIN_KEY = stringPreferencesKey("login") + val TOKEN_KEY = stringPreferencesKey("basicAuthToken") } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/profile/GetUserInfoUseCase.kt b/app/src/main/java/ru/myitschool/work/domain/profile/GetUserInfoUseCase.kt index d9cda8f..f0394f9 100644 --- a/app/src/main/java/ru/myitschool/work/domain/profile/GetUserInfoUseCase.kt +++ b/app/src/main/java/ru/myitschool/work/domain/profile/GetUserInfoUseCase.kt @@ -11,7 +11,7 @@ class GetUserInfoUseCase @Inject constructor( ) { suspend operator fun invoke(): Result { return getLoginUseCase().fold( - onSuccess = { username -> repo.getInfo(username = username) }, + onSuccess = { basicAuth -> repo.getInfo(basicAuth) }, onFailure = { error -> Result.failure(error) } ) } diff --git a/app/src/main/java/ru/myitschool/work/domain/profile/OpenByQrUseCase.kt b/app/src/main/java/ru/myitschool/work/domain/profile/OpenByQrUseCase.kt index 8e6d8ae..b0e6578 100644 --- a/app/src/main/java/ru/myitschool/work/domain/profile/OpenByQrUseCase.kt +++ b/app/src/main/java/ru/myitschool/work/domain/profile/OpenByQrUseCase.kt @@ -10,7 +10,7 @@ class OpenByQrUseCase @Inject constructor( ) { suspend operator fun invoke(content: String): Result { return getLoginUseCase().fold( - onSuccess = { username -> repo.openByQr(username = username, content = content) }, + onSuccess = { basicAuth -> repo.openByQr(basicAuth = basicAuth, content = content) }, onFailure = { error -> Result.failure(error) } ) } diff --git a/app/src/main/java/ru/myitschool/work/domain/profile/repo/UserInfoRepository.kt b/app/src/main/java/ru/myitschool/work/domain/profile/repo/UserInfoRepository.kt index d10df45..db56cf0 100644 --- a/app/src/main/java/ru/myitschool/work/domain/profile/repo/UserInfoRepository.kt +++ b/app/src/main/java/ru/myitschool/work/domain/profile/repo/UserInfoRepository.kt @@ -3,7 +3,7 @@ package ru.myitschool.work.domain.profile.repo import ru.myitschool.work.domain.profile.entities.UserInfoEntity interface UserInfoRepository { - suspend fun getInfo(username: String) : Result + suspend fun getInfo(basicAuth: String) : Result - suspend fun openByQr(username: String, content: String) : Result + suspend fun openByQr(basicAuth: String, content: String) : Result } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt b/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt index 17d2623..abb97b2 100644 --- a/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt +++ b/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt @@ -64,6 +64,11 @@ class LoginFragment : Fragment(R.layout.fragment_login) { viewModel.inputLogin(s.toString()) } }) + binding.password.addTextChangedListener(object : TextChangedListener() { + override fun afterTextChanged(s: Editable?) { + viewModel.inputPassword(s.toString()) + } + }) } diff --git a/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt index 3e8811c..104df6c 100644 --- a/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt +++ b/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt @@ -32,12 +32,12 @@ class LoginViewModel @Inject constructor( val state = _state.asStateFlow() private var login: String = "" - private var password: String = "123"//пока пароль по дефалту + private var password: String = "" fun clickLogin() { viewModelScope.launch { _state.update { State.Loading } - val data = LoginDto(login,password) + val data = LoginDto(login, password) loginUseCase.get().invoke(data).fold( onSuccess = { _action.emit(Action.OpenProfile) @@ -56,7 +56,7 @@ class LoginViewModel @Inject constructor( - //Чек пароля на правильность + // Чек пароля на правильность fun inputLogin(login: String) { this.login = login viewModelScope.launch { @@ -67,7 +67,7 @@ class LoginViewModel @Inject constructor( } } } - fun passwordCheck(password: String){ + fun inputPassword(password: String){ this.password = password }