mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2026-03-13 09:24:25 +08:00
MySQL/MariaDB: Implement support for expired passwords. Show a change-password dialog after the very first query of a connection when it returns "Error 1820: You must SET PASSWORD before executing this statement". See http://www.heidisql.com/forum.php?t=17921
This commit is contained in:
@@ -41,7 +41,8 @@ uses
|
||||
syncdb in '..\..\source\syncdb.pas' {frmSyncDB},
|
||||
gnugettext in '..\..\source\gnugettext.pas',
|
||||
JumpList in '..\..\source\JumpList.pas',
|
||||
extra_controls in '..\..\source\extra_controls.pas';
|
||||
extra_controls in '..\..\source\extra_controls.pas',
|
||||
change_password in '..\..\source\change_password.pas' {frmPasswordChange};
|
||||
|
||||
{$R ..\..\res\icon.RES}
|
||||
{$R ..\..\res\icon-question.RES}
|
||||
|
||||
@@ -194,6 +194,10 @@
|
||||
<DCCReference Include="..\..\source\gnugettext.pas"/>
|
||||
<DCCReference Include="..\..\source\JumpList.pas"/>
|
||||
<DCCReference Include="..\..\source\extra_controls.pas"/>
|
||||
<DCCReference Include="..\..\source\change_password.pas">
|
||||
<Form>frmPasswordChange</Form>
|
||||
<FormType>dfm</FormType>
|
||||
</DCCReference>
|
||||
<BuildConfiguration Include="Debug">
|
||||
<Key>Cfg_2</Key>
|
||||
<CfgParent>Base</CfgParent>
|
||||
|
||||
162
source/change_password.dfm
Normal file
162
source/change_password.dfm
Normal file
@@ -0,0 +1,162 @@
|
||||
object frmPasswordChange: TfrmPasswordChange
|
||||
Left = 0
|
||||
Top = 0
|
||||
BorderStyle = bsDialog
|
||||
Caption = 'Change expired password'
|
||||
ClientHeight = 187
|
||||
ClientWidth = 456
|
||||
Color = clBtnFace
|
||||
Constraints.MaxHeight = 300
|
||||
Constraints.MaxWidth = 600
|
||||
Constraints.MinHeight = 185
|
||||
Constraints.MinWidth = 400
|
||||
Font.Charset = DEFAULT_CHARSET
|
||||
Font.Color = clWindowText
|
||||
Font.Height = -11
|
||||
Font.Name = 'Tahoma'
|
||||
Font.Style = []
|
||||
OldCreateOrder = False
|
||||
Position = poMainFormCenter
|
||||
OnShow = FormShow
|
||||
DesignSize = (
|
||||
456
|
||||
187)
|
||||
PixelsPerInch = 96
|
||||
TextHeight = 13
|
||||
object lblHeading: TLabel
|
||||
Left = 8
|
||||
Top = 16
|
||||
Width = 440
|
||||
Height = 51
|
||||
Anchors = [akLeft, akTop, akRight, akBottom]
|
||||
AutoSize = False
|
||||
Caption = 'lblHeading'
|
||||
WordWrap = True
|
||||
end
|
||||
object lblPassword: TLabel
|
||||
Left = 8
|
||||
Top = 76
|
||||
Width = 74
|
||||
Height = 13
|
||||
Anchors = [akLeft, akBottom]
|
||||
Caption = 'New password:'
|
||||
end
|
||||
object lblRepeatPassword: TLabel
|
||||
Left = 8
|
||||
Top = 103
|
||||
Width = 111
|
||||
Height = 13
|
||||
Anchors = [akLeft, akBottom]
|
||||
Caption = 'Repeat new password:'
|
||||
end
|
||||
object lblStatus: TLabel
|
||||
Left = 8
|
||||
Top = 132
|
||||
Width = 41
|
||||
Height = 13
|
||||
Anchors = [akLeft, akBottom]
|
||||
Caption = 'lblStatus'
|
||||
end
|
||||
object editPassword: TButtonedEdit
|
||||
Left = 146
|
||||
Top = 73
|
||||
Width = 302
|
||||
Height = 21
|
||||
Anchors = [akLeft, akRight, akBottom]
|
||||
Images = MainForm.ImageListMain
|
||||
RightButton.DropDownMenu = popupPassword
|
||||
RightButton.ImageIndex = 75
|
||||
RightButton.Visible = True
|
||||
TabOrder = 0
|
||||
TextHint = 'Type new password or select a suggestion'
|
||||
OnChange = editPasswordChange
|
||||
OnClick = editPasswordChange
|
||||
OnKeyDown = editPasswordKeyDown
|
||||
end
|
||||
object editRepeatPassword: TButtonedEdit
|
||||
Left = 146
|
||||
Top = 100
|
||||
Width = 302
|
||||
Height = 21
|
||||
Anchors = [akLeft, akRight, akBottom]
|
||||
Images = MainForm.ImageListMain
|
||||
PasswordChar = '*'
|
||||
TabOrder = 1
|
||||
OnChange = editPasswordChange
|
||||
OnClick = editPasswordChange
|
||||
OnKeyDown = editPasswordKeyDown
|
||||
end
|
||||
object btnCancel: TButton
|
||||
Left = 292
|
||||
Top = 154
|
||||
Width = 75
|
||||
Height = 25
|
||||
Anchors = [akRight, akBottom]
|
||||
Caption = 'Cancel'
|
||||
ModalResult = 2
|
||||
TabOrder = 2
|
||||
end
|
||||
object btnOK: TButton
|
||||
Left = 213
|
||||
Top = 154
|
||||
Width = 75
|
||||
Height = 25
|
||||
Anchors = [akRight, akBottom]
|
||||
Caption = 'OK'
|
||||
Default = True
|
||||
ModalResult = 1
|
||||
TabOrder = 3
|
||||
end
|
||||
object btnCopyToClipboard: TButton
|
||||
Left = 373
|
||||
Top = 154
|
||||
Width = 75
|
||||
Height = 25
|
||||
Anchors = [akRight, akBottom]
|
||||
Caption = 'Copy'
|
||||
ImageIndex = 3
|
||||
Images = MainForm.ImageListMain
|
||||
TabOrder = 4
|
||||
OnClick = btnCopyToClipboardClick
|
||||
end
|
||||
object popupPassword: TPopupMenu
|
||||
Left = 344
|
||||
Top = 8
|
||||
object N6characters1: TMenuItem
|
||||
Caption = '6 characters'
|
||||
OnClick = menuPasswordClick
|
||||
object menuDummy1: TMenuItem
|
||||
Caption = 'dummy'
|
||||
OnClick = menuPasswordInsert
|
||||
end
|
||||
end
|
||||
object N8characters1: TMenuItem
|
||||
Caption = '8 characters'
|
||||
OnClick = menuPasswordClick
|
||||
object menuDummy2: TMenuItem
|
||||
Caption = 'dummy'
|
||||
end
|
||||
end
|
||||
object N10characters1: TMenuItem
|
||||
Caption = '10 characters'
|
||||
OnClick = menuPasswordClick
|
||||
object menuDummy3: TMenuItem
|
||||
Caption = 'dummy'
|
||||
end
|
||||
end
|
||||
object N12characters1: TMenuItem
|
||||
Caption = '12 characters'
|
||||
OnClick = menuPasswordClick
|
||||
object menuDummy4: TMenuItem
|
||||
Caption = 'dummy'
|
||||
end
|
||||
end
|
||||
object N30characters1: TMenuItem
|
||||
Caption = '30 characters'
|
||||
OnClick = menuPasswordClick
|
||||
object menuDummy5: TMenuItem
|
||||
Caption = 'dummy'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
137
source/change_password.pas
Normal file
137
source/change_password.pas
Normal file
@@ -0,0 +1,137 @@
|
||||
unit change_password;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
|
||||
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, extra_controls, gnugettext,
|
||||
Vcl.Menus, Clipbrd;
|
||||
|
||||
type
|
||||
TfrmPasswordChange = class(TFormWithSizeGrip)
|
||||
lblHeading: TLabel;
|
||||
lblPassword: TLabel;
|
||||
lblRepeatPassword: TLabel;
|
||||
editPassword: TButtonedEdit;
|
||||
editRepeatPassword: TButtonedEdit;
|
||||
btnCancel: TButton;
|
||||
btnOK: TButton;
|
||||
lblStatus: TLabel;
|
||||
popupPassword: TPopupMenu;
|
||||
N6characters1: TMenuItem;
|
||||
N8characters1: TMenuItem;
|
||||
N10characters1: TMenuItem;
|
||||
N12characters1: TMenuItem;
|
||||
N30characters1: TMenuItem;
|
||||
menuDummy1: TMenuItem;
|
||||
menuDummy2: TMenuItem;
|
||||
menuDummy3: TMenuItem;
|
||||
menuDummy4: TMenuItem;
|
||||
menuDummy5: TMenuItem;
|
||||
btnCopyToClipboard: TButton;
|
||||
procedure editPasswordChange(Sender: TObject);
|
||||
procedure FormShow(Sender: TObject);
|
||||
procedure editPasswordKeyDown(Sender: TObject; var Key: Word;
|
||||
Shift: TShiftState);
|
||||
procedure menuPasswordClick(Sender: TObject);
|
||||
procedure menuPasswordInsert(Sender: TObject);
|
||||
procedure btnCopyToClipboardClick(Sender: TObject);
|
||||
private
|
||||
{ Private-Deklarationen }
|
||||
public
|
||||
{ Public-Deklarationen }
|
||||
end;
|
||||
|
||||
var
|
||||
frmPasswordChange: TfrmPasswordChange;
|
||||
|
||||
implementation
|
||||
|
||||
uses main, helpers;
|
||||
|
||||
{$R *.dfm}
|
||||
|
||||
|
||||
procedure TfrmPasswordChange.FormShow(Sender: TObject);
|
||||
begin
|
||||
// Manually trigger change event on password box
|
||||
editPassword.OnChange(Sender);
|
||||
end;
|
||||
|
||||
|
||||
procedure TfrmPasswordChange.menuPasswordClick(Sender: TObject);
|
||||
var
|
||||
Parent, Item: TMenuItem;
|
||||
PasswordLen, i: Integer;
|
||||
begin
|
||||
// Create menu items with random passwords
|
||||
Parent := Sender as TMenuItem;
|
||||
PasswordLen := MakeInt(Parent.Caption);
|
||||
for i:=0 to 19 do begin
|
||||
if Parent.Count > i then
|
||||
Item := Parent[i]
|
||||
else begin
|
||||
Item := TMenuItem.Create(Parent);
|
||||
Parent.Add(Item);
|
||||
end;
|
||||
Item.OnClick := menuPasswordInsert;
|
||||
Item.Caption := GeneratePassword(PasswordLen);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TfrmPasswordChange.menuPasswordInsert(Sender: TObject);
|
||||
var
|
||||
Item: TMenuItem;
|
||||
begin
|
||||
// Insert password from menu item
|
||||
Item := Sender as TMenuItem;
|
||||
editPassword.Text := Item.Caption;
|
||||
editRepeatPassword.Text := editPassword.Text;
|
||||
end;
|
||||
|
||||
|
||||
procedure TfrmPasswordChange.btnCopyToClipboardClick(Sender: TObject);
|
||||
var
|
||||
OldImageIndex: Integer;
|
||||
begin
|
||||
// Copy new password to clipboard
|
||||
Clipboard.AsText := editPassword.Text;
|
||||
OldImageIndex := btnCopyToClipboard.ImageIndex;
|
||||
btnCopyToClipboard.ImageIndex := 55;
|
||||
btnCopyToClipboard.Repaint;
|
||||
Sleep(500);
|
||||
btnCopyToClipboard.ImageIndex := OldImageIndex;
|
||||
end;
|
||||
|
||||
|
||||
procedure TfrmPasswordChange.editPasswordChange(Sender: TObject);
|
||||
begin
|
||||
// User has entered something on one or both password fields
|
||||
btnOK.Enabled := False;
|
||||
btnCopyToClipboard.Enabled := False;
|
||||
|
||||
if editPassword.Text = '' then begin
|
||||
editPassword.PasswordChar := #0;
|
||||
lblStatus.Caption := _('Please change your password')
|
||||
end else begin
|
||||
editPassword.PasswordChar := '*';
|
||||
if editPassword.Text <> editRepeatPassword.Text then
|
||||
lblStatus.Caption := _('Error: Passwords do not match!')
|
||||
else begin
|
||||
lblStatus.Caption := '';
|
||||
btnOK.Enabled := True;
|
||||
btnCopyToClipboard.Enabled := True;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TfrmPasswordChange.editPasswordKeyDown(Sender: TObject; var Key: Word;
|
||||
Shift: TShiftState);
|
||||
begin
|
||||
editPassword.OnChange(Sender);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
@@ -757,7 +757,7 @@ var
|
||||
|
||||
implementation
|
||||
|
||||
uses helpers, loginform;
|
||||
uses helpers, loginform, change_password;
|
||||
|
||||
|
||||
{ TProcessPipe }
|
||||
@@ -1614,6 +1614,7 @@ var
|
||||
sslca, sslkey, sslcert, sslcipher: PAnsiChar;
|
||||
PluginDir: AnsiString;
|
||||
Vars, Status: TDBQuery;
|
||||
PasswordChangeDialog: TfrmPasswordChange;
|
||||
begin
|
||||
if Value and (FHandle = nil) then begin
|
||||
DoBeforeConnect;
|
||||
@@ -1667,7 +1668,7 @@ begin
|
||||
end;
|
||||
|
||||
// Gather client options
|
||||
ClientFlags := CLIENT_LOCAL_FILES or CLIENT_INTERACTIVE or CLIENT_PROTOCOL_41 or CLIENT_MULTI_STATEMENTS;
|
||||
ClientFlags := CLIENT_LOCAL_FILES or CLIENT_INTERACTIVE or CLIENT_PROTOCOL_41 or CLIENT_MULTI_STATEMENTS or CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
|
||||
if Parameters.Compressed then
|
||||
ClientFlags := ClientFlags or CLIENT_COMPRESS;
|
||||
if Parameters.WantSSL then
|
||||
@@ -1708,6 +1709,28 @@ begin
|
||||
'* either to fix the "%s" variable,'+CRLF+
|
||||
'* or to grant you missing privileges.'),
|
||||
['init_connect', 'init_connect']);
|
||||
// Try to fire the very first query against the server, which probably run into the following error:
|
||||
// "Error 1820: You must SET PASSWORD before executing this statement"
|
||||
try
|
||||
ThreadId;
|
||||
except
|
||||
on E:EDatabaseError do begin
|
||||
if GetLastErrorCode = 1820 then begin
|
||||
PasswordChangeDialog := TfrmPasswordChange.Create(Self);
|
||||
PasswordChangeDialog.lblHeading.Caption := GetLastError;
|
||||
PasswordChangeDialog.ShowModal;
|
||||
if PasswordChangeDialog.ModalResult = mrOk then begin
|
||||
if ExecRegExpr('\sALTER USER\s', GetLastError) then
|
||||
Query('ALTER USER USER() IDENTIFIED BY '+EscapeString(PasswordChangeDialog.editPassword.Text))
|
||||
else
|
||||
Query('SET PASSWORD=PASSWORD('+EscapeString(PasswordChangeDialog.editPassword.Text)+')');
|
||||
end else // Dialog cancelled
|
||||
Raise;
|
||||
PasswordChangeDialog.Free;
|
||||
end else
|
||||
Raise;
|
||||
end;
|
||||
end;
|
||||
Log(lcInfo, f_('Connected. Thread-ID: %d', [ThreadId]));
|
||||
try
|
||||
CharacterSet := 'utf8mb4';
|
||||
|
||||
Reference in New Issue
Block a user