I'm trying to encox user password to save in postgresql database. Using kotlin and argon2. I did the functions and in the tests, it works perfectly. But when I save it in the database, and I try to compare the passwords, it always gives an error. Could anyone help?
I created the following functions:
private val argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id, 32, 64)
fun String.encode(): String = argon2.hash(3, 64 * 1024, 1, this.toCharArray())
fun String.checkEncoding(hash: String): Boolean = argon2.verify(hash, this.toCharArray())
and in tests, everything works perfectly
class HashingTest : ShouldSpec({
context("Encoding a string") {
should("encode correctly") {
val dataOne = "S#!sc_%kah"
val encondeOne = dataOne.encode()
val dataTwo = "S#!sc_%kah"
dataTwo.checkEncoding(encondeOne) shouldBe true // works fine!
}
}
})
When I save to the database, and try to compare, I always get an error
//ENTITY
data class User(
override val id: UUID,
val username: String,
val password: String,
) : IEntity
//INSERT SCRITP -> EXPOSED FRAMEWORK
private fun newUser(schema: String, entity: User) {
val table = toUserTable(schema)
table
.insert {
it[id] = entity.id
it[username] = entity.username
it[password] = entity.password.encode()
}
}
//FIND USER
fun findUser(schema: String, model: SignInModel): User? {
val table = toUserTable(schema)
val user = table
.select { table.username eq model.username }
.firstNotNullOfOrNull { toUser(schema, it) }
val verifying = model.password.checkEncoding(user!!.password) // ERROR HERE, ALWAYS RETURNS FALSE
return when (verifying) {
true -> user
false -> null
}
}
I don't know if you used your findUser() and newUser() in a transaction but queries must be called in a transaction like below:
private fun newUser(schema: String, entity: User) {
val table = toUserTable(schema)
transaction {
table
.insert {
it[id] = entity.id
it[username] = entity.username
it[password] = entity.password.encode()
}
}
}
and:
fun findUser(schema: String, model: SignInModel): User? {
val table = toUserTable(schema)
val user = transaction {
table
.select { table.username eq model.username }
.firstNotNullOfOrNull { toUser(schema, it) }
}
val verifying = model.password.checkEncoding(user!!.password)
return when (verifying) {
true -> user
false -> null
}
}
Related
I have the following structure at present:
#Entity
#Table(name = "table_app_settings")
data class AppSetting(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "app_setting_id")
val id: Long? = null,
#Column(name = "app_setting_name")
val name: String = "",
#Column(name = "app_setting_value")
var value: String = "",
#Column(name = "app_setting_type")
val type: AppSettingType,
)
enum class AppSettingType {
CHAR,
STRING,
BYTE,
SHORT,
INT,
LONG,
DOUBLE,
FLOAT,
BOOLEAN,
}
This is then saved to the database with the following:
override fun saveAppSetting(setting: AppSetting): DatabaseResult<AppSetting> {
log.info("Saving App Setting ${setting.name} to database.")
return try {
// Attempt to save the entity to the database. If we do not throw an exception, return success.
val savedSetting = appSettingsRepository.save(setting)
DatabaseResult(
code = ResultCode.CREATION_SUCCESS,
entity = savedSetting
)
} catch(exception: DataAccessException) {
log.error("Unable to save App Setting ${setting.name} to database. Reason: ${exception.message}")
DatabaseResult(
code = ResultCode.CREATION_FAILURE
)
}
}
Now, let's say that I wish to save a Char type to database, I figure I would use the following:
override fun saveAppSetting(name: String, value: Char): DatabaseResult<Char> {
val appSettingResult = saveAppSetting(AppSetting(
name = name,
value = value.toString(),
type = AppSettingType.CHAR,
))
return if(appSettingResult.code != ResultCode.CREATION_FAILURE) {
val entity = getAppSetting<Char>(appSettingResult.entity?.name!!).entity.toString().first()
DatabaseResult(
code = appSettingResult.code,
entity = entity
)
} else {
DatabaseResult(
code = ResultCode.CREATION_FAILURE,
)
}
}
I also figured that I would need to do the following in order to retrieve the correct object type:
override fun getAppSetting(name: String): DatabaseResult<Any?> {
log.info("Getting App Setting $name from database.")
val appSetting = appSettingsRepository.findAppSettingByName(name)
return if(appSetting != null) {
log.info("App Setting $name has ID of ${appSetting.id} within the database")
when(appSetting.type) {
AppSettingType.CHAR -> {
DatabaseResult<Char>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.first(),
)
}
AppSettingType.STRING -> {
DatabaseResult<String>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value,
)
}
AppSettingType.BYTE -> {
DatabaseResult<Byte>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toByte(),
)
}
AppSettingType.SHORT -> {
DatabaseResult<Short>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toShort(),
)
}
AppSettingType.INT -> {
DatabaseResult<Int>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toInt(),
)
}
AppSettingType.LONG -> {
DatabaseResult<Long>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toLong(),
)
}
AppSettingType.DOUBLE -> {
DatabaseResult<Double>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toDouble(),
)
}
AppSettingType.FLOAT -> {
DatabaseResult<Float>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toFloat()
)
}
AppSettingType.BOOLEAN -> {
DatabaseResult<Boolean>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toBoolean()
)
}
}
} else {
log.error("App Setting $name does not seem to exist within the database.")
DatabaseResult(
code = ResultCode.FETCH_FAILURE
)
}
However, when I then wish to use said object, I still have to write something like the following:
val newBarcode = getAppSetting("barcode_value").entity.toString().toInt()
Assuming I've "initialised" barcode_value with a value of 177 (for example).
How can I get the function to return what I need without having to do .toString.to...()?
Yes this all possible, here is a simplified demo, firstly
import kotlin.reflect.KClass
data class AppSetting(
val id: Long? = null,
val name: String = "",
var value: String = "",
val type: AppSettingType,
)
enum class AppSettingType(val clazz: KClass<out Any>) {
CHAR(Char::class),
STRING(String::class),
INT(Int::class),
}
So I added a clazz so from the enum we know the Kotlin type
and now a function to simulate your repository fetch
fun findAppSettingByName(name: String): AppSetting? {
return when(name) {
"Char thing" -> AppSetting(value= "C", type = AppSettingType.CHAR)
"String thing" -> AppSetting(value= "Str", type = AppSettingType.STRING)
"Int thing" -> AppSetting(value= "42", type = AppSettingType.INT)
else -> throw IllegalArgumentException()
}
}
Next in the function declaration I have made it generic with T and for the purposes of the demo removed the DatabaseResult container. Then I added a clazz parameter which is the typical Java way of carrying the required class information into the function:
fun <T : Any> getAppSetting(name: String, clazz: KClass<T>): T? {
val appSetting: AppSetting? = findAppSettingByName(name)
return appSetting?.let {
require(clazz == appSetting.type.clazz) {
"appSetting.type=${appSetting.type.clazz} mismatched with requested class=${clazz}"
}
when (appSetting.type) {
AppSettingType.CHAR -> appSetting.value.first()
AppSettingType.STRING -> appSetting.value
AppSettingType.INT -> appSetting.value.toInt()
} as T
}
}
the as T is important to cast the values into the required return type - this is unchecked but the when() clause should be creating the correct types.
Now let's test it:
val c1: Char? = getAppSetting("Char thing", Char::class)
val s1: String? = getAppSetting("String thing", String::class)
val i1: Int? = getAppSetting("Int thing", Int::class)
println("c1=$c1 s1=$s1 i1=$i1")
val c2: Char? = getAppSetting("Char thing")
val s2: String? = getAppSetting("String thing")
val i2: Int? = getAppSetting("Int thing")
println("c2=$c2 s2=$s2 i2=$i2")
}
The output is
c1=C s1=Str i1=42
c2=C s2=Str i2=42
But how do c2/s2/i2 work, the final part is this function
inline fun <reified T : Any> getAppSetting(name: String) = getAppSetting(name, T::class)
This is reified generic parameters... there is no need to pass the clazz because this can be found from the data type of the receiving variable.
There are many articles about this advanced topic, e.g.
https://typealias.com/guides/getting-real-with-reified-type-parameters/
https://medium.com/kotlin-thursdays/introduction-to-kotlin-generics-reified-generic-parameters-7643f53ba513
Now, I didn't completely answer what you wanted because you wanted to receive a DatabaseResult<T> wrapper. What might be possible, is to have a function that returns DatabaseResult<T> and you can obtain the T from it as the "clazz" parameter, but I'll leave that for someone else to improve on :-) but I think that gets you pretty close.
I have a question, what value I need to pass to "init: (index: Int)->Contact" as it expects:
The integer literal does not conform to the expected type (Int) ->
TypeVariable(T)
Snippet of the function
#Composable
fun ContactList(textVal: MutableState<TextFieldValue>, navigateToProfile: (Contact) -> Unit) {
var contacts = remember { DataProvider.contactList }
var contactList = contacts.toMutableList()
var filteredContacts: MutableList<Contact>
LazyColumn(
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
) {
val searchText = textVal.value.text
filteredContacts = if (searchText.isEmpty()){
contactList
}
else{
val resultList = MutableList<Contact>(10, "init: (inded: Int)->Contact")
for (contact in contactList) {
if (contact.contains(searchText.lowercase())) {
resultList.add(contact)
}
}
resultList
}
items(filteredContacts) {
ContactListItem(contact = it, navigateToProfile)
}
}
}
Snippet of the Contact
data class Contact(
val id: Int,
val name: String,
val description: String,
val recipe: String,
val ImageId: Int = 0
)
(index: Int)->Contact means: a function that receives Int and returns Contact. But I guess this does not really solve your problem.
The problem is that you use MutableList "constructor" (it is actually a function) which is intended to create a list with exactly 10 items. Then you need to provide these items and this is what init function is for. What you actually need here is to create an empty list and you can do this with:
val resultList = mutableListOf<Contact>()
However, if you just need to filter some collection and create another one, it is much easier to use filter():
val resultList = contactList.filter { it.contains(searchText.lowercase()) }
If Contact.contains() is an operator then we can also simplify it to:
val resultList = contactList.filter { searchText.lowercase() in it }
all! I want to get data in DB at first and than call server if DB is empty. But I don't have any response when I use this way. I tried to call server at first and it was successful. Whats wrong??? This is my code:
private fun getDataFromRepository() {
val subscription =
carRepository.getCars()!!.
subscribeOn(Schedulers.io()).
observeOn(AndroidSchedulers.mainThread()).
subscribe(
{ cars ->
LOG.info(cars.size.toString())
carRepository.saveCarsInDB(cars)
data.postValue(cars)
},
{ e ->
loadError.postValue(e.toString())
LOG.warning(e.toString())
})
subscriptions.add(subscription)
}
Flowables:
fun getCars(): Single<List<Car>>? {
val db = getDataFromDB()
val server = getDataFromServerFlowable()
val mock = getDataFromMock()
return Flowable.concat(db, server).first(mock)
}
private fun getDataFromServerFlowable(): Flowable<List<Car>> {
return carApi.getPostsFlowable()
}
private fun getDataFromDB(): Flowable<List<Car>> {
return RealmCar().queryAllAsFlowable() //"com.github.vicpinm:krealmextensions:2.4.0"
.map { cars -> mapper.convertListRealmCarToListCar(cars) }
.filter { car -> car.isNotEmpty()}
}
private fun getDataFromMock(): List<Car> {
val cars: MutableList<Car> = mutableListOf()
val car = Car(0, 0, "Test", "Test", "Test")
cars.add(car)
return cars
}
Server call:
#GET("/photos")
fun getPostsFlowable(): Flowable<List<Car>>
Depending on your logic you should consider using merge instead of concat to interleave the elements. In your case getDataFromDB() is not emitting, so the final Flowable is waiting for it before emitting getDataFromServerFlowable(), There are plenty of good answers of merge vs concat (i.e this one)
I have a data class like this:
data class TestModel(
val id: Int,
val description: String,
val picture: String)
If I create JSON from this data class using GSON and it generates a result like this
{"id":1,"description":"Test", "picture": "picturePath"}
What to do if I need the following JSON from my data class:
{"id":1, "description":"Test"}
And other times:
`{"id":1, "picture": "picturePath"}
`
Thanks in advance!
You can solve this problem with writing custom adapter and with optional types:
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
data class TestModel(
val id: Int,
val description: String? = "",
val picture: String? = "")
class TesModelTypeAdapter : TypeAdapter<TestModel>() {
override fun read(reader: JsonReader?): TestModel {
var id: Int? = null
var picture: String? = null
var description: String? = null
reader?.beginObject()
while (reader?.hasNext() == true) {
val name = reader.nextName()
if (reader.peek() == JsonToken.NULL) {
reader.nextNull()
continue
}
when (name) {
"id" -> id = reader.nextInt()
"picture" -> picture = reader.nextString()
"description" -> description = reader.nextString()
}
}
reader?.endObject()
return when {
!picture.isNullOrBlank() && description.isNullOrBlank() -> TestModel(id = id ?: 0, picture = picture)
!description.isNullOrBlank() && picture.isNullOrBlank() -> TestModel(id = id ?: 0, description = description)
else -> TestModel(id ?: 0, picture, description)
}
}
override fun write(out: JsonWriter?, value: TestModel?) {
out?.apply {
beginObject()
value?.let {
when {
!it.picture.isNullOrBlank() && it.description.isNullOrBlank() -> {
name("id").value(it.id)
name("picture").value(it.picture)
}
!it.description.isNullOrBlank() && it.picture.isNullOrBlank() -> {
name("id").value(it.id)
name("description").value(it.description)
}
else -> {
name("id").value(it.id)
name("picture").value(it.picture)
name("description").value(it.description)
}
}
}
endObject()
}
}
}
class App {
companion object {
#JvmStatic fun main(args: Array<String>) {
val tm = TestModel(12, description = "Hello desc")
val tm2 = TestModel(23, picture = "https://www.pexels.com/photo/daylight-forest-glossy-lake-443446/")
val tm3 = TestModel(12, "Hello desc", "https://www.pexels.com/photo/daylight-forest-glossy-lake-443446/")
val gson = GsonBuilder().registerTypeAdapter(TestModel::class.java, TesModelTypeAdapter()).create()
System.out.println(gson.toJson(tm))
System.out.println(gson.toJson(tm2))
System.out.println(gson.toJson(tm3))
}
}
}
Here is actually a way to ignore fields, that are not marked via #Exposed annotation. In order for this to work, special configuration should be used when instantiating Gson. Here is how you can to this.
Easy way is to mark the field as #Transient. Then it would not be either serialized and deserialized.
I want to give you alternative ways without manually serialization/deserialization.
data class TestModel(
val id: Int,
val description: String? = null,
val picture: String? = null)
When you create json from data class
val params = TestModel(id = 1, description = "custom text")
or
val params = TestModel(id = 1, picture = "picture path")
If one of them field is null of data class GSON skips that field
automatically.
I made a custom feature that should check the user's permissions to use the request. Can I monitor request LocationInfo?
Can this looks like that?
if (!User.accessTo.contains(CALL_LOCATION_INFO)){
call.respond(HttpStatusCode.BadRequest) }
That`s my feature code:
data class UserRights(
val haveFullAccess:Boolean,
val accessTo:List<String>,
val canUpdate:Boolean,
val canDelete:Boolean,
val canBan:Boolean,
val canMute:Boolean)
var User = UserRights(false, listOf(""),false,false,false,false)
class RightsChecker(configuration: Configuration) {
val prop = configuration.prop // get snapshot of config into immutable property
class Configuration {
var prop = "value"
}
companion object Feature : ApplicationFeature<ApplicationCallPipeline, Configuration, RightsChecker> {
override val key = AttributeKey<RightsChecker>("RightsChecker")
override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): RightsChecker {
val configuration = RightsChecker.Configuration().apply(configure)
val feature = RightsChecker(configuration)
val FilterPhase = PipelinePhase("CallFilter")
pipeline.insertPhaseAfter(ApplicationCallPipeline.Infrastructure, FilterPhase)
pipeline.intercept(FilterPhase) {
val session = call.sessions.get<SessionData>() ?: SessionData(0, "Guest")
when (session.role) {
"Guest" -> User = UserRights(
haveFullAccess = false,
accessTo = listOf(""),
canUpdate = false,
canDelete = false,
canBan = false,
canMute = false)
"User" -> User = UserRights(
haveFullAccess = false,
accessTo = listOf("lUsers"),
canUpdate = false,
canDelete = false,
canBan = false,
canMute = false)
"Admin" -> User = UserRights(
haveFullAccess = true,
accessTo = listOf("lUsers"),
canUpdate = true,
canDelete = true,
canBan = true,
canMute = true)
}
if (!User.accessTo.contains(CALL_LOCATION_INFO)){
call.respond(HttpStatusCode.BadRequest)
}
}
return feature
}
}
}
How you can see, I`m using UserRights data class with rights in it. "accesTo" - is list of location names (format can be changed) what user can use. Feature must just check location name contain in "accesTo" list before request processing.
Thank you for help!
UPD: Locations code:
#Location("/login") data class lLoginData(val email:String, val password: String)
#Location("/users") data class lGetUsers(val page:Int, val limit:Int)
#Location("/users/user") data class lUser(val email: String)
#Location("/users") data class lUpdateData(val userID: Int, val datatype:String, val newData:String)
#Location("/users") data class lRegData(val email: String, val username:String, val userpass:String)
If I understand you correctly, then you simply want to know what route / uri was called.
Here is a small server that answers with the route called.
private val locationKey = AttributeKey("location")
val module = fun Application.() {
install(Routing) {
intercept(ApplicationCallPipeline.Call) {
val location = call.request.local.uri
call.attributes.put(locationKey, location)
}
get("{...}") {
val location = call.attributes[locationKey]
call.respond(location)
}
}
}
As can be seen, I am using call.request.local.uri to get the uri of the call.
When I navigate to http://localhost:5001/hello/route, the server answers with /hello/route.
Does this answer your question?