7
7
from libpvarki .schemas .product import UserCRUDRequest
8
8
from pydantic import BaseModel , Extra , Field
9
9
from keycloak .keycloak_admin import KeycloakAdmin
10
+ from keycloak .exceptions import KeycloakError
10
11
11
12
12
13
from .rmsettings import RMSettings
@@ -97,6 +98,26 @@ async def check_user_roles(self, user: KCUserData) -> bool:
97
98
return True
98
99
return False
99
100
101
+ async def resolve_kc_id (self , email : str ) -> Optional [str ]:
102
+ """Find user with given email"""
103
+ found = await self .kcadmin .a_get_users ({"email" : email })
104
+ if not found :
105
+ return None
106
+ LOGGER .debug ("found: {}" .format (found ))
107
+ if len (found ) > 1 :
108
+ LOGGER .warning ("Found more than one result, using the first one" )
109
+ item = found [0 ]
110
+ if "id" not in item :
111
+ LOGGER .error ("Found user does not have id" )
112
+ return None
113
+ return str (item ["id" ])
114
+
115
+ def user_kc_email (self , user : KCUserData ) -> str :
116
+ """Return the unique email for user in KC"""
117
+ conf = RMSettings .singleton ()
118
+ manifest = conf .kraftwerk_manifest_dict
119
+ return f"{ user .productdata .uuid } @{ manifest ['dns' ]} "
120
+
100
121
async def create_kc_user (self , user : KCUserData ) -> Optional [KCUserData ]:
101
122
"""Create a new user in KC"""
102
123
conf = RMSettings .singleton ()
@@ -106,9 +127,11 @@ async def create_kc_user(self, user: KCUserData) -> Optional[KCUserData]:
106
127
if user .kc_id :
107
128
raise ValueError ("Cannot specify KC id when creating" )
108
129
pdata = user .productdata
130
+ user_email = self .user_kc_email (user )
131
+
109
132
send_payload = {
110
133
"username" : pdata .callsign , # NOTE: KeyCloak now forces this all lowercase
111
- "email" : f" { pdata . uuid } @ { manifest [ 'dns' ] } " ,
134
+ "email" : user_email ,
112
135
"firstName" : pdata .callsign ,
113
136
"lastName" : manifest ["deployment" ],
114
137
"enabled" : True ,
@@ -156,28 +179,47 @@ async def update_kc_user(self, user: KCUserData) -> Optional[KCUserData]:
156
179
conf = RMSettings .singleton ()
157
180
if not conf .kc_enabled :
158
181
return None
159
- if not user .kc_id :
160
- LOGGER .error ("No KC id defined" )
161
- return None
162
- await self .check_user_roles (user )
163
182
manifest = conf .kraftwerk_manifest_dict
164
183
pdata = user .productdata
184
+ user_email = self .user_kc_email (user )
185
+
186
+ if not user .kc_id :
187
+ LOGGER .warning ("No KC id defined, trying to resolve" )
188
+ resolved = await self .resolve_kc_id (user_email )
189
+ if not resolved :
190
+ LOGGER .error ("Could not resolve KC id, trying to create the user" )
191
+ return await self .create_kc_user (user )
192
+ user .kc_id = resolved
193
+ await self .check_user_roles (user )
165
194
send_payload = user .kc_data
166
195
send_payload .update (
167
196
{
168
- "email" : f" { pdata . uuid } @ { manifest [ 'dns' ] } " ,
197
+ "email" : user_email ,
169
198
"firstName" : pdata .callsign ,
170
199
"lastName" : manifest ["deployment" ],
171
200
"enabled" : True ,
172
201
}
173
202
)
203
+ if "attributes" not in send_payload :
204
+ send_payload ["attributes" ] = {
205
+ "callsign" : pdata .callsign ,
206
+ }
174
207
send_payload ["attributes" ].update (
175
208
{
176
209
"certpem" : pdata .x509cert ,
177
210
"altUsernames" : [f"{ pdata .callsign } _{ productname } " for productname in manifest ["products" ].keys ()],
178
211
}
179
212
)
180
- await self .kcadmin .a_update_user (user .kc_id , send_payload )
213
+ for rofieldname in ("createTimestamp" , "createdTimestamp" , "modifyTimestamp" ):
214
+ if rofieldname in send_payload :
215
+ del send_payload [rofieldname ]
216
+ if rofieldname in send_payload ["attributes" ]:
217
+ del send_payload ["attributes" ][rofieldname ]
218
+ LOGGER .debug ("Sending payload: {}" .format (send_payload ))
219
+ try :
220
+ await self .kcadmin .a_update_user (user .kc_id , send_payload )
221
+ except KeycloakError as exc :
222
+ LOGGER .exception ("Could not update KC user: {}" .format (exc ))
181
223
return await self ._refresh_user (user .kc_id , pdata )
182
224
183
225
async def delete_kc_user (self , user : KCUserData ) -> bool :
0 commit comments