mirror of
https://github.com/greenart7c3/Amber.git
synced 2026-10-06 03:18:23 +00:00
Add per-relay permissions for kind 22242 client authentication (NIP-42)
Kind 22242 events contain a ["relay", "wss://..."] tag identifying which relay is requesting authentication. This change makes permissions for these events per-relay URL rather than global: - DB: Add `relay` field to ApplicationPermissionsEntity (migration 16→17) with a new unique index on (pkKey, type, kind, relay). Non-22242 permissions keep relay="" to maintain backward compatibility. - DAO: Add getPermissionForRelay() and getWildcardRelayPermission() for relay-specific lookups, plus deletePermissions(key, type, kind, relay) and deletePermissionsForKind() helpers. - AmberUtils: acceptOrRejectPermission() and acceptPermission() accept an optional relay parameter. Wildcard "*" clears all relay-specific entries for a kind; a specific URL only clears its own entry. - UI: New BunkerRelayAuthScreen composable shows the relay URL prominently and presents a 2-option "Authentication scope" toggle: · "This relay only" — stores permission for the specific relay URL · "All relays" — stores permission with wildcard "*" Both options still include the RememberMyChoice time-based memory control. - BunkerSingleEventHomeScreen & IntentSingleEventHomeScreen: detect kind 22242 requests and show BunkerRelayAuthScreen instead of the generic BunkerEventData. Permission lookup checks specific relay first, then the "*" wildcard fallback. - SignerProvider (ContentProvider path): kind 22242 checks relay-specific permission first, then wildcard, before auto-approving. - AmberToggles: add optional segmentWidth parameter (default 55.dp) so the relay scope toggle can use wider 120dp segments. https://claude.ai/code/session_01Dm9zoUbbzdJzuXTNdAVhfW
This commit is contained in:
@@ -12,6 +12,7 @@ import com.greenart7c3.nostrsigner.models.SignerType
|
||||
import com.greenart7c3.nostrsigner.models.kindToNip
|
||||
import com.greenart7c3.nostrsigner.service.AmberUtils
|
||||
import com.greenart7c3.nostrsigner.service.IntentUtils
|
||||
import com.greenart7c3.nostrsigner.service.model.AmberEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
@@ -175,7 +176,12 @@ class SignerProvider : ContentProvider() {
|
||||
|
||||
val database = Amber.instance.getDatabase(account.npub)
|
||||
val historyDatabase = Amber.instance.getHistoryDatabase(account.npub)
|
||||
var permission =
|
||||
var permission = if (event.kind == 22242) {
|
||||
// Kind 22242 = relay client auth (NIP-42): check relay-specific permission first
|
||||
val relayUrl = AmberEvent.relay(event) ?: ""
|
||||
database.dao().getPermissionForRelay(packageName, "SIGN_EVENT", 22242, relayUrl)
|
||||
?: database.dao().getWildcardRelayPermission(packageName, "SIGN_EVENT", 22242)
|
||||
} else {
|
||||
database
|
||||
.dao()
|
||||
.getPermission(
|
||||
@@ -183,7 +189,8 @@ class SignerProvider : ContentProvider() {
|
||||
"SIGN_EVENT",
|
||||
event.kind,
|
||||
)
|
||||
if (permission == null) {
|
||||
}
|
||||
if (permission == null && event.kind != 22242) {
|
||||
event.kind.kindToNip()?.let {
|
||||
val nipNumber = it.toIntOrNull()
|
||||
permission = if (nipNumber == null) {
|
||||
|
||||
@@ -148,12 +148,20 @@ val MIGRATION_15_16 = object : Migration(15, 16) {
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_16_17 = object : Migration(16, 17) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("ALTER TABLE `applicationPermission` ADD COLUMN `relay` TEXT NOT NULL DEFAULT ''")
|
||||
db.execSQL("DROP INDEX IF EXISTS `permissions_unique`")
|
||||
db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `permissions_unique` ON `applicationPermission` (`pkKey`, `type`, `kind`, `relay`)")
|
||||
}
|
||||
}
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
ApplicationEntity::class,
|
||||
ApplicationPermissionsEntity::class,
|
||||
],
|
||||
version = 16,
|
||||
version = 17,
|
||||
)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
@@ -190,6 +198,7 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
.addMigrations(MIGRATION_13_14)
|
||||
.addMigrations(MIGRATION_14_15)
|
||||
.addMigrations(MIGRATION_15_16)
|
||||
.addMigrations(MIGRATION_16_17)
|
||||
.build()
|
||||
|
||||
instance
|
||||
|
||||
@@ -59,7 +59,7 @@ interface ApplicationDao {
|
||||
@Transaction
|
||||
fun getAllAcceptedPermissions(): List<ApplicationPermissionsEntity>
|
||||
|
||||
@Query("SELECT * FROM applicationPermission WHERE pkKey = :key AND type = :type AND kind = :kind")
|
||||
@Query("SELECT * FROM applicationPermission WHERE pkKey = :key AND type = :type AND kind = :kind AND relay = ''")
|
||||
fun getPermission(
|
||||
key: String,
|
||||
type: String,
|
||||
@@ -72,6 +72,21 @@ interface ApplicationDao {
|
||||
type: String,
|
||||
): ApplicationPermissionsEntity?
|
||||
|
||||
@Query("SELECT * FROM applicationPermission WHERE pkKey = :key AND type = :type AND kind = :kind AND relay = :relay LIMIT 1")
|
||||
fun getPermissionForRelay(
|
||||
key: String,
|
||||
type: String,
|
||||
kind: Int,
|
||||
relay: String,
|
||||
): ApplicationPermissionsEntity?
|
||||
|
||||
@Query("SELECT * FROM applicationPermission WHERE pkKey = :key AND type = :type AND kind = :kind AND relay = '*' LIMIT 1")
|
||||
fun getWildcardRelayPermission(
|
||||
key: String,
|
||||
type: String,
|
||||
kind: Int,
|
||||
): ApplicationPermissionsEntity?
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
@Transaction
|
||||
suspend fun insertApplication(event: ApplicationEntity): Long?
|
||||
@@ -84,7 +99,17 @@ interface ApplicationDao {
|
||||
suspend fun insertPermissions(permissions: List<ApplicationPermissionsEntity>): List<Long>? {
|
||||
permissions.forEach {
|
||||
if (it.kind != null) {
|
||||
deletePermissions(it.pkKey, it.type, it.kind)
|
||||
if (it.relay.isNotEmpty()) {
|
||||
// For relay-specific permissions (kind 22242): wildcard "*" clears all relay entries,
|
||||
// specific relay only clears its own entry
|
||||
if (it.relay == "*") {
|
||||
deletePermissionsForKind(it.pkKey, it.type, it.kind)
|
||||
} else {
|
||||
deletePermissions(it.pkKey, it.type, it.kind, it.relay)
|
||||
}
|
||||
} else {
|
||||
deletePermissions(it.pkKey, it.type, it.kind)
|
||||
}
|
||||
} else {
|
||||
deletePermissions(it.pkKey, it.type)
|
||||
}
|
||||
@@ -107,7 +132,7 @@ interface ApplicationDao {
|
||||
type: String,
|
||||
)
|
||||
|
||||
@Query("DELETE FROM applicationPermission WHERE pkKey = :key AND type = :type and kind = :kind")
|
||||
@Query("DELETE FROM applicationPermission WHERE pkKey = :key AND type = :type AND kind = :kind AND relay = ''")
|
||||
@Transaction
|
||||
suspend fun deletePermissions(
|
||||
key: String,
|
||||
@@ -115,6 +140,23 @@ interface ApplicationDao {
|
||||
kind: Int,
|
||||
)
|
||||
|
||||
@Query("DELETE FROM applicationPermission WHERE pkKey = :key AND type = :type AND kind = :kind AND relay = :relay")
|
||||
@Transaction
|
||||
suspend fun deletePermissions(
|
||||
key: String,
|
||||
type: String,
|
||||
kind: Int,
|
||||
relay: String,
|
||||
)
|
||||
|
||||
@Query("DELETE FROM applicationPermission WHERE pkKey = :key AND type = :type AND kind = :kind")
|
||||
@Transaction
|
||||
suspend fun deletePermissionsForKind(
|
||||
key: String,
|
||||
type: String,
|
||||
kind: Int,
|
||||
)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
@Transaction
|
||||
suspend fun insertApplicationWithPermissions(application: ApplicationWithPermissions) {
|
||||
|
||||
+2
-1
@@ -21,7 +21,7 @@ import androidx.room.PrimaryKey
|
||||
name = "permissions_by_pk_key",
|
||||
),
|
||||
Index(
|
||||
value = ["pkKey", "type", "kind"],
|
||||
value = ["pkKey", "type", "kind", "relay"],
|
||||
name = "permissions_unique",
|
||||
unique = true,
|
||||
),
|
||||
@@ -37,4 +37,5 @@ data class ApplicationPermissionsEntity(
|
||||
val rememberType: Int,
|
||||
var acceptUntil: Long,
|
||||
var rejectUntil: Long,
|
||||
val relay: String = "",
|
||||
)
|
||||
|
||||
@@ -81,6 +81,7 @@ object AmberUtils {
|
||||
value: Boolean,
|
||||
rememberType: RememberType,
|
||||
account: Account,
|
||||
relay: String = "",
|
||||
) {
|
||||
val until = when (rememberType) {
|
||||
RememberType.ALWAYS -> Long.MAX_VALUE / 1000
|
||||
@@ -91,7 +92,17 @@ object AmberUtils {
|
||||
}
|
||||
|
||||
if (kind != null) {
|
||||
application.permissions.removeIf { it.kind == kind && it.type == signerType.toString() }
|
||||
if (relay.isNotEmpty()) {
|
||||
// For relay-specific permissions: wildcard "*" removes all relay entries for this kind,
|
||||
// specific relay removes only its own entry
|
||||
if (relay == "*") {
|
||||
application.permissions.removeIf { it.kind == kind && it.type == signerType.toString() }
|
||||
} else {
|
||||
application.permissions.removeIf { it.kind == kind && it.type == signerType.toString() && it.relay == relay }
|
||||
}
|
||||
} else {
|
||||
application.permissions.removeIf { it.kind == kind && it.type == signerType.toString() && it.relay.isEmpty() }
|
||||
}
|
||||
} else {
|
||||
application.permissions.removeIf { it.type == signerType.toString() && it.type != "SIGN_EVENT" }
|
||||
}
|
||||
@@ -106,6 +117,7 @@ object AmberUtils {
|
||||
rememberType.screenCode,
|
||||
if (value) until else 0L,
|
||||
if (!value) until else 0L,
|
||||
relay,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -173,6 +185,7 @@ object AmberUtils {
|
||||
type: SignerType,
|
||||
kind: Int?,
|
||||
rememberType: RememberType,
|
||||
relay: String = "",
|
||||
) {
|
||||
val until = when (rememberType) {
|
||||
RememberType.ALWAYS -> Long.MAX_VALUE / 1000
|
||||
@@ -183,7 +196,15 @@ object AmberUtils {
|
||||
}
|
||||
|
||||
if (kind != null) {
|
||||
application.permissions.removeIf { it.kind == kind && it.type == type.toString() }
|
||||
if (relay.isNotEmpty()) {
|
||||
if (relay == "*") {
|
||||
application.permissions.removeIf { it.kind == kind && it.type == type.toString() }
|
||||
} else {
|
||||
application.permissions.removeIf { it.kind == kind && it.type == type.toString() && it.relay == relay }
|
||||
}
|
||||
} else {
|
||||
application.permissions.removeIf { it.kind == kind && it.type == type.toString() && it.relay.isEmpty() }
|
||||
}
|
||||
} else {
|
||||
application.permissions.removeIf { it.type == type.toString() && it.type != "SIGN_EVENT" }
|
||||
}
|
||||
@@ -198,6 +219,7 @@ object AmberUtils {
|
||||
rememberType.screenCode,
|
||||
until,
|
||||
0,
|
||||
relay,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -240,6 +240,7 @@ object BunkerRequestUtils {
|
||||
rememberType: RememberType,
|
||||
oldKey: String = "",
|
||||
deleteAfter: Long = 0L,
|
||||
relay: String = "",
|
||||
) {
|
||||
onLoading(true)
|
||||
Amber.instance.applicationIOScope.launch {
|
||||
@@ -319,6 +320,7 @@ object BunkerRequestUtils {
|
||||
type = type,
|
||||
kind = kind,
|
||||
rememberType = rememberType,
|
||||
relay = relay,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -434,6 +436,7 @@ object BunkerRequestUtils {
|
||||
signerType: SignerType,
|
||||
kind: Int?,
|
||||
onLoading: (Boolean) -> Unit,
|
||||
relay: String = "",
|
||||
) {
|
||||
onLoading(true)
|
||||
Amber.instance.applicationIOScope.launch(Dispatchers.IO) {
|
||||
@@ -481,6 +484,7 @@ object BunkerRequestUtils {
|
||||
false,
|
||||
rememberType,
|
||||
account,
|
||||
relay,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -681,6 +681,7 @@ object IntentUtils {
|
||||
shouldCloseApplication: Boolean? = null,
|
||||
rememberType: RememberType,
|
||||
deleteAfter: Long = 0L,
|
||||
relay: String = "",
|
||||
) {
|
||||
onLoading(true)
|
||||
Amber.instance.applicationIOScope.launch {
|
||||
@@ -732,6 +733,7 @@ object IntentUtils {
|
||||
type = intentData.type,
|
||||
kind = kind,
|
||||
rememberType = rememberType,
|
||||
relay = relay,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -864,6 +866,7 @@ object IntentUtils {
|
||||
kind: Int?,
|
||||
onLoading: (Boolean) -> Unit,
|
||||
onRemoveIntentData: (List<IntentData>, IntentResultType) -> Unit,
|
||||
relay: String = "",
|
||||
) {
|
||||
Amber.instance.applicationIOScope.launch(Dispatchers.IO) {
|
||||
if (key == "null") {
|
||||
@@ -901,6 +904,7 @@ object IntentUtils {
|
||||
false,
|
||||
rememberType,
|
||||
account,
|
||||
relay,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,15 +20,17 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun AmberToggles(
|
||||
selectedIndex: Int,
|
||||
count: Int,
|
||||
segmentWidth: Dp = 55.dp,
|
||||
content: @Composable RowScope.() -> Unit,
|
||||
) {
|
||||
val fixedSegmentWidth = 55.dp
|
||||
val fixedSegmentWidth = segmentWidth
|
||||
val padding = 2.dp
|
||||
val totalWidth = (fixedSegmentWidth * count) + (padding * 2)
|
||||
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.greenart7c3.nostrsigner.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.greenart7c3.nostrsigner.R
|
||||
import com.greenart7c3.nostrsigner.models.Account
|
||||
import com.greenart7c3.nostrsigner.ui.RememberType
|
||||
|
||||
/**
|
||||
* Scope for a kind 22242 relay authentication permission.
|
||||
* [SPECIFIC] means the permission applies only to the specific relay URL in the event.
|
||||
* [ALL] means the permission applies to all relays (wildcard "*").
|
||||
*/
|
||||
enum class RelayAuthScope {
|
||||
SPECIFIC,
|
||||
ALL,
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BunkerRelayAuthScreen(
|
||||
modifier: Modifier,
|
||||
appName: String,
|
||||
relayUrl: String,
|
||||
shouldAcceptOrReject: Boolean?,
|
||||
defaultScope: RelayAuthScope,
|
||||
account: Account,
|
||||
onAccept: (RememberType, RelayAuthScope) -> Unit,
|
||||
onReject: (RememberType, RelayAuthScope) -> Unit,
|
||||
) {
|
||||
var rememberType by remember { mutableStateOf(RememberType.NEVER) }
|
||||
var scope by remember { mutableStateOf(defaultScope) }
|
||||
val scopeIndex by remember(scope) { mutableIntStateOf(if (scope == RelayAuthScope.SPECIFIC) 0 else 1) }
|
||||
|
||||
if (shouldAcceptOrReject != null) {
|
||||
LaunchedEffect(Unit) {
|
||||
if (shouldAcceptOrReject) {
|
||||
onAccept(RememberType.entries[0], scope)
|
||||
} else {
|
||||
onReject(RememberType.entries[0], scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Spacer(Modifier.size(16.dp))
|
||||
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(appName)
|
||||
}
|
||||
append(" ")
|
||||
append(stringResource(R.string.relay_auth_request, ""))
|
||||
},
|
||||
fontSize = 18.sp,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
||||
Spacer(Modifier.size(8.dp))
|
||||
|
||||
Text(
|
||||
relayUrl,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
||||
Spacer(Modifier.size(16.dp))
|
||||
|
||||
SigningAs(account)
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
LabeledBorderBox(
|
||||
label = stringResource(R.string.relay_auth_scope),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
) {
|
||||
AmberToggles(
|
||||
selectedIndex = scopeIndex,
|
||||
count = 2,
|
||||
segmentWidth = 120.dp,
|
||||
content = {
|
||||
ToggleOption(
|
||||
modifier = Modifier.width(120.dp),
|
||||
text = stringResource(R.string.for_this_relay_only),
|
||||
isSelected = scope == RelayAuthScope.SPECIFIC,
|
||||
onClick = { scope = RelayAuthScope.SPECIFIC },
|
||||
)
|
||||
ToggleOption(
|
||||
modifier = Modifier.width(120.dp),
|
||||
text = stringResource(R.string.for_all_relays),
|
||||
isSelected = scope == RelayAuthScope.ALL,
|
||||
onClick = { scope = RelayAuthScope.ALL },
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.size(8.dp))
|
||||
|
||||
RememberMyChoice(
|
||||
shouldRunAcceptOrReject = null,
|
||||
packageName = null,
|
||||
alwaysShow = true,
|
||||
onAccept = { onAccept(it, scope) },
|
||||
onReject = { onReject(it, scope) },
|
||||
) {
|
||||
rememberType = it
|
||||
}
|
||||
|
||||
AcceptRejectButtons(
|
||||
onAccept = { onAccept(rememberType, scope) },
|
||||
onReject = { onReject(rememberType, scope) },
|
||||
)
|
||||
}
|
||||
}
|
||||
+77
@@ -35,6 +35,7 @@ import com.greenart7c3.nostrsigner.models.SignerType
|
||||
import com.greenart7c3.nostrsigner.models.kindToNip
|
||||
import com.greenart7c3.nostrsigner.service.BunkerRequestUtils
|
||||
import com.greenart7c3.nostrsigner.service.isPrivateEvent
|
||||
import com.greenart7c3.nostrsigner.service.model.AmberEvent
|
||||
import com.greenart7c3.nostrsigner.service.toShortenHex
|
||||
import com.greenart7c3.nostrsigner.ui.RememberType
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
@@ -616,6 +617,82 @@ fun BunkerSingleEventHomeScreen(
|
||||
Spacer(Modifier.size(8.dp))
|
||||
Text("Not logged in")
|
||||
}
|
||||
} else if (event.kind == 22242) {
|
||||
// Kind 22242 = relay client authentication (NIP-42)
|
||||
// Permission is per-relay URL extracted from the event's "relay" tag
|
||||
val relayUrl = AmberEvent.relay(event) ?: ""
|
||||
|
||||
// Check for a relay-specific permission first, then wildcard "*" (all relays)
|
||||
val permission = applicationEntity?.permissions?.firstOrNull {
|
||||
it.pkKey == key && it.type == type.toString() && it.kind == 22242 && it.relay == relayUrl
|
||||
} ?: applicationEntity?.permissions?.firstOrNull {
|
||||
it.pkKey == key && it.type == type.toString() && it.kind == 22242 && it.relay == "*"
|
||||
}
|
||||
|
||||
val acceptUntil = permission?.acceptUntil ?: 0
|
||||
val rejectUntil = permission?.rejectUntil ?: 0
|
||||
|
||||
val acceptOrReject = if (rejectUntil == 0L && acceptUntil == 0L) {
|
||||
null
|
||||
} else if (rejectUntil > TimeUtils.now() && rejectUntil > 0 && permission?.acceptable == false) {
|
||||
false
|
||||
} else if (acceptUntil > TimeUtils.now() && acceptUntil > 0 && permission?.acceptable == true) {
|
||||
true
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
BunkerRelayAuthScreen(
|
||||
modifier = modifier,
|
||||
appName = appName,
|
||||
relayUrl = relayUrl,
|
||||
shouldAcceptOrReject = acceptOrReject,
|
||||
defaultScope = RelayAuthScope.SPECIFIC,
|
||||
account = account,
|
||||
onAccept = { rememberType, scope ->
|
||||
if (event.pubKey != account.hexKey && !isPrivateEvent(event.kind, event.tags)) {
|
||||
coroutineScope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.event_pubkey_is_not_equal_to_current_logged_in_user),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
return@BunkerRelayAuthScreen
|
||||
}
|
||||
|
||||
val relayPermission = if (scope == RelayAuthScope.ALL) "*" else relayUrl
|
||||
BunkerRequestUtils.sendResult(
|
||||
context = context,
|
||||
account = account,
|
||||
key = key,
|
||||
response = event.toJson(),
|
||||
bunkerRequest = bunkerRequest,
|
||||
kind = event.kind,
|
||||
onLoading = onLoading,
|
||||
permissions = null,
|
||||
appName = appName,
|
||||
signPolicy = null,
|
||||
shouldCloseApplication = bunkerRequest.closeApplication,
|
||||
rememberType = rememberType,
|
||||
relay = relayPermission,
|
||||
)
|
||||
},
|
||||
onReject = { rememberType, scope ->
|
||||
val relayPermission = if (scope == RelayAuthScope.ALL) "*" else relayUrl
|
||||
BunkerRequestUtils.sendRejection(
|
||||
key = key,
|
||||
account = account,
|
||||
bunkerRequest = bunkerRequest,
|
||||
appName = appName,
|
||||
rememberType = rememberType,
|
||||
signerType = type,
|
||||
kind = event.kind,
|
||||
onLoading = onLoading,
|
||||
relay = relayPermission,
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
val permission =
|
||||
applicationEntity?.permissions?.firstOrNull {
|
||||
|
||||
+70
@@ -30,6 +30,7 @@ import com.greenart7c3.nostrsigner.models.SignerType
|
||||
import com.greenart7c3.nostrsigner.models.kindToNip
|
||||
import com.greenart7c3.nostrsigner.service.IntentUtils
|
||||
import com.greenart7c3.nostrsigner.service.isPrivateEvent
|
||||
import com.greenart7c3.nostrsigner.service.model.AmberEvent
|
||||
import com.greenart7c3.nostrsigner.service.toShortenHex
|
||||
import com.greenart7c3.nostrsigner.ui.ToastManager
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
@@ -242,6 +243,75 @@ fun IntentSingleEventHomeScreen(
|
||||
Spacer(Modifier.size(8.dp))
|
||||
Text("Not logged in")
|
||||
}
|
||||
} else if (event.kind == 22242) {
|
||||
// Kind 22242 = relay client authentication (NIP-42)
|
||||
// Permission is per-relay URL extracted from the event's "relay" tag
|
||||
val relayUrl = AmberEvent.relay(event) ?: ""
|
||||
|
||||
// Check for relay-specific permission first, then wildcard "*" (all relays)
|
||||
val permission = applicationEntity?.permissions?.firstOrNull {
|
||||
it.pkKey == key && it.type == intentData.type.toString() && it.kind == 22242 && it.relay == relayUrl
|
||||
} ?: applicationEntity?.permissions?.firstOrNull {
|
||||
it.pkKey == key && it.type == intentData.type.toString() && it.kind == 22242 && it.relay == "*"
|
||||
}
|
||||
|
||||
val acceptOrReject = if (permission == null) {
|
||||
IntentUtils.isRemembered(applicationEntity?.application?.signPolicy, null)
|
||||
} else {
|
||||
IntentUtils.isRemembered(applicationEntity?.application?.signPolicy, permission)
|
||||
}
|
||||
|
||||
BunkerRelayAuthScreen(
|
||||
modifier = modifier,
|
||||
appName = appName,
|
||||
relayUrl = relayUrl,
|
||||
shouldAcceptOrReject = acceptOrReject,
|
||||
defaultScope = RelayAuthScope.SPECIFIC,
|
||||
account = account,
|
||||
onAccept = { rememberType, scope ->
|
||||
if (intentData.unsignedEventKey.isNotBlank() && intentData.unsignedEventKey != account.hexKey && !isPrivateEvent(event.kind, event.tags)) {
|
||||
ToastManager.toast(
|
||||
title = context.getString(R.string.warning),
|
||||
message = context.getString(R.string.event_pubkey_is_not_equal_to_current_logged_in_user),
|
||||
)
|
||||
return@BunkerRelayAuthScreen
|
||||
}
|
||||
|
||||
val relayPermission = if (scope == RelayAuthScope.ALL) "*" else relayUrl
|
||||
IntentUtils.sendResult(
|
||||
context = context,
|
||||
packageName = packageName,
|
||||
account = account,
|
||||
key = key,
|
||||
clipboardManager = clipboardManager,
|
||||
event = event.toJson(),
|
||||
value = event.sig,
|
||||
intentData = intentData,
|
||||
kind = event.kind,
|
||||
onLoading = onLoading,
|
||||
onRemoveIntentData = onRemoveIntentData,
|
||||
signPolicy = null,
|
||||
appName = applicationName ?: appName,
|
||||
permissions = null,
|
||||
rememberType = rememberType,
|
||||
relay = relayPermission,
|
||||
)
|
||||
},
|
||||
onReject = { rememberType, scope ->
|
||||
val relayPermission = if (scope == RelayAuthScope.ALL) "*" else relayUrl
|
||||
IntentUtils.sendRejection(
|
||||
key = key,
|
||||
account = account,
|
||||
intentData = intentData,
|
||||
appName = appName,
|
||||
rememberType = rememberType,
|
||||
onLoading = onLoading,
|
||||
onRemoveIntentData = onRemoveIntentData,
|
||||
kind = event.kind,
|
||||
relay = relayPermission,
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
val permission =
|
||||
applicationEntity?.permissions?.firstOrNull {
|
||||
|
||||
@@ -643,4 +643,9 @@
|
||||
<string name="account_picture">Account picture</string>
|
||||
<string name="encrypt_and_copy_to_clipboard">Encrypt and copy to clipboard</string>
|
||||
<string name="encrypt_and_show_qr_code">Encrypt and show QR Code</string>
|
||||
<string name="relay_authentication">Relay authentication</string>
|
||||
<string name="relay_auth_request">%1$s wants to authenticate to relay</string>
|
||||
<string name="for_this_relay_only">This relay only</string>
|
||||
<string name="for_all_relays">All relays</string>
|
||||
<string name="relay_auth_scope">Authentication scope</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user