bugfix(CON-01)
This commit is contained in:
parent
9f25cfd160
commit
18fb8e32b3
2 changed files with 47 additions and 8 deletions
|
|
@ -191,7 +191,6 @@ class UserConnection(PowerOnModel):
|
||||||
json_schema_extra={"frontend_type": "list", "frontend_readonly": True, "frontend_required": False, "label": "Gewährte Berechtigungen"},
|
json_schema_extra={"frontend_type": "list", "frontend_readonly": True, "frontend_required": False, "label": "Gewährte Berechtigungen"},
|
||||||
)
|
)
|
||||||
|
|
||||||
@computed_field
|
|
||||||
@computed_field
|
@computed_field
|
||||||
@property
|
@property
|
||||||
def connectionReference(self) -> str:
|
def connectionReference(self) -> str:
|
||||||
|
|
|
||||||
|
|
@ -427,14 +427,54 @@ def update_connection(
|
||||||
detail=routeApiMsg("Connection not found")
|
detail=routeApiMsg("Connection not found")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update connection fields
|
# Merge incoming changes into a dict and re-validate via pydantic.
|
||||||
|
# Direct setattr() bypasses type coercion (PowerOnModel doesn't enable
|
||||||
|
# validate_assignment), which leaves enum fields as raw strings and
|
||||||
|
# later breaks .value access. Also filters out computed / unknown keys.
|
||||||
|
writableFields = set(UserConnection.model_fields.keys())
|
||||||
|
previous = connection.model_dump()
|
||||||
|
merged = dict(previous)
|
||||||
for field, value in connection_data.items():
|
for field, value in connection_data.items():
|
||||||
if hasattr(connection, field):
|
if field in writableFields:
|
||||||
setattr(connection, field, value)
|
merged[field] = value
|
||||||
|
merged["lastChecked"] = getUtcTimestamp()
|
||||||
# Update lastChecked timestamp using UTC timestamp
|
connection = UserConnection.model_validate(merged)
|
||||||
connection.lastChecked = getUtcTimestamp()
|
|
||||||
|
# If this is a remote (non-local) connection and any identity-bearing
|
||||||
|
# field changed, the stored OAuth tokens no longer match the account.
|
||||||
|
# Force the user to reconnect: mark PENDING and revoke existing tokens.
|
||||||
|
identityFields = ("externalUsername", "externalEmail", "externalId", "authority")
|
||||||
|
authorityValue = (
|
||||||
|
connection.authority.value
|
||||||
|
if hasattr(connection.authority, "value")
|
||||||
|
else str(connection.authority)
|
||||||
|
)
|
||||||
|
isRemote = authorityValue != AuthAuthority.LOCAL.value
|
||||||
|
identityChanged = any(
|
||||||
|
previous.get(field) != merged.get(field) for field in identityFields
|
||||||
|
)
|
||||||
|
if isRemote and identityChanged:
|
||||||
|
connection.status = ConnectionStatus.PENDING
|
||||||
|
connection.expiresAt = None
|
||||||
|
try:
|
||||||
|
existingTokens = interface.db.getRecordset(
|
||||||
|
Token, recordFilter={"connectionId": connectionId}
|
||||||
|
)
|
||||||
|
for token in existingTokens:
|
||||||
|
interface.revokeTokenById(
|
||||||
|
token["id"],
|
||||||
|
revokedBy=currentUser.id,
|
||||||
|
reason="connection identity changed",
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
f"Revoked {len(existingTokens)} token(s) for connection "
|
||||||
|
f"{connectionId} after identity change; reconnect required."
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(
|
||||||
|
f"Failed to revoke tokens for connection {connectionId}: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
# Update connection - models now handle timestamp serialization automatically
|
# Update connection - models now handle timestamp serialization automatically
|
||||||
interface.db.recordModify(UserConnection, connectionId, connection.model_dump())
|
interface.db.recordModify(UserConnection, connectionId, connection.model_dump())
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue