@@ -163,6 +163,159 @@ def get_role(role: str):
163163
164164class ManageUsers :
165165
166+ @app .command ()
167+ @update_sqlite_path
168+ def load_users (input_file : str ,
169+ sqlite_path : Optional [str ] = None ):
170+ """Load users from a JSON file.
171+
172+ Expected JSON format:
173+ {
174+ "users": [
175+ {
176+ "username": "user@example.com",
177+ "email": "user@example.com",
178+ "password": "password123",
179+ "role": "User",
180+ "active": true,
181+ "auth_source": "internal"
182+ },
183+ {
184+ "username": "ldap_user",
185+ "email": "ldap@example.com",
186+ "role": "Administrator",
187+ "active": true,
188+ "auth_source": "ldap"
189+ }
190+ ]
191+ }
192+ """
193+ from urllib .parse import unquote
194+
195+ print ('----------' )
196+ print ('Loading users from:' , input_file )
197+ print ('SQLite pgAdmin config:' , config .SQLITE_PATH )
198+ print ('----------' )
199+
200+ # Parse the input file path
201+ try :
202+ file_path = unquote (input_file )
203+ except Exception as e :
204+ return _handle_error (str (e ), True )
205+
206+ # Read and parse JSON file
207+ try :
208+ with open (file_path ) as f :
209+ data = jsonlib .load (f )
210+ except jsonlib .decoder .JSONDecodeError as e :
211+ return _handle_error (
212+ gettext ("Error parsing input file %s: %s" % (file_path , e )),
213+ True )
214+ except Exception as e :
215+ return _handle_error (
216+ gettext ("Error reading input file %s: [%d] %s" %
217+ (file_path , e .errno , e .strerror )), True )
218+
219+ # Validate JSON structure
220+ if 'users' not in data :
221+ return _handle_error (
222+ gettext ("Invalid JSON format: 'users' key not found" ), True )
223+
224+ users_data = data ['users' ]
225+ if not isinstance (users_data , list ):
226+ return _handle_error (
227+ gettext ("Invalid JSON format: 'users' must be a list" ), True )
228+
229+ created_count = 0
230+ skipped_count = 0
231+ error_count = 0
232+
233+ app = create_app (config .APP_NAME + '-cli' )
234+ with (app .test_request_context ()):
235+ for user_entry in users_data :
236+ try :
237+ # Validate required fields
238+ if 'username' not in user_entry and \
239+ 'email' not in user_entry :
240+ print (f"Skipping user: missing 'username' or 'email'" )
241+ error_count += 1
242+ continue
243+
244+ # Determine auth_source (default to internal)
245+ auth_source = user_entry .get ('auth_source' , INTERNAL )
246+
247+ # Build user data dict
248+ user_data = {
249+ 'username' : user_entry .get ('username' ,
250+ user_entry .get ('email' )),
251+ 'email' : user_entry .get ('email' ),
252+ 'role' : user_entry .get ('role' , 'User' ),
253+ 'active' : user_entry .get ('active' , True ),
254+ 'auth_source' : auth_source
255+ }
256+
257+ # For internal auth, password is required
258+ if auth_source == INTERNAL :
259+ if 'password' not in user_entry :
260+ print (f"Skipping user '{ user_data ['username' ]} ': "
261+ f"password required for internal auth" )
262+ error_count += 1
263+ continue
264+ user_data ['newPassword' ] = user_entry ['password' ]
265+ user_data ['confirmPassword' ] = user_entry ['password' ]
266+
267+ # Check if user already exists
268+ usr = User .query .filter_by (username = user_data ['username' ],
269+ auth_source = auth_source ).first ()
270+
271+ uid = usr .id if usr else None
272+
273+ if uid :
274+ print (f"Skipping user '{ user_data ['username' ]} ': "
275+ f"already exists" )
276+ skipped_count += 1
277+ continue
278+
279+ # Get role ID
280+ role = Role .query .filter_by (name = user_data ['role' ]).first ()
281+ rid = role .id if role else None
282+
283+ if rid is None :
284+ print (f"Skipping user '{ user_data ['username' ]} ': "
285+ f"role '{ user_data ['role' ]} ' does not exist" )
286+ error_count += 1
287+ continue
288+
289+ user_data ['role' ] = rid
290+
291+ # Validate password length for internal users
292+ if auth_source == INTERNAL :
293+ if len (user_data ['newPassword' ]) < 6 :
294+ print (f"Skipping user '{ user_data ['username' ]} ': "
295+ f"password must be at least 6 characters" )
296+ error_count += 1
297+ continue
298+
299+ # Create the user
300+ status , msg = create_user (user_data )
301+ if status :
302+ print (f"Created user: { user_data ['username' ]} " )
303+ created_count += 1
304+ else :
305+ print (f"Error creating user '{ user_data ['username' ]} '"
306+ f": { msg } " )
307+ error_count += 1
308+
309+ except Exception as e :
310+ print (f"Error processing user entry: { str (e )} " )
311+ error_count += 1
312+
313+ print ('----------' )
314+ print (f"Users created: { created_count } " )
315+ print (f"Users skipped (already exist): { skipped_count } " )
316+ print (f"Errors: { error_count } " )
317+ print ('----------' )
318+
166319 @app .command ()
167320 @update_sqlite_path
168321 def add_user (email : str , password : str ,
0 commit comments