diff --git a/android/app/src/main/java/com/epfl/dedis/hbt/data/user/UserDataSource.kt b/android/app/src/main/java/com/epfl/dedis/hbt/data/user/UserDataSource.kt index 4159e162..b333ded4 100644 --- a/android/app/src/main/java/com/epfl/dedis/hbt/data/user/UserDataSource.kt +++ b/android/app/src/main/java/com/epfl/dedis/hbt/data/user/UserDataSource.kt @@ -2,6 +2,8 @@ package com.epfl.dedis.hbt.data.user import android.content.SharedPreferences import com.epfl.dedis.hbt.data.Result +import com.epfl.dedis.hbt.data.document.Portrait +import com.epfl.dedis.hbt.service.document.DocumentService import com.epfl.dedis.hbt.service.json.JsonService import com.epfl.dedis.hbt.service.json.JsonType.USER_DATA import javax.inject.Inject @@ -13,7 +15,8 @@ import javax.inject.Singleton @Singleton class UserDataSource @Inject constructor( private val sharedPref: SharedPreferences, - private val jsonService: JsonService + private val jsonService: JsonService, + private val documentService: DocumentService ) { private val usernamesKey: String = "users" @@ -47,11 +50,23 @@ class UserDataSource @Inject constructor( return username == usernamesKey || users.containsKey(username) } - fun register(username: String, pincode: Int, passport: String, role: Role): Result { + fun register( + username: String, + pincode: Int, + passport: String, + role: Role, + portrait: Portrait + ): Result { if (isRegistered(username)) return Result.Error(Exception("Already registered")) // create user val user = User(username, pincode, passport, role) + val call = documentService.create(user, portrait, false) + val response = call.execute() + if (response.errorBody() != null) { + return Result.Error(Exception("Failed to register : " + response.message())) + } + users[username] = user //create wallet diff --git a/android/app/src/main/java/com/epfl/dedis/hbt/data/user/UserRepository.kt b/android/app/src/main/java/com/epfl/dedis/hbt/data/user/UserRepository.kt index 3f467872..2ae2fdfe 100644 --- a/android/app/src/main/java/com/epfl/dedis/hbt/data/user/UserRepository.kt +++ b/android/app/src/main/java/com/epfl/dedis/hbt/data/user/UserRepository.kt @@ -1,6 +1,7 @@ package com.epfl.dedis.hbt.data.user import com.epfl.dedis.hbt.data.Result +import com.epfl.dedis.hbt.data.document.Portrait import javax.inject.Inject import javax.inject.Singleton @@ -36,9 +37,15 @@ class UserRepository @Inject constructor(private val dataSource: UserDataSource) loggedInUser = null } - fun register(username: String, pincode: String, passport: String, role: Role): Result { + fun register( + username: String, + pincode: String, + passport: String, + role: Role, + portrait: Portrait + ): Result { val pin = pincode.toIntOrNull() ?: return Result.Error(NumberFormatException()) - val result = dataSource.register(username, pin, passport, role) + val result = dataSource.register(username, pin, passport, role, portrait) if (result is Result.Success) { setLoggedInUser(result.data) diff --git a/android/app/src/main/java/com/epfl/dedis/hbt/service/document/DocumentService.kt b/android/app/src/main/java/com/epfl/dedis/hbt/service/document/DocumentService.kt index c6b7989d..86df9f62 100644 --- a/android/app/src/main/java/com/epfl/dedis/hbt/service/document/DocumentService.kt +++ b/android/app/src/main/java/com/epfl/dedis/hbt/service/document/DocumentService.kt @@ -1,6 +1,9 @@ package com.epfl.dedis.hbt.service.document import com.epfl.dedis.hbt.data.document.Document +import com.epfl.dedis.hbt.data.document.Portrait +import com.epfl.dedis.hbt.data.user.User +import okhttp3.MediaType import okhttp3.RequestBody import retrofit2.Call import retrofit2.http.Multipart @@ -18,4 +21,13 @@ interface DocumentService { @Part("image") image: RequestBody, @Part("registered") registered: Boolean ): Call + + fun create(user: User, portrait: Portrait, registered: Boolean): Call = + create( + user.name, + user.passport, + user.role.ordinal, + RequestBody.create(MediaType.parse(portrait.type), portrait.data), + registered + ) } \ No newline at end of file diff --git a/android/app/src/main/java/com/epfl/dedis/hbt/service/passport/Passport.kt b/android/app/src/main/java/com/epfl/dedis/hbt/service/passport/Passport.kt index a168cecd..04fde74b 100644 --- a/android/app/src/main/java/com/epfl/dedis/hbt/service/passport/Passport.kt +++ b/android/app/src/main/java/com/epfl/dedis/hbt/service/passport/Passport.kt @@ -1,5 +1,6 @@ package com.epfl.dedis.hbt.service.passport +import com.epfl.dedis.hbt.data.document.Portrait import com.epfl.dedis.hbt.service.passport.mrz.MRZInfo import org.jmrtd.lds.SODFile import org.jmrtd.lds.icao.DG11File @@ -7,5 +8,6 @@ import org.jmrtd.lds.icao.DG11File data class Passport( val mrzInfo: MRZInfo, val sodFile: SODFile, + val portrait: Portrait, val dg11File: DG11File? ) \ No newline at end of file diff --git a/android/app/src/main/java/com/epfl/dedis/hbt/service/passport/ncf/NFCReader.kt b/android/app/src/main/java/com/epfl/dedis/hbt/service/passport/ncf/NFCReader.kt index fbb19fb2..9ca2a239 100644 --- a/android/app/src/main/java/com/epfl/dedis/hbt/service/passport/ncf/NFCReader.kt +++ b/android/app/src/main/java/com/epfl/dedis/hbt/service/passport/ncf/NFCReader.kt @@ -8,6 +8,7 @@ import android.util.Log import com.epfl.dedis.hbt.data.Result import com.epfl.dedis.hbt.data.Result.Error import com.epfl.dedis.hbt.data.Result.Success +import com.epfl.dedis.hbt.data.document.Portrait import com.epfl.dedis.hbt.service.passport.Passport import com.epfl.dedis.hbt.service.passport.mrz.BACData import com.epfl.dedis.hbt.service.passport.mrz.MRZInfo @@ -45,10 +46,18 @@ object NFCReader { val passportNFC = PassportNFC(ps, bacData) Log.d("PASS_RESULT", passportNFC.dg1File?.mrzInfo.toString()) + + val portraitImage = passportNFC.dg5File!!.images.first() + val portrait = Portrait( + portraitImage.mimeType, + portraitImage.encoded + ) + Success( Passport( MRZInfo(passportNFC.dg1File!!.mrzInfo), passportNFC.sodFile!!, + portrait, passportNFC.dg11File ) ) diff --git a/android/app/src/main/java/com/epfl/dedis/hbt/service/passport/ncf/PassportNFC.kt b/android/app/src/main/java/com/epfl/dedis/hbt/service/passport/ncf/PassportNFC.kt index 84710bd5..335ebd8a 100644 --- a/android/app/src/main/java/com/epfl/dedis/hbt/service/passport/ncf/PassportNFC.kt +++ b/android/app/src/main/java/com/epfl/dedis/hbt/service/passport/ncf/PassportNFC.kt @@ -10,6 +10,7 @@ import org.jmrtd.lds.* import org.jmrtd.lds.icao.COMFile import org.jmrtd.lds.icao.DG11File import org.jmrtd.lds.icao.DG1File +import org.jmrtd.lds.icao.DG5File import java.io.IOException import java.security.GeneralSecurityException @@ -31,6 +32,9 @@ constructor(service: PassportService, bacData: BACData) { private set var dg1File: DG1File? = null private set + var dg5File: DG5File? = null + private set + var dg11File: DG11File? = null private set @@ -107,6 +111,7 @@ constructor(service: PassportService, bacData: BACData) { try { sodFile = service.getSodFile() dg1File = service.getDG1File() + dg5File = service.getDG5File() dg11File = service.getDG11File() } catch (ioe: IOException) { ioe.printStackTrace() @@ -147,6 +152,10 @@ constructor(service: PassportService, bacData: BACData) { private fun PassportService.getDG1File(): DG1File = getFile(PassportService.EF_DG1) + @Throws(CardServiceException::class, IOException::class) + private fun PassportService.getDG5File(): DG5File = + getFile(PassportService.EF_DG5) + @Throws(CardServiceException::class, IOException::class) private fun PassportService.getDG11File(): DG11File = getFile(PassportService.EF_DG11) diff --git a/android/app/src/main/java/com/epfl/dedis/hbt/ui/register/NFCPassportFragment.kt b/android/app/src/main/java/com/epfl/dedis/hbt/ui/register/NFCPassportFragment.kt index 4155e813..96a65c93 100644 --- a/android/app/src/main/java/com/epfl/dedis/hbt/ui/register/NFCPassportFragment.kt +++ b/android/app/src/main/java/com/epfl/dedis/hbt/ui/register/NFCPassportFragment.kt @@ -64,7 +64,8 @@ class NFCPassportFragment : Fragment() { parentFragmentManager, RegisterFragment.newInstance( passport.mrzInfo.number, - personalData + personalData, + passport.portrait ) ) } diff --git a/android/app/src/main/java/com/epfl/dedis/hbt/ui/register/RegisterFragment.kt b/android/app/src/main/java/com/epfl/dedis/hbt/ui/register/RegisterFragment.kt index 6adc5de5..7d652efa 100644 --- a/android/app/src/main/java/com/epfl/dedis/hbt/ui/register/RegisterFragment.kt +++ b/android/app/src/main/java/com/epfl/dedis/hbt/ui/register/RegisterFragment.kt @@ -13,6 +13,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.Observer import com.epfl.dedis.hbt.R +import com.epfl.dedis.hbt.data.document.Portrait import com.epfl.dedis.hbt.data.user.Role import com.epfl.dedis.hbt.databinding.FragmentRegisterBinding import com.epfl.dedis.hbt.ui.MainActivity @@ -26,13 +27,18 @@ class RegisterFragment : Fragment() { private const val PASSPORT = "PASSPORT" private const val CHECKSUM = "CHECKSUM" - - fun newInstance(passport: String, checksum: String) = RegisterFragment().apply { - val bundle = Bundle() - bundle.putString(PASSPORT, passport) - bundle.putString(CHECKSUM, checksum) - arguments = bundle - } + private const val PORTRAIT_TYPE = "PORTRAIT_TYPE" + private const val PORTRAIT_DATA = "PORTRAIT_DATA" + + fun newInstance(passport: String, checksum: String, portrait: Portrait) = + RegisterFragment().apply { + val bundle = Bundle() + bundle.putString(PASSPORT, passport) + bundle.putString(CHECKSUM, checksum) + bundle.putString(PORTRAIT_TYPE, portrait.type) + bundle.putByteArray(PORTRAIT_DATA, portrait.data) + arguments = bundle + } } private val registerViewModel: RegisterViewModel by viewModels(ownerProducer = { requireActivity() }) @@ -44,6 +50,7 @@ class RegisterFragment : Fragment() { private lateinit var passport: String private lateinit var checksum: ByteArray + private lateinit var portrait: Portrait override fun onCreateView( inflater: LayoutInflater, @@ -61,6 +68,10 @@ class RegisterFragment : Fragment() { checksum = it.toByteArray() passportChecksum.text = it } + + val portraitType = requireArguments().getString(PORTRAIT_TYPE)!! + val portraitData = requireArguments().getByteArray(PORTRAIT_DATA)!! + portrait = Portrait(portraitType, portraitData) } return binding.root @@ -135,6 +146,7 @@ class RegisterFragment : Fragment() { usernameEditText.text.toString(), pincodeEditText.text.toString(), passport, + portrait, checksum, role ) diff --git a/android/app/src/main/java/com/epfl/dedis/hbt/ui/register/RegisterViewModel.kt b/android/app/src/main/java/com/epfl/dedis/hbt/ui/register/RegisterViewModel.kt index 6e052be5..9673878a 100644 --- a/android/app/src/main/java/com/epfl/dedis/hbt/ui/register/RegisterViewModel.kt +++ b/android/app/src/main/java/com/epfl/dedis/hbt/ui/register/RegisterViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.epfl.dedis.hbt.R import com.epfl.dedis.hbt.data.Result.Success +import com.epfl.dedis.hbt.data.document.Portrait import com.epfl.dedis.hbt.data.user.Role import com.epfl.dedis.hbt.data.user.UserRepository import dagger.hilt.android.lifecycle.HiltViewModel @@ -24,11 +25,12 @@ class RegisterViewModel @Inject constructor(private val userRepository: UserRepo username: String, pincode: String, passport: String, + portrait: Portrait, checksum: ByteArray, role: Role ) { // can be launched in a separate asynchronous job - val result = userRepository.register(username, pincode, passport, role) + val result = userRepository.register(username, pincode, passport, role, portrait) if (result is Success) { _registerResult.value = RegisterResult(error = null) diff --git a/android/app/src/test/java/com/epfl/dedis/hbt/data/UserDataSourceTest.kt b/android/app/src/test/java/com/epfl/dedis/hbt/data/UserDataSourceTest.kt index 5031b984..f040a86b 100644 --- a/android/app/src/test/java/com/epfl/dedis/hbt/data/UserDataSourceTest.kt +++ b/android/app/src/test/java/com/epfl/dedis/hbt/data/UserDataSourceTest.kt @@ -2,16 +2,24 @@ package com.epfl.dedis.hbt.data import androidx.test.espresso.matcher.ViewMatchers.assertThat import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.epfl.dedis.hbt.data.document.Document import com.epfl.dedis.hbt.data.user.Role import com.epfl.dedis.hbt.data.user.User import com.epfl.dedis.hbt.data.user.UserDataSource import com.epfl.dedis.hbt.di.JsonModule.provideObjectMapper +import com.epfl.dedis.hbt.service.document.DocumentService +import com.epfl.dedis.hbt.service.document.DocumentServiceTest import com.epfl.dedis.hbt.service.json.JsonService import com.epfl.dedis.hbt.test.MockSharedPreferences import org.hamcrest.CoreMatchers.instanceOf import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import retrofit2.Call +import retrofit2.Response import org.hamcrest.CoreMatchers.`is` as eq /** @@ -21,8 +29,18 @@ import org.hamcrest.CoreMatchers.`is` as eq class UserDataSourceTest { private val jsonService = JsonService(provideObjectMapper()) + private val docService = mock { + on { create(any(), any(), any(), any(), any()) } doReturn mockCall(Document("ID")) + } + + private fun mockCall(body: T): Call = mock { + on { execute() } doReturn Response.success(body) + } + private val preferences = MockSharedPreferences() + private val mockPortrait = DocumentServiceTest.getMockPortrait() + private val alice = User("Alice", 12345, "XX4130X3") private val bob = User("Bob", 67890, "54X62C3", Role.MERCHANT) @@ -33,13 +51,16 @@ class UserDataSourceTest { @Test fun userDataSourceRegistrationTest() { - val dataSource = UserDataSource(preferences, jsonService) + val dataSource = UserDataSource(preferences, jsonService, docService) - assertThat(dataSource.login(alice.name, alice.pincode), instanceOf(Result.Error::class.java)) + assertThat( + dataSource.login(alice.name, alice.pincode), + instanceOf(Result.Error::class.java) + ) assertThat(dataSource.login(bob.name, bob.pincode), instanceOf(Result.Error::class.java)) - dataSource.register(alice.name, alice.pincode, alice.passport, alice.role) - dataSource.register(bob.name, bob.pincode, bob.passport, bob.role) + dataSource.register(alice.name, alice.pincode, alice.passport, alice.role, mockPortrait) + dataSource.register(bob.name, bob.pincode, bob.passport, bob.role, mockPortrait) assertThat(dataSource.login(alice.name, alice.pincode), eq(Result.Success(alice))) assertThat(dataSource.login(bob.name, bob.pincode), eq(Result.Success(bob))) @@ -47,12 +68,12 @@ class UserDataSourceTest { @Test fun userDataSourceStoresUsers() { - val dataSource = UserDataSource(preferences, jsonService) + val dataSource = UserDataSource(preferences, jsonService, docService) - dataSource.register(alice.name, alice.pincode, alice.passport, alice.role) - dataSource.register(bob.name, bob.pincode, bob.passport, bob.role) + dataSource.register(alice.name, alice.pincode, alice.passport, alice.role, mockPortrait) + dataSource.register(bob.name, bob.pincode, bob.passport, bob.role, mockPortrait) - val dataSourceLoaded = UserDataSource(preferences, jsonService) + val dataSourceLoaded = UserDataSource(preferences, jsonService, docService) assertThat(dataSourceLoaded.login(alice.name, alice.pincode), eq(Result.Success(alice))) assertThat(dataSourceLoaded.login(bob.name, bob.pincode), eq(Result.Success(bob)))