@@ -1165,6 +1165,7 @@ async def set_user_jwt(
11651165 :param namespace: The Kubernetes namespace of the CrateDB cluster.
11661166 :param name: The CrateDB custom resource name defining the CrateDB cluster.
11671167 :param username: The name of the user the JWT properties should be set for.
1168+ :param logger: Logger for operation tracking.
11681169 """
11691170 cratedb = await get_cratedb_resource (namespace , name )
11701171 await cursor .execute (
@@ -1175,36 +1176,67 @@ async def set_user_jwt(
11751176
11761177 username_ident = quote_ident (username , cursor ._impl )
11771178 iss = cratedb ["spec" ].get ("grandCentral" , {}).get ("jwkUrl" )
1178- logger .info ("Setting JWT auth properties for user '%s'" , username )
1179- if user_exists and iss :
1180- query = (
1181- f"ALTER USER { username_ident } SET "
1182- f"""(jwt = {{"iss" = '{ iss } ', "username" = '{ username } ', """
1183- f""""aud" = '{ name } '}})"""
1184- )
1185- pod_name = get_crash_pod_name (cratedb , name )
1186- scheme = get_crash_scheme (cratedb )
1187- logger .info ("... executing query: %s" , query )
1188- result = await execute_sql (
1189- namespace = namespace ,
1190- name = name ,
1191- pod_name = pod_name ,
1192- scheme = scheme ,
1193- sql = query ,
1194- args = None ,
1195- logger = logger ,
1179+
1180+ if not (user_exists and iss ):
1181+ return
1182+
1183+ pod_name = get_crash_pod_name (cratedb , name )
1184+ scheme = get_crash_scheme (cratedb )
1185+
1186+ # Step 1: Reset JWT to NULL first
1187+ # This prevents RoleAlreadyExistsException when restoring snapshots
1188+ # where the user might have JWT properties with different aud/iss
1189+ logger .info ("Resetting JWT auth properties for user '%s'" , username )
1190+ reset_query = f"ALTER USER { username_ident } SET (jwt = NULL)"
1191+ logger .info ("... executing query: %s" , reset_query )
1192+
1193+ reset_result = await execute_sql (
1194+ namespace = namespace ,
1195+ name = name ,
1196+ pod_name = pod_name ,
1197+ scheme = scheme ,
1198+ sql = reset_query ,
1199+ args = None ,
1200+ logger = logger ,
1201+ )
1202+ logger .info ("... result: %s" , reset_result )
1203+
1204+ if (reset_result .rowcount or 0 ) > 0 :
1205+ logger .info ("... JWT reset successful" )
1206+ else :
1207+ logger .info (
1208+ "... JWT reset had no effect (might not have been set). %s" , reset_result
11961209 )
1197- logger .info ("... result: %s" , result )
1198- if (result .rowcount or 0 ) > 0 :
1199- logger .info ("... success" )
1200- else :
1201- logger .info ("... error. %s" , result )
1202- # Continue if the same JWT properties are already set
1203- if (
1204- not result .error_message
1205- or "RoleAlreadyExistsException" not in result .error_message
1206- ):
1207- raise kopf .TemporaryError (delay = config .BOOTSTRAP_RETRY_DELAY )
1210+
1211+ # Step 2: Set new JWT properties with current cluster's aud
1212+ logger .info ("Setting JWT auth properties for user '%s'" , username )
1213+ set_query = (
1214+ f"ALTER USER { username_ident } SET "
1215+ f"""(jwt = {{"iss" = '{ iss } ', "username" = '{ username } ', """
1216+ f""""aud" = '{ name } '}})"""
1217+ )
1218+ logger .info ("... executing query: %s" , set_query )
1219+
1220+ result = await execute_sql (
1221+ namespace = namespace ,
1222+ name = name ,
1223+ pod_name = pod_name ,
1224+ scheme = scheme ,
1225+ sql = set_query ,
1226+ args = None ,
1227+ logger = logger ,
1228+ )
1229+ logger .info ("... result: %s" , result )
1230+
1231+ if (result .rowcount or 0 ) > 0 :
1232+ logger .info ("... success" )
1233+ else :
1234+ logger .info ("... error. %s" , result )
1235+ if (
1236+ not result .error_message
1237+ or "RoleAlreadyExistsException" not in result .error_message
1238+ ):
1239+ raise kopf .TemporaryError (delay = config .BOOTSTRAP_RETRY_DELAY )
12081240
12091241
12101242class StartClusterSubHandler (StateBasedSubHandler ):
0 commit comments