Skip to content

Commit 07112a0

Browse files
committed
feat: support authentication plugin selection in user manager
Refs #1728
1 parent 5f2959d commit 07112a0

5 files changed

Lines changed: 102 additions & 22 deletions

File tree

extra/locale/heidisql.po

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6834,6 +6834,9 @@ msgstr "No password hash column available"
68346834
msgid "Default role:"
68356835
msgstr "Default role:"
68366836

6837+
msgid "Plugin:"
6838+
msgstr "Plugin:"
6839+
68376840
msgid "Foreign key table not found"
68386841
msgstr "Foreign key table not found"
68396842

source/dbstructures.mysql.pas

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3244,7 +3244,10 @@ procedure TMySQLLib.AssignProcedures;
32443244
{ TMySqlProvider }
32453245

32463246
function TMySqlProvider.GetSql(AId: TQueryId): string;
3247+
var
3248+
IsMariaDB: Boolean;
32473249
begin
3250+
IsMariaDB := ServerVersion >= 100000;
32483251
case AId of
32493252
qDatabaseDrop: Result := 'DROP DATABASE %s';
32503253
qEmptyTable: Result := 'TRUNCATE %s';
@@ -3420,6 +3423,11 @@ function TMySqlProvider.GetSql(AId: TQueryId): string;
34203423
''
34213424
)
34223425
);
3426+
qGetAuthPlugins: Result := IfThen(
3427+
(FServerVersion >= 50100) or IsMariaDB, // mysql 5.1+ and all mariadb versions
3428+
'SELECT PLUGIN_NAME FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_TYPE=''AUTHENTICATION'' AND PLUGIN_STATUS=''ACTIVE''',
3429+
''
3430+
);
34233431
else Result := inherited;
34243432
end;
34253433
end;

source/dbstructures.pas

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ interface
5050
qShowFunctionStatus, qShowProcedureStatus, qShowTriggers, qShowEvents, qShowCreateTrigger,
5151
qHelpKeyword, qShowWarnings, qGetEnumTypes,
5252
qDropUser, qCreateRole, qDropRole, qReloadPrivileges, qGrantRole, qRevokeRole, qSetDefaultRole,
53-
qAutoInc, qIndexVisible, qIndexInvisible);
53+
qAutoInc, qIndexVisible, qIndexInvisible, qGetAuthPlugins);
5454
TSqlProvider = class
5555
strict protected
5656
FNetType: TNetType;

source/usermanager.lfm

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ object UserManagerForm: TUserManagerForm
173173
TabOrder = 1
174174
object PageControlSettings: TPageControl
175175
Left = 0
176-
Height = 173
176+
Height = 201
177177
Top = 0
178178
Width = 385
179179
ActivePage = tabCredentials
@@ -184,7 +184,7 @@ object UserManagerForm: TUserManagerForm
184184
object tabCredentials: TTabSheet
185185
AutoSize = True
186186
Caption = 'Credentials'
187-
ClientHeight = 145
187+
ClientHeight = 173
188188
ClientWidth = 377
189189
object lblUsername: TLabel
190190
AnchorSideLeft.Control = tabCredentials
@@ -306,13 +306,13 @@ object UserManagerForm: TUserManagerForm
306306
OnChange = Modification
307307
end
308308
object comboDefaultRole: TComboBox
309-
AnchorSideTop.Control = editRepeatPassword
309+
AnchorSideTop.Control = comboPlugins
310310
AnchorSideTop.Side = asrBottom
311311
AnchorSideRight.Control = tabCredentials
312312
AnchorSideRight.Side = asrBottom
313313
Left = 150
314314
Height = 23
315-
Top = 117
315+
Top = 145
316316
Width = 222
317317
Anchors = [akTop, akLeft, akRight]
318318
BorderSpacing.Around = 5
@@ -327,16 +327,43 @@ object UserManagerForm: TUserManagerForm
327327
AnchorSideTop.Side = asrCenter
328328
Left = 5
329329
Height = 15
330-
Top = 121
330+
Top = 149
331331
Width = 64
332332
BorderSpacing.Around = 5
333333
Caption = 'Default role:'
334334
end
335+
object lblPlugin: TLabel
336+
AnchorSideLeft.Control = tabCredentials
337+
AnchorSideTop.Control = comboPlugins
338+
AnchorSideTop.Side = asrCenter
339+
Left = 5
340+
Height = 15
341+
Top = 121
342+
Width = 37
343+
BorderSpacing.Around = 5
344+
Caption = 'Plugin:'
345+
end
346+
object comboPlugins: TComboBox
347+
AnchorSideTop.Control = editRepeatPassword
348+
AnchorSideTop.Side = asrBottom
349+
AnchorSideRight.Control = tabCredentials
350+
AnchorSideRight.Side = asrBottom
351+
Left = 150
352+
Height = 23
353+
Top = 117
354+
Width = 222
355+
Anchors = [akTop, akLeft, akRight]
356+
BorderSpacing.Around = 5
357+
ItemHeight = 15
358+
Style = csDropDownList
359+
TabOrder = 5
360+
OnChange = Modification
361+
end
335362
end
336363
object tabLimitations: TTabSheet
337364
AutoSize = True
338365
Caption = 'Limitations'
339-
ClientHeight = 145
366+
ClientHeight = 173
340367
ClientWidth = 377
341368
ImageIndex = 1
342369
object lblMaxQueries: TLabel
@@ -450,7 +477,7 @@ object UserManagerForm: TUserManagerForm
450477
object tabSSL: TTabSheet
451478
AutoSize = True
452479
Caption = 'SSL options'
453-
ClientHeight = 145
480+
ClientHeight = 173
454481
ClientWidth = 377
455482
ImageIndex = 2
456483
object lblCipher: TLabel
@@ -562,20 +589,20 @@ object UserManagerForm: TUserManagerForm
562589
end
563590
object PageControlAccess: TPageControl
564591
Left = 0
565-
Height = 150
566-
Top = 173
592+
Height = 122
593+
Top = 201
567594
Width = 385
568595
ActivePage = tabPrivileges
569596
Align = alClient
570597
TabIndex = 0
571598
TabOrder = 1
572599
object tabPrivileges: TTabSheet
573600
Caption = 'Privileges'
574-
ClientHeight = 122
601+
ClientHeight = 94
575602
ClientWidth = 377
576603
object treePrivs: TLazVirtualStringTree
577604
Left = 0
578-
Height = 94
605+
Height = 66
579606
Top = 28
580607
Width = 377
581608
Align = alClient
@@ -626,11 +653,11 @@ object UserManagerForm: TUserManagerForm
626653
end
627654
object tabRoles: TTabSheet
628655
Caption = 'Roles'
629-
ClientHeight = 122
656+
ClientHeight = 94
630657
ClientWidth = 377
631658
object ValueListEditorRoles: TValueListEditor
632659
Left = 0
633-
Height = 122
660+
Height = 94
634661
Top = 0
635662
Width = 377
636663
Align = alClient
@@ -645,7 +672,7 @@ object UserManagerForm: TUserManagerForm
645672
OnGetPickList = ValueListEditorRolesGetPickList
646673
ColWidths = (
647674
64
648-
292
675+
309
649676
)
650677
end
651678
end

source/usermanager.pas

Lines changed: 49 additions & 7 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, DefaultRole: String;
35+
Username, Host, Password, Cipher, Issuer, Subject, DefaultRole, Plugin: String;
3636
MaxQueries, MaxUpdates, MaxConnections, MaxUserConnections, SSL: Integer;
3737
Problem: TUserProblem;
3838
IsRole: Boolean;
@@ -63,6 +63,8 @@ EInputError = class(Exception);
6363
TUserManagerForm = class(TExtForm)
6464
btnCancel: TSpeedButton;
6565
btnSave: TSpeedButton;
66+
comboPlugins: TComboBox;
67+
lblPlugin: TLabel;
6668
pnlBottom: TPanel;
6769
pnlLeft: TPanel;
6870
listUsers: TLazVirtualStringTree;
@@ -197,11 +199,14 @@ TUserManagerForm = class(TExtForm)
197199
FUsers: TUserList;
198200
FModified, FAdded: Boolean;
199201
FHasIsRole, FHasDefaultRole: Boolean;
202+
FHasPlugin: Boolean;
203+
FPlugins: TStringList;
200204
FCloneGrants: TStringList;
201205
FPrivObjects: TPrivObjList;
202206
FPrivsGlobal, FPrivsDb, FPrivsTable, FPrivsRoutine, FPrivsColumn: TStringList;
203207
FConnection: TDBConnection;
204208
FColorReadPriv, FColorWritePriv, FColorAdminPriv: TColor;
209+
FSQLPluginPrefix, FSQLPluginPassPrefix: String;
205210
procedure SetModified(Value: Boolean);
206211
property Modified: Boolean read FModified write SetModified;
207212
function GetPrivByNode(Node: PVirtualNode): TPrivObj;
@@ -296,7 +301,7 @@ procedure TUserManagerForm.FormShow(Sender: TObject);
296301
Version, i: Integer;
297302
Users: TDBQuery;
298303
U: TUser;
299-
tmp, PasswordExpr, IsRoleExpr, DefaultRoleExpr: String;
304+
tmp, PasswordExpr, IsRoleExpr, DefaultRoleExpr, PluginExpr: String;
300305
SkipNameResolve,
301306
HasPassword, HasAuthString: Boolean;
302307
PasswordLengthMatters: Boolean;
@@ -328,6 +333,8 @@ procedure TUserManagerForm.FormShow(Sender: TObject);
328333
FPrivsRoutine := InitPrivList('GRANT');
329334
FPrivsColumn := InitPrivList('INSERT,SELECT,UPDATE,REFERENCES');
330335
PasswordLengthMatters := True;
336+
FSQLPluginPrefix := IfThen(FConnection.Parameters.IsMariaDB, 'VIA', 'WITH');
337+
FSQLPluginPassPrefix := IfThen(FConnection.Parameters.IsMariaDB, 'USING', 'BY');
331338

332339
if Version >= 40002 then begin
333340
FPrivsGlobal.Add('REPLICATION CLIENT');
@@ -419,6 +426,7 @@ procedure TUserManagerForm.FormShow(Sender: TObject);
419426
HasAuthString := UserTableColumns.IndexOf('authentication_string') > -1;
420427
FHasIsRole := UserTableColumns.IndexOf('is_role') > -1;
421428
FHasDefaultRole := UserTableColumns.IndexOf('default_role') > -1;
429+
FHasPlugin := UserTableColumns.IndexOf('plugin') > -1;
422430
if HasPassword and (not HasAuthString) then
423431
PasswordExpr := 'password'
424432
else if (not HasPassword) and HasAuthString then
@@ -430,14 +438,21 @@ procedure TUserManagerForm.FormShow(Sender: TObject);
430438
PasswordExpr := PasswordExpr + ' AS ' + FConnection.QuoteIdent('password');
431439
IsRoleExpr := IfThen(FHasIsRole, 'is_role', FConnection.EscapeString('N')+' AS is_role');
432440
DefaultRoleExpr := IfThen(FHasDefaultRole, 'default_role', FConnection.EscapeString('')+' AS default_role');
441+
PluginExpr := IfThen(FHasPlugin, 'plugin', FConnection.EscapeString('')+' AS plugin');
442+
if FConnection.SqlProvider.Has(qGetAuthPlugins) then
443+
FPlugins := FConnection.GetCol(FConnection.SqlProvider.GetSql(qGetAuthPlugins))
444+
else
445+
FPlugins := TStringList.Create;
446+
comboPlugins.Enabled := FHasPlugin;
433447

434448
Users := FConnection.GetResults(
435449
'SELECT '+
436450
FConnection.QuoteIdent('user') + ', ' +
437451
FConnection.QuoteIdent('host') + ', ' +
438452
PasswordExpr + ', ' +
439453
IsRoleExpr + ', ' +
440-
DefaultRoleExpr + ' ' +
454+
DefaultRoleExpr + ', ' +
455+
PluginExpr + ' ' +
441456
'FROM '+FConnection.QuoteIdent('mysql')+'.'+FConnection.QuoteIdent('user')
442457
);
443458
FUsers := TUserList.Create(True);
@@ -449,6 +464,7 @@ procedure TUserManagerForm.FormShow(Sender: TObject);
449464
U.Password := Users.Col('password');
450465
U.IsRole := UpperCase(Users.Col('is_role')) = 'Y';
451466
U.DefaultRole := Users.Col('default_role');
467+
U.Plugin := Users.Col('plugin');
452468
U.Problem := upNone;
453469
if U.IsUser then begin
454470
if Length(U.Password) = 0 then
@@ -630,26 +646,35 @@ procedure TUserManagerForm.listUsersFocusChanged(Sender: TBaseVirtualTree; Node:
630646
User := nil;
631647
FPrivObjects.Clear;
632648
Caption := MainForm.actUserManager.Caption;
649+
// Credentials tab
633650
editUsername.Clear;
634651
editFromHost.Clear;
635652
editPassword.Clear;
636653
editPassword.TextHint := '';
637654
editRepeatPassword.Clear;
655+
comboPlugins.Items.Clear;
656+
comboPlugins.Items.Add(_('None'));
657+
comboPlugins.Items.AddStrings(FPlugins);
658+
comboPlugins.ItemIndex := 0;
638659
comboDefaultRole.Items.Clear;
639660
comboDefaultRole.Items.Add(_('None'));
640661
FUsers.GetRoleNames(comboDefaultRole.Items);
641662
comboDefaultRole.ItemIndex := 0;
663+
// Limitations tab
642664
spinMaxQueries.Value := 0;
643665
spinMaxUpdates.Value := 0;
644666
spinMaxConnections.Value := 0;
645667
spinMaxUserConnections.Value := 0;
668+
// SSL tab
646669
comboSSL.ItemIndex := 0;
647670
comboSSL.OnChange(Sender);
648671
editCipher.Clear;
649672
editIssuer.Clear;
650673
editSubject.Clear;
674+
// Page control
651675
tabPrivileges.Caption := _('Privileges');
652676
tabRoles.Caption := _('Roles');
677+
653678
// All possible quote chars, escaped for RegExpr. Todo: use in all relevant expressions.
654679
RxQuotes := '['+QuoteRegExprMetaChars(FConnection.QuoteChars + FConnection.StringQuoteChar)+']';
655680

@@ -663,6 +688,9 @@ procedure TUserManagerForm.listUsersFocusChanged(Sender: TBaseVirtualTree; Node:
663688
end;
664689
editUsername.Text := User.Username;
665690
editFromHost.Text := User.Host;
691+
i := comboPlugins.Items.IndexOf(User.Plugin);
692+
if i > -1 then
693+
comboPlugins.ItemIndex := i;
666694
i := comboDefaultRole.Items.IndexOf(User.DefaultRole);
667695
if i > -1 then
668696
comboDefaultRole.ItemIndex := i;
@@ -1403,13 +1431,18 @@ procedure TUserManagerForm.btnSaveClick(Sender: TObject);
14031431
// Create added user
14041432
PasswordSet := False;
14051433
if FAdded and (FConnection.ServerVersionInt >= 50002) then begin
1406-
Create := 'CREATE USER '+UserHost;
1434+
Create := 'CREATE USER '+UserHost+' ';
14071435
if editPassword.Modified then begin
1436+
// Insert authentication plugin with minor MariaDB/MySQL difference
1437+
if comboPlugins.ItemIndex > 0 then
1438+
Create := Create + 'IDENTIFIED ' + FSQLPluginPrefix + ' ' + comboPlugins.Text+' ' + FSQLPluginPassPrefix + ' '
1439+
else
1440+
Create := Create + 'IDENTIFIED BY ';
14081441
// Add "PASSWORD" clause when it's a hash already
14091442
if (Copy(editPassword.Text, 1, 1) = '*') and (Length(editPassword.Text) = 41) then
1410-
Create := Create + ' IDENTIFIED BY PASSWORD '+FConnection.EscapeString(editPassword.Text)
1443+
Create := Create + 'PASSWORD '+FConnection.EscapeString(editPassword.Text)
14111444
else
1412-
Create := Create + ' IDENTIFIED BY '+FConnection.EscapeString(editPassword.Text);
1445+
Create := Create + FConnection.EscapeString(editPassword.Text);
14131446
end;
14141447
FConnection.Query(Create);
14151448
FConnection.ShowWarnings;
@@ -1477,6 +1510,12 @@ procedure TUserManagerForm.btnSaveClick(Sender: TObject);
14771510

14781511
// General user options
14791512
if (P.DBObj.NodeType = lntNone) and FocusedUser.IsUser then begin
1513+
// Plugin
1514+
if comboPlugins.ItemIndex > 0 then begin
1515+
FConnection.Query('ALTER USER ' + UserHost + ' IDENTIFIED ' + FSQLPluginPrefix + ' ' + comboPlugins.Text);
1516+
FConnection.ShowWarnings;
1517+
end;
1518+
14801519
// SSL
14811520
case comboSSL.ItemIndex of
14821521
1: RequireClause := 'SSL';
@@ -1574,6 +1613,7 @@ procedure TUserManagerForm.btnSaveClick(Sender: TObject);
15741613
FocusedUser.Host := editFromHost.Text;
15751614
if editPassword.Modified then
15761615
FocusedUser.Password := editPassword.Text;
1616+
FocusedUser.Plugin := IfThen(comboPlugins.ItemIndex=0, '', comboPlugins.Text);
15771617
FocusedUser.DefaultRole := IfThen(comboDefaultRole.ItemIndex=0, '', comboDefaultRole.Text);
15781618
FocusedUser.SSL := comboSSL.ItemIndex;
15791619
FocusedUser.Cipher := editCipher.Text;
@@ -1814,7 +1854,9 @@ procedure TUser.ParseSettings(GrantOrCreate: String; Priv: TPrivObj);
18141854
rx: TRegExpr;
18151855
RequireClause, WithClause: String;
18161856
begin
1817-
// REQUIRE SSL X509 ISSUER '456' SUBJECT '789' CIPHER '123' NONE
1857+
// CREATE USER ...
1858+
// mysql: IDENTIFIED WITH 'mysql_native_password' AS '*23AE809DDACAF96AF0FD78ED04B6A265E05AA257' REQUIRE SSL X509 ISSUER '456' SUBJECT '789' CIPHER '123' NONE
1859+
// mariadb: IDENTIFIED BY PASSWORD '23AE809DDACAF96A';
18181860
rx := TRegExpr.Create;
18191861
rx.ModifierI := True;
18201862
rx.Expression := '\sREQUIRE\s+(.+)';

0 commit comments

Comments
 (0)