|
@@ -0,0 +1,96 @@
|
|
|
+package inn.ocsf.bee.freigeld.serve.rest
|
|
|
+
|
|
|
+import com.oblac.nomen.Nomen
|
|
|
+import dev.samstevens.totp.code.CodeGenerator
|
|
|
+import dev.samstevens.totp.code.DefaultCodeGenerator
|
|
|
+import dev.samstevens.totp.code.DefaultCodeVerifier
|
|
|
+import dev.samstevens.totp.code.HashingAlgorithm
|
|
|
+import dev.samstevens.totp.qr.QrData
|
|
|
+import dev.samstevens.totp.qr.ZxingPngQrGenerator
|
|
|
+import dev.samstevens.totp.secret.DefaultSecretGenerator
|
|
|
+import dev.samstevens.totp.time.SystemTimeProvider
|
|
|
+import dev.samstevens.totp.time.TimeProvider
|
|
|
+import dev.samstevens.totp.util.Utils.getDataUriForImage
|
|
|
+import inn.ocsf.bee.freigeld.core.model.GlobalWorld
|
|
|
+import inn.ocsf.bee.freigeld.core.model.PersonIdentityFullName
|
|
|
+import inn.ocsf.bee.freigeld.core.model.PersonIdentitySecret
|
|
|
+import inn.ocsf.bee.freigeld.core.model.data.PersonData
|
|
|
+import inn.ocsf.bee.freigeld.core.repo.PersonRepository
|
|
|
+import inn.ocsf.bee.freigeld.utils.KeyValueBucket.personToSite
|
|
|
+import inn.ocsf.bee.freigeld.utils.KeyValueBucket.secretToSite
|
|
|
+import inn.ocsf.bee.freigeld.utils.KeyValueStorage
|
|
|
+import org.apache.commons.lang3.RandomStringUtils
|
|
|
+import org.springframework.http.ResponseEntity
|
|
|
+import org.springframework.web.bind.annotation.*
|
|
|
+import java.util.*
|
|
|
+import javax.inject.Inject
|
|
|
+
|
|
|
+
|
|
|
+@RestController
|
|
|
+class PersonController {
|
|
|
+
|
|
|
+ @Inject
|
|
|
+ private lateinit var storage: KeyValueStorage
|
|
|
+
|
|
|
+ @Inject
|
|
|
+ private lateinit var personRepo: PersonRepository
|
|
|
+
|
|
|
+ @Inject
|
|
|
+ private lateinit var world: GlobalWorld
|
|
|
+
|
|
|
+ @RequestMapping("api/new/person/qr", method = [RequestMethod.GET])
|
|
|
+ fun newqr(@RequestParam("siteId") siteId: UUID): ResponseEntity<QrDataResponse> {
|
|
|
+ val secretGenerator = DefaultSecretGenerator()
|
|
|
+ val secret = secretGenerator.generate()
|
|
|
+
|
|
|
+ storage.put(secretToSite, secret, siteId)
|
|
|
+
|
|
|
+ val data = QrData.Builder().secret(secret).algorithm(HashingAlgorithm.SHA1).label("personal wallet").issuer("FreiGeld").digits(6).period(30).build()
|
|
|
+
|
|
|
+ val generator = ZxingPngQrGenerator()
|
|
|
+ val imageData = generator.generate(data)
|
|
|
+ val mimeType = generator.imageMimeType
|
|
|
+ val dataUri = getDataUriForImage(imageData, mimeType)
|
|
|
+
|
|
|
+ /*
|
|
|
+ val recoveryCodeGenerator = RecoveryCodeGenerator()
|
|
|
+ val codes = recoveryCodeGenerator.generateCodes(16)
|
|
|
+ */
|
|
|
+ return ResponseEntity.ok(QrDataResponse(secret, dataUri))
|
|
|
+ }
|
|
|
+
|
|
|
+ @RequestMapping("api/new/person/check6", method = [RequestMethod.POST])
|
|
|
+ fun checkqr(@RequestBody req: QrSixRequest): ResponseEntity<QrSixResponse> {
|
|
|
+ val timeProvider: TimeProvider = SystemTimeProvider()
|
|
|
+ val codeGenerator: CodeGenerator = DefaultCodeGenerator(HashingAlgorithm.SHA1)
|
|
|
+ val verifier = DefaultCodeVerifier(codeGenerator, timeProvider)
|
|
|
+ verifier.setTimePeriod(30)
|
|
|
+ verifier.setAllowedTimePeriodDiscrepancy(4)
|
|
|
+
|
|
|
+ var register = false
|
|
|
+
|
|
|
+ val thisPerson = if (verifier.isValidCode(req.key, req.code)) { //new registration
|
|
|
+ val person = PersonData.NaturalPersonImpl("${Nomen.randomName()}_${RandomStringUtils.randomAlphanumeric(5)}")
|
|
|
+ world.addPerson(person)
|
|
|
+ world.setPersonIdentity(person, PersonIdentityFullName(person.fullName))
|
|
|
+ world.setPersonIdentity(person, PersonIdentitySecret(req.key))
|
|
|
+ storage.put(personToSite, person.id, req.siteId)
|
|
|
+ register = true
|
|
|
+ person
|
|
|
+ } else {
|
|
|
+ storage.getByValue<UUID>(personToSite, req.siteId).map { id -> personRepo.findOneByPersonId(id).orElseThrow { throw RuntimeException("strange person ${id} not found") } }.filterNotNull().filter {
|
|
|
+ val secret = it.identities.values.filter { it is PersonIdentitySecret }.first()
|
|
|
+ verifier.isValidCode(secret.key, req.code)
|
|
|
+ }.map { it.person }.firstOrNull()
|
|
|
+ }
|
|
|
+
|
|
|
+ return ResponseEntity.ok(QrSixResponse(thisPerson != null, register))
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+data class QrDataResponse(val key: String, val img: String)
|
|
|
+
|
|
|
+data class QrSixRequest(val siteId: UUID, val code: String, val key: String)
|
|
|
+
|
|
|
+data class QrSixResponse(val ok: Boolean, val register: Boolean)
|