My favorites | Sign in
Project Home Source
Checkout   Browse   Changes  
Changes to /trunk/source/usermanager.pas
r4101 vs. r4112 Compare: vs.  Format:
Revision r4112
Go to: 
/trunk/source/usermanager.pas   r4101 /trunk/source/usermanager.pas   r4112
1 unit usermanager; 1 unit usermanager;
2 2
3 3
4 interface 4 interface
5 5
6 uses 6 uses
7 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, StdCtrls, 7 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, StdCtrls,
8 ExtCtrls, ToolWin, ClipBrd, Generics.Collections, Generics.Defaults, SynRegExpr, 8 ExtCtrls, ToolWin, ClipBrd, Generics.Collections, Generics.Defaults, SynRegExpr,
9 dbconnection, helpers, VirtualTrees, Menus; 9 dbconnection, helpers, VirtualTrees, Menus;
10 10
11 {$I const.inc} 11 {$I const.inc}
12 12
13 13
14 type 14 type
15 TUser = class(TObject) 15 TUser = class(TObject)
16 Username, Host, Password, Cipher, Issuer, Subject: String; 16 Username, Host, Password, Cipher, Issuer, Subject: String;
17 MaxQueries, MaxUpdates, MaxConnections, MaxUserConnections, SSL: Integer; 17 MaxQueries, MaxUpdates, MaxConnections, MaxUserConnections, SSL: Integer;
18 end; 18 end;
19 PUser = ^TUser; 19 PUser = ^TUser;
20 TUserList = TObjectList<TUser>; 20 TUserList = TObjectList<TUser>;
21 21
22 TPrivObj = class(TObject) 22 TPrivObj = class(TObject)
23 GrantCode: String; 23 GrantCode: String;
24 DBObj: TDBObject; 24 DBObj: TDBObject;
25 OrgPrivs, AddedPrivs, DeletedPrivs: TStringList; 25 OrgPrivs, AddedPrivs, DeletedPrivs: TStringList;
26 AllPrivileges: TStringList; 26 AllPrivileges: TStringList;
27 Added: Boolean; 27 Added: Boolean;
28 public 28 public
29 constructor Create; 29 constructor Create;
30 destructor Destroy; override; 30 destructor Destroy; override;
31 end; 31 end;
32 TPrivObjList = TObjectList<TPrivObj>; 32 TPrivObjList = TObjectList<TPrivObj>;
33 TPrivComparer = class(TComparer<TPrivObj>) 33 TPrivComparer = class(TComparer<TPrivObj>)
34 function Compare(const Left, Right: TPrivObj): Integer; override; 34 function Compare(const Left, Right: TPrivObj): Integer; override;
35 end; 35 end;
36 36
37 EInputError = class(Exception); 37 EInputError = class(Exception);
38 38
39 TUserManagerForm = class(TForm) 39 TUserManagerForm = class(TForm)
40 btnCancel: TButton; 40 btnCancel: TButton;
41 btnSave: TButton; 41 btnSave: TButton;
42 pnlLeft: TPanel; 42 pnlLeft: TPanel;
43 listUsers: TVirtualStringTree; 43 listUsers: TVirtualStringTree;
44 Splitter1: TSplitter; 44 Splitter1: TSplitter;
45 pnlRight: TPanel; 45 pnlRight: TPanel;
46 tlbObjects: TToolBar; 46 tlbObjects: TToolBar;
47 btnAddObject: TToolButton; 47 btnAddObject: TToolButton;
48 treePrivs: TVirtualStringTree; 48 treePrivs: TVirtualStringTree;
49 btnDiscard: TButton; 49 btnDiscard: TButton;
50 lblUsers: TLabel; 50 lblUsers: TLabel;
51 ToolBar1: TToolBar; 51 ToolBar1: TToolBar;
52 btnAddUser: TToolButton; 52 btnAddUser: TToolButton;
53 btnDeleteUser: TToolButton; 53 btnDeleteUser: TToolButton;
54 btnCloneUser: TToolButton; 54 btnCloneUser: TToolButton;
55 lblWarning: TLabel; 55 lblWarning: TLabel;
56 Label1: TLabel; 56 Label1: TLabel;
57 menuHost: TPopupMenu; 57 menuHost: TPopupMenu;
58 menuHost1: TMenuItem; 58 menuHost1: TMenuItem;
59 menuHostLocal4: TMenuItem; 59 menuHostLocal4: TMenuItem;
60 menuHost2: TMenuItem; 60 menuHost2: TMenuItem;
61 menuHost3: TMenuItem; 61 menuHost3: TMenuItem;
62 N1: TMenuItem; 62 N1: TMenuItem;
63 menuPassword: TPopupMenu; 63 menuPassword: TPopupMenu;
64 menuPassword1: TMenuItem; 64 menuPassword1: TMenuItem;
65 menuPassword2: TMenuItem; 65 menuPassword2: TMenuItem;
66 menuPassword3: TMenuItem; 66 menuPassword3: TMenuItem;
67 menuPassword4: TMenuItem; 67 menuPassword4: TMenuItem;
68 menuPassword5: TMenuItem; 68 menuPassword5: TMenuItem;
69 menuDummy1: TMenuItem; 69 menuDummy1: TMenuItem;
70 menuDummy2: TMenuItem; 70 menuDummy2: TMenuItem;
71 menuDummy3: TMenuItem; 71 menuDummy3: TMenuItem;
72 menuDummy4: TMenuItem; 72 menuDummy4: TMenuItem;
73 menuDummy5: TMenuItem; 73 menuDummy5: TMenuItem;
74 PageControlSettings: TPageControl; 74 PageControlSettings: TPageControl;
75 tabCredentials: TTabSheet; 75 tabCredentials: TTabSheet;
76 tabLimitations: TTabSheet; 76 tabLimitations: TTabSheet;
77 lblUsername: TLabel; 77 lblUsername: TLabel;
78 lblFromHost: TLabel; 78 lblFromHost: TLabel;
79 lblPassword: TLabel; 79 lblPassword: TLabel;
80 lblRepeatPassword: TLabel; 80 lblRepeatPassword: TLabel;
81 editRepeatPassword: TEdit; 81 editRepeatPassword: TEdit;
82 editPassword: TButtonedEdit; 82 editPassword: TButtonedEdit;
83 editFromHost: TButtonedEdit; 83 editFromHost: TButtonedEdit;
84 editUsername: TEdit; 84 editUsername: TEdit;
85 lblMaxQueries: TLabel; 85 lblMaxQueries: TLabel;
86 lblMaxUpdates: TLabel; 86 lblMaxUpdates: TLabel;
87 lblMaxConnections: TLabel; 87 lblMaxConnections: TLabel;
88 lblMaxUserConnections: TLabel; 88 lblMaxUserConnections: TLabel;
89 editMaxQueries: TEdit; 89 editMaxQueries: TEdit;
90 editMaxUpdates: TEdit; 90 editMaxUpdates: TEdit;
91 editMaxConnections: TEdit; 91 editMaxConnections: TEdit;
92 editMaxUserConnections: TEdit; 92 editMaxUserConnections: TEdit;
93 udMaxQueries: TUpDown; 93 udMaxQueries: TUpDown;
94 udMaxUpdates: TUpDown; 94 udMaxUpdates: TUpDown;
95 udMaxConnections: TUpDown; 95 udMaxConnections: TUpDown;
96 udMaxUserConnections: TUpDown; 96 udMaxUserConnections: TUpDown;
97 tabSSL: TTabSheet; 97 tabSSL: TTabSheet;
98 lblCipher: TLabel; 98 lblCipher: TLabel;
99 editCipher: TEdit; 99 editCipher: TEdit;
100 lblIssuer: TLabel; 100 lblIssuer: TLabel;
101 lblSubject: TLabel; 101 lblSubject: TLabel;
102 editIssuer: TEdit; 102 editIssuer: TEdit;
103 editSubject: TEdit; 103 editSubject: TEdit;
104 comboSSL: TComboBox; 104 comboSSL: TComboBox;
105 lblSSL: TLabel; 105 lblSSL: TLabel;
106 procedure FormCreate(Sender: TObject); 106 procedure FormCreate(Sender: TObject);
107 procedure FormDestroy(Sender: TObject); 107 procedure FormDestroy(Sender: TObject);
108 procedure FormShow(Sender: TObject); 108 procedure FormShow(Sender: TObject);
109 procedure btnAddUserClick(Sender: TObject); 109 procedure btnAddUserClick(Sender: TObject);
110 procedure btnDeleteUserClick(Sender: TObject); 110 procedure btnDeleteUserClick(Sender: TObject);
111 procedure listUsersFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; 111 procedure listUsersFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode;
112 Column: TColumnIndex); 112 Column: TColumnIndex);
113 procedure FormClose(Sender: TObject; var Action: TCloseAction); 113 procedure FormClose(Sender: TObject; var Action: TCloseAction);
114 procedure listUsersBeforePaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas); 114 procedure listUsersBeforePaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas);
115 procedure listUsersGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer); 115 procedure listUsersGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
116 procedure listUsersInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; 116 procedure listUsersInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
117 var InitialStates: TVirtualNodeInitStates); 117 var InitialStates: TVirtualNodeInitStates);
118 procedure listUsersGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; 118 procedure listUsersGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
119 TextType: TVSTTextType; var CellText: string); 119 TextType: TVSTTextType; var CellText: string);
120 procedure listUsersGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; 120 procedure listUsersGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode;
121 Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer); 121 Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer);
122 procedure listUsersFocusChanging(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode; 122 procedure listUsersFocusChanging(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode;
123 OldColumn, NewColumn: TColumnIndex; var Allowed: Boolean); 123 OldColumn, NewColumn: TColumnIndex; var Allowed: Boolean);
124 procedure btnSaveClick(Sender: TObject); 124 procedure btnSaveClick(Sender: TObject);
125 procedure Modification(Sender: TObject); 125 procedure Modification(Sender: TObject);
126 procedure treePrivsGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; 126 procedure treePrivsGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode;
127 Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer); 127 Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer);
128 procedure treePrivsInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; 128 procedure treePrivsInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
129 var InitialStates: TVirtualNodeInitStates); 129 var InitialStates: TVirtualNodeInitStates);
130 procedure treePrivsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; 130 procedure treePrivsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
131 TextType: TVSTTextType; var CellText: string); 131 TextType: TVSTTextType; var CellText: string);
132 procedure treePrivsInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode; 132 procedure treePrivsInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode;
133 var ChildCount: Cardinal); 133 var ChildCount: Cardinal);
134 procedure treePrivsChecked(Sender: TBaseVirtualTree; Node: PVirtualNode); 134 procedure treePrivsChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
135 procedure btnDiscardClick(Sender: TObject); 135 procedure btnDiscardClick(Sender: TObject);
136 procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); 136 procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
137 procedure btnAddObjectClick(Sender: TObject); 137 procedure btnAddObjectClick(Sender: TObject);
138 procedure treePrivsExpanded(Sender: TBaseVirtualTree; Node: PVirtualNode); 138 procedure treePrivsExpanded(Sender: TBaseVirtualTree; Node: PVirtualNode);
139 procedure treePrivsPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; 139 procedure treePrivsPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas;
140 Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); 140 Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType);
141 procedure listUsersHeaderClick(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo); 141 procedure listUsersHeaderClick(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo);
142 procedure listUsersCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer); 142 procedure listUsersCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
143 procedure listUsersAfterPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas); 143 procedure listUsersAfterPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas);
144 procedure menuHostClick(Sender: TObject); 144 procedure menuHostClick(Sender: TObject);
145 procedure menuHostPopup(Sender: TObject); 145 procedure menuHostPopup(Sender: TObject);
146 procedure menuPasswordClick(Sender: TObject); 146 procedure menuPasswordClick(Sender: TObject);
147 procedure menuPasswordInsert(Sender: TObject); 147 procedure menuPasswordInsert(Sender: TObject);
148 procedure editPasswordChange(Sender: TObject); 148 procedure editPasswordChange(Sender: TObject);
149 procedure listUsersHotChange(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode); 149 procedure listUsersHotChange(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode);
150 procedure udMaxQueriesClick(Sender: TObject; Button: TUDBtnType); 150 procedure udMaxQueriesClick(Sender: TObject; Button: TUDBtnType);
151 procedure comboSSLChange(Sender: TObject); 151 procedure comboSSLChange(Sender: TObject);
152 private 152 private
153 { Private declarations } 153 { Private declarations }
154 FUsers: TUserList; 154 FUsers: TUserList;
155 FModified, FAdded: Boolean; 155 FModified, FAdded: Boolean;
156 CloneGrants: TStringList; 156 CloneGrants: TStringList;
157 FFocusedUserGrants: TStringList;
158 FPrivObjects: TPrivObjList; 157 FPrivObjects: TPrivObjList;
159 PrivsGlobal, PrivsDb, PrivsTable, PrivsRoutine, PrivsColumn: TStringList; 158 PrivsGlobal, PrivsDb, PrivsTable, PrivsRoutine, PrivsColumn: TStringList;
160 FConnection: TDBConnection; 159 FConnection: TDBConnection;
161 procedure SetModified(Value: Boolean); 160 procedure SetModified(Value: Boolean);
162 property Modified: Boolean read FModified write SetModified; 161 property Modified: Boolean read FModified write SetModified;
163 function GetPrivByNode(Node: PVirtualNode): TPrivObj; 162 function GetPrivByNode(Node: PVirtualNode): TPrivObj;
164 public 163 public
165 { Public declarations } 164 { Public declarations }
166 end; 165 end;
167 166
168 function ComparePrivs(List: TStringList; Index1, Index2: Integer): Integer; 167 function ComparePrivs(List: TStringList; Index1, Index2: Integer): Integer;
169 168
170 implementation 169 implementation
171 170
172 uses 171 uses
173 main, selectdbobject; 172 main, selectdbobject;
174 var 173 var
175 PrivsRead, PrivsWrite, PrivsAdmin: TStringList; 174 PrivsRead, PrivsWrite, PrivsAdmin: TStringList;
176 175
177 {$R *.DFM} 176 {$R *.DFM}
178 177
179 function ComparePrivs(List: TStringList; Index1, Index2: Integer): Integer; 178 function ComparePrivs(List: TStringList; Index1, Index2: Integer): Integer;
180 var 179 var
181 s1, s2: String; 180 s1, s2: String;
182 s1val, s2val: Integer; 181 s1val, s2val: Integer;
183 begin 182 begin
184 s1 := List[Index1]; 183 s1 := List[Index1];
185 s2 := List[Index2]; 184 s2 := List[Index2];
186 s1val := 0; 185 s1val := 0;
187 s2val := 0; 186 s2val := 0;
188 if PrivsRead.IndexOf(s1) > -1 then s1val := 1 187 if PrivsRead.IndexOf(s1) > -1 then s1val := 1
189 else if PrivsWrite.IndexOf(s1) > -1 then s1val := 2 188 else if PrivsWrite.IndexOf(s1) > -1 then s1val := 2
190 else if PrivsAdmin.IndexOf(s1) > -1 then s1val := 3; 189 else if PrivsAdmin.IndexOf(s1) > -1 then s1val := 3;
191 if PrivsRead.IndexOf(s2) > -1 then s2val := 1 190 if PrivsRead.IndexOf(s2) > -1 then s2val := 1
192 else if PrivsWrite.IndexOf(s2) > -1 then s2val := 2 191 else if PrivsWrite.IndexOf(s2) > -1 then s2val := 2
193 else if PrivsAdmin.IndexOf(s2) > -1 then s2val := 3; 192 else if PrivsAdmin.IndexOf(s2) > -1 then s2val := 3;
194 if s1val > s2val then 193 if s1val > s2val then
195 Result := 1 194 Result := 1
196 else if s1val = s2val then 195 else if s1val = s2val then
197 Result := CompareText(s1, s2) 196 Result := CompareText(s1, s2)
198 else 197 else
199 Result := -1; 198 Result := -1;
200 end; 199 end;
201 200
202 201
203 procedure TUserManagerForm.FormCreate(Sender: TObject); 202 procedure TUserManagerForm.FormCreate(Sender: TObject);
204 begin 203 begin
205 // Restore GUI setup 204 // Restore GUI setup
206 InheritFont(Font); 205 InheritFont(Font);
207 Width := GetRegValue(REGNAME_USERMNGR_WINWIDTH, Width); 206 Width := GetRegValue(REGNAME_USERMNGR_WINWIDTH, Width);
208 Height := GetRegValue(REGNAME_USERMNGR_WINHEIGHT, Height); 207 Height := GetRegValue(REGNAME_USERMNGR_WINHEIGHT, Height);
209 pnlLeft.Width := GetRegValue(REGNAME_USERMNGR_LISTWIDTH, pnlLeft.Width); 208 pnlLeft.Width := GetRegValue(REGNAME_USERMNGR_LISTWIDTH, pnlLeft.Width);
210 SetWindowSizeGrip( Self.Handle, True ); 209 SetWindowSizeGrip( Self.Handle, True );
211 FixVT(listUsers); 210 FixVT(listUsers);
212 FixVT(treePrivs); 211 FixVT(treePrivs);
213 Mainform.RestoreListSetup(listUsers); 212 Mainform.RestoreListSetup(listUsers);
214 PrivsRead := Explode(',', 'SELECT,SHOW VIEW,SHOW DATABASES,PROCESS,EXECUTE'); 213 PrivsRead := Explode(',', 'SELECT,SHOW VIEW,SHOW DATABASES,PROCESS,EXECUTE');
215 PrivsWrite := Explode(',', 'ALTER,CREATE,DROP,DELETE,UPDATE,INSERT,ALTER ROUTINE,CREATE ROUTINE,CREATE TEMPORARY TABLES,CREATE VIEW,INDEX,TRIGGER,EVENT,REFERENCES,CREATE TABLESPACE'); 214 PrivsWrite := Explode(',', 'ALTER,CREATE,DROP,DELETE,UPDATE,INSERT,ALTER ROUTINE,CREATE ROUTINE,CREATE TEMPORARY TABLES,CREATE VIEW,INDEX,TRIGGER,EVENT,REFERENCES,CREATE TABLESPACE');
216 PrivsAdmin := Explode(',', 'RELOAD,SHUTDOWN,REPLICATION CLIENT,REPLICATION SLAVE,SUPER,LOCK TABLES,GRANT,FILE,CREATE USER'); 215 PrivsAdmin := Explode(',', 'RELOAD,SHUTDOWN,REPLICATION CLIENT,REPLICATION SLAVE,SUPER,LOCK TABLES,GRANT,FILE,CREATE USER');
217 end; 216 end;
218 217
219 218
220 procedure TUserManagerForm.FormDestroy(Sender: TObject); 219 procedure TUserManagerForm.FormDestroy(Sender: TObject);
221 begin 220 begin
222 // FormDestroy: Save GUI setup 221 // FormDestroy: Save GUI setup
223 OpenRegistry; 222 OpenRegistry;
224 MainReg.WriteInteger( REGNAME_USERMNGR_WINWIDTH, Width ); 223 MainReg.WriteInteger( REGNAME_USERMNGR_WINWIDTH, Width );
225 MainReg.WriteInteger( REGNAME_USERMNGR_WINHEIGHT, Height ); 224 MainReg.WriteInteger( REGNAME_USERMNGR_WINHEIGHT, Height );
226 MainReg.WriteInteger( REGNAME_USERMNGR_LISTWIDTH, pnlLeft.Width ); 225 MainReg.WriteInteger( REGNAME_USERMNGR_LISTWIDTH, pnlLeft.Width );
227 Mainform.SaveListSetup(listUsers); 226 Mainform.SaveListSetup(listUsers);
228 end; 227 end;
229 228
230 229
231 procedure TUserManagerForm.FormShow(Sender: TObject); 230 procedure TUserManagerForm.FormShow(Sender: TObject);
232 var 231 var
233 Version: Integer; 232 Version: Integer;
234 Users: TDBQuery; 233 Users: TDBQuery;
235 U: TUser; 234 U: TUser;
236 235
237 function InitPrivList(Values: String): TStringList; 236 function InitPrivList(Values: String): TStringList;
238 begin 237 begin
239 Result := Explode(',', Values); 238 Result := Explode(',', Values);
240 Result.Sorted := True; 239 Result.Sorted := True;
241 Result.Duplicates := dupIgnore; 240 Result.Duplicates := dupIgnore;
242 end; 241 end;
243 242
244 begin 243 begin
245 FConnection := Mainform.ActiveConnection; 244 FConnection := Mainform.ActiveConnection;
246 Version := FConnection.ServerVersionInt; 245 Version := FConnection.ServerVersionInt;
247 PrivsGlobal := InitPrivList('FILE,PROCESS,RELOAD,SHUTDOWN'); 246 PrivsGlobal := InitPrivList('FILE,PROCESS,RELOAD,SHUTDOWN');
248 PrivsDb := InitPrivList(''); 247 PrivsDb := InitPrivList('');
249 PrivsTable := InitPrivList('ALTER,CREATE,DELETE,DROP,GRANT,INDEX'); 248 PrivsTable := InitPrivList('ALTER,CREATE,DELETE,DROP,GRANT,INDEX');
250 PrivsRoutine := InitPrivList('GRANT'); 249 PrivsRoutine := InitPrivList('GRANT');
251 PrivsColumn := InitPrivList('INSERT,SELECT,UPDATE,REFERENCES'); 250 PrivsColumn := InitPrivList('INSERT,SELECT,UPDATE,REFERENCES');
252 251
253 if Version >= 40002 then begin 252 if Version >= 40002 then begin
254 PrivsGlobal.Add('REPLICATION CLIENT'); 253 PrivsGlobal.Add('REPLICATION CLIENT');
255 PrivsGlobal.Add('REPLICATION SLAVE'); 254 PrivsGlobal.Add('REPLICATION SLAVE');
256 PrivsGlobal.Add('SHOW DATABASES'); 255 PrivsGlobal.Add('SHOW DATABASES');
257 PrivsGlobal.Add('SUPER'); 256 PrivsGlobal.Add('SUPER');
258 PrivsDb.Add('CREATE TEMPORARY TABLES'); 257 PrivsDb.Add('CREATE TEMPORARY TABLES');
259 PrivsDb.Add('LOCK TABLES'); 258 PrivsDb.Add('LOCK TABLES');
260 PrivsRoutine.Add('EXECUTE'); 259 PrivsRoutine.Add('EXECUTE');
261 end; 260 end;
262 if Version >= 50001 then begin 261 if Version >= 50001 then begin
263 PrivsTable.Add('CREATE VIEW'); 262 PrivsTable.Add('CREATE VIEW');
264 PrivsTable.Add('SHOW VIEW'); 263 PrivsTable.Add('SHOW VIEW');
265 end; 264 end;
266 if Version >= 50003 then begin 265 if Version >= 50003 then begin
267 PrivsGlobal.Add('CREATE USER'); 266 PrivsGlobal.Add('CREATE USER');
268 PrivsDb.Add('CREATE ROUTINE'); 267 PrivsDb.Add('CREATE ROUTINE');
269 PrivsRoutine.Add('ALTER ROUTINE'); 268 PrivsRoutine.Add('ALTER ROUTINE');
270 end; 269 end;
271 if Version >= 50106 then begin 270 if Version >= 50106 then begin
272 PrivsDb.Add('TRIGGER'); 271 PrivsDb.Add('TRIGGER');
273 PrivsDb.Add('EVENT'); 272 PrivsDb.Add('EVENT');
274 end; 273 end;
275 if Version >= 50404 then begin 274 if Version >= 50404 then begin
276 PrivsGlobal.Add('CREATE TABLESPACE'); 275 PrivsGlobal.Add('CREATE TABLESPACE');
277 end; 276 end;
278 { TODO: PROXY priv must be applied with another GRANT syntax: 277 { TODO: PROXY priv must be applied with another GRANT syntax:
279 GRANT PROXY ON 'employee'@'localhost' TO 'external_auth'@'localhost'; 278 GRANT PROXY ON 'employee'@'localhost' TO 'external_auth'@'localhost';
280 if Version >= 50507 then begin 279 if Version >= 50507 then begin
281 PrivsDb.Add('PROXY'); 280 PrivsDb.Add('PROXY');
282 end; 281 end;
283 } 282 }
284 283
285 PrivsTable.AddStrings(PrivsColumn); 284 PrivsTable.AddStrings(PrivsColumn);
286 PrivsDb.AddStrings(PrivsTable); 285 PrivsDb.AddStrings(PrivsTable);
287 PrivsDb.AddStrings(PrivsRoutine); 286 PrivsDb.AddStrings(PrivsRoutine);
288 PrivsGlobal.AddStrings(PrivsDb); 287 PrivsGlobal.AddStrings(PrivsDb);
289 288
290 PrivsGlobal.Sorted := False; 289 PrivsGlobal.Sorted := False;
291 PrivsGlobal.CustomSort(ComparePrivs); 290 PrivsGlobal.CustomSort(ComparePrivs);
292 PrivsDb.Sorted := False; 291 PrivsDb.Sorted := False;
293 PrivsDb.CustomSort(ComparePrivs); 292 PrivsDb.CustomSort(ComparePrivs);
294 PrivsTable.Sorted := False; 293 PrivsTable.Sorted := False;
295 PrivsTable.CustomSort(ComparePrivs); 294 PrivsTable.CustomSort(ComparePrivs);
296 PrivsRoutine.Sorted := False; 295 PrivsRoutine.Sorted := False;
297 PrivsRoutine.CustomSort(ComparePrivs); 296 PrivsRoutine.CustomSort(ComparePrivs);
298 PrivsColumn.Sorted := False; 297 PrivsColumn.Sorted := False;
299 PrivsColumn.CustomSort(ComparePrivs); 298 PrivsColumn.CustomSort(ComparePrivs);
300 299
301 300
302 // Load user@host list 301 // Load user@host list
303 try 302 try
303 FConnection.Query('FLUSH PRIVILEGES');
304 Users := FConnection.GetResults( 304 Users := FConnection.GetResults(
305 'SELECT '+FConnection.QuoteIdent('user')+', '+FConnection.QuoteIdent('host')+', '+FConnection.QuoteIdent('password')+' '+ 305 'SELECT '+FConnection.QuoteIdent('user')+', '+FConnection.QuoteIdent('host')+', '+FConnection.QuoteIdent('password')+' '+
306 'FROM '+FConnection.QuoteIdent('mysql')+'.'+FConnection.QuoteIdent('user') 306 'FROM '+FConnection.QuoteIdent('mysql')+'.'+FConnection.QuoteIdent('user')
307 ); 307 );
308 FUsers := TUserList.Create(True); 308 FUsers := TUserList.Create(True);
309 while not Users.Eof do begin 309 while not Users.Eof do begin
310 U := TUser.Create; 310 U := TUser.Create;
311 U.Username := Users.Col('user'); 311 U.Username := Users.Col('user');
312 U.Host := Users.Col('host'); 312 U.Host := Users.Col('host');
313 U.Password := Users.Col('password'); 313 U.Password := Users.Col('password');
314 FUsers.Add(U); 314 FUsers.Add(U);
315 Users.Next; 315 Users.Next;
316 end; 316 end;
317 listUsers.Clear; 317 listUsers.Clear;
318 InvalidateVT(listUsers, VTREE_NOTLOADED, False); 318 InvalidateVT(listUsers, VTREE_NOTLOADED, False);
319 FPrivObjects := TPrivObjList.Create(TPrivComparer.Create, True); 319 FPrivObjects := TPrivObjList.Create(TPrivComparer.Create, True);
320 Modified := False; 320 Modified := False;
321 FAdded := False; 321 FAdded := False;
322 listUsers.OnFocusChanged(listUsers, listUsers.FocusedNode, listUsers.FocusedColumn); 322 listUsers.OnFocusChanged(listUsers, listUsers.FocusedNode, listUsers.FocusedColumn);
323 except 323 except
324 on E:EDatabaseError do begin 324 on E:EDatabaseError do begin
325 ErrorDialog(E.Message); 325 ErrorDialog(E.Message);
326 // Closing form in OnShow does not work. Instead, do that in listUsers.OnBeforePaint. 326 // Closing form in OnShow does not work. Instead, do that in listUsers.OnBeforePaint.
327 end; 327 end;
328 end; 328 end;
329 end; 329 end;
330 330
331 331
332 procedure TUserManagerForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); 332 procedure TUserManagerForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
333 begin 333 begin
334 // Try to unfocus user item. If not done, user clicked "Cancel" 334 // Try to unfocus user item. If not done, user clicked "Cancel"
335 listUsers.FocusedNode := nil; 335 listUsers.FocusedNode := nil;
336 CanClose := not Assigned(listUsers.FocusedNode); 336 CanClose := not Assigned(listUsers.FocusedNode);
337 end; 337 end;
338 338
339 339
340 procedure TUserManagerForm.FormClose(Sender: TObject; var Action: TCloseAction); 340 procedure TUserManagerForm.FormClose(Sender: TObject; var Action: TCloseAction);
341 begin 341 begin
342 // Free user list and list of available priv names 342 // Free user list and list of available priv names
343 FreeAndNil(FUsers); 343 FreeAndNil(FUsers);
344 FreeAndNil(FPrivObjects); 344 FreeAndNil(FPrivObjects);
345 FreeAndNil(PrivsGlobal); 345 FreeAndNil(PrivsGlobal);
346 FreeAndNil(PrivsDb); 346 FreeAndNil(PrivsDb);
347 FreeAndNil(PrivsTable); 347 FreeAndNil(PrivsTable);
348 FreeAndNil(PrivsRoutine); 348 FreeAndNil(PrivsRoutine);
349 FreeAndNil(PrivsColumn); 349 FreeAndNil(PrivsColumn);
350 Action := caFree; 350 Action := caFree;
351 end; 351 end;
352 352
353 353
354 procedure TUserManagerForm.SetModified(Value: Boolean); 354 procedure TUserManagerForm.SetModified(Value: Boolean);
355 begin 355 begin
356 FModified := Value; 356 FModified := Value;
357 btnSave.Enabled := FModified; 357 btnSave.Enabled := FModified;
358 btnDiscard.Enabled := FModified and (not FAdded); 358 btnDiscard.Enabled := FModified and (not FAdded);
359 listUsers.Invalidate; 359 listUsers.Invalidate;
360 end; 360 end;
361 361
362 362
363 procedure TUserManagerForm.Modification(Sender: TObject); 363 procedure TUserManagerForm.Modification(Sender: TObject);
364 var 364 var
365 User: PUser; 365 User: PUser;
366 begin 366 begin
367 if Assigned(listUsers.FocusedNode) and (TWinControl(Sender).Parent = tabLimitations) then begin 367 if Assigned(listUsers.FocusedNode) and (TWinControl(Sender).Parent = tabLimitations) then begin
368 // Any TUpDown triggers a OnChange event on its TEdit when the UpDown gets painted 368 // Any TUpDown triggers a OnChange event on its TEdit when the UpDown gets painted
369 User := listUsers.GetNodeData(listUsers.FocusedNode); 369 User := listUsers.GetNodeData(listUsers.FocusedNode);
370 Modified := Modified 370 Modified := Modified
371 or (editMaxQueries.Text <> IntToStr(User.MaxQueries)) 371 or (editMaxQueries.Text <> IntToStr(User.MaxQueries))
372 or (editMaxUpdates.Text <> IntToStr(User.MaxUpdates)) 372 or (editMaxUpdates.Text <> IntToStr(User.MaxUpdates))
373 or (editMaxConnections.Text <> IntToStr(User.MaxConnections)) 373 or (editMaxConnections.Text <> IntToStr(User.MaxConnections))
374 or (editMaxUserConnections.Text <> IntToStr(User.MaxUserConnections)); 374 or (editMaxUserConnections.Text <> IntToStr(User.MaxUserConnections));
375 end else 375 end else
376 Modified := True; 376 Modified := True;
377 end; 377 end;
378 378
379 379
380 procedure TUserManagerForm.udMaxQueriesClick(Sender: TObject; Button: TUDBtnType); 380 procedure TUserManagerForm.udMaxQueriesClick(Sender: TObject; Button: TUDBtnType);
381 begin 381 begin
382 Modification(Sender); 382 Modification(Sender);
383 end; 383 end;
384 384
385 385
386 procedure TUserManagerForm.listUsersAfterPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas); 386 procedure TUserManagerForm.listUsersAfterPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas);
387 begin 387 begin
388 // Background painting for sorted column 388 // Background painting for sorted column
389 MainForm.AnyGridAfterPaint(Sender, TargetCanvas); 389 MainForm.AnyGridAfterPaint(Sender, TargetCanvas);
390 end; 390 end;
391 391
392 392
393 procedure TUserManagerForm.listUsersBeforePaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas); 393 procedure TUserManagerForm.listUsersBeforePaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas);
394 var 394 var
395 VT: TVirtualStringTree; 395 VT: TVirtualStringTree;
396 begin 396 begin
397 // Users may have got new or removed ones - reinit nodes. 397 // Users may have got new or removed ones - reinit nodes.
398 // If Form.OnShow failed to get the list of users, close form from here. 398 // If Form.OnShow failed to get the list of users, close form from here.
399 if not Assigned(FUsers) then 399 if not Assigned(FUsers) then
400 Close 400 Close
401 else begin 401 else begin
402 VT := Sender as TVirtualStringTree; 402 VT := Sender as TVirtualStringTree;
403 if VT.Tag = VTREE_NOTLOADED then begin 403 if VT.Tag = VTREE_NOTLOADED then begin
404 VT.RootNodeCount := FUsers.Count; 404 VT.RootNodeCount := FUsers.Count;
405 VT.FocusedNode := nil; 405 VT.FocusedNode := nil;
406 VT.ClearSelection; 406 VT.ClearSelection;
407 VT.Tag := VTREE_LOADED; 407 VT.Tag := VTREE_LOADED;
408 end; 408 end;
409 end; 409 end;
410 end; 410 end;
411 411
412 412
413 procedure TUserManagerForm.listUsersFocusChanging(Sender: TBaseVirtualTree; OldNode, 413 procedure TUserManagerForm.listUsersFocusChanging(Sender: TBaseVirtualTree; OldNode,
414 NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex; var Allowed: Boolean); 414 NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex; var Allowed: Boolean);
415 var
416 User: PUser;
417 Msg: String;
418 begin 415 begin
419 // Allow selecting a user? Also, set allowed to false if new node is the same as 416 // Allow selecting a user? Also, set allowed to false if new node is the same as
420 // the old one, otherwise OnFocusChanged will be triggered. 417 // the old one, otherwise OnFocusChanged will be triggered.
421 Allowed := (NewNode <> OldNode) and (not Assigned(NewNode) or (not (vsDisabled in NewNode.States))); 418 Allowed := (NewNode <> OldNode) and (not Assigned(NewNode) or (not (vsDisabled in NewNode.States)));
422
423 if Allowed and (not FAdded) and Assigned(NewNode) then begin
424 // Check grants before node has focus. Could be a non-yet flushed user which throws
425 // "There is no such grant defined for user"
426 // when firing SHOW GRANTS. See http://www.heidisql.com/forum.php?t=10364
427 User := Sender.GetNodeData(NewNode);
428 try
429 FFocusedUserGrants := FConnection.GetCol('SHOW GRANTS FOR '+esc(User.Username)+'@'+esc(User.Host));
430 except
431 on E:EDatabaseError do begin
432 Allowed := False;
433 Include(NewNode.States, vsDisabled);
434 Msg := FConnection.LastError;
435 if FConnection.LastErrorCode = 1141 then
436 Msg := Msg + CRLF + CRLF + 'You need to run FLUSH PRIVILEGES before you can edit this user.';
437 MessageDialog(Msg, mtError, [mbOK]);
438 end;
439 end;
440 end;
441
442 if Allowed and FModified then begin 419 if Allowed and FModified then begin
443 case MessageDialog('Save modified user?', mtConfirmation, [mbYes, mbNo, mbCancel]) of 420 case MessageDialog('Save modified user?', mtConfirmation, [mbYes, mbNo, mbCancel]) of
444 mrYes: begin 421 mrYes: begin
445 btnSave.Click; 422 btnSave.Click;
446 Allowed := not FModified; 423 Allowed := not FModified;
447 end; 424 end;
448 mrNo: begin 425 mrNo: begin
449 Allowed := True; 426 Allowed := True;
450 if FAdded then 427 if FAdded then
451 btnDeleteUser.Click; 428 btnDeleteUser.Click;
452 end; 429 end;
453 mrCancel: Allowed := False; 430 mrCancel: Allowed := False;
454 end; 431 end;
455 end; 432 end;
456 end; 433 end;
457 434
458 435
459 procedure TUserManagerForm.listUsersFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; 436 procedure TUserManagerForm.listUsersFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode;
460 Column: TColumnIndex); 437 Column: TColumnIndex);
461 var 438 var
462 P, Ptmp, PCol: TPrivObj; 439 P, Ptmp, PCol: TPrivObj;
463 User: PUser; 440 User: PUser;
464 UserHost, RequireClause, WithClause: String; 441 UserHost, RequireClause, WithClause: String;
465 Grants, AllPNames, Cols: TStringList; 442 Grants, AllPNames, Cols: TStringList;
466 rxTemp, rxGrant: TRegExpr; 443 rxTemp, rxGrant: TRegExpr;
467 i, j: Integer; 444 i, j: Integer;
468 UserSelected: Boolean; 445 UserSelected: Boolean;
469 begin 446 begin
470 // Parse and display privileges of focused user 447 // Parse and display privileges of focused user
471 UserSelected := Assigned(Node); 448 UserSelected := Assigned(Node);
472 FPrivObjects.Clear; 449 FPrivObjects.Clear;
473 Caption := MainForm.actUserManager.Caption; 450 Caption := MainForm.actUserManager.Caption;
474 editUsername.Clear; 451 editUsername.Clear;
475 editFromHost.Clear; 452 editFromHost.Clear;
476 editPassword.Clear; 453 editPassword.Clear;
477 editPassword.TextHint := ''; 454 editPassword.TextHint := '';
478 editRepeatPassword.Clear; 455 editRepeatPassword.Clear;
479 udMaxQueries.Position := 0; 456 udMaxQueries.Position := 0;
480 udMaxUpdates.Position := 0; 457 udMaxUpdates.Position := 0;
481 udMaxConnections.Position := 0; 458 udMaxConnections.Position := 0;
482 udMaxUserConnections.Position := 0; 459 udMaxUserConnections.Position := 0;
483 comboSSL.ItemIndex := 0; 460 comboSSL.ItemIndex := 0;
484 comboSSL.OnChange(Sender); 461 comboSSL.OnChange(Sender);
485 editCipher.Clear; 462 editCipher.Clear;
486 editIssuer.Clear; 463 editIssuer.Clear;
487 editSubject.Clear; 464 editSubject.Clear;
488 465
489 if UserSelected then begin 466 if UserSelected then begin
490 User := Sender.GetNodeData(Node); 467 User := Sender.GetNodeData(Node);
491 UserHost := esc(User.Username)+'@'+esc(User.Host); 468 UserHost := esc(User.Username)+'@'+esc(User.Host);
492 editUsername.Text := User.Username; 469 editUsername.Text := User.Username;
493 editFromHost.Text := User.Host; 470 editFromHost.Text := User.Host;
494 Caption := Caption + ' - ' + User.Username; 471 Caption := Caption + ' - ' + User.Username;
495 472
496 AllPNames := TStringList.Create; 473 AllPNames := TStringList.Create;
497 AllPNames.AddStrings(PrivsGlobal); 474 AllPNames.AddStrings(PrivsGlobal);
498 AllPNames.AddStrings(PrivsDb); 475 AllPNames.AddStrings(PrivsDb);
499 AllPNames.AddStrings(PrivsTable); 476 AllPNames.AddStrings(PrivsTable);
500 AllPNames.AddStrings(PrivsRoutine); 477 AllPNames.AddStrings(PrivsRoutine);
501 AllPNames.AddStrings(PrivsColumn); 478 AllPNames.AddStrings(PrivsColumn);
502 479
503 // New or existing user mode 480 // New or existing user mode
504 if FAdded then begin 481 if FAdded then begin
505 if Assigned(CloneGrants) then begin 482 if Assigned(CloneGrants) then begin
506 Grants := TStringList.Create; 483 Grants := TStringList.Create;
507 Grants.AddStrings(CloneGrants); 484 Grants.AddStrings(CloneGrants);
508 end else begin 485 end else begin
509 Grants := TStringList.Create; 486 Grants := TStringList.Create;
510 Grants.Add('GRANT USAGE ON *.* TO '+UserHost); 487 Grants.Add('GRANT USAGE ON *.* TO '+UserHost);
511 end; 488 end;
512 end else 489 end else
513 Grants := FFocusedUserGrants; 490 Grants := FConnection.GetCol('SHOW GRANTS FOR '+esc(User.Username)+'@'+esc(User.Host));
514 491
515 { GRANT USAGE ON *.* TO 'newbie'@'%' IDENTIFIED BY PASSWORD '*99D8973ECC09819DF81624F051BFF4FC6695140B' REQUIRE (NONE | ssl_option [[AND] ssl_option] ...) WITH GRANT OPTION 492 { GRANT USAGE ON *.* TO 'newbie'@'%' IDENTIFIED BY PASSWORD '*99D8973ECC09819DF81624F051BFF4FC6695140B' REQUIRE (NONE | ssl_option [[AND] ssl_option] ...) WITH GRANT OPTION
516 GRANT SELECT ON `avtoserver`.* TO 'newbie'@'%' 493 GRANT SELECT ON `avtoserver`.* TO 'newbie'@'%'
517 GRANT SELECT, SELECT (Enter (column) name), INSERT, INSERT (Enter (column) name), UPDATE, UPDATE (Enter (column) name), DELETE, CREATE ON `avtoserver`.`avtomodel` TO 'newbie'@'%' 494 GRANT SELECT, SELECT (Enter (column) name), INSERT, INSERT (Enter (column) name), UPDATE, UPDATE (Enter (column) name), DELETE, CREATE ON `avtoserver`.`avtomodel` TO 'newbie'@'%'
518 GRANT EXECUTE, ALTER ROUTINE ON PROCEDURE `pulle`.`f_procedure` TO 'newbie'@'%' } 495 GRANT EXECUTE, ALTER ROUTINE ON PROCEDURE `pulle`.`f_procedure` TO 'newbie'@'%' }
519 rxTemp := TRegExpr.Create; 496 rxTemp := TRegExpr.Create;
520 rxTemp.ModifierI := True; 497 rxTemp.ModifierI := True;
521 rxGrant := TRegExpr.Create; 498 rxGrant := TRegExpr.Create;
522 rxGrant.ModifierI := True; 499 rxGrant.ModifierI := True;
523 rxGrant.Expression := '^GRANT\s+(.+)\s+ON\s+((TABLE|FUNCTION|PROCEDURE)\s+)?`?([^`.]+)`?\.`?([^`]+)`?\s+TO\s+\S+(\s+IDENTIFIED\s+BY\s+(PASSWORD)?\s+''?([^'']+)''?)?(\s+.+)?$'; 500 rxGrant.Expression := '^GRANT\s+(.+)\s+ON\s+((TABLE|FUNCTION|PROCEDURE)\s+)?`?([^`.]+)`?\.`?([^`]+)`?\s+TO\s+\S+(\s+IDENTIFIED\s+BY\s+(PASSWORD)?\s+''?([^'']+)''?)?(\s+.+)?$';
524 501
525 for i:=0 to Grants.Count-1 do begin 502 for i:=0 to Grants.Count-1 do begin
526 // Find selected priv objects via regular expression 503 // Find selected priv objects via regular expression
527 if rxGrant.Exec(Grants[i]) then begin 504 if rxGrant.Exec(Grants[i]) then begin
528 P := TPrivObj.Create; 505 P := TPrivObj.Create;
529 P.GrantCode := Grants[i]; 506 P.GrantCode := Grants[i];
530 P.Added := FAdded; 507 P.Added := FAdded;
531 FPrivObjects.Add(P); 508 FPrivObjects.Add(P);
532 509
533 if (rxGrant.Match[4] = '*') and (rxGrant.Match[5] = '*') then begin 510 if (rxGrant.Match[4] = '*') and (rxGrant.Match[5] = '*') then begin
534 P.DBObj.NodeType := lntNone; 511 P.DBObj.NodeType := lntNone;
535 P.AllPrivileges := PrivsGlobal; 512 P.AllPrivileges := PrivsGlobal;
536 if not FAdded then begin 513 if not FAdded then begin
537 editPassword.TextHint := FConnection.UnescapeString(rxGrant.Match[8]); 514 editPassword.TextHint := FConnection.UnescapeString(rxGrant.Match[8]);
538 // Set password for changed user, to silence the error message about invalid length 515 // Set password for changed user, to silence the error message about invalid length
539 User.Password := editPassword.TextHint; 516 User.Password := editPassword.TextHint;
540 end; 517 end;
541 end else if (rxGrant.Match[5] = '*') then begin 518 end else if (rxGrant.Match[5] = '*') then begin
542 P.DBObj.NodeType := lntDb; 519 P.DBObj.NodeType := lntDb;
543 P.DBObj.Database := rxGrant.Match[4]; 520 P.DBObj.Database := rxGrant.Match[4];
544 P.AllPrivileges := PrivsDb; 521 P.AllPrivileges := PrivsDb;
545 end else begin 522 end else begin
546 P.DBObj.Database := rxGrant.Match[4]; 523 P.DBObj.Database := rxGrant.Match[4];
547 P.DBObj.Name := rxGrant.Match[5]; 524 P.DBObj.Name := rxGrant.Match[5];
548 if UpperCase(rxGrant.Match[3]) = 'FUNCTION' then begin 525 if UpperCase(rxGrant.Match[3]) = 'FUNCTION' then begin
549 P.DBObj.NodeType := lntFunction; 526 P.DBObj.NodeType := lntFunction;
550 P.AllPrivileges := PrivsRoutine; 527 P.AllPrivileges := PrivsRoutine;
551 end else if (UpperCase(rxGrant.Match[3]) = 'PROCEDURE') then begin 528 end else if (UpperCase(rxGrant.Match[3]) = 'PROCEDURE') then begin
552 P.DBObj.NodeType := lntProcedure; 529 P.DBObj.NodeType := lntProcedure;
553 P.AllPrivileges := PrivsRoutine; 530 P.AllPrivileges := PrivsRoutine;
554 end else begin 531 end else begin
555 P.DBObj.NodeType := lntTable; 532 P.DBObj.NodeType := lntTable;
556 P.AllPrivileges := PrivsTable; 533 P.AllPrivileges := PrivsTable;
557 end; 534 end;
558 end; 535 end;
559 536
560 // Find selected privileges 537 // Find selected privileges
561 { USAGE 538 { USAGE
562 SELECT, SELECT (id, colname), INSERT, INSERT (id, colname), UPDATE, UPDATE (colname), DELETE, CREATE 539 SELECT, SELECT (id, colname), INSERT, INSERT (id, colname), UPDATE, UPDATE (colname), DELETE, CREATE
563 EXECUTE, ALTER ROUTINE } 540 EXECUTE, ALTER ROUTINE }
564 if rxGrant.Match[1] = 'ALL PRIVILEGES' then begin 541 if rxGrant.Match[1] = 'ALL PRIVILEGES' then begin
565 P.OrgPrivs.AddStrings(P.AllPrivileges); 542 P.OrgPrivs.AddStrings(P.AllPrivileges);
566 P.OrgPrivs.Delete(P.OrgPrivs.IndexOf('GRANT')); 543 P.OrgPrivs.Delete(P.OrgPrivs.IndexOf('GRANT'));
567 end else begin 544 end else begin
568 rxTemp.Expression := '\b('+ImplodeStr('|', AllPnames)+')(\s+\(([^\)]+)\))?,'; 545 rxTemp.Expression := '\b('+ImplodeStr('|', AllPnames)+')(\s+\(([^\)]+)\))?,';
569 if rxTemp.Exec(rxGrant.Match[1]+',') then while True do begin 546 if rxTemp.Exec(rxGrant.Match[1]+',') then while True do begin
570 if rxTemp.Match[3] = '' then 547 if rxTemp.Match[3] = '' then
571 P.OrgPrivs.Add(rxTemp.Match[1]) 548 P.OrgPrivs.Add(rxTemp.Match[1])
572 else begin 549 else begin
573 // Find previously created column priv or create new one 550 // Find previously created column priv or create new one
574 Cols := Explode(',', rxTemp.Match[3]); 551 Cols := Explode(',', rxTemp.Match[3]);
575 for j:=0 to Cols.Count-1 do begin 552 for j:=0 to Cols.Count-1 do begin
576 PCol := nil; 553 PCol := nil;
577 for Ptmp in FPrivObjects do begin 554 for Ptmp in FPrivObjects do begin
578 if (Ptmp.DBObj.NodeType=lntColumn) 555 if (Ptmp.DBObj.NodeType=lntColumn)
579 and (Ptmp.DBObj.Database=P.DBObj.Database) 556 and (Ptmp.DBObj.Database=P.DBObj.Database)
580 and (Ptmp.DBObj.Name=P.DBObj.Name) 557 and (Ptmp.DBObj.Name=P.DBObj.Name)
581 and (Ptmp.DBObj.Column=Trim(Cols[j])) then begin 558 and (Ptmp.DBObj.Column=Trim(Cols[j])) then begin
582 PCol := Ptmp; 559 PCol := Ptmp;
583 break; 560 break;
584 end; 561 end;
585 end; 562 end;
586 if PCol = nil then begin 563 if PCol = nil then begin
587 PCol := TPrivObj.Create; 564 PCol := TPrivObj.Create;
588 PCol.DBObj.NodeType := lntColumn; 565 PCol.DBObj.NodeType := lntColumn;
589 PCol.DBObj.Database := P.DBObj.Database; 566 PCol.DBObj.Database := P.DBObj.Database;
590 PCol.DBObj.Name := P.DBObj.Name; 567 PCol.DBObj.Name := P.DBObj.Name;
591 PCol.DBObj.Column := Trim(Cols[j]); 568 PCol.DBObj.Column := Trim(Cols[j]);
592 PCol.AllPrivileges := PrivsColumn; 569 PCol.AllPrivileges := PrivsColumn;
593 FPrivObjects.Add(PCol); 570 FPrivObjects.Add(PCol);
594 end; 571 end;
595 PCol.OrgPrivs.Add(rxTemp.Match[1]); 572 PCol.OrgPrivs.Add(rxTemp.Match[1]);
596 PCol.GrantCode := PCol.GrantCode + rxTemp.Match[1] + ' ('+Trim(Cols[j])+')' + ', '; 573 PCol.GrantCode := PCol.GrantCode + rxTemp.Match[1] + ' ('+Trim(Cols[j])+')' + ', ';
597 end; 574 end;
598 575
599 end; 576 end;
600 if not rxTemp.ExecNext then 577 if not rxTemp.ExecNext then
601 break; 578 break;
602 end; 579 end;
603 580
604 end; 581 end;
605 582
606 // REQUIRE SSL X509 ISSUER '456' SUBJECT '789' CIPHER '123' NONE 583 // REQUIRE SSL X509 ISSUER '456' SUBJECT '789' CIPHER '123' NONE
607 rxTemp.Expression := '\sREQUIRE\s+(.+)'; 584 rxTemp.Expression := '\sREQUIRE\s+(.+)';
608 if rxTemp.Exec(rxGrant.Match[9]) then begin 585 if rxTemp.Exec(rxGrant.Match[9]) then begin
609 RequireClause := rxTemp.Match[1]; 586 RequireClause := rxTemp.Match[1];
610 User.SSL := 0; 587 User.SSL := 0;
611 User.Cipher := ''; 588 User.Cipher := '';
612 User.Issuer := ''; 589 User.Issuer := '';
613 User.Subject := ''; 590 User.Subject := '';
614 rxTemp.Expression := '\bSSL\b'; 591 rxTemp.Expression := '\bSSL\b';
615 if rxTemp.Exec(RequireClause) then 592 if rxTemp.Exec(RequireClause) then
616 User.SSL := 1; 593 User.SSL := 1;
617 rxTemp.Expression := '\bX509\b'; 594 rxTemp.Expression := '\bX509\b';
618 if rxTemp.Exec(RequireClause) then 595 if rxTemp.Exec(RequireClause) then
619 User.SSL := 2; 596 User.SSL := 2;
620 rxTemp.Expression := '\bCIPHER\s+''([^'']+)'; 597 rxTemp.Expression := '\bCIPHER\s+''([^'']+)';
621 if rxTemp.Exec(RequireClause) then 598 if rxTemp.Exec(RequireClause) then
622 User.Cipher := rxTemp.Match[1]; 599 User.Cipher := rxTemp.Match[1];
623 rxTemp.Expression := '\bISSUER\s+''([^'']+)'; 600 rxTemp.Expression := '\bISSUER\s+''([^'']+)';
624 if rxTemp.Exec(RequireClause) then 601 if rxTemp.Exec(RequireClause) then
625 User.Issuer := rxTemp.Match[1]; 602 User.Issuer := rxTemp.Match[1];
626 rxTemp.Expression := '\bSUBJECT\s+''([^'']+)'; 603 rxTemp.Expression := '\bSUBJECT\s+''([^'']+)';
627 if rxTemp.Exec(RequireClause) then 604 if rxTemp.Exec(RequireClause) then
628 User.Subject := rxTemp.Match[1]; 605 User.Subject := rxTemp.Match[1];
629 if IsNotEmpty(User.Cipher) or IsNotEmpty(User.Issuer) or IsNotEmpty(User.Subject) then 606 if IsNotEmpty(User.Cipher) or IsNotEmpty(User.Issuer) or IsNotEmpty(User.Subject) then
630 User.SSL := 3; 607 User.SSL := 3;
631 comboSSL.ItemIndex := User.SSL; 608 comboSSL.ItemIndex := User.SSL;
632 comboSSL.OnChange(Sender); 609 comboSSL.OnChange(Sender);
633 editCipher.Text := User.Cipher; 610 editCipher.Text := User.Cipher;
634 editIssuer.Text := User.Issuer; 611 editIssuer.Text := User.Issuer;
635 editSubject.Text := User.Subject; 612 editSubject.Text := User.Subject;
636 end; 613 end;
637 614
638 // WITH .. GRANT OPTION 615 // WITH .. GRANT OPTION
639 // MAX_QUERIES_PER_HOUR 20 MAX_UPDATES_PER_HOUR 10 MAX_CONNECTIONS_PER_HOUR 5 MAX_USER_CONNECTIONS 2 616 // MAX_QUERIES_PER_HOUR 20 MAX_UPDATES_PER_HOUR 10 MAX_CONNECTIONS_PER_HOUR 5 MAX_USER_CONNECTIONS 2
640 rxTemp.Expression := '\sWITH\s+(.+)'; 617 rxTemp.Expression := '\sWITH\s+(.+)';
641 if rxTemp.Exec(rxGrant.Match[9]) then begin 618 if rxTemp.Exec(rxGrant.Match[9]) then begin
642 WithClause := rxTemp.Match[1]; 619 WithClause := rxTemp.Match[1];
643 User.MaxQueries := 0; 620 User.MaxQueries := 0;
644 User.MaxUpdates := 0; 621 User.MaxUpdates := 0;
645 User.MaxConnections := 0; 622 User.MaxConnections := 0;
646 User.MaxUserConnections := 0; 623 User.MaxUserConnections := 0;
647 if ExecRegExpr('\bGRANT\s+OPTION\b', WithClause) then 624 if ExecRegExpr('\bGRANT\s+OPTION\b', WithClause) then
648 P.OrgPrivs.Add('GRANT'); 625 P.OrgPrivs.Add('GRANT');
649 rxTemp.Expression := '\bMAX_QUERIES_PER_HOUR\s+(\d+)\b'; 626 rxTemp.Expression := '\bMAX_QUERIES_PER_HOUR\s+(\d+)\b';
650 if rxTemp.Exec(WithClause) then 627 if rxTemp.Exec(WithClause) then
651 User.MaxQueries := MakeInt(rxTemp.Match[1]); 628 User.MaxQueries := MakeInt(rxTemp.Match[1]);
652 rxTemp.Expression := '\bMAX_UPDATES_PER_HOUR\s+(\d+)\b'; 629 rxTemp.Expression := '\bMAX_UPDATES_PER_HOUR\s+(\d+)\b';
653 if rxTemp.Exec(WithClause) then 630 if rxTemp.Exec(WithClause) then
654 User.MaxUpdates := MakeInt(rxTemp.Match[1]); 631 User.MaxUpdates := MakeInt(rxTemp.Match[1]);
655 rxTemp.Expression := '\bMAX_CONNECTIONS_PER_HOUR\s+(\d+)\b'; 632 rxTemp.Expression := '\bMAX_CONNECTIONS_PER_HOUR\s+(\d+)\b';
656 if rxTemp.Exec(WithClause) then 633 if rxTemp.Exec(WithClause) then
657 User.MaxConnections := MakeInt(rxTemp.Match[1]); 634 User.MaxConnections := MakeInt(rxTemp.Match[1]);
658 rxTemp.Expression := '\bMAX_USER_CONNECTIONS\s+(\d+)\b'; 635 rxTemp.Expression := '\bMAX_USER_CONNECTIONS\s+(\d+)\b';
659 if rxTemp.Exec(WithClause) then 636 if rxTemp.Exec(WithClause) then
660 User.MaxUserConnections := MakeInt(rxTemp.Match[1]); 637 User.MaxUserConnections := MakeInt(rxTemp.Match[1]);
661 udMaxQueries.Position := User.MaxQueries; 638 udMaxQueries.Position := User.MaxQueries;
662 udMaxUpdates.Position := User.MaxUpdates; 639 udMaxUpdates.Position := User.MaxUpdates;
663 udMaxConnections.Position := User.MaxConnections; 640 udMaxConnections.Position := User.MaxConnections;
664 udMaxUserConnections.Position := User.MaxUserConnections; 641 udMaxUserConnections.Position := User.MaxUserConnections;
665 end; 642 end;
666 643
667 if (P.OrgPrivs.Count = 0) and (P.DBObj.NodeType = lntTable) then 644 if (P.OrgPrivs.Count = 0) and (P.DBObj.NodeType = lntTable) then
668 FPrivObjects.Remove(P); 645 FPrivObjects.Remove(P);
669 end; 646 end;
670 end; 647 end;
671 648
672 // Generate grant code for column privs by hand 649 // Generate grant code for column privs by hand
673 for Ptmp in FPrivObjects do begin 650 for Ptmp in FPrivObjects do begin
674 if Ptmp.DBObj.NodeType = lntColumn then begin 651 if Ptmp.DBObj.NodeType = lntColumn then begin
675 Ptmp.GrantCode := 'GRANT ' + Copy(Ptmp.GrantCode, 1, Length(Ptmp.GrantCode)-2) + ' ON ' + 652 Ptmp.GrantCode := 'GRANT ' + Copy(Ptmp.GrantCode, 1, Length(Ptmp.GrantCode)-2) + ' ON ' +
676 Ptmp.DBObj.QuotedDatabase + '.' + 653 Ptmp.DBObj.QuotedDatabase + '.' +
677 Ptmp.DBObj.QuotedName + 654 Ptmp.DBObj.QuotedName +
678 ' TO ' + UserHost; 655 ' TO ' + UserHost;
679 end; 656 end;
680 // Flag all privs as added, so "Save" action applies them 657 // Flag all privs as added, so "Save" action applies them
681 if Assigned(CloneGrants) then 658 if Assigned(CloneGrants) then
682 Ptmp.AddedPrivs.AddStrings(Ptmp.OrgPrivs); 659 Ptmp.AddedPrivs.AddStrings(Ptmp.OrgPrivs);
683 end; 660 end;
684 661
685 FPrivObjects.Sort; 662 FPrivObjects.Sort;
686 rxGrant.Free; 663 rxGrant.Free;
687 rxTemp.Free; 664 rxTemp.Free;
688 FreeAndNil(Grants); 665 FreeAndNil(Grants);
689 FreeAndNil(CloneGrants); 666 FreeAndNil(CloneGrants);
690 FreeAndNil(AllPnames); 667 FreeAndNil(AllPnames);
691 FreeAndNil(Cols); 668 FreeAndNil(Cols);
692 end; 669 end;
693 670
694 // Populate privilege tree 671 // Populate privilege tree
695 Modified := False; 672 Modified := False;
696 treePrivs.FocusedNode := nil; 673 treePrivs.FocusedNode := nil;
697 treePrivs.Clear; 674 treePrivs.Clear;
698 treePrivs.RootNodeCount := FPrivObjects.Count; 675 treePrivs.RootNodeCount := FPrivObjects.Count;
699 treePrivs.ReinitNode(nil, True); 676 treePrivs.ReinitNode(nil, True);
700 treePrivs.Invalidate; 677 treePrivs.Invalidate;
701 678
702 // Enable input boxes 679 // Enable input boxes
703 lblUsername.Enabled := UserSelected; 680 lblUsername.Enabled := UserSelected;
704 editUsername.Enabled := UserSelected; 681 editUsername.Enabled := UserSelected;
705 lblFromHost.Enabled := UserSelected; 682 lblFromHost.Enabled := UserSelected;
706 editFromHost.Enabled := UserSelected; 683 editFromHost.Enabled := UserSelected;
707 lblPassword.Enabled := UserSelected; 684 lblPassword.Enabled := UserSelected;
708 editPassword.Enabled := UserSelected; 685 editPassword.Enabled := UserSelected;
709 lblRepeatPassword.Enabled := UserSelected; 686 lblRepeatPassword.Enabled := UserSelected;
710 editRepeatPassword.Enabled := UserSelected; 687 editRepeatPassword.Enabled := UserSelected;
711 tabCredentials.Enabled := UserSelected; 688 tabCredentials.Enabled := UserSelected;
712 lblMaxQueries.Enabled := UserSelected and (FConnection.ServerVersionInt >= 40002); 689 lblMaxQueries.Enabled := UserSelected and (FConnection.ServerVersionInt >= 40002);
713 690
714 tabLimitations.Enabled := UserSelected; 691 tabLimitations.Enabled := UserSelected;
715 editMaxQueries.Enabled := lblMaxQueries.Enabled; 692 editMaxQueries.Enabled := lblMaxQueries.Enabled;
716 udMaxQueries.Enabled := lblMaxQueries.Enabled; 693 udMaxQueries.Enabled := lblMaxQueries.Enabled;
717 lblMaxUpdates.Enabled := lblMaxQueries.Enabled; 694 lblMaxUpdates.Enabled := lblMaxQueries.Enabled;
718 editMaxUpdates.Enabled := lblMaxQueries.Enabled; 695 editMaxUpdates.Enabled := lblMaxQueries.Enabled;
719 udMaxUpdates.Enabled := lblMaxQueries.Enabled; 696 udMaxUpdates.Enabled := lblMaxQueries.Enabled;
720 lblMaxConnections.Enabled := lblMaxQueries.Enabled; 697 lblMaxConnections.Enabled := lblMaxQueries.Enabled;
721 editMaxConnections.Enabled := lblMaxQueries.Enabled; 698 editMaxConnections.Enabled := lblMaxQueries.Enabled;
722 udMaxConnections.Enabled := lblMaxQueries.Enabled; 699 udMaxConnections.Enabled := lblMaxQueries.Enabled;
723 lblMaxUserConnections.Enabled := UserSelected and (FConnection.ServerVersionInt >= 50003); 700 lblMaxUserConnections.Enabled := UserSelected and (FConnection.ServerVersionInt >= 50003);
724 editMaxUserConnections.Enabled := lblMaxUserConnections.Enabled; 701 editMaxUserConnections.Enabled := lblMaxUserConnections.Enabled;
725 udMaxUserConnections.Enabled := lblMaxUserConnections.Enabled; 702 udMaxUserConnections.Enabled := lblMaxUserConnections.Enabled;
726 703
727 tabSSL.Enabled := UserSelected; 704 tabSSL.Enabled := UserSelected;
728 comboSSL.Enabled := UserSelected; 705 comboSSL.Enabled := UserSelected;
729 706
730 btnAddObject.Enabled := UserSelected; 707 btnAddObject.Enabled := UserSelected;
731 btnDeleteUser.Enabled := UserSelected; 708 btnDeleteUser.Enabled := UserSelected;
732 btnCloneUser.Enabled := UserSelected and (not FAdded); 709 btnCloneUser.Enabled := UserSelected and (not FAdded);
733 710
734 // Ensure the warning hint is displayed or cleared. This is not done when the dialog shows up. 711 // Ensure the warning hint is displayed or cleared. This is not done when the dialog shows up.
735 listUsers.OnHotChange(Sender, nil, Node); 712 listUsers.OnHotChange(Sender, nil, Node);
736 end; 713 end;
737 714
738 715
739 procedure TUserManagerForm.listUsersGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; 716 procedure TUserManagerForm.listUsersGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode;
740 Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer); 717 Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer);
741 var 718 var
742 User: PUser; 719 User: PUser;
743 begin 720 begin
744 if Column <> 0 then 721 if Column <> 0 then
745 Exit; 722 Exit;
746 case Kind of 723 case Kind of
747 ikNormal, ikSelected: ImageIndex := 43; 724 ikNormal, ikSelected: ImageIndex := 43;
748 ikOverlay: begin 725 ikOverlay: begin
749 User := Sender.GetNodeData(Node); 726 User := Sender.GetNodeData(Node);
750 if User.Password = '' then 727 if User.Password = '' then
751 ImageIndex := 161; 728 ImageIndex := 161;
752 if FModified and (Node = Sender.FocusedNode) then 729 if FModified and (Node = Sender.FocusedNode) then
753 ImageIndex := 162; 730 ImageIndex := 162;
754 end; 731 end;
755 end; 732 end;
756 end; 733 end;
757 734
758 735
759 procedure TUserManagerForm.listUsersGetNodeDataSize(Sender: TBaseVirtualTree; 736 procedure TUserManagerForm.listUsersGetNodeDataSize(Sender: TBaseVirtualTree;
760 var NodeDataSize: Integer); 737 var NodeDataSize: Integer);
761 begin 738 begin
762 NodeDataSize := SizeOf(TUser); 739 NodeDataSize := SizeOf(TUser);
763 end; 740 end;
764 741
765 742
766 procedure TUserManagerForm.listUsersGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; 743 procedure TUserManagerForm.listUsersGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
767 Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); 744 Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
768 var 745 var
769 User: PUser; 746 User: PUser;
770 begin 747 begin
771 if not Assigned(FUsers) then 748 if not Assigned(FUsers) then
772 Exit; 749 Exit;
773 User := Sender.GetNodeData(Node); 750 User := Sender.GetNodeData(Node);
774 case Column of 751 case Column of
775 0: CellText := User.Username; 752 0: CellText := User.Username;
776 1: CellText := User.Host; 753 1: CellText := User.Host;
777 end; 754 end;
778 end; 755 end;
779 756
780 757
781 procedure TUserManagerForm.listUsersHeaderClick(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo); 758 procedure TUserManagerForm.listUsersHeaderClick(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo);
782 begin 759 begin
783 Mainform.AnyGridHeaderClick(Sender, HitInfo); 760 Mainform.AnyGridHeaderClick(Sender, HitInfo);
784 end; 761 end;
785 762
786 763
787 procedure TUserManagerForm.listUsersHotChange(Sender: TBaseVirtualTree; OldNode, 764 procedure TUserManagerForm.listUsersHotChange(Sender: TBaseVirtualTree; OldNode,
788 NewNode: PVirtualNode); 765 NewNode: PVirtualNode);
789 var 766 var
790 Node: PVirtualNode; 767 Node: PVirtualNode;
791 User: PUser; 768 User: PUser;
792 Msg: String; 769 Msg: String;
793 begin 770 begin
794 // Display warning hint for problematic stuff in the lower left corner. 771 // Display warning hint for problematic stuff in the lower left corner.
795 Node := NewNode; 772 Node := NewNode;
796 if not Assigned(Node) then 773 if not Assigned(Node) then
797 Node := Sender.FocusedNode; 774 Node := Sender.FocusedNode;
798 Msg := ''; 775 Msg := '';
799 if Assigned(Node) then begin 776 if Assigned(Node) then begin
800 User := Sender.GetNodeData(Node); 777 User := Sender.GetNodeData(Node);
801 case Length(User.Password) of 778 if User.Host <> LowerCase(User.Host) then begin
779 Msg := 'This user is broken due to uppercase characters in its hostname. Please fix that in the mysql.user table.';
780 end else case Length(User.Password) of
802 0: Msg := 'This user has an empty password.'; 781 0: Msg := 'This user has an empty password.';
803 16, 41: Msg := ''; 782 16, 41: Msg := '';
804 else Msg := 'This user is inactive due to an invalid length of its encrypted password. Please fix that in the mysql.user table.'; 783 else Msg := 'This user is inactive due to an invalid length of its encrypted password. Please fix that in the mysql.user table.';
805 end; 784 end;
806 end; 785 end;
807 lblWarning.Caption := Msg; 786 lblWarning.Caption := Msg;
808 end; 787 end;
809 788
810 789
811 procedure TUserManagerForm.listUsersCompareNodes(Sender: TBaseVirtualTree; 790 procedure TUserManagerForm.listUsersCompareNodes(Sender: TBaseVirtualTree;
812 Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer); 791 Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
813 begin 792 begin
814 Mainform.AnyGridCompareNodes(Sender, Node1, Node2, Column, Result); 793 Mainform.AnyGridCompareNodes(Sender, Node1, Node2, Column, Result);
815 end; 794 end;
816 795
817 796
818 procedure TUserManagerForm.listUsersInitNode(Sender: TBaseVirtualTree; ParentNode, 797 procedure TUserManagerForm.listUsersInitNode(Sender: TBaseVirtualTree; ParentNode,
819 Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates); 798 Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
820 var 799 var
821 User: PUser; 800 User: PUser;
822 begin 801 begin
823 User := Sender.GetNodeData(Node); 802 User := Sender.GetNodeData(Node);
824 User^ := FUsers[Node.Index]; 803 User^ := FUsers[Node.Index];
825 if not (Length(User.Password) in [0, 16, 41]) then 804 if (not (Length(User.Password) in [0, 16, 41])) or (LowerCase(User.Host) <> User.Host) then
826 Include(InitialStates, ivsDisabled); 805 Include(InitialStates, ivsDisabled);
827 end; 806 end;
828 807
829 808
830 function TUserManagerForm.GetPrivByNode(Node: PVirtualNode): TPrivObj; 809 function TUserManagerForm.GetPrivByNode(Node: PVirtualNode): TPrivObj;
831 begin 810 begin
832 // Return priv object by node 811 // Return priv object by node
833 if treePrivs.GetNodeLevel(Node) = 0 then 812 if treePrivs.GetNodeLevel(Node) = 0 then
834 Result := FPrivObjects[Node.Index] 813 Result := FPrivObjects[Node.Index]
835 else 814 else
836 Result := FPrivObjects[Node.Parent.Index]; 815 Result := FPrivObjects[Node.Parent.Index];
837 end; 816 end;
838 817
839 818
840 procedure TUserManagerForm.treePrivsChecked(Sender: TBaseVirtualTree; Node: PVirtualNode); 819 procedure TUserManagerForm.treePrivsChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
841 var 820 var
842 P: TPrivObj; 821 P: TPrivObj;
843 idxO, idxA, idxD: Integer; 822 idxO, idxA, idxD: Integer;
844 PrivName: String; 823 PrivName: String;
845 begin 824 begin
846 // Checked some privilege check box 825 // Checked some privilege check box
847 case Sender.GetNodeLevel(Node) of 826 case Sender.GetNodeLevel(Node) of
848 0: begin 827 0: begin
849 Sender.Expanded[Node] := True; 828 Sender.Expanded[Node] := True;
850 Sender.Invalidate; 829 Sender.Invalidate;
851 end; 830 end;
852 1: begin 831 1: begin
853 Modification(Sender); 832 Modification(Sender);
854 P := GetPrivByNode(Node); 833 P := GetPrivByNode(Node);
855 PrivName := P.AllPrivileges[Node.Index]; 834 PrivName := P.AllPrivileges[Node.Index];
856 idxO := P.OrgPrivs.IndexOf(PrivName); 835 idxO := P.OrgPrivs.IndexOf(PrivName);
857 idxA := P.AddedPrivs.IndexOf(PrivName); 836 idxA := P.AddedPrivs.IndexOf(PrivName);
858 idxD := P.DeletedPrivs.IndexOf(PrivName); 837 idxD := P.DeletedPrivs.IndexOf(PrivName);
859 if idxA > -1 then 838 if idxA > -1 then
860 P.AddedPrivs.Delete(idxA); 839 P.AddedPrivs.Delete(idxA);
861 if idxD > -1 then 840 if idxD > -1 then
862 P.DeletedPrivs.Delete(idxD); 841 P.DeletedPrivs.Delete(idxD);
863 if (Node.CheckState in CheckedStates) and (idxO = -1) then 842 if (Node.CheckState in CheckedStates) and (idxO = -1) then
864 P.AddedPrivs.Add(PrivName); 843 P.AddedPrivs.Add(PrivName);
865 if (not (Node.CheckState in CheckedStates)) and (idxO > -1) then 844 if (not (Node.CheckState in CheckedStates)) and (idxO > -1) then
866 P.DeletedPrivs.Add(PrivName); 845 P.DeletedPrivs.Add(PrivName);
867 end; 846 end;
868 end; 847 end;
869 end; 848 end;
870 849
871 850
872 procedure TUserManagerForm.treePrivsExpanded(Sender: TBaseVirtualTree; Node: PVirtualNode); 851 procedure TUserManagerForm.treePrivsExpanded(Sender: TBaseVirtualTree; Node: PVirtualNode);
873 var 852 var
874 n: PVirtualNode; 853 n: PVirtualNode;
875 begin 854 begin
876 // Collapse all uninvolved tree nodes, keeping the tree usable 855 // Collapse all uninvolved tree nodes, keeping the tree usable
877 n := Sender.GetFirstChild(Node.Parent); 856 n := Sender.GetFirstChild(Node.Parent);
878 while Assigned(n) do begin 857 while Assigned(n) do begin
879 Sender.Expanded[n] := n = Node; 858 Sender.Expanded[n] := n = Node;
880 n := Sender.GetNextSibling(n); 859 n := Sender.GetNextSibling(n);
881 end; 860 end;
882 end; 861 end;
883 862
884 863
885 procedure TUserManagerForm.treePrivsGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; 864 procedure TUserManagerForm.treePrivsGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode;
886 Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer); 865 Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer);
887 begin 866 begin
888 // Icon for privilege 867 // Icon for privilege
889 if Sender.GetNodeLevel(Node) <> 0 then 868 if Sender.GetNodeLevel(Node) <> 0 then
890 Exit; 869 Exit;
891 case Kind of 870 case Kind of
892 ikNormal, ikSelected: 871 ikNormal, ikSelected:
893 ImageIndex := FPrivObjects[Node.Index].DBObj.ImageIndex; 872 ImageIndex := FPrivObjects[Node.Index].DBObj.ImageIndex;
894 ikOverlay: begin 873 ikOverlay: begin
895 if FPrivObjects[Node.Index].Added then 874 if FPrivObjects[Node.Index].Added then
896 ImageIndex := 163; 875 ImageIndex := 163;
897 end; 876 end;
898 end; 877 end;
899 end; 878 end;
900 879
901 880
902 procedure TUserManagerForm.treePrivsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; 881 procedure TUserManagerForm.treePrivsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
903 Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); 882 Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
904 var 883 var
905 p: TPrivObj; 884 p: TPrivObj;
906 begin 885 begin
907 // Display priv object text 886 // Display priv object text
908 p := GetPrivByNode(Node); 887 p := GetPrivByNode(Node);
909 case Sender.GetNodeLevel(Node) of 888 case Sender.GetNodeLevel(Node) of
910 0: begin 889 0: begin
911 case p.DBObj.NodeType of 890 case p.DBObj.NodeType of
912 lntNone: 891 lntNone:
913 CellText := 'Global privileges'; 892 CellText := 'Global privileges';
914 lntDb: 893 lntDb:
915 CellText := 'Database: '+p.DBObj.Database; 894 CellText := 'Database: '+p.DBObj.Database;
916 lntTable, lntProcedure, lntFunction: 895 lntTable, lntProcedure, lntFunction:
917 CellText := p.DBObj.ObjType+': '+p.DBObj.Database+'.'+p.DBObj.Name; 896 CellText := p.DBObj.ObjType+': '+p.DBObj.Database+'.'+p.DBObj.Name;
918 lntColumn: 897 lntColumn:
919 CellText := p.DBObj.ObjType+': '+p.DBObj.Database+'.'+p.DBObj.Name+'.'+p.DBObj.Column; 898 CellText := p.DBObj.ObjType+': '+p.DBObj.Database+'.'+p.DBObj.Name+'.'+p.DBObj.Column;
920 end; 899 end;
921 end; 900 end;
922 1: CellText := p.AllPrivileges[Node.Index]; 901 1: CellText := p.AllPrivileges[Node.Index];
923 end; 902 end;
924 end; 903 end;
925 904
926 905
927 procedure TUserManagerForm.treePrivsInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode; 906 procedure TUserManagerForm.treePrivsInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode;
928 var ChildCount: Cardinal); 907 var ChildCount: Cardinal);
929 begin 908 begin
930 if Sender.GetNodeLevel(Node) = 0 then 909 if Sender.GetNodeLevel(Node) = 0 then
931 ChildCount := FPrivObjects[Node.Index].AllPrivileges.Count; 910 ChildCount := FPrivObjects[Node.Index].AllPrivileges.Count;
932 end; 911 end;
933 912
934 913
935 procedure TUserManagerForm.treePrivsInitNode(Sender: TBaseVirtualTree; ParentNode, 914 procedure TUserManagerForm.treePrivsInitNode(Sender: TBaseVirtualTree; ParentNode,
936 Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates); 915 Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
937 var 916 var
938 p: TPrivObj; 917 p: TPrivObj;
939 begin 918 begin
940 Node.CheckType := ctTriStateCheckBox; 919 Node.CheckType := ctTriStateCheckBox;
941 p := GetPrivByNode(Node); 920 p := GetPrivByNode(Node);
942 case Sender.GetNodeLevel(Node) of 921 case Sender.GetNodeLevel(Node) of
943 0: begin 922 0: begin
944 // Display plus/minus button 923 // Display plus/minus button
945 Include(InitialStates, ivsHasChildren); 924 Include(InitialStates, ivsHasChildren);
946 // AutoOptions.toAutoTristateTracking does a good job but it does not auto-check parent nodes when initializing 925 // AutoOptions.toAutoTristateTracking does a good job but it does not auto-check parent nodes when initializing
947 if p.OrgPrivs.Count = 0 then 926 if p.OrgPrivs.Count = 0 then
948 Node.CheckState := csUncheckedNormal 927 Node.CheckState := csUncheckedNormal
949 else if p.OrgPrivs.Count < p.AllPrivileges.Count then 928 else if p.OrgPrivs.Count < p.AllPrivileges.Count then
950 Node.CheckState := csMixedNormal 929 Node.CheckState := csMixedNormal
951 else 930 else
952 Node.CheckState := csCheckedNormal; 931 Node.CheckState := csCheckedNormal;
953 end; 932 end;
954 1: begin 933 1: begin
955 // Added objects have some basic added privs, others only have original privs. 934 // Added objects have some basic added privs, others only have original privs.
956 Node.CheckState := csUncheckedNormal; 935 Node.CheckState := csUncheckedNormal;
957 if (p.OrgPrivs.IndexOf(p.AllPrivileges[Node.Index]) > -1) 936 if (p.OrgPrivs.IndexOf(p.AllPrivileges[Node.Index]) > -1)
958 or (p.AddedPrivs.IndexOf(p.AllPrivileges[Node.Index]) > -1) then 937 or (p.AddedPrivs.IndexOf(p.AllPrivileges[Node.Index]) > -1) then
959 Node.CheckState := csCheckedNormal; 938 Node.CheckState := csCheckedNormal;
960 end; 939 end;
961 end; 940 end;
962 end; 941 end;
963 942
964 943
965 procedure TUserManagerForm.treePrivsPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; 944 procedure TUserManagerForm.treePrivsPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas;
966 Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); 945 Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType);
967 var 946 var
968 PrivName: String; 947 PrivName: String;
969 begin 948 begin
970 // Colors for privilege names 949 // Colors for privilege names
971 if (Sender.GetNodeLevel(Node) = 1) and (not (vsSelected in Node.States)) then begin 950 if (Sender.GetNodeLevel(Node) = 1) and (not (vsSelected in Node.States)) then begin
972 PrivName := FPrivObjects[Node.Parent.Index].AllPrivileges[Node.Index]; 951 PrivName := FPrivObjects[Node.Parent.Index].AllPrivileges[Node.Index];
973 if PrivsRead.IndexOf(PrivName) > -1 then 952 if PrivsRead.IndexOf(PrivName) > -1 then
974 TargetCanvas.Font.Color := clGreen 953 TargetCanvas.Font.Color := clGreen
975 else if PrivsWrite.IndexOf(PrivName) > -1 then 954 else if PrivsWrite.IndexOf(PrivName) > -1 then
976 TargetCanvas.Font.Color := clMaroon 955 TargetCanvas.Font.Color := clMaroon
977 else if PrivsAdmin.IndexOf(PrivName) > -1 then 956 else if PrivsAdmin.IndexOf(PrivName) > -1 then
978 TargetCanvas.Font.Color := clNavy; 957 TargetCanvas.Font.Color := clNavy;
979 end; 958 end;
980 end; 959 end;
981 960
982 961
983 procedure TUserManagerForm.btnAddUserClick(Sender: TObject); 962 procedure TUserManagerForm.btnAddUserClick(Sender: TObject);
984 var 963 var
985 P: TPrivObj; 964 P: TPrivObj;
986 User: TUser; 965 User: TUser;
987 NodeUser: PUser; 966 NodeUser: PUser;
988 Node: PVirtualNode; 967 Node: PVirtualNode;
989 begin 968 begin
990 // Create new or clone existing user 969 // Create new or clone existing user
991 if Sender = btnCloneUser then begin 970 if Sender = btnCloneUser then begin
992 CloneGrants := TStringList.Create; 971 CloneGrants := TStringList.Create;
993 for P in FPrivObjects do 972 for P in FPrivObjects do
994 CloneGrants.Add(P.GrantCode); 973 CloneGrants.Add(P.GrantCode);
995 end; 974 end;
996 // Try to unfocus current user which triggers saving modifications. 975 // Try to unfocus current user which triggers saving modifications.
997 listUsers.FocusedNode := nil; 976 listUsers.FocusedNode := nil;
998 if Assigned(listUsers.FocusedNode) then 977 if Assigned(listUsers.FocusedNode) then
999 Exit; 978 Exit;
1000 User := TUser.Create; 979 User := TUser.Create;
1001 User.Username := 'Unnamed'; 980 User.Username := 'Unnamed';
1002 User.Host := 'localhost'; 981 User.Host := 'localhost';
1003 FUsers.Add(User); 982 FUsers.Add(User);
1004 FAdded := True; 983 FAdded := True;
1005 InvalidateVT(listUsers, VTREE_NOTLOADED, True); 984 InvalidateVT(listUsers, VTREE_NOTLOADED, True);
1006 // Select newly added item. 985 // Select newly added item.
1007 Node := listUsers.GetFirst; 986 Node := listUsers.GetFirst;
1008 while Assigned(Node) do begin 987 while Assigned(Node) do begin
1009 NodeUser := listUsers.GetNodeData(Node); 988 NodeUser := listUsers.GetNodeData(Node);
1010 if User = NodeUser^ then begin 989 if User = NodeUser^ then begin
1011 SelectNode(listUsers, Node); 990 SelectNode(listUsers, Node);
1012 break; 991 break;
1013 end; 992 end;
1014 Node := listUsers.GetNextSibling(Node); 993 Node := listUsers.GetNextSibling(Node);
1015 end; 994 end;
1016 Modified := True; 995 Modified := True;
1017 // Focus the user name entry box. 996 // Focus the user name entry box.
1018 editUserName.SetFocus; 997 editUserName.SetFocus;
1019 end; 998 end;
1020 999
1021 1000
1022 procedure TUserManagerForm.btnAddObjectClick(Sender: TObject); 1001 procedure TUserManagerForm.btnAddObjectClick(Sender: TObject);
1023 var 1002 var
1024 DBObj: TDBObject; 1003 DBObj: TDBObject;
1025 Priv: TPrivObj; 1004 Priv: TPrivObj;
1026 Node, Child: PVirtualNode; 1005 Node, Child: PVirtualNode;
1027 begin 1006 begin
1028 // Add new privilege object 1007 // Add new privilege object
1029 DBObj := SelectDBO; 1008 DBObj := SelectDBO;
1030 if not Assigned(DBObj) then 1009 if not Assigned(DBObj) then
1031 Exit; 1010 Exit;
1032 // Check for unsupported object type, selectable in tree 1011 // Check for unsupported object type, selectable in tree
1033 if not (DBObj.NodeType in [lntDb, lntTable, lntFunction, lntProcedure, lntColumn]) then begin 1012 if not (DBObj.NodeType in [lntDb, lntTable, lntFunction, lntProcedure, lntColumn]) then begin
1034 ErrorDialog('Objects of type '+DBObj.ObjType+' cannot be part of privileges.'); 1013 ErrorDialog('Objects of type '+DBObj.ObjType+' cannot be part of privileges.');
1035 Exit; 1014 Exit;
1036 end; 1015 end;
1037 // Check if this would be a duplicate object 1016 // Check if this would be a duplicate object
1038 for Priv in FPrivObjects do begin 1017 for Priv in FPrivObjects do begin
1039 if Priv.DBObj.IsSameAs(DBObj) then begin 1018 if Priv.DBObj.IsSameAs(DBObj) then begin
1040 ErrorDialog('Selected object is already accessible.'); 1019 ErrorDialog('Selected object is already accessible.');
1041 Exit; 1020 Exit;
1042 end; 1021 end;
1043 end; 1022 end;
1044 1023
1045 Priv := TPrivObj.Create; 1024 Priv := TPrivObj.Create;
1046 Priv.DBObj := DBObj; 1025 Priv.DBObj := DBObj;
1047 case Priv.DBObj.NodeType of 1026 case Priv.DBObj.NodeType of
1048 lntNone: Priv.AllPrivileges := PrivsGlobal; 1027 lntNone: Priv.AllPrivileges := PrivsGlobal;
1049 lntDb: Priv.AllPrivileges := PrivsDb; 1028 lntDb: Priv.AllPrivileges := PrivsDb;
1050 lntTable: Priv.AllPrivileges := PrivsTable; 1029 lntTable: Priv.AllPrivileges := PrivsTable;
1051 lntFunction, lntProcedure: Priv.AllPrivileges := PrivsRoutine; 1030 lntFunction, lntProcedure: Priv.AllPrivileges := PrivsRoutine;
1052 lntColumn: Priv.AllPrivileges := PrivsColumn; 1031 lntColumn: Priv.AllPrivileges := PrivsColumn;
1053 end; 1032 end;
1054 // Assign minimum privileges 1033 // Assign minimum privileges
1055 case Priv.DBObj.NodeType of 1034 case Priv.DBObj.NodeType of
1056 lntFunction, lntProcedure: Priv.AddedPrivs.Add('EXECUTE'); 1035 lntFunction, lntProcedure: Priv.AddedPrivs.Add('EXECUTE');
1057 else Priv.AddedPrivs.Add('SELECT'); 1036 else Priv.AddedPrivs.Add('SELECT');
1058 end; 1037 end;
1059 Priv.Added := True; 1038 Priv.Added := True;
1060 FPrivObjects.Add(Priv); 1039 FPrivObjects.Add(Priv);
1061 Node := treePrivs.AddChild(nil); 1040 Node := treePrivs.AddChild(nil);
1062 Child := treePrivs.GetFirstChild(Node); 1041 Child := treePrivs.GetFirstChild(Node);
1063 while Assigned(Child) do 1042 while Assigned(Child) do
1064 Child := treePrivs.GetNextSibling(Child); 1043 Child := treePrivs.GetNextSibling(Child);
1065 treePrivs.Expanded[Node] := True; 1044 treePrivs.Expanded[Node] := True;
1066 treePrivs.SetFocus; 1045 treePrivs.SetFocus;
1067 SelectNode(treePrivs, Node); 1046 SelectNode(treePrivs, Node);
1068 Modified := True; 1047 Modified := True;
1069 end; 1048 end;
1070 1049
1071 1050
1072 procedure TUserManagerForm.btnSaveClick(Sender: TObject); 1051 procedure TUserManagerForm.btnSaveClick(Sender: TObject);
1073 var 1052 var
1074 UserHost, OrgUserHost, Create, Table, Revoke, Grant, OnObj, RequireClause: String; 1053 UserHost, OrgUserHost, Create, Table, Revoke, Grant, OnObj, RequireClause: String;
1075 User: TUser; 1054 User: TUser;
1076 FocusedUser: PUser; 1055 FocusedUser: PUser;
1077 Tables, WithClauses: TStringList; 1056 Tables, WithClauses: TStringList;
1078 P: TPrivObj; 1057 P: TPrivObj;
1079 i: Integer; 1058 i: Integer;
1080 PasswordSet: Boolean; 1059 PasswordSet: Boolean;
1081 1060
1082 function GetObjectType(ObjType: String): String; 1061 function GetObjectType(ObjType: String): String;
1083 begin 1062 begin
1084 // Decide if object type can be part of a GRANT or REVOKE query 1063 // Decide if object type can be part of a GRANT or REVOKE query
1085 Result := ''; 1064 Result := '';
1086 if FConnection.ServerVersionInt >= 50006 then 1065 if FConnection.ServerVersionInt >= 50006 then
1087 Result := UpperCase(ObjType) + ' '; 1066 Result := UpperCase(ObjType) + ' ';
1088 end; 1067 end;
1089 1068
1090 begin 1069 begin
1091 // Save changes 1070 // Save changes
1092 FocusedUser := listUsers.GetNodeData(listUsers.FocusedNode); 1071 FocusedUser := listUsers.GetNodeData(listUsers.FocusedNode);
1093 if FAdded then begin 1072 if FAdded then begin
1094 FocusedUser.Username := editUsername.Text; 1073 FocusedUser.Username := editUsername.Text;
1095 FocusedUser.Host := editFromHost.Text; 1074 FocusedUser.Host := editFromHost.Text;
1096 end; 1075 end;
1097 OrgUserHost := esc(FocusedUser.Username)+'@'+esc(FocusedUser.Host); 1076 OrgUserHost := esc(FocusedUser.Username)+'@'+esc(FocusedUser.Host);
1098 UserHost := esc(editUsername.Text)+'@'+esc(editFromHost.Text); 1077 UserHost := esc(editUsername.Text)+'@'+esc(editFromHost.Text);
1099 1078
1100 try 1079 try
1101 // Ensure we have a unique user@host combination 1080 // Ensure we have a unique user@host combination
1102 for User in FUsers do begin 1081 for User in FUsers do begin
1103 if User = FocusedUser^ then 1082 if User = FocusedUser^ then
1104 Continue; 1083 Continue;
1105 if (User.Username = editUsername.Text) and (User.Host = editFromHost.Text) then 1084 if (User.Username = editUsername.Text) and (User.Host = editFromHost.Text) then
1106 raise EInputError.Create('User <'+editUsername.Text+'@'+editFromHost.Text+'> already exists.'); 1085 raise EInputError.Create('User <'+editUsername.Text+'@'+editFromHost.Text+'> already exists.');
1107 end; 1086 end;
1108 1087
1109 // Check input: Ensure we have a unique user@host combination 1088 // Check input: Ensure we have a unique user@host combination
1110 if editPassword.Text <> editRepeatPassword.Text then 1089 if editPassword.Text <> editRepeatPassword.Text then
1111 raise EInputError.Create('Repeated password does not match first one.'); 1090 raise EInputError.Create('Repeated password does not match first one.');
1112 1091
1113 // Create added user 1092 // Create added user
1114 PasswordSet := False; 1093 PasswordSet := False;
1115 if FAdded and (FConnection.ServerVersionInt >= 50002) then begin 1094 if FAdded and (FConnection.ServerVersionInt >= 50002) then begin
1116 Create := 'CREATE USER '+UserHost; 1095 Create := 'CREATE USER '+UserHost;
1117 if editPassword.Modified then 1096 if editPassword.Modified then
1118 Create := Create + ' IDENTIFIED BY '+esc(editPassword.Text); 1097 Create := Create + ' IDENTIFIED BY '+esc(editPassword.Text);
1119 FConnection.Query(Create); 1098 FConnection.Query(Create);
1120 PasswordSet := True; 1099 PasswordSet := True;
1121 end; 1100 end;
1122 1101
1123 // Grant added privileges and revoke deleted ones 1102 // Grant added privileges and revoke deleted ones
1124 for P in FPrivObjects do begin 1103 for P in FPrivObjects do begin
1125 1104
1126 case P.DBObj.NodeType of 1105 case P.DBObj.NodeType of
1127 lntNone: 1106 lntNone:
1128 OnObj := '*.*'; 1107 OnObj := '*.*';
1129 lntDb: 1108 lntDb:
1130 OnObj := P.DBObj.QuotedDatabase + '.*'; 1109 OnObj := P.DBObj.QuotedDatabase + '.*';
1131 lntTable, lntFunction, lntProcedure: 1110 lntTable, lntFunction, lntProcedure:
1132 OnObj := GetObjectType(P.DBObj.ObjType) + P.DBObj.QuotedDatabase + '.' + P.DBObj.QuotedName; 1111 OnObj := GetObjectType(P.DBObj.ObjType) + P.DBObj.QuotedDatabase + '.' + P.DBObj.QuotedName;
1133 lntColumn: 1112 lntColumn:
1134 OnObj := GetObjectType('TABLE') + P.DBObj.QuotedDatabase + '.' + P.DBObj.QuotedName; 1113 OnObj := GetObjectType('TABLE') + P.DBObj.QuotedDatabase + '.' + P.DBObj.QuotedName;
1135 else 1114 else
1136 raise Exception.Create('Unhandled privilege object: '+P.DBObj.ObjType); 1115 raise Exception.Create('Unhandled privilege object: '+P.DBObj.ObjType);
1137 end; 1116 end;
1138 1117
1139 // Revoke privileges 1118 // Revoke privileges
1140 if (not P.Added) and (P.DeletedPrivs.Count > 0) then begin 1119 if (not P.Added) and (P.DeletedPrivs.Count > 0) then begin
1141 Revoke := ''; 1120 Revoke := '';
1142 for i:=0 to P.DeletedPrivs.Count-1 do begin 1121 for i:=0 to P.DeletedPrivs.Count-1 do begin
1143 Revoke := Revoke + P.DeletedPrivs[i]; 1122 Revoke := Revoke + P.DeletedPrivs[i];
1144 if P.DeletedPrivs[i] = 'GRANT' then 1123 if P.DeletedPrivs[i] = 'GRANT' then
1145 Revoke := Revoke + ' OPTION'; 1124 Revoke := Revoke + ' OPTION';
1146 if P.DBObj.NodeType = lntColumn then 1125 if P.DBObj.NodeType = lntColumn then
1147 Revoke := Revoke + '('+P.DBObj.QuotedColumn+')'; 1126 Revoke := Revoke + '('+P.DBObj.QuotedColumn+')';
1148 Revoke := Revoke + ', '; 1127 Revoke := Revoke + ', ';
1149 end; 1128 end;
1150 Delete(Revoke, Length(Revoke)-1, 1); 1129 Delete(Revoke, Length(Revoke)-1, 1);
1151 Revoke := 'REVOKE ' + Revoke + ' ON ' + OnObj + ' FROM ' + OrgUserHost; 1130 Revoke := 'REVOKE ' + Revoke + ' ON ' + OnObj + ' FROM ' + OrgUserHost;
1152 FConnection.Query(Revoke); 1131 FConnection.Query(Revoke);
1153 end; 1132 end;
1154 1133
1155 // Grant privileges. Must be applied with USAGE for added users without specific privs. 1134 // Grant privileges. Must be applied with USAGE for added users without specific privs.
1156 Grant := ''; 1135 Grant := '';
1157 for i:=0 to P.AddedPrivs.Count-1 do begin 1136 for i:=0 to P.AddedPrivs.Count-1 do begin
1158 if P.AddedPrivs[i] = 'GRANT' then 1137 if P.AddedPrivs[i] = 'GRANT' then
1159 Continue; 1138 Continue;
1160 Grant := Grant + P.AddedPrivs[i]; 1139 Grant := Grant + P.AddedPrivs[i];
1161 if P.DBObj.NodeType = lntColumn then 1140 if P.DBObj.NodeType = lntColumn then
1162 Grant := Grant + '('+P.DBObj.QuotedColumn+')'; 1141 Grant := Grant + '('+P.DBObj.QuotedColumn+')';
1163 Grant := Grant + ', '; 1142 Grant := Grant + ', ';
1164 end; 1143 end;
1165 Delete(Grant, Length(Grant)-1, 1); 1144 Delete(Grant, Length(Grant)-1, 1);
1166 if Grant = '' then 1145 if Grant = '' then
1167 Grant := 'USAGE'; 1146 Grant := 'USAGE';
1168 Grant := 'GRANT ' + Grant + ' ON ' + OnObj + ' TO ' + OrgUserHost; 1147 Grant := 'GRANT ' + Grant + ' ON ' + OnObj + ' TO ' + OrgUserHost;
1169 1148
1170 // SSL options 1149 // SSL options
1171 if P.DBObj.NodeType = lntNone then begin 1150 if P.DBObj.NodeType = lntNone then begin
1172 RequireClause := ' REQUIRE '; 1151 RequireClause := ' REQUIRE ';
1173 case comboSSL.ItemIndex of 1152 case comboSSL.ItemIndex of
1174 0: RequireClause := RequireClause + 'NONE'; 1153 0: RequireClause := RequireClause + 'NONE';
1175 1: RequireClause := RequireClause + 'SSL'; 1154 1: RequireClause := RequireClause + 'SSL';
1176 2: RequireClause := RequireClause + 'X509'; 1155 2: RequireClause := RequireClause + 'X509';
1177 3: RequireClause := RequireClause + 'CIPHER '+esc(editCipher.Text)+' ISSUER '+esc(editIssuer.Text)+' SUBJECT '+esc(editSubject.Text); 1156 3: RequireClause := RequireClause + 'CIPHER '+esc(editCipher.Text)+' ISSUER '+esc(editIssuer.Text)+' SUBJECT '+esc(editSubject.Text);
1178 end; 1157 end;
1179 if (FocusedUser.SSL = comboSSL.ItemIndex) 1158 if (FocusedUser.SSL = comboSSL.ItemIndex)
1180 and (FocusedUser.Cipher = editCipher.Text) 1159 and (FocusedUser.Cipher = editCipher.Text)
1181 and (FocusedUser.Issuer = editIssuer.Text) 1160 and (FocusedUser.Issuer = editIssuer.Text)
1182 and (FocusedUser.Subject = editSubject.Text) 1161 and (FocusedUser.Subject = editSubject.Text)
1183 then 1162 then
1184 RequireClause := ''; 1163 RequireClause := '';
1185 Grant := Grant + RequireClause; 1164 Grant := Grant + RequireClause;
1186 end; 1165 end;
1187 1166
1188 WithClauses := TStringList.Create; 1167 WithClauses := TStringList.Create;
1189 if P.AddedPrivs.IndexOf('GRANT') > -1 then 1168 if P.AddedPrivs.IndexOf('GRANT') > -1 then
1190 WithClauses.Add('GRANT OPTION'); 1169 WithClauses.Add('GRANT OPTION');
1191 if P.DBObj.NodeType = lntNone then begin 1170 if P.DBObj.NodeType = lntNone then begin
1192 // Apply resource limits only to global privilege 1171 // Apply resource limits only to global privilege
1193 if udMaxQueries.Position <> FocusedUser.MaxQueries then 1172 if udMaxQueries.Position <> FocusedUser.MaxQueries then
1194 WithClauses.Add('MAX_QUERIES_PER_HOUR '+IntToStr(udMaxQueries.Position)); 1173 WithClauses.Add('MAX_QUERIES_PER_HOUR '+IntToStr(udMaxQueries.Position));
1195 if udMaxUpdates.Position <> FocusedUser.MaxUpdates then 1174 if udMaxUpdates.Position <> FocusedUser.MaxUpdates then
1196 WithClauses.Add('MAX_UPDATES_PER_HOUR '+IntToStr(udMaxUpdates.Position)); 1175 WithClauses.Add('MAX_UPDATES_PER_HOUR '+IntToStr(udMaxUpdates.Position));
1197 if udMaxConnections.Position <> FocusedUser.MaxConnections then 1176 if udMaxConnections.Position <> FocusedUser.MaxConnections then
1198 WithClauses.Add('MAX_CONNECTIONS_PER_HOUR '+IntToStr(udMaxConnections.Position)); 1177 WithClauses.Add('MAX_CONNECTIONS_PER_HOUR '+IntToStr(udMaxConnections.Position));
1199 if udMaxUserConnections.Position <> FocusedUser.MaxUserConnections then 1178 if udMaxUserConnections.Position <> FocusedUser.MaxUserConnections then
1200 WithClauses.Add('MAX_USER_CONNECTIONS '+IntToStr(udMaxUserConnections.Position)); 1179 WithClauses.Add('MAX_USER_CONNECTIONS '+IntToStr(udMaxUserConnections.Position));
1201 end; 1180 end;
1202 if WithClauses.Count > 0 then 1181 if WithClauses.Count > 0 then
1203 Grant := Grant + ' WITH ' + ImplodeStr(' ', WithClauses); 1182 Grant := Grant + ' WITH ' + ImplodeStr(' ', WithClauses);
1204 1183
1205 if P.Added or (P.AddedPrivs.Count > 0) or (WithClauses.Count > 0) or (RequireClause <> '') then 1184 if P.Added or (P.AddedPrivs.Count > 0) or (WithClauses.Count > 0) or (RequireClause <> '') then
1206 FConnection.Query(Grant); 1185 FConnection.Query(Grant);
1207 1186
1208 WithClauses.Free; 1187 WithClauses.Free;
1209 end; 1188 end;
1210 1189
1211 // Set password 1190 // Set password
1212 if editPassword.Modified and (not PasswordSet) then begin 1191 if editPassword.Modified and (not PasswordSet) then begin
1213 FConnection.Query('SET PASSWORD FOR ' + OrgUserHost + ' = PASSWORD('+esc(editPassword.Text)+')'); 1192 FConnection.Query('SET PASSWORD FOR ' + OrgUserHost + ' = PASSWORD('+esc(editPassword.Text)+')');
1214 end; 1193 end;
1215 1194
1216 // Rename user 1195 // Rename user
1217 if (FocusedUser.Username <> editUsername.Text) or (FocusedUser.Host <> editFromHost.Text) then begin 1196 if (FocusedUser.Username <> editUsername.Text) or (FocusedUser.Host <> editFromHost.Text) then begin
1218 if FConnection.ServerVersionInt >= 50002 then 1197 if FConnection.ServerVersionInt >= 50002 then
1219 FConnection.Query('RENAME USER '+OrgUserHost+' TO '+UserHost) 1198 FConnection.Query('RENAME USER '+OrgUserHost+' TO '+UserHost)
1220 else begin 1199 else begin
1221 Tables := Explode(',', 'user,db,tables_priv,columns_priv'); 1200 Tables := Explode(',', 'user,db,tables_priv,columns_priv');
1222 for Table in Tables do begin 1201 for Table in Tables do begin
1223 FConnection.Query('UPDATE '+FConnection.QuoteIdent('mysql')+'.'+FConnection.QuoteIdent(Table)+ 1202 FConnection.Query('UPDATE '+FConnection.QuoteIdent('mysql')+'.'+FConnection.QuoteIdent(Table)+
1224 ' SET User='+esc(editUsername.Text)+', Host='+esc(editFromHost.Text)+ 1203 ' SET User='+esc(editUsername.Text)+', Host='+esc(editFromHost.Text)+
1225 ' WHERE User='+esc(FocusedUser.Username)+' AND Host='+esc(FocusedUser.Host) 1204 ' WHERE User='+esc(FocusedUser.Username)+' AND Host='+esc(FocusedUser.Host)
1226 ); 1205 );
1227 end; 1206 end;
1228 FreeAndNil(Tables); 1207 FreeAndNil(Tables);
1229 end; 1208 end;
1230 end; 1209 end;
1231 1210
1232 FConnection.Query('FLUSH PRIVILEGES'); 1211 FConnection.Query('FLUSH PRIVILEGES');
1233 Modified := False; 1212 Modified := False;
1234 FAdded := False; 1213 FAdded := False;
1235 FocusedUser.Username := editUsername.Text; 1214 FocusedUser.Username := editUsername.Text;
1236 FocusedUser.Host := editFromHost.Text; 1215 FocusedUser.Host := editFromHost.Text;
1237 if editPassword.Modified then 1216 if editPassword.Modified then
1238 FocusedUser.Password := editPassword.Text; 1217 FocusedUser.Password := editPassword.Text;
1239 FocusedUser.SSL := comboSSL.ItemIndex; 1218 FocusedUser.SSL := comboSSL.ItemIndex;
1240 FocusedUser.Cipher := editCipher.Text; 1219 FocusedUser.Cipher := editCipher.Text;
1241 FocusedUser.Issuer := editIssuer.Text; 1220 FocusedUser.Issuer := editIssuer.Text;
1242 FocusedUser.Subject := editSubject.Text; 1221 FocusedUser.Subject := editSubject.Text;
1243 listUsers.OnFocusChanged(listUsers, listUsers.FocusedNode, listUsers.FocusedColumn); 1222 listUsers.OnFocusChanged(listUsers, listUsers.FocusedNode, listUsers.FocusedColumn);
1244 except 1223 except
1245 on E:EDatabaseError do 1224 on E:EDatabaseError do
1246 ErrorDialog(E.Message); 1225 ErrorDialog(E.Message);
1247 on E:EInputError do 1226 on E:EInputError do
1248 ErrorDialog(E.Message); 1227 ErrorDialog(E.Message);
1249 end; 1228 end;
1250 1229
1251 end; 1230 end;
1252 1231
1253 1232
1254 procedure TUserManagerForm.comboSSLChange(Sender: TObject); 1233 procedure TUserManagerForm.comboSSLChange(Sender: TObject);
1255 begin 1234 begin
1256 // Enable custom SSL settings 1235 // Enable custom SSL settings
1257 lblCipher.Enabled := (comboSSL.ItemIndex = 3) and Assigned(listUsers.FocusedNode); 1236 lblCipher.Enabled := (comboSSL.ItemIndex = 3) and Assigned(listUsers.FocusedNode);
1258 editCipher.Enabled := lblCipher.Enabled; 1237 editCipher.Enabled := lblCipher.Enabled;
1259 lblIssuer.Enabled := lblCipher.Enabled; 1238 lblIssuer.Enabled := lblCipher.Enabled;
1260 editIssuer.Enabled := lblCipher.Enabled; 1239 editIssuer.Enabled := lblCipher.Enabled;
1261 lblSubject.Enabled := lblCipher.Enabled; 1240 lblSubject.Enabled := lblCipher.Enabled;
1262 editSubject.Enabled := lblCipher.Enabled; 1241 editSubject.Enabled := lblCipher.Enabled;
1263 Modification(Sender); 1242 Modification(Sender);
1264 end; 1243 end;
1265 1244
1266 1245
1267 procedure TUserManagerForm.btnDeleteUserClick(Sender: TObject); 1246 procedure TUserManagerForm.btnDeleteUserClick(Sender: TObject);
1268 var 1247 var
1269 UserHost: String; 1248 UserHost: String;
1270 User: PUser; 1249 User: PUser;
1271 begin 1250 begin
1272 // Delete user 1251 // Delete user
1273 User := listUsers.GetNodeData(listUsers.FocusedNode); 1252 User := listUsers.GetNodeData(listUsers.FocusedNode);
1274 if FAdded then begin 1253 if FAdded then begin
1275 FUsers.Remove(User^); 1254 FUsers.Remove(User^);
1276 listUsers.DeleteNode(listUsers.FocusedNode); 1255 listUsers.DeleteNode(listUsers.FocusedNode);
1277 FAdded := False; 1256 FAdded := False;
1278 end else if MessageDialog('Delete user '+User.Username+'@'+User.Host+'?', mtConfirmation, [mbYes, mbCancel]) = mrYes then begin 1257 end else if MessageDialog('Delete user '+User.Username+'@'+User.Host+'?', mtConfirmation, [mbYes, mbCancel]) = mrYes then begin
1279 UserHost := esc(User.Username)+'@'+esc(User.Host); 1258 UserHost := esc(User.Username)+'@'+esc(User.Host);
1280 try 1259 try
1281 // Revoke privs explicitly, required on old servers. 1260 // Revoke privs explicitly, required on old servers.
1282 // Newer servers only require one DROP USER query 1261 // Newer servers only require one DROP USER query
1283 if FConnection.ServerVersionInt < 50002 then begin 1262 if FConnection.ServerVersionInt < 50002 then begin
1284 FConnection.Query('REVOKE ALL PRIVILEGES ON *.* FROM '+UserHost); 1263 FConnection.Query('REVOKE ALL PRIVILEGES ON *.* FROM '+UserHost);
1285 FConnection.Query('REVOKE GRANT OPTION ON *.* FROM '+UserHost); 1264 FConnection.Query('REVOKE GRANT OPTION ON *.* FROM '+UserHost);
1286 end; 1265 end;
1287 if FConnection.ServerVersionInt < 40101 then 1266 if FConnection.ServerVersionInt < 40101 then
1288 FConnection.Query('DELETE FROM mysql.user WHERE User='+esc(User.Username)+' AND Host='+esc(User.Host)) 1267 FConnection.Query('DELETE FROM mysql.user WHERE User='+esc(User.Username)+' AND Host='+esc(User.Host))
1289 else 1268 else
1290 FConnection.Query('DROP USER '+UserHost); 1269 FConnection.Query('DROP USER '+UserHost);
1291 FConnection.Query('FLUSH PRIVILEGES'); 1270 FConnection.Query('FLUSH PRIVILEGES');
1292 FUsers.Remove(User^); 1271 FUsers.Remove(User^);
1293 listUsers.DeleteNode(listUsers.FocusedNode); 1272 listUsers.DeleteNode(listUsers.FocusedNode);
1294 except on E:EDatabaseError do 1273 except on E:EDatabaseError do
1295 ErrorDialog(E.Message); 1274 ErrorDialog(E.Message);
1296 end; 1275 end;
1297 end; 1276 end;
1298 end; 1277 end;
1299 1278
1300 1279
1301 procedure TUserManagerForm.btnDiscardClick(Sender: TObject); 1280 procedure TUserManagerForm.btnDiscardClick(Sender: TObject);
1302 begin 1281 begin
1303 // Reset modifications 1282 // Reset modifications
1304 Modified := False; 1283 Modified := False;
1305 listUsers.OnFocusChanged(listUsers, listUsers.FocusedNode, listUsers.FocusedColumn); 1284 listUsers.OnFocusChanged(listUsers, listUsers.FocusedNode, listUsers.FocusedColumn);
1306 end; 1285 end;
1307 1286
1308 1287
1309 procedure TUserManagerForm.menuHostClick(Sender: TObject); 1288 procedure TUserManagerForm.menuHostClick(Sender: TObject);
1310 begin 1289 begin
1311 // Insert predefined host 1290 // Insert predefined host
1312 editFromHost.Text := (Sender as TMenuItem).Hint; 1291 editFromHost.Text := (Sender as TMenuItem).Hint;
1313 end; 1292 end;
1314 1293
1315 1294
1316 procedure TUserManagerForm.menuHostPopup(Sender: TObject); 1295 procedure TUserManagerForm.menuHostPopup(Sender: TObject);
1317 var 1296 var
1318 Item: TMenuItem; 1297 Item: TMenuItem;
1319 i: Integer; 1298 i: Integer;
1320 User: TUser; 1299 User: TUser;
1321 ItemExists: Boolean; 1300 ItemExists: Boolean;
1322 begin 1301 begin
1323 // Delete custom items and readd unique ones 1302 // Delete custom items and readd unique ones
1324 for i:=menuHost.Items.Count-1 downto 0 do begin 1303 for i:=menuHost.Items.Count-1 downto 0 do begin
1325 if menuHost.Items[i].Caption = '-' then 1304 if menuHost.Items[i].Caption = '-' then
1326 break; 1305 break;
1327 menuHost.Items.Delete(i); 1306 menuHost.Items.Delete(i);
1328 end; 1307 end;
1329 for User in FUsers do begin 1308 for User in FUsers do begin
1330 if User.Host = '' then 1309 if User.Host = '' then
1331 Continue; 1310 Continue;
1332 ItemExists := False; 1311 ItemExists := False;
1333 for Item in menuHost.Items do begin 1312 for Item in menuHost.Items do begin
1334 if Item.Hint = User.Host then begin 1313 if Item.Hint = User.Host then begin
1335 ItemExists := True; 1314 ItemExists := True;
1336 Break; 1315 Break;
1337 end; 1316 end;
1338 end; 1317 end;
1339 if not ItemExists then begin 1318 if not ItemExists then begin
1340 Item := TMenuItem.Create(menuHost); 1319 Item := TMenuItem.Create(menuHost);
1341 Item.Caption := User.Host; 1320 Item.Caption := User.Host;
1342 Item.Hint := User.Host; 1321 Item.Hint := User.Host;
1343 Item.OnClick := menuHostClick; 1322 Item.OnClick := menuHostClick;
1344 menuHost.Items.Add(Item); 1323 menuHost.Items.Add(Item);
1345 end; 1324 end;
1346 end; 1325 end;
1347 // Auto check current host if any matches 1326 // Auto check current host if any matches
1348 for Item in menuHost.Items do 1327 for Item in menuHost.Items do
1349 Item.Checked := Item.Hint = editFromHost.Text; 1328 Item.Checked := Item.Hint = editFromHost.Text;
1350 end; 1329 end;
1351 1330
1352 1331
1353 procedure TUserManagerForm.editPasswordChange(Sender: TObject); 1332 procedure TUserManagerForm.editPasswordChange(Sender: TObject);
1354 begin 1333 begin
1355 // Password manually edited 1334 // Password manually edited
1356 editRepeatPassword.Enabled := True; 1335 editRepeatPassword.Enabled := True;
1357 editPassword.PasswordChar := '*'; 1336 editPassword.PasswordChar := '*';
1358 editRepeatPassword.PasswordChar := editPassword.PasswordChar; 1337 editRepeatPassword.PasswordChar := editPassword.PasswordChar;
1359 Modification(Sender); 1338 Modification(Sender);
1360 end; 1339 end;
1361 1340
1362 1341
1363 procedure TUserManagerForm.menuPasswordInsert(Sender: TObject); 1342 procedure TUserManagerForm.menuPasswordInsert(Sender: TObject);
1364 var 1343 var
1365 Item: TMenuItem; 1344 Item: TMenuItem;
1366 begin 1345 begin
1367 // Insert password from menu item 1346 // Insert password from menu item
1368 Item := Sender as TMenuItem; 1347 Item := Sender as TMenuItem;
1369 editPassword.Text := Item.Caption; 1348 editPassword.Text := Item.Caption;
1370 editPassword.Modified := True; 1349 editPassword.Modified := True;
1371 editPassword.PasswordChar := #0; 1350 editPassword.PasswordChar := #0;
1372 editRepeatPassword.Text := editPassword.Text; 1351 editRepeatPassword.Text := editPassword.Text;
1373 editRepeatPassword.PasswordChar := editPassword.PasswordChar; 1352 editRepeatPassword.PasswordChar := editPassword.PasswordChar;
1374 editRepeatPassword.Enabled := False; 1353 editRepeatPassword.Enabled := False;
1375 end; 1354 end;
1376 1355
1377 1356
1378 procedure TUserManagerForm.menuPasswordClick(Sender: TObject); 1357 procedure TUserManagerForm.menuPasswordClick(Sender: TObject);
1379 var 1358 var
1380 Parent, Item: TMenuItem; 1359 Parent, Item: TMenuItem;
1381 PasswordLen, i: Integer; 1360 PasswordLen, i: Integer;
1382 begin 1361 begin
1383 // Create menu items with random passwords 1362 // Create menu items with random passwords
1384 Parent := Sender as TMenuItem; 1363 Parent := Sender as TMenuItem;
1385 PasswordLen := MakeInt(Parent.Caption); 1364 PasswordLen := MakeInt(Parent.Caption);
1386 for i:=0 to 19 do begin 1365 for i:=0 to 19 do begin
1387 if Parent.Count > i then 1366 if Parent.Count > i then
1388 Item := Parent[i] 1367 Item := Parent[i]
1389 else begin 1368 else begin
1390 Item := TMenuItem.Create(Parent); 1369 Item := TMenuItem.Create(Parent);
1391 Parent.Add(Item); 1370 Parent.Add(Item);
1392 end; 1371 end;
1393 Item.OnClick := menuPasswordInsert; 1372 Item.OnClick := menuPasswordInsert;
1394 Item.Caption := GeneratePassword(PasswordLen); 1373 Item.Caption := GeneratePassword(PasswordLen);
1395 end; 1374 end;
1396 end; 1375 end;
1397 1376
1398 1377
1399 1378
1400 { TPrivObj } 1379 { TPrivObj }
1401 1380
1402 constructor TPrivObj.Create; 1381 constructor TPrivObj.Create;
1403 begin 1382 begin
1404 OrgPrivs := TStringList.Create; 1383 OrgPrivs := TStringList.Create;
1405 AddedPrivs := TStringList.Create; 1384 AddedPrivs := TStringList.Create;
1406 AddedPrivs.Duplicates := dupIgnore; 1385 AddedPrivs.Duplicates := dupIgnore;
1407 DeletedPrivs := TStringList.Create; 1386 DeletedPrivs := TStringList.Create;
1408 DeletedPrivs.Duplicates := dupIgnore; 1387 DeletedPrivs.Duplicates := dupIgnore;
1409 Added := False; 1388 Added := False;
1410 DBObj := TDBObject.Create(MainForm.ActiveConnection); 1389 DBObj := TDBObject.Create(MainForm.ActiveConnection);
1411 end; 1390 end;
1412 1391
1413 1392
1414 destructor TPrivObj.Destroy; 1393 destructor TPrivObj.Destroy;
1415 begin 1394 begin
1416 FreeAndNil(DBObj); 1395 FreeAndNil(DBObj);
1417 FreeAndNil(OrgPrivs); 1396 FreeAndNil(OrgPrivs);
1418 FreeAndNil(AddedPrivs); 1397 FreeAndNil(AddedPrivs);
1419 FreeAndNil(DeletedPrivs); 1398 FreeAndNil(DeletedPrivs);
1420 end; 1399 end;
1421 1400
1422 1401
1423 { TPrivComparer } 1402 { TPrivComparer }
1424 1403
1425 function TPrivComparer.Compare(const Left, Right: TPrivObj): Integer; 1404 function TPrivComparer.Compare(const Left, Right: TPrivObj): Integer;
1426 begin 1405 begin
1427 // Prio for global > db > table > view > function > proc > event > column 1406 // Prio for global > db > table > view > function > proc > event > column
1428 if (Left.DBObj.NodeType < Right.DBObj.NodeType) then 1407 if (Left.DBObj.NodeType < Right.DBObj.NodeType) then
1429 Result := -1 1408 Result := -1
1430 else if (Left.DBObj.NodeType > Right.DBObj.NodeType) then 1409 else if (Left.DBObj.NodeType > Right.DBObj.NodeType) then
1431 Result := 1 1410 Result := 1
1432 else begin 1411 else begin
1433 Result := CompareText( 1412 Result := CompareText(
1434 Left.DBObj.Database+Left.DBObj.Name+Left.DBObj.Column, 1413 Left.DBObj.Database+Left.DBObj.Name+Left.DBObj.Column,
1435 Right.DBObj.Database+Right.DBObj.Name+Right.DBObj.Column 1414 Right.DBObj.Database+Right.DBObj.Name+Right.DBObj.Column
1436 ); 1415 );
1437 end; 1416 end;
1438 end; 1417 end;
1439 1418
1440 1419
1441 end. 1420 end.
Powered by Google Project Hosting