Skip to content

Commit 96d2aef

Browse files
committed
feat: support assigning a default role to a user
Refs #1155
1 parent 96717cd commit 96d2aef

File tree

5 files changed

+110
-25
lines changed

5 files changed

+110
-25
lines changed

extra/locale/heidisql.po

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ msgid ""
77
msgstr ""
88
"Project-Id-Version: HeidiSQL\n"
99
"POT-Creation-Date: 2012-11-05 21:40\n"
10-
"PO-Revision-Date: 2026-03-24 12:08+0100\n"
10+
"PO-Revision-Date: 2026-03-24 15:09+0100\n"
1111
"Last-Translator: Ansgar Becker <anse@heidisql.com>\n"
1212
"Language-Team: English (http://www.transifex.com/projects/p/heidisql/language/en/)\n"
1313
"Language: en\n"
@@ -6833,3 +6833,6 @@ msgstr "Yes, with admin option"
68336833

68346834
msgid "No password hash column available"
68356835
msgstr "No password hash column available"
6836+
6837+
msgid "Default role:"
6838+
msgstr "Default role:"

source/dbstructures.mysql.pas

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3401,6 +3401,7 @@ function TMySqlProvider.GetSql(AId: TQueryId): string;
34013401
qReloadPrivileges: Result := 'FLUSH PRIVILEGES';
34023402
qGrantRole: Result := 'GRANT %s TO %s%s';
34033403
qRevokeRole: Result := 'REVOKE %s FROM %s';
3404+
qSetDefaultRole: Result := 'SET DEFAULT ROLE %s FOR %s';
34043405
else Result := inherited;
34053406
end;
34063407
end;

source/dbstructures.pas

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ interface
4949
qGetReverseForeignKeys, qExplain, qSetTimezone,
5050
qShowFunctionStatus, qShowProcedureStatus, qShowTriggers, qShowEvents, qShowCreateTrigger,
5151
qHelpKeyword, qShowWarnings, qGetEnumTypes,
52-
qDropUser, qCreateRole, qDropRole, qReloadPrivileges, qGrantRole, qRevokeRole);
52+
qDropUser, qCreateRole, qDropRole, qReloadPrivileges, qGrantRole, qRevokeRole, qSetDefaultRole);
5353
TSqlProvider = class
5454
strict protected
5555
FNetType: TNetType;

source/usermanager.lfm

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ object UserManagerForm: TUserManagerForm
174174
TabOrder = 1
175175
object PageControlSettings: TPageControl
176176
Left = 0
177-
Height = 145
177+
Height = 173
178178
Top = 0
179179
Width = 385
180180
ActivePage = tabCredentials
@@ -185,7 +185,7 @@ object UserManagerForm: TUserManagerForm
185185
object tabCredentials: TTabSheet
186186
AutoSize = True
187187
Caption = 'Credentials'
188-
ClientHeight = 117
188+
ClientHeight = 145
189189
ClientWidth = 377
190190
object lblUsername: TLabel
191191
AnchorSideLeft.Control = tabCredentials
@@ -237,10 +237,12 @@ object UserManagerForm: TUserManagerForm
237237
object editRepeatPassword: TEdit
238238
AnchorSideTop.Control = editPassword
239239
AnchorSideTop.Side = asrBottom
240-
Left = 178
240+
AnchorSideRight.Control = tabCredentials
241+
AnchorSideRight.Side = asrBottom
242+
Left = 150
241243
Height = 23
242244
Top = 89
243-
Width = 194
245+
Width = 222
244246
Anchors = [akTop, akLeft, akRight]
245247
BorderSpacing.Around = 5
246248
EchoMode = emPassword
@@ -251,10 +253,12 @@ object UserManagerForm: TUserManagerForm
251253
object editPassword: TEditButton
252254
AnchorSideTop.Control = editFromHost
253255
AnchorSideTop.Side = asrBottom
254-
Left = 178
256+
AnchorSideRight.Control = tabCredentials
257+
AnchorSideRight.Side = asrBottom
258+
Left = 150
255259
Height = 23
256260
Top = 61
257-
Width = 194
261+
Width = 222
258262
Anchors = [akTop, akLeft, akRight]
259263
BorderSpacing.Around = 5
260264
ButtonWidth = 23
@@ -271,10 +275,12 @@ object UserManagerForm: TUserManagerForm
271275
object editFromHost: TEditButton
272276
AnchorSideTop.Control = editUsername
273277
AnchorSideTop.Side = asrBottom
274-
Left = 178
278+
AnchorSideRight.Control = tabCredentials
279+
AnchorSideRight.Side = asrBottom
280+
Left = 150
275281
Height = 23
276282
Top = 33
277-
Width = 194
283+
Width = 222
278284
Anchors = [akTop, akLeft, akRight]
279285
BorderSpacing.Around = 5
280286
ButtonWidth = 23
@@ -289,20 +295,48 @@ object UserManagerForm: TUserManagerForm
289295
end
290296
object editUsername: TEdit
291297
AnchorSideTop.Control = tabCredentials
292-
Left = 178
298+
AnchorSideRight.Control = tabCredentials
299+
AnchorSideRight.Side = asrBottom
300+
Left = 150
293301
Height = 23
294302
Top = 5
295-
Width = 194
303+
Width = 222
296304
Anchors = [akTop, akLeft, akRight]
297305
BorderSpacing.Around = 5
298306
TabOrder = 0
299307
OnChange = Modification
300308
end
309+
object comboDefaultRole: TComboBox
310+
AnchorSideTop.Control = editRepeatPassword
311+
AnchorSideTop.Side = asrBottom
312+
AnchorSideRight.Control = tabCredentials
313+
AnchorSideRight.Side = asrBottom
314+
Left = 150
315+
Height = 23
316+
Top = 117
317+
Width = 222
318+
Anchors = [akTop, akLeft, akRight]
319+
BorderSpacing.Around = 5
320+
ItemHeight = 15
321+
Style = csDropDownList
322+
TabOrder = 4
323+
end
324+
object lblDefaultRole: TLabel
325+
AnchorSideLeft.Control = tabCredentials
326+
AnchorSideTop.Control = comboDefaultRole
327+
AnchorSideTop.Side = asrCenter
328+
Left = 5
329+
Height = 15
330+
Top = 121
331+
Width = 64
332+
BorderSpacing.Around = 5
333+
Caption = 'Default role:'
334+
end
301335
end
302336
object tabLimitations: TTabSheet
303337
AutoSize = True
304338
Caption = 'Limitations'
305-
ClientHeight = 117
339+
ClientHeight = 145
306340
ClientWidth = 377
307341
ImageIndex = 1
308342
object lblMaxQueries: TLabel
@@ -416,7 +450,7 @@ object UserManagerForm: TUserManagerForm
416450
object tabSSL: TTabSheet
417451
AutoSize = True
418452
Caption = 'SSL options'
419-
ClientHeight = 117
453+
ClientHeight = 145
420454
ClientWidth = 377
421455
ImageIndex = 2
422456
object lblCipher: TLabel
@@ -528,20 +562,20 @@ object UserManagerForm: TUserManagerForm
528562
end
529563
object PageControlAccess: TPageControl
530564
Left = 0
531-
Height = 178
532-
Top = 145
565+
Height = 150
566+
Top = 173
533567
Width = 385
534568
ActivePage = tabPrivileges
535569
Align = alClient
536570
TabIndex = 0
537571
TabOrder = 1
538572
object tabPrivileges: TTabSheet
539573
Caption = 'Privileges'
540-
ClientHeight = 150
574+
ClientHeight = 122
541575
ClientWidth = 377
542576
object treePrivs: TLazVirtualStringTree
543577
Left = 0
544-
Height = 122
578+
Height = 94
545579
Top = 28
546580
Width = 377
547581
Align = alClient
@@ -592,11 +626,11 @@ object UserManagerForm: TUserManagerForm
592626
end
593627
object tabRoles: TTabSheet
594628
Caption = 'Roles'
595-
ClientHeight = 150
629+
ClientHeight = 122
596630
ClientWidth = 377
597631
object ValueListEditorRoles: TValueListEditor
598632
Left = 0
599-
Height = 150
633+
Height = 122
600634
Top = 0
601635
Width = 377
602636
Align = alClient

source/usermanager.pas

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ TPrivComparer = class(TComparer<TPrivObj>)
3232
TUserProblem = (upNone, upEmptyPassword, upInvalidPasswordLen, upSkipNameResolve, upUnknown);
3333

3434
TUser = class(TObject)
35-
Username, Host, Password, Cipher, Issuer, Subject: String;
35+
Username, Host, Password, Cipher, Issuer, Subject, DefaultRole: String;
3636
MaxQueries, MaxUpdates, MaxConnections, MaxUserConnections, SSL: Integer;
3737
Problem: TUserProblem;
3838
IsRole: Boolean;
@@ -51,7 +51,8 @@ TUser = class(TObject)
5151
PUser = ^TUser;
5252
TUserList = class(TObjectList<TUser>)
5353
public
54-
function GetRoleNames: TStringList;
54+
function GetRoleNames: TStringList; overload;
55+
procedure GetRoleNames(Strings: TStrings); overload;
5556
function GetDefaultRoles: TStringList;
5657
end;
5758

@@ -130,6 +131,8 @@ TUserManagerForm = class(TExtForm)
130131
tlbObjects: TToolBar;
131132
btnAddObject: TToolButton;
132133
ValueListEditorRoles: TValueListEditor;
134+
lblDefaultRole: TLabel;
135+
comboDefaultRole: TComboBox;
133136
procedure btnCancelClick(Sender: TObject);
134137
procedure editFromHostButtonClick(Sender: TObject);
135138
procedure editPasswordButtonClick(Sender: TObject);
@@ -193,7 +196,7 @@ TUserManagerForm = class(TExtForm)
193196
{ Private declarations }
194197
FUsers: TUserList;
195198
FModified, FAdded: Boolean;
196-
FHasIsRole: Boolean;
199+
FHasIsRole, FHasDefaultRole: Boolean;
197200
FCloneGrants: TStringList;
198201
FPrivObjects: TPrivObjList;
199202
FPrivsGlobal, FPrivsDb, FPrivsTable, FPrivsRoutine, FPrivsColumn: TStringList;
@@ -257,6 +260,8 @@ procedure TUserManagerForm.FormCreate(Sender: TObject);
257260
'REPLICATION SLAVE ADMIN,SET USER,SLAVE MONITOR');
258261
FixVT(listUsers);
259262
FixVT(treePrivs);
263+
FHasIsRole := False;
264+
FHasDefaultRole := False;
260265
end;
261266

262267
procedure TUserManagerForm.FormDestroy(Sender: TObject);
@@ -289,7 +294,7 @@ procedure TUserManagerForm.FormShow(Sender: TObject);
289294
Version, i: Integer;
290295
Users: TDBQuery;
291296
U: TUser;
292-
tmp, PasswordExpr, IsRoleExpr: String;
297+
tmp, PasswordExpr, IsRoleExpr, DefaultRoleExpr: String;
293298
SkipNameResolve,
294299
HasPassword, HasAuthString: Boolean;
295300
PasswordLengthMatters: Boolean;
@@ -419,6 +424,7 @@ procedure TUserManagerForm.FormShow(Sender: TObject);
419424
HasPassword := UserTableColumns.IndexOf('password') > -1;
420425
HasAuthString := UserTableColumns.IndexOf('authentication_string') > -1;
421426
FHasIsRole := UserTableColumns.IndexOf('is_role') > -1;
427+
FHasDefaultRole := UserTableColumns.IndexOf('default_role') > -1;
422428
if HasPassword and (not HasAuthString) then
423429
PasswordExpr := 'password'
424430
else if (not HasPassword) and HasAuthString then
@@ -429,13 +435,15 @@ procedure TUserManagerForm.FormShow(Sender: TObject);
429435
Raise Exception.Create(_('No password hash column available'));
430436
PasswordExpr := PasswordExpr + ' AS ' + FConnection.QuoteIdent('password');
431437
IsRoleExpr := IfThen(FHasIsRole, 'is_role', FConnection.EscapeString('N')+' AS is_role');
438+
DefaultRoleExpr := IfThen(FHasDefaultRole, 'default_role', FConnection.EscapeString('')+' AS default_role');
432439

433440
Users := FConnection.GetResults(
434441
'SELECT '+
435442
FConnection.QuoteIdent('user') + ', ' +
436443
FConnection.QuoteIdent('host') + ', ' +
437444
PasswordExpr + ', ' +
438-
IsRoleExpr + ' ' +
445+
IsRoleExpr + ', ' +
446+
DefaultRoleExpr + ' ' +
439447
'FROM '+FConnection.QuoteIdent('mysql')+'.'+FConnection.QuoteIdent('user')
440448
);
441449
FUsers := TUserList.Create(True);
@@ -446,6 +454,7 @@ procedure TUserManagerForm.FormShow(Sender: TObject);
446454
U.Host := Users.Col('host');
447455
U.Password := Users.Col('password');
448456
U.IsRole := UpperCase(Users.Col('is_role')) = 'Y';
457+
U.DefaultRole := Users.Col('default_role');
449458
U.Problem := upNone;
450459
if U.IsUser then begin
451460
if Length(U.Password) = 0 then
@@ -635,6 +644,10 @@ procedure TUserManagerForm.listUsersFocusChanged(Sender: TBaseVirtualTree; Node:
635644
editPassword.Clear;
636645
editPassword.TextHint := '';
637646
editRepeatPassword.Clear;
647+
comboDefaultRole.Items.Clear;
648+
comboDefaultRole.Items.Add(_('None'));
649+
FUsers.GetRoleNames(comboDefaultRole.Items);
650+
comboDefaultRole.ItemIndex := 0;
638651
spinMaxQueries.Value := 0;
639652
spinMaxUpdates.Value := 0;
640653
spinMaxConnections.Value := 0;
@@ -655,6 +668,10 @@ procedure TUserManagerForm.listUsersFocusChanged(Sender: TBaseVirtualTree; Node:
655668
UserHost := FConnection.EscapeString(User.Username);
656669
editUsername.Text := User.Username;
657670
editFromHost.Text := User.Host;
671+
i := comboDefaultRole.Items.IndexOf(User.DefaultRole);
672+
if i > -1 then
673+
comboDefaultRole.ItemIndex := i;
674+
658675
Caption := Caption + ' - ' + User.Username;
659676

660677
AllPNames := TStringList.Create;
@@ -893,6 +910,8 @@ procedure TUserManagerForm.listUsersFocusChanged(Sender: TBaseVirtualTree; Node:
893910
editPassword.Enabled := UserSelected and User.IsUser;
894911
lblRepeatPassword.Enabled := UserSelected and User.IsUser;
895912
editRepeatPassword.Enabled := UserSelected and User.IsUser;
913+
comboDefaultRole.Enabled := UserSelected and User.IsUser and FHasDefaultRole;
914+
lblDefaultRole.Enabled := comboDefaultRole.Enabled;
896915
tabCredentials.Enabled := UserSelected;
897916
lblMaxQueries.Enabled := UserSelected and User.IsUser and (FConnection.ServerVersionInt >= 40002);
898917

@@ -1508,6 +1527,23 @@ procedure TUserManagerForm.btnSaveClick(Sender: TObject);
15081527
end;
15091528
end;
15101529

1530+
// Set default role
1531+
if comboDefaultRole.Enabled and (comboDefaultRole.ItemIndex > -1) then begin
1532+
if comboDefaultRole.ItemIndex = 0 then begin
1533+
FConnection.Query(qSetDefaultRole, ['NONE', OrgUserHost]);
1534+
end
1535+
else try
1536+
RoleName := comboDefaultRole.Text;
1537+
RoleAssigned := ValueListEditorRoles.Strings.Values[RoleName];
1538+
if (RoleAssigned = TUser.RoleYes) or (RoleAssigned = TUser.RoleYesAdmin) then
1539+
FConnection.Query(qSetDefaultRole, [FConnection.EscapeString(RoleName), OrgUserHost]);
1540+
except
1541+
on E:EDbError do; // Happens when this role was not granted before
1542+
end;
1543+
FConnection.ShowWarnings;
1544+
end;
1545+
1546+
15111547
// Rename user
15121548
if (FocusedUser.Username <> editUsername.Text) or (FocusedUser.Host <> editFromHost.Text) then begin
15131549

@@ -1540,6 +1576,7 @@ procedure TUserManagerForm.btnSaveClick(Sender: TObject);
15401576
FocusedUser.Host := editFromHost.Text;
15411577
if editPassword.Modified then
15421578
FocusedUser.Password := editPassword.Text;
1579+
FocusedUser.DefaultRole := IfThen(comboDefaultRole.ItemIndex=0, '', comboDefaultRole.Text);
15431580
FocusedUser.SSL := comboSSL.ItemIndex;
15441581
FocusedUser.Cipher := editCipher.Text;
15451582
FocusedUser.Issuer := editIssuer.Text;
@@ -1740,6 +1777,7 @@ constructor TUser.Create;
17401777
Username := '';
17411778
Host := '';
17421779
Password := '';
1780+
DefaultRole := '';
17431781
Cipher := '';
17441782
Issuer := '';
17451783
Subject := '';
@@ -1862,6 +1900,15 @@ function TUserList.GetRoleNames: TStringList;
18621900
end;
18631901
end;
18641902

1903+
procedure TUserList.GetRoleNames(Strings: TStrings);
1904+
var
1905+
RoleNames: TStringList;
1906+
begin
1907+
RoleNames := GetRoleNames;
1908+
Strings.AddStrings(RoleNames);
1909+
RoleNames.Free;
1910+
end;
1911+
18651912
function TUserList.GetDefaultRoles: TStringList;
18661913
var
18671914
RoleNames: TStringList;

0 commit comments

Comments
 (0)