Skip to content

Commit ec6f7ec

Browse files
committed
feat: support authentication plugin selection in user manager, and fix complaint about invalid password length on user plugins which have no fixed password length
Refs #1728, #1671
1 parent 369354d commit ec6f7ec

4 files changed

Lines changed: 95 additions & 26 deletions

File tree

source/dbstructures.mysql.pas

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

32403240
function TMySqlProvider.GetSql(AId: TQueryId): string;
3241+
var
3242+
IsMariaDB: Boolean;
32413243
begin
3244+
IsMariaDB := ServerVersion >= 100000;
32423245
case AId of
32433246
qDatabaseDrop: Result := 'DROP DATABASE %s';
32443247
qEmptyTable: Result := 'TRUNCATE %s';
@@ -3414,6 +3417,11 @@ function TMySqlProvider.GetSql(AId: TQueryId): string;
34143417
''
34153418
)
34163419
);
3420+
qGetAuthPlugins: Result := IfThen(
3421+
(FServerVersion >= 50100) or IsMariaDB, // mysql 5.1+ and all mariadb versions
3422+
'SELECT PLUGIN_NAME FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_TYPE=''AUTHENTICATION'' AND PLUGIN_STATUS=''ACTIVE''',
3423+
''
3424+
);
34173425
else Result := inherited;
34183426
end;
34193427
end;

source/dbstructures.pas

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

source/usermanager.dfm

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,16 @@ object UserManagerForm: TUserManagerForm
131131
item
132132
Position = 0
133133
Text = 'Username'
134-
Width = 93
134+
Width = 43
135135
end
136136
item
137137
Position = 1
138138
Text = 'Host'
139139
Width = 80
140+
end
141+
item
142+
Position = 2
143+
Text = 'Plugin'
140144
end>
141145
end
142146
object ToolBar1: TToolBar
@@ -212,15 +216,15 @@ object UserManagerForm: TUserManagerForm
212216
Left = 0
213217
Top = 0
214218
Width = 283
215-
Height = 177
219+
Height = 205
216220
ActivePage = tabCredentials
217221
Align = alTop
218222
TabOrder = 0
219223
object tabCredentials: TTabSheet
220224
Caption = 'Credentials'
221225
DesignSize = (
222226
275
223-
148)
227+
176)
224228
object lblUsername: TLabel
225229
Left = 3
226230
Top = 10
@@ -254,11 +258,18 @@ object UserManagerForm: TUserManagerForm
254258
end
255259
object lblDefaultRole: TLabel
256260
Left = 3
257-
Top = 120
261+
Top = 147
258262
Width = 67
259263
Height = 14
260264
Caption = 'Default role:'
261265
end
266+
object lblPlugin: TLabel
267+
Left = 3
268+
Top = 120
269+
Width = 36
270+
Height = 14
271+
Caption = 'Plugin:'
272+
end
262273
object editRepeatPassword: TEdit
263274
Left = 176
264275
Top = 88
@@ -307,6 +318,16 @@ object UserManagerForm: TUserManagerForm
307318
OnChange = Modification
308319
end
309320
object comboDefaultRole: TComboBox
321+
Left = 176
322+
Top = 144
323+
Width = 96
324+
Height = 22
325+
Style = csDropDownList
326+
Anchors = [akLeft, akTop, akRight]
327+
TabOrder = 5
328+
OnChange = Modification
329+
end
330+
object comboPlugins: TComboBox
310331
Left = 176
311332
Top = 116
312333
Width = 96
@@ -322,7 +343,7 @@ object UserManagerForm: TUserManagerForm
322343
ImageIndex = 1
323344
DesignSize = (
324345
275
325-
148)
346+
176)
326347
object lblMaxQueries: TLabel
327348
Left = 3
328349
Top = 10
@@ -446,7 +467,7 @@ object UserManagerForm: TUserManagerForm
446467
ImageIndex = 2
447468
DesignSize = (
448469
275
449-
148)
470+
176)
450471
object lblCipher: TLabel
451472
Left = 3
452473
Top = 36
@@ -527,9 +548,9 @@ object UserManagerForm: TUserManagerForm
527548
end
528549
object PageControlAccess: TPageControl
529550
Left = 0
530-
Top = 177
551+
Top = 205
531552
Width = 283
532-
Height = 139
553+
Height = 111
533554
ActivePage = tabPrivileges
534555
Align = alClient
535556
TabOrder = 1
@@ -539,7 +560,7 @@ object UserManagerForm: TUserManagerForm
539560
Left = 0
540561
Top = 22
541562
Width = 275
542-
Height = 88
563+
Height = 60
543564
Align = alClient
544565
Header.AutoSizeIndex = 0
545566
Header.Height = 14
@@ -594,7 +615,7 @@ object UserManagerForm: TUserManagerForm
594615
Left = 0
595616
Top = 0
596617
Width = 275
597-
Height = 110
618+
Height = 82
598619
Align = alClient
599620
Strings.Strings = (
600621
'Roll=off')

source/usermanager.pas

Lines changed: 55 additions & 15 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;
@@ -61,6 +61,8 @@ EInputError = class(Exception);
6161
TUserManagerForm = class(TExtForm)
6262
btnCancel: TButton;
6363
btnSave: TButton;
64+
comboPlugins: TComboBox;
65+
lblPlugin: TLabel;
6466
pnlLeft: TPanel;
6567
listUsers: TVirtualStringTree;
6668
Splitter1: TSplitter;
@@ -192,11 +194,14 @@ TUserManagerForm = class(TExtForm)
192194
FUsers: TUserList;
193195
FModified, FAdded: Boolean;
194196
FHasIsRole, FHasDefaultRole: Boolean;
197+
FHasPlugin: Boolean;
198+
FPlugins: TStringList;
195199
FCloneGrants: TStringList;
196200
FPrivObjects: TPrivObjList;
197201
FPrivsGlobal, FPrivsDb, FPrivsTable, FPrivsRoutine, FPrivsColumn: TStringList;
198202
FConnection: TDBConnection;
199203
FColorReadPriv, FColorWritePriv, FColorAdminPriv: TColor;
204+
FSQLPluginPrefix, FSQLPluginPassPrefix: String;
200205
procedure SetModified(Value: Boolean);
201206
property Modified: Boolean read FModified write SetModified;
202207
function GetPrivByNode(Node: PVirtualNode): TPrivObj;
@@ -265,10 +270,10 @@ procedure TUserManagerForm.FormShow(Sender: TObject);
265270
Version, i: Integer;
266271
Users: TDBQuery;
267272
U: TUser;
268-
tmp, PasswordExpr, IsRoleExpr, DefaultRoleExpr: String;
273+
tmp, PasswordExpr, IsRoleExpr, DefaultRoleExpr, PluginExpr: String;
269274
SkipNameResolve,
270275
HasPassword, HasAuthString: Boolean;
271-
PasswordLengthMatters: Boolean;
276+
PasswordLengthMatters, PasswordLengthValid: Boolean;
272277
UserTableColumns: TStringList;
273278

274279
function InitPrivList(Values: String): TStringList;
@@ -299,7 +304,8 @@ procedure TUserManagerForm.FormShow(Sender: TObject);
299304
FPrivsTable := InitPrivList('ALTER,CREATE,DELETE,DROP,GRANT,INDEX');
300305
FPrivsRoutine := InitPrivList('GRANT');
301306
FPrivsColumn := InitPrivList('INSERT,SELECT,UPDATE,REFERENCES');
302-
PasswordLengthMatters := True;
307+
FSQLPluginPrefix := IfThen(FConnection.Parameters.IsMariaDB, 'VIA', 'WITH');
308+
FSQLPluginPassPrefix := IfThen(FConnection.Parameters.IsMariaDB, 'USING', 'BY');
303309

304310
if Version >= 40002 then begin
305311
FPrivsGlobal.Add('REPLICATION CLIENT');
@@ -332,11 +338,6 @@ procedure TUserManagerForm.FormShow(Sender: TObject);
332338
PrivsDb.Add('PROXY');
333339
end;
334340
}
335-
if Version >= 80000 then begin
336-
// MySQL 8 has predefined length of hashed passwords only with
337-
// mysql_native_password plugin enabled users
338-
PasswordLengthMatters := False;
339-
end;
340341
// See https://mariadb.com/kb/en/changes-improvements-in-mariadb-105/#privileges-made-more-granular
341342
if FConnection.Parameters.IsMariaDB then begin
342343
if Version > 100502 then begin
@@ -391,6 +392,7 @@ procedure TUserManagerForm.FormShow(Sender: TObject);
391392
HasAuthString := UserTableColumns.IndexOf('authentication_string') > -1;
392393
FHasIsRole := UserTableColumns.IndexOf('is_role') > -1;
393394
FHasDefaultRole := UserTableColumns.IndexOf('default_role') > -1;
395+
FHasPlugin := UserTableColumns.IndexOf('plugin') > -1;
394396
if HasPassword and (not HasAuthString) then
395397
PasswordExpr := 'password'
396398
else if (not HasPassword) and HasAuthString then
@@ -402,14 +404,20 @@ procedure TUserManagerForm.FormShow(Sender: TObject);
402404
PasswordExpr := PasswordExpr + ' AS ' + FConnection.QuoteIdent('password');
403405
IsRoleExpr := IfThen(FHasIsRole, 'is_role', FConnection.EscapeString('N')+' AS is_role');
404406
DefaultRoleExpr := IfThen(FHasDefaultRole, 'default_role', FConnection.EscapeString('')+' AS default_role');
407+
PluginExpr := IfThen(FHasPlugin, 'plugin', FConnection.EscapeString('')+' AS plugin');
408+
if FConnection.SqlProvider.Has(qGetAuthPlugins) then
409+
FPlugins := FConnection.GetCol(FConnection.SqlProvider.GetSql(qGetAuthPlugins))
410+
else
411+
FPlugins := TStringList.Create;
405412

406413
Users := FConnection.GetResults(
407414
'SELECT '+
408415
FConnection.QuoteIdent('user') + ', ' +
409416
FConnection.QuoteIdent('host') + ', ' +
410417
PasswordExpr + ', ' +
411418
IsRoleExpr + ', ' +
412-
DefaultRoleExpr + ' ' +
419+
DefaultRoleExpr + ', ' +
420+
PluginExpr + ' ' +
413421
'FROM '+FConnection.QuoteIdent('mysql')+'.'+FConnection.QuoteIdent('user')
414422
);
415423
FUsers := TUserList.Create(True);
@@ -421,11 +429,14 @@ procedure TUserManagerForm.FormShow(Sender: TObject);
421429
U.Password := Users.Col('password');
422430
U.IsRole := UpperCase(Users.Col('is_role')) = 'Y';
423431
U.DefaultRole := Users.Col('default_role');
432+
U.Plugin := Users.Col('plugin');
424433
U.Problem := upNone;
425434
if U.IsUser then begin
426435
if Length(U.Password) = 0 then
427436
U.Problem := upEmptyPassword;
428-
if PasswordLengthMatters and (not (Length(U.Password) in [0, 16, 41])) then
437+
PasswordLengthMatters := ExecRegExpr('(mysql_native_password|mysql_old_password)', U.Plugin) or (not FHasPlugin);
438+
PasswordLengthValid := Byte(Length(U.Password)) in [0, 16, 41];
439+
if PasswordLengthMatters and (not PasswordLengthValid) then
429440
U.Problem := upInvalidPasswordLen
430441
else if SkipNameResolve and U.HostRequiresNameResolve then
431442
U.Problem := upSkipNameResolve;
@@ -594,26 +605,35 @@ procedure TUserManagerForm.listUsersFocusChanged(Sender: TBaseVirtualTree; Node:
594605
User := nil;
595606
FPrivObjects.Clear;
596607
Caption := MainForm.actUserManager.Caption;
608+
// Credentials tab
597609
editUsername.Clear;
598610
editFromHost.Clear;
599611
editPassword.Clear;
600612
editPassword.TextHint := '';
601613
editRepeatPassword.Clear;
614+
comboPlugins.Items.Clear;
615+
comboPlugins.Items.Add(_('None'));
616+
comboPlugins.Items.AddStrings(FPlugins);
617+
comboPlugins.ItemIndex := 0;
602618
comboDefaultRole.Items.Clear;
603619
comboDefaultRole.Items.Add(_('None'));
604620
FUsers.GetRoleNames(comboDefaultRole.Items);
605621
comboDefaultRole.ItemIndex := 0;
622+
// Limitations tab
606623
udMaxQueries.Position := 0;
607624
udMaxUpdates.Position := 0;
608625
udMaxConnections.Position := 0;
609626
udMaxUserConnections.Position := 0;
627+
// SSL tab
610628
comboSSL.ItemIndex := 0;
611629
comboSSL.OnChange(Sender);
612630
editCipher.Clear;
613631
editIssuer.Clear;
614632
editSubject.Clear;
633+
// Page control
615634
tabPrivileges.Caption := _('Privileges');
616635
tabRoles.Caption := _('Roles');
636+
617637
// All possible quote chars, escaped for RegExpr. Todo: use in all relevant expressions.
618638
RxQuotes := '['+QuoteRegExprMetaChars(FConnection.QuoteChars + FConnection.StringQuoteChar)+']';
619639

@@ -627,6 +647,9 @@ procedure TUserManagerForm.listUsersFocusChanged(Sender: TBaseVirtualTree; Node:
627647
end;
628648
editUsername.Text := User.Username;
629649
editFromHost.Text := User.Host;
650+
i := comboPlugins.Items.IndexOf(User.Plugin);
651+
if i > -1 then
652+
comboPlugins.ItemIndex := i;
630653
i := comboDefaultRole.Items.IndexOf(User.DefaultRole);
631654
if i > -1 then
632655
comboDefaultRole.ItemIndex := i;
@@ -872,6 +895,8 @@ procedure TUserManagerForm.listUsersFocusChanged(Sender: TBaseVirtualTree; Node:
872895
editPassword.Enabled := UserSelected and User.IsUser;
873896
lblRepeatPassword.Enabled := UserSelected and User.IsUser;
874897
editRepeatPassword.Enabled := UserSelected and User.IsUser;
898+
comboPlugins.Enabled := UserSelected and User.IsUser and FHasPlugin;
899+
lblPlugin.Enabled := comboPlugins.Enabled;
875900
comboDefaultRole.Enabled := UserSelected and User.IsUser and FHasDefaultRole;
876901
lblDefaultRole.Enabled := comboDefaultRole.Enabled;
877902
tabCredentials.Enabled := UserSelected;
@@ -945,6 +970,7 @@ procedure TUserManagerForm.listUsersGetText(Sender: TBaseVirtualTree; Node: PVir
945970
case Column of
946971
0: CellText := User.Username;
947972
1: CellText := User.Host;
973+
2: CellText := User.Plugin;
948974
end;
949975
end;
950976

@@ -1371,13 +1397,18 @@ procedure TUserManagerForm.btnSaveClick(Sender: TObject);
13711397
// Create added user
13721398
PasswordSet := False;
13731399
if FAdded and (FConnection.ServerVersionInt >= 50002) then begin
1374-
Create := 'CREATE USER '+UserHost;
1400+
Create := 'CREATE USER '+UserHost+' ';
13751401
if editPassword.Modified then begin
1402+
// Insert authentication plugin with minor MariaDB/MySQL difference
1403+
if comboPlugins.ItemIndex > 0 then
1404+
Create := Create + 'IDENTIFIED ' + FSQLPluginPrefix + ' ' + comboPlugins.Text+' ' + FSQLPluginPassPrefix + ' '
1405+
else
1406+
Create := Create + 'IDENTIFIED BY ';
13761407
// Add "PASSWORD" clause when it's a hash already
13771408
if (Copy(editPassword.Text, 1, 1) = '*') and (Length(editPassword.Text) = 41) then
1378-
Create := Create + ' IDENTIFIED BY PASSWORD '+FConnection.EscapeString(editPassword.Text)
1409+
Create := Create + 'PASSWORD '+FConnection.EscapeString(editPassword.Text)
13791410
else
1380-
Create := Create + ' IDENTIFIED BY '+FConnection.EscapeString(editPassword.Text);
1411+
Create := Create + FConnection.EscapeString(editPassword.Text);
13811412
end;
13821413
FConnection.Query(Create);
13831414
FConnection.ShowWarnings;
@@ -1445,6 +1476,12 @@ procedure TUserManagerForm.btnSaveClick(Sender: TObject);
14451476

14461477
// General user options
14471478
if (P.DBObj.NodeType = lntNone) and FocusedUser.IsUser then begin
1479+
// Plugin
1480+
if comboPlugins.ItemIndex > 0 then begin
1481+
FConnection.Query('ALTER USER ' + UserHost + ' IDENTIFIED ' + FSQLPluginPrefix + ' ' + comboPlugins.Text);
1482+
FConnection.ShowWarnings;
1483+
end;
1484+
14481485
// SSL
14491486
case comboSSL.ItemIndex of
14501487
1: RequireClause := 'SSL';
@@ -1542,6 +1579,7 @@ procedure TUserManagerForm.btnSaveClick(Sender: TObject);
15421579
FocusedUser.Host := editFromHost.Text;
15431580
if editPassword.Modified then
15441581
FocusedUser.Password := editPassword.Text;
1582+
FocusedUser.Plugin := IfThen(comboPlugins.ItemIndex=0, '', comboPlugins.Text);
15451583
FocusedUser.DefaultRole := IfThen(comboDefaultRole.ItemIndex=0, '', comboDefaultRole.Text);
15461584
FocusedUser.SSL := comboSSL.ItemIndex;
15471585
FocusedUser.Cipher := editCipher.Text;
@@ -1782,7 +1820,9 @@ procedure TUser.ParseSettings(GrantOrCreate: String; Priv: TPrivObj);
17821820
rx: TRegExpr;
17831821
RequireClause, WithClause: String;
17841822
begin
1785-
// REQUIRE SSL X509 ISSUER '456' SUBJECT '789' CIPHER '123' NONE
1823+
// CREATE USER ...
1824+
// mysql: IDENTIFIED WITH 'mysql_native_password' AS '*23AE809DDACAF96AF0FD78ED04B6A265E05AA257' REQUIRE SSL X509 ISSUER '456' SUBJECT '789' CIPHER '123' NONE
1825+
// mariadb: IDENTIFIED BY PASSWORD '23AE809DDACAF96A';
17861826
rx := TRegExpr.Create;
17871827
rx.ModifierI := True;
17881828
rx.Expression := '\sREQUIRE\s+(.+)';

0 commit comments

Comments
 (0)