From aedbb997abb0ecd4d0ab3cb87707cb6afcda6d59 Mon Sep 17 00:00:00 2001 From: Ansgar Becker Date: Thu, 5 Jan 2012 20:44:49 +0000 Subject: [PATCH] Support GRANT .. REQUIRE syntax in user manager with a new "SSL options" tab. Fixes issue #2671. --- components/synedit/Source/SynRegExpr.pas | 2 +- source/usermanager.dfm | 83 +++++++++++++++ source/usermanager.pas | 126 +++++++++++++++++++---- 3 files changed, 189 insertions(+), 22 deletions(-) diff --git a/components/synedit/Source/SynRegExpr.pas b/components/synedit/Source/SynRegExpr.pas index 7363ba8d..626f256d 100644 --- a/components/synedit/Source/SynRegExpr.pas +++ b/components/synedit/Source/SynRegExpr.pas @@ -161,7 +161,7 @@ const const - NSUBEXP = 15; // max number of subexpression //###0.929 + NSUBEXP = 30; // max number of subexpression //###0.929 // Cannot be more than NSUBEXPMAX // Be carefull - don't use values which overflow CLOSE opcode // (in this case you'll get compiler erorr). diff --git a/source/usermanager.dfm b/source/usermanager.dfm index f3c95627..9a6a4e50 100644 --- a/source/usermanager.dfm +++ b/source/usermanager.dfm @@ -456,6 +456,89 @@ object UserManagerForm: TUserManagerForm Thousands = False end end + object tabSSL: TTabSheet + Caption = 'SSL options' + ImageIndex = 2 + DesignSize = ( + 278 + 117) + object lblCipher: TLabel + Left = 3 + Top = 36 + Width = 35 + Height = 13 + Caption = '&Cipher:' + FocusControl = editCipher + end + object lblIssuer: TLabel + Left = 3 + Top = 62 + Width = 34 + Height = 13 + Caption = '&Issuer:' + FocusControl = editIssuer + end + object lblSubject: TLabel + Left = 3 + Top = 89 + Width = 40 + Height = 13 + Caption = '&Subject:' + FocusControl = editSubject + end + object lblSSL: TLabel + Left = 3 + Top = 9 + Width = 61 + Height = 13 + Caption = '&Require SSL:' + end + object editCipher: TEdit + Left = 106 + Top = 33 + Width = 169 + Height = 21 + Anchors = [akLeft, akTop, akRight] + TabOrder = 1 + Text = 'editCipher' + OnChange = Modification + end + object editIssuer: TEdit + Left = 106 + Top = 59 + Width = 169 + Height = 21 + Anchors = [akLeft, akTop, akRight] + TabOrder = 2 + Text = 'editIssuer' + OnChange = Modification + end + object editSubject: TEdit + Left = 106 + Top = 86 + Width = 169 + Height = 21 + Anchors = [akLeft, akTop, akRight] + TabOrder = 3 + Text = 'editSubject' + OnChange = Modification + end + object comboSSL: TComboBox + Left = 106 + Top = 6 + Width = 169 + Height = 21 + Style = csDropDownList + Anchors = [akLeft, akTop, akRight] + TabOrder = 0 + OnChange = comboSSLChange + Items.Strings = ( + 'No SSL or X509 requirements' + 'Only permit SSL-encrypted connections' + 'X509 (certificate, issuer and subject do not matter)' + 'Specify requirements...') + end + end end end object btnDiscard: TButton diff --git a/source/usermanager.pas b/source/usermanager.pas index c1c19a5a..68707f9b 100644 --- a/source/usermanager.pas +++ b/source/usermanager.pas @@ -13,8 +13,8 @@ uses type TUser = class(TObject) - Username, Host, Password: String; - MaxQueries, MaxUpdates, MaxConnections, MaxUserConnections: Integer; + Username, Host, Password, Cipher, Issuer, Subject: String; + MaxQueries, MaxUpdates, MaxConnections, MaxUserConnections, SSL: Integer; end; PUser = ^TUser; TUserList = TObjectList; @@ -94,6 +94,15 @@ type udMaxUpdates: TUpDown; udMaxConnections: TUpDown; udMaxUserConnections: TUpDown; + tabSSL: TTabSheet; + lblCipher: TLabel; + editCipher: TEdit; + lblIssuer: TLabel; + lblSubject: TLabel; + editIssuer: TEdit; + editSubject: TEdit; + comboSSL: TComboBox; + lblSSL: TLabel; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormShow(Sender: TObject); @@ -139,6 +148,7 @@ type procedure editPasswordChange(Sender: TObject); procedure listUsersHotChange(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode); procedure udMaxQueriesClick(Sender: TObject; Button: TUDBtnType); + procedure comboSSLChange(Sender: TObject); private { Private declarations } FUsers: TUserList; @@ -427,7 +437,7 @@ procedure TUserManagerForm.listUsersFocusChanged(Sender: TBaseVirtualTree; Node: var P, Ptmp, PCol: TPrivObj; User: PUser; - UserHost, WithClause: String; + UserHost, RequireClause, WithClause: String; Grants, AllPNames, Cols: TStringList; rxA, rxB: TRegExpr; i, j: Integer; @@ -446,6 +456,10 @@ begin udMaxUpdates.Position := 0; udMaxConnections.Position := 0; udMaxUserConnections.Position := 0; + comboSSL.ItemIndex := 0; + editCipher.Clear; + editIssuer.Clear; + editSubject.Clear; if UserSelected then begin User := Sender.GetNodeData(Node); @@ -480,11 +494,11 @@ begin for i:=0 to Grants.Count-1 do begin // Find selected priv objects via regular expression - { GRANT USAGE ON *.* TO 'newbie'@'%' IDENTIFIED BY PASSWORD '*99D8973ECC09819DF81624F051BFF4FC6695140B' WITH GRANT OPTION + { GRANT USAGE ON *.* TO 'newbie'@'%' IDENTIFIED BY PASSWORD '*99D8973ECC09819DF81624F051BFF4FC6695140B' REQUIRE (NONE | ssl_option [[AND] ssl_option] ...) WITH GRANT OPTION GRANT SELECT ON `avtoserver`.* TO 'newbie'@'%' GRANT SELECT, SELECT (Enter (column) name), INSERT, INSERT (Enter (column) name), UPDATE, UPDATE (Enter (column) name), DELETE, CREATE ON `avtoserver`.`avtomodel` TO 'newbie'@'%' GRANT EXECUTE, ALTER ROUTINE ON PROCEDURE `pulle`.`f_procedure` TO 'newbie'@'%' } - rxA.Expression := '^GRANT\s+(.+)\s+ON\s+((TABLE|FUNCTION|PROCEDURE)\s+)?`?([^`.]+)`?\.`?([^`]+)`?\s+TO\s+\S+(\s+IDENTIFIED\s+BY\s+(PASSWORD)?\s+''?([^'']+)''?)?(\s+WITH.+GRANT\s+OPTION)?(.*)$'; + rxA.Expression := '^GRANT\s+(.+)\s+ON\s+((TABLE|FUNCTION|PROCEDURE)\s+)?`?([^`.]+)`?\.`?([^`]+)`?\s+TO\s+\S+(\s+IDENTIFIED\s+BY\s+(PASSWORD)?\s+''?([^'']+)''?)?(\s+REQUIRE\s+((NONE|SSL|X509|CIPHER|ISSUER|SUBJECT)(\s+''[^'']*'')?(\s+AND)?\s+)+)?(\s+WITH\s+GRANT\s+OPTION)?(.*)$'; if rxA.Exec(Grants[i]) then begin P := TPrivObj.Create; P.GrantCode := Grants[i]; @@ -563,8 +577,40 @@ begin end; end; + + // REQUIRE SSL X509 ISSUER '456' SUBJECT '789' CIPHER '123' NONE + RequireClause := rxA.Match[9]; + if RequireClause <> '' then begin + User.SSL := 0; + User.Cipher := ''; + User.Issuer := ''; + User.Subject := ''; + rxB.Expression := '\bSSL\b'; + if rxB.Exec(RequireClause) then + User.SSL := 1; + rxB.Expression := '\bX509\b'; + if rxB.Exec(RequireClause) then + User.SSL := 2; + rxB.Expression := '\bCIPHER\s+''([^'']+)'; + if rxB.Exec(RequireClause) then + User.Cipher := rxB.Match[1]; + rxB.Expression := '\bISSUER\s+''([^'']+)'; + if rxB.Exec(RequireClause) then + User.Issuer := rxB.Match[1]; + rxB.Expression := '\bSUBJECT\s+''([^'']+)'; + if rxB.Exec(RequireClause) then + User.Subject := rxB.Match[1]; + if IsNotEmpty(User.Cipher) or IsNotEmpty(User.Issuer) or IsNotEmpty(User.Subject) then + User.SSL := 3; + comboSSL.ItemIndex := User.SSL; + comboSSL.OnChange(Sender); + editCipher.Text := User.Cipher; + editIssuer.Text := User.Issuer; + editSubject.Text := User.Subject; + end; + // WITH .. GRANT OPTION ? - if rxA.Match[9] <> '' then + if rxA.Match[15] <> '' then P.OrgPrivs.Add('GRANT'); if (P.OrgPrivs.Count = 0) and (P.DBObj.NodeType = lntTable) then FPrivObjects.Remove(P); @@ -573,24 +619,24 @@ begin // MAX_UPDATES_PER_HOUR 10 // MAX_CONNECTIONS_PER_HOUR 5 // MAX_USER_CONNECTIONS 2; - WithClause := rxA.Match[10]; + WithClause := rxA.Match[16]; if WithClause <> '' then begin User.MaxQueries := 0; User.MaxUpdates := 0; User.MaxConnections := 0; User.MaxUserConnections := 0; - rxA.Expression := '\bMAX_QUERIES_PER_HOUR\s+(\d+)\b'; - if rxA.Exec(WithClause) then - User.MaxQueries := MakeInt(rxA.Match[1]); - rxA.Expression := '\bMAX_UPDATES_PER_HOUR\s+(\d+)\b'; - if rxA.Exec(WithClause) then - User.MaxUpdates := MakeInt(rxA.Match[1]); - rxA.Expression := '\bMAX_CONNECTIONS_PER_HOUR\s+(\d+)\b'; - if rxA.Exec(WithClause) then - User.MaxConnections := MakeInt(rxA.Match[1]); - rxA.Expression := '\bMAX_USER_CONNECTIONS\s+(\d+)\b'; - if rxA.Exec(WithClause) then - User.MaxUserConnections := MakeInt(rxA.Match[1]); + rxB.Expression := '\bMAX_QUERIES_PER_HOUR\s+(\d+)\b'; + if rxB.Exec(WithClause) then + User.MaxQueries := MakeInt(rxB.Match[1]); + rxB.Expression := '\bMAX_UPDATES_PER_HOUR\s+(\d+)\b'; + if rxB.Exec(WithClause) then + User.MaxUpdates := MakeInt(rxB.Match[1]); + rxB.Expression := '\bMAX_CONNECTIONS_PER_HOUR\s+(\d+)\b'; + if rxB.Exec(WithClause) then + User.MaxConnections := MakeInt(rxB.Match[1]); + rxB.Expression := '\bMAX_USER_CONNECTIONS\s+(\d+)\b'; + if rxB.Exec(WithClause) then + User.MaxUserConnections := MakeInt(rxB.Match[1]); udMaxQueries.Position := User.MaxQueries; udMaxUpdates.Position := User.MaxUpdates; udMaxConnections.Position := User.MaxConnections; @@ -654,6 +700,9 @@ begin editMaxUserConnections.Enabled := lblMaxUserConnections.Enabled; udMaxUserConnections.Enabled := lblMaxUserConnections.Enabled; + tabSSL.Enabled := UserSelected; + comboSSL.Enabled := UserSelected; + btnAddObject.Enabled := UserSelected; btnDeleteUser.Enabled := UserSelected; btnCloneUser.Enabled := UserSelected and (not FAdded); @@ -998,7 +1047,7 @@ end; procedure TUserManagerForm.btnSaveClick(Sender: TObject); var - UserHost, OrgUserHost, Create, Table, Revoke, Grant, OnObj: String; + UserHost, OrgUserHost, Create, Table, Revoke, Grant, OnObj, RequireClause: String; User: TUser; FocusedUser: PUser; Tables, WithClauses: TStringList; @@ -1094,6 +1143,24 @@ begin Grant := 'USAGE'; Grant := 'GRANT ' + Grant + ' ON ' + OnObj + ' TO ' + OrgUserHost; + // SSL options + if P.DBObj.NodeType = lntNone then begin + RequireClause := ' REQUIRE '; + case comboSSL.ItemIndex of + 0: RequireClause := RequireClause + 'NONE'; + 1: RequireClause := RequireClause + 'SSL'; + 2: RequireClause := RequireClause + 'X509'; + 3: RequireClause := RequireClause + 'CIPHER '+esc(editCipher.Text)+' ISSUER '+esc(editIssuer.Text)+' SUBJECT '+esc(editSubject.Text); + end; + if (FocusedUser.SSL = comboSSL.ItemIndex) + and (FocusedUser.Cipher = editCipher.Text) + and (FocusedUser.Issuer = editIssuer.Text) + and (FocusedUser.Subject = editSubject.Text) + then + RequireClause := ''; + Grant := Grant + RequireClause; + end; + WithClauses := TStringList.Create; if P.AddedPrivs.IndexOf('GRANT') > -1 then WithClauses.Add('GRANT OPTION'); @@ -1111,7 +1178,7 @@ begin if WithClauses.Count > 0 then Grant := Grant + ' WITH ' + ImplodeStr(' ', WithClauses); - if P.Added or (P.AddedPrivs.Count > 0) or (WithClauses.Count > 0) then + if P.Added or (P.AddedPrivs.Count > 0) or (WithClauses.Count > 0) or (RequireClause <> '') then FConnection.Query(Grant); WithClauses.Free; @@ -1145,6 +1212,10 @@ begin FocusedUser.Host := editFromHost.Text; if editPassword.Modified then FocusedUser.Password := editPassword.Text; + FocusedUser.SSL := comboSSL.ItemIndex; + FocusedUser.Cipher := editCipher.Text; + FocusedUser.Issuer := editIssuer.Text; + FocusedUser.Subject := editSubject.Text; listUsers.OnFocusChanged(listUsers, listUsers.FocusedNode, listUsers.FocusedColumn); except on E:EDatabaseError do @@ -1156,6 +1227,19 @@ begin end; +procedure TUserManagerForm.comboSSLChange(Sender: TObject); +begin + // Enable custom SSL settings + lblCipher.Enabled := (comboSSL.ItemIndex = 3) and Assigned(listUsers.FocusedNode); + editCipher.Enabled := lblCipher.Enabled; + lblIssuer.Enabled := lblCipher.Enabled; + editIssuer.Enabled := lblCipher.Enabled; + lblSubject.Enabled := lblCipher.Enabled; + editSubject.Enabled := lblCipher.Enabled; + Modification(Sender); +end; + + procedure TUserManagerForm.btnDeleteUserClick(Sender: TObject); var UserHost: String;