log out happens before db operation Kotlin KMM Realm Mongodb - kotlin

I have a kmm app, and I have a shared repository in Kotlin.
I have a login that does the normal login to Realm MongoDb and add a FCM key.
I have also a logout method that should remove first the FCM token and then do the logOUT() from Realm MongoDB
My logOut method:
suspend fun doLogout(token: String) {
val userId = appService.currentUser?.id
realm.write {
var user = query<UserInfo>("_id = $0", userId).first().find()
if (user != null) {
val productIndex =
user.FCMToken.withIndex().findLast { it.value == token }!!.index
user = findLatest(user)!!.also {
it.FCMToken.removeAt(productIndex)
}
copyToRealm(user)
}
}
appService.currentUser?.logOut()
}
I think what happens is that the logout happens before the DB change.
How can I do the logout after the db change is completed?

Related

401 Unauthorized Error when trying to login Discord Bot with API Token

I am trying to login my Discord bot to my server using the API token but I am getting a "401 Unauthorized" error. My bot is offline in my Discord channel and I can't seem to get it online. I have double checked that the token is correct and that my account has the proper permissions.
Here is my code (I changed some letters and numbers in the token, so it is not a real token, but I am using a very similar one, I wanted to share a similar one to see if I am making a mistake here.):
#RequiresApi(Build.VERSION_CODES.N)
#HiltViewModel
class HomeViewModel #Inject constructor(
private val discordModel: DiscordModel
) : ViewModel() {
init {
login()
}
private val _homeState = mutableStateOf(HomeState())
val homeState = _homeState
fun onTextChange(text: String) {
_homeState.value = _homeState.value.copy(
text = text
)
}
private fun login() {
Log.d("Mesaj: ", "Başladı")
val client =
DiscordClient.create("MTA2NzIxOYk0NDYyKzg0NTQ3Ng.Gu8Bdp.uEWiUg13-1jIWJXVJig-3FXxOOVDbARPHLTP2R")
client.withGateway {
mono {
it.on(MessageCreateEvent::class.java)
.asFlow()
.collect {
val message = it.message
if (message.content == "!ping") {
val channel = message.channel.awaitSingle()
channel.createMessage("Pong!").awaitSingle()
}
}
}
}.block()
Log.d("Mesaj: ", "Bitti")
}
}
I have also checked that my account has the proper permissions to add the bot to the server. Can anyone help me figure out what is causing this error and how to fix it?

How to reuse cached value in ProjectReactor

I would like my server to call the login endpoint of another server and cache the auth token for later use. My issue is that when the server tries to reuse the existing token it hangs indefinitely or infinite loops.
#Component
class ApiWebClient {
private var authToken = Mono.just(AuthToken("", Instant.ofEpochSecond(0)))
fun login(): Mono<AuthToken> {
authToken = doesTokenNeedRefreshing().flatMap { needsRefreshing ->
if (needsRefreshing) {
WebClient.create().post()
.uri("https://example.com/login")
.body(
Mono.just("Credentials"),
String::class.java
).exchangeToMono { response ->
response.bodyToMono<LoginResponse>()
}.map { response ->
LOGGER.info("Successfully logged in")
AuthToken(response.token, Instant.now())
}
} else {
LOGGER.info("Reuse token")
authToken
}
}.cache()
return authToken
}
private fun doesTokenNeedRefreshing(): Mono<Boolean> {
return authToken.map {
Instant.now().minusMillis(ONE_MINUTE_IN_MILLIS).isAfter(it.lastModified)
}
}
class AuthToken(
var token: String,
var lastModified: Instant
)
companion object {
private const val ONE_MINUTE_IN_MILLIS = 60 * 1000L
#Suppress("JAVA_CLASS_ON_COMPANION")
#JvmStatic
private val LOGGER = LoggerFactory.getLogger(javaClass.enclosingClass)
}
}
If login gets called twice within the ONE_MINUTE_IN_MILLIS amount of time then it just hangs. I suspect this is because the doesTokenNeedRefreshing() calls a .map {} on authToken and then later down the chain authToken is reassigned to itself. As well, there's an attempt to recache that exact same value. I've played around with recreating AuthToken each time instead of returning the same instance but no luck. The server either hangs or infinite loops.
How can I achieve returning the same instance of the cached value so I don't have to make a web request each time?
The best way would be to use ServerOAuth2AuthorizedClientExchangeFilterFunction from Spring Security that you could customize to satisfy your needs. In this case token will be updated automatically behind the scene.
If you are looking for "manual" approach, you just need to combine 2 requests.
public Mono<String> getResourceWithToken() {
return getToken()
.flatMap(token -> getResource(token));
}
private Mono<String> getResource(String authToken) {
return client.get()
.uri("/resource")
.headers(h -> h.setBearerAuth(authToken))
.retrieve()
.bodyToMono(String.class);
}
private Mono<String> getToken() {
return client.post()
.uri("/oauth/token")
.header("Authorization", "Basic " + {CREDS})
.body(BodyInserters.fromFormData("grant_type", "client_credentials"))
.retrieve()
.bodyToMono(JsonNode.class)
.map(tokenResponse ->
tokenResponse.get("access_token").textValue()
);
}
In this case you will always execute 2 requests and get token for every resource request. Usually token has expiration and you could save some requests by caching getToken() Mono.
private Mono<String> tokenRequest = getToken().cache(Duration.ofMinutes(30));
public Mono<String> getResourceWithToken() {
return tokenRequest
.flatMap(token -> getResource(token));
}
What I was looking for was a switchIfEmpty statement to return the existing cached value if it doesn't need to be refreshed.
#Component
class ApiWebClient {
private var authToken = Mono.just(AuthToken("", Instant.ofEpochSecond(0)))
fun login(): Mono<AuthToken> {
authToken = doesTokenNeedRefreshing().flatMap { needsRefreshing ->
if (needsRefreshing) {
WebClient.create().post()
.uri("https://example.com/login")
.body(
Mono.just("Credentials"),
String::class.java
).exchangeToMono { response ->
response.bodyToMono<LoginResponse>()
}.map { response ->
LOGGER.info("Successfully logged in")
AuthToken(response.token, Instant.now())
}
} else {
LOGGER.info("Reuse token")
Mono.empty()
}
}.cache()
.switchIfEmpty(authToken)
return authToken
}
}
In this example, if the token doesn't need to be refreshed then the stream returns an empty Mono. Then the switchIfEmpty statement returns the original auth token. This, therefore, avoids "recursively" returning the same stream over and over again.

Pass synched Realm to other Activity

When I try to access the synched Realm from e.g. Main Activity I always get the error lateinit var not initialised.
I can successful open a database on the device and also the synched Database. But because the open of the synched Database must be in "run blocking" I don't know how to access "synchedRealm" then. Can't pass it to Main Activity.
I am sure its kind of fundamental knowledge I don't get out of the Kotlin/Realm documentation because English is not my primary language.
Thank you for your help!!!
In another Activity: MyApp:
....
runBlocking {
// Neuen User Registrieren
// app.emailPasswordAuth.registerUser(email, pw)
// once registered, you can log in with the user credentials
val appid: String = "MYAppId"
val apikey: String = "MyApiKey"
val app: App = App.create(
AppConfiguration.Builder(appid)
.log(LogLevel.ALL)
.build()
)
val user_api = app.login(io.realm.kotlin.mongodb.Credentials.apiKey(apikey))
val config = SyncConfiguration.Builder(
user_api,
setOf(MyClass::class)
)
.initialSubscriptions { realm ->
add(
realm.query<PersonCollection>(
"MyField == $0",
"Mustermann"
),
"subscription name"
)
}
.build()
val realmSynced = Realm.open(config)
}
I tried also the above code in an object{} or the runblocking{} in an init{} block and dozend other things
In Mainactivity:
lateinit var realm:Realm
lateinit var realmSynched:Realm
...
realm=MyApp.Databaseobject.realm //Works for Database on device
realmSynched= ???? //How to initialise this ???

Spring Security and Token Catching

I'm currently developing an Integration Platform, but having some issues finding an idiotmatic way to set up the Spring Security Filter Chain.
Spring Security is used in 2 ways.
To authenticate the user of the application. (we'll call this the primary client registration)
To allow the user to authenticate with an external system.
In the second case, we saved their access/refresh tokens and use those later to interact with the external system(s) APIs and sync data.
When the user authenticated with the external system, the new token will overwrite the Security Context.
So far, the best solution we could come up with was overriding the SecurityContext Strategy.
class CompositeSecurityContextHolderStrategy(
val primaryClientRegistrationId: String = "primary"
) : SecurityContextHolderStrategy {
companion object {
val contextHolder = ThreadLocal<CompositeSecurityContext>()
}
override fun createEmptyContext(): SecurityContext = CompositeSecurityContext()
override fun clearContext() {
contextHolder.remove()
}
override fun getContext(): CompositeSecurityContext {
if (contextHolder.get() == null) {
contextHolder.set(createEmptyContext() as CompositeSecurityContext)
}
return contextHolder.get()
}
override fun setContext(context: SecurityContext) {
val currentContext = getContext()
val authentication = context.authentication
if (authentication is OAuth2AuthenticationToken) {
if (authentication.authorizedClientRegistrationId == primaryClientRegistrationId) currentContext.authentication = authentication
else currentContext.addAssociateAuthentication(authentication)
} else {
currentContext.authentication = context.authentication
}
}
}
class CompositeSecurityContext : SecurityContextImpl() {
val associatedAuthentications = mutableMapOf<String, Authentication>()
fun addAssociateAuthentication(authentication: Authentication) {
if (authentication is OAuth2AuthenticationToken) associatedAuthentications[authentication.authorizedClientRegistrationId] = authentication
}
fun retrieveAssociatedAuthentication(clientRegistrationId: String): Authentication? = associatedAuthentications[clientRegistrationId]
}
SecurityContextHolder.setContextHolderStrategy(CompositeSecurityContextHolderStrategy("primary"))
This works as intended. The Primary session is never overrwritten and associated token is available for consumption when our custom implementation of OAuth2AuthorizedClientService is invoked.
However, it still feels like a hack, but cannot find a better way to achieve authentication with token retrieval, without overwriting the security context.
I've seen a few older posts reference overriding OAuth2ClientAuthenticationProcessingFilter, but that is now deprecated in newer versions of SpringSecurity 5.
Any help will be appreciated.

How to extract access rights validation in Kotlin's Ktor

I have Ktor based REST API application which uses the jwt token's as authentication. Then I have to restrict certain routes for the specific role. In order to do it I am creating principal, containing the relevant info:
data class UserPrincipal (
val id: Long,
val username: String,
val roleId: Long,
): Princpal {
override fun getName() = username
}
object AuthLogin {
fun Application.auth(jwt: JwtProvider) {
install(Authentication) {
jwt("jwt") {
realm = jwt.realm()
verifier(jwt.verifier())
validate {
val userId = it.payload.getClaim("id").asLong()
val username = it.payload.getClain("name")
val roleId = it.payload.getClaim("roleId").asLong()
UserPrincipal(userId, username, roleId)
}
}
}
}
}
The claims with userId and roleId are being provided when signing the correctly logged in user. Now I can restrict REST endpoints like that:
object RestModule {
fun Application.enititiesOne(userRepo: UserRepo) {
routing {
authenticate("jwt") {
route("/entities1") {
get {
val principal = call.principal<UserPrincipal>()
when(userRepo.hasAccessByRole(principal!!.roleId, "CAN_R_E1") {
false -> call.respond(HttpStatusCode.Forbidden)
true -> // some retrieval logic
}
post {
val principal = call.principal<UserPrincipal>()
when(userRepo.hasAccessByRole(principal!!.roleId, "CAN_W_E1") {
false -> call.respond(HttpStatusCode.Forbidden)
true -> // some update logic
}
}
}
}
}
}
As you can see even inside one routing function I have to duplicate code that checks the principal's role twice. I can move it out to function but what I want is a single place to define my security roles. Something like that:
authenticate {
val principal = call.principal<UserPrincipal()
val rights = userRepo.rightsByRole(principal.roleId)
when(routes) {
get("/entities1/**") ->
if(rights.contain("CAN_R_E1")) call.proceed
else call.respond(HttpStatusCode.Forbidden)
post("/entites1) -> rights.contain("CAN_W_E1") // similar
get("/entities2/**") -> rights.contain("CAN_R_E2") // similar
else -> call.respond(401)
}
}
And then plug it into the rest endpoints. Or is there some similar approach that I can use in Kotlin's Ktor? Seems like interceptors is what I need but I'm not sure how to use them in an intended way.
You can check the method and uri in the validate block.
install(Authentication) {
jwt {
validate {
val userId = it.payload.getClaim("id").asLong()
val username = it.payload.getClaim("name").asString()
val roleId = it.payload.getClaim("roleId").asLong()
UserPrincipal(userId, username, roleId)
val requiredRole = when (request.httpMethod) {
HttpMethod.Get -> // get role
HttpMethod.Post -> // get other role
}
// check if role exists in repo
}
}
}
install(Routing) {
get {
val principal = call.principal<UserPrincipal>()!!
call.respond(principal)
}
post {
val principal = call.principal<UserPrincipal>()!!
call.respond(principal)
}
}
By the way, there were several issues with the code you posted, so it wouldn't compile.