Commit a1b49c5d by Mac Stephens

Add rich text functionality to issue and notes, add hashed client password,…

Add rich text functionality to issue and notes, add hashed client password, remove params from url after manual login, update row focus, add insert row button
parent 7fc891cf
......@@ -4,7 +4,7 @@ interface
uses
SysUtils, Web, JS,
XData.Web.Client;
XData.Web.Client, WEBLib.Crypto;
const
TOKEN_NAME = 'EMT3_WEB_TOKEN';
......@@ -23,8 +23,8 @@ type
public
constructor Create; reintroduce;
destructor Destroy; override;
procedure Login(AUser, APassword, AClientVersion: string; ASuccess: TOnLoginSuccess; AError: TOnLoginError);
procedure WebLogin(AUser, ATaskId, APassword: string; ASuccess: TOnLoginSuccess; AError: TOnLoginError);
procedure Login(AUserId, ATaskId, AUrlCode: string; ASuccess: TOnLoginSuccess; AError: TOnLoginError);
[async] procedure WebLogin(AUserName, ATaskId, APassword: string; ASuccess: TOnLoginSuccess; AError: TOnLoginError);
procedure Logout;
function GetToken: string;
function Authenticated: Boolean;
......@@ -91,7 +91,7 @@ begin
Result := window.localStorage.getItem(TOKEN_NAME);
end;
procedure TAuthService.Login(AUser, APassword, AClientVersion: string; ASuccess: TOnLoginSuccess;
procedure TAuthService.Login(AUserId, ATaskId, AUrlCode: string; ASuccess: TOnLoginSuccess;
AError: TOnLoginError);
procedure OnLoad(Response: TXDataClientResponse);
......@@ -109,21 +109,24 @@ procedure TAuthService.Login(AUser, APassword, AClientVersion: string; ASuccess:
end;
begin
if (AUser = '') or (APassword = '') then
if (AUserId = '') or (ATaskId = '') or (AUrlCode = '') then
begin
AError('Invalid or expired code, please try again.');
Exit;
end;
FClient.RawInvoke(
'IAuthService.Login', [AUser, APassword, AClientVersion],
'IAuthService.Login', [AUserId, ATaskId, AUrlCode],
@OnLoad, @OnError
);
end;
procedure TAuthService.WebLogin(AUser, ATaskId, APassword: string; ASuccess: TOnLoginSuccess;
[async] procedure TAuthService.WebLogin(AUserName, ATaskId, APassword: string; ASuccess: TOnLoginSuccess;
AError: TOnLoginError);
var
sha: TWebSHAHash;
passwordHash: string;
procedure OnLoad(Response: TXDataClientResponse);
var
......@@ -140,14 +143,21 @@ procedure TAuthService.WebLogin(AUser, ATaskId, APassword: string; ASuccess: TOn
end;
begin
if (AUser = '') or (ATaskId = '') or (APassword = '') then
if (AUserName = '') or (ATaskId = '') or (APassword = '') then
begin
AError('Please enter user id, task id, and password.');
Exit;
end;
sha := TWebSHAHash.Create(ehSHA256);
try
passwordHash := Await(string, sha.Hash(UpperCase(Trim(APassword))));
finally
sha.Free;
end;
FClient.RawInvoke(
'IAuthService.WebLogin', [AUser, ATaskId, APassword],
'IAuthService.WebLogin', [AUserName, ATaskId, passwordHash],
@OnLoad, @OnError
);
end;
......@@ -156,6 +166,7 @@ end;
procedure TAuthService.Logout;
begin
DeleteToken;
DMConnection.currentTaskId := '';
end;
procedure TAuthService.SetToken(AToken: string);
......
......@@ -20,6 +20,7 @@ type
FUnauthorizedAccessProc: TUnauthorizedAccessProc;
public
currentTaskId: string;
const clientVersion = '0.8.8';
procedure InitApp(SuccessProc: TSuccessProc;
UnauthorizedAccessProc: TUnauthorizedAccessProc);
......
......@@ -49,18 +49,8 @@ uses
procedure TFViewLogin.btnLoginClick(Sender: TObject);
procedure LoginSuccess;
var
newUrl: string;
begin
newUrl := window.location.pathname +
'?user_id=' + edtUsername.Text +
'&task_id=' + edtTaskId.Text +
'&url_code=000000';
asm
window.history.replaceState({}, '', newUrl);
end;
DMConnection.currentTaskId := edtTaskId.Text;
FLoginProc;
end;
......@@ -97,10 +87,12 @@ class procedure TFViewLogin.Display(LoginProc: TSuccessProc; AUserId, ATaskId, A
end;
begin
console.log('TFViewLogin.Display start AUserId=' + AUserId + ' ATaskId=' + ATaskId + ' AMsg=' + AMsg);
if Assigned(FViewLogin) then
FViewLogin.Free;
FViewLogin := TFViewLogin.CreateNew(@FormCreate);
FViewLogin.FLoginProc := LoginProc;
console.log('TFViewLogin.Display end');
end;
procedure TFViewLogin.HideNotification;
......@@ -117,15 +109,15 @@ begin
end;
end;
procedure TFViewLogin.btnCloseNotificationClick(Sender: TObject);
begin
HideNotification;
end;
procedure TFViewLogin.WebFormCreate(Sender: TObject);
begin
edtUsername.Text := FUserId;
edtTaskId.Text := FTaskId;
console.log('TFViewLogin.WebFormCreate FUserId=' + FUserId + ' FTaskId=' + FTaskId + ' FMessage=' + FMessage);
console.log('TFViewLogin.WebFormCreate URL task_id=' + Application.Parameters.Values['task_id']);
edtUsername.Text := '';
edtTaskId.Text := Application.Parameters.Values['task_id'];
console.log('TFViewLogin.WebFormCreate after assign edtUsername=' + edtUsername.Text + ' edtTaskId=' + edtTaskId.Text);
if FMessage <> '' then
ShowNotification(FMessage)
......@@ -133,4 +125,9 @@ begin
HideNotification;
end;
procedure TFViewLogin.btnCloseNotificationClick(Sender: TObject);
begin
HideNotification;
end;
end.
......@@ -58,6 +58,62 @@ object FTaskItems: TFTaskItems
WidthPercent = 100.000000000000000000
OnClick = btnInsertRowClick
end
object btnTextColor: TWebButton
Left = 282
Top = 197
Width = 96
Height = 25
Caption = 'Text Color'
ChildOrder = 4
ElementID = 'btn_text_color'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnTextColorClick
end
object btnFmtBold: TWebButton
Left = 282
Top = 228
Width = 96
Height = 25
Caption = 'Bold'
ChildOrder = 4
ElementID = 'btn_fmt_bold'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnFmtBoldClick
end
object btnFmtItalic: TWebButton
Left = 282
Top = 259
Width = 96
Height = 25
Caption = 'Italic'
ChildOrder = 4
ElementID = 'btn_fmt_italic'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnFmtItalicClick
end
object btnFmtUnderline: TWebButton
Left = 282
Top = 290
Width = 96
Height = 25
Caption = 'Underline'
ChildOrder = 4
ElementID = 'btn_fmt_underline'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnFmtUnderlineClick
end
object xdwcTasks: TXDataWebClient
Connection = DMConnection.ApiConnection
Left = 506
......
<div class="container-fluid p-2 d-flex flex-column h-100 overflow-hidden">
<div class="d-flex align-items-center justify-content-between mb-2 flex-shrink-0">
<div class="d-flex align-items-center justify-content-between mb-2 flex-shrink-0 gap-3">
<h5 class="mb-0" id="lbl_project_name"></h5>
<div class="d-flex align-items-center gap-3">
<div id="lbl_total_rows"></div>
<div class="d-flex align-items-center gap-2 ms-auto flex-nowrap">
<div id="lbl_total_rows" class="me-2 text-nowrap"></div>
<div class="task-toolbar d-flex align-items-center gap-1 px-2 py-1">
<button id="btn_fmt_bold" type="button" class="btn btn-sm btn-light task-toolbar-btn" title="Bold">
<i class="fas fa-bold"></i>
</button>
<button id="btn_fmt_italic" type="button" class="btn btn-sm btn-light task-toolbar-btn" title="Italic">
<i class="fas fa-italic"></i>
</button>
<button id="btn_fmt_underline" type="button" class="btn btn-sm btn-light task-toolbar-btn" title="Underline">
<i class="fas fa-underline"></i>
</button>
<div class="vr mx-1"></div>
<button id="btn_text_color" type="button" class="btn btn-sm btn-light task-toolbar-btn task-color-btn" title="Text color">
<span class="task-color-glyph">A</span>
<span id="text_color_swatch" class="task-color-swatch"></span>
<input id="issue_notes_text_color" type="color" class="task-hidden-color-input" value="#000000">
</button>
</div>
<div class="d-flex gap-2">
<button id="btn_add_row" class="btn btn-sm btn-success">Add Row</button>
<button id="btn_delete_row" class="btn btn-sm btn-danger">Delete Row</button>
<button id="btn_insert_row" class="btn btn-sm btn-success">Insert Row</button>
<button id="btn_reload" class="btn btn-sm btn-primary">Reload</button>
</div>
<button id="btn_add_row" class="btn btn-sm btn-success text-nowrap">Add Row</button>
<button id="btn_insert_row" class="btn btn-sm btn-success text-nowrap">Insert Row</button>
<button id="btn_delete_row" class="btn btn-sm btn-danger text-nowrap">Delete Row</button>
<button id="btn_reload" class="btn btn-sm btn-primary text-nowrap">Reload</button>
</div>
</div>
......
......@@ -31,11 +31,19 @@ type
xdwdsTaskstaskItemId: TIntegerField;
btnDeleteRow: TWebButton;
btnInsertRow: TWebButton;
btnTextColor: TWebButton;
btnFmtBold: TWebButton;
btnFmtItalic: TWebButton;
btnFmtUnderline: TWebButton;
[async] procedure btnAddRowClick(Sender: TObject);
procedure btnReloadClick(Sender: TObject);
procedure WebFormCreate(Sender: TObject);
[async] procedure btnDeleteRowClick(Sender: TObject);
[async] procedure btnInsertRowClick(Sender: TObject);
procedure btnTextColorClick(Sender: TObject);
procedure btnFmtBoldClick(Sender: TObject);
procedure btnFmtItalicClick(Sender: TObject);
procedure btnFmtUnderlineClick(Sender: TObject);
private
FTaskId: string;
FApplicationOptions: TJSArray;
......@@ -50,6 +58,8 @@ type
FPendingScrollLeft: Integer;
FPendingFocusItemNum: Integer;
FPendingFocusTaskField: string;
FActiveRichEditorId: string;
FLastSelectionRange: JSValue;
FNameManager: TNameManager;
[async] procedure LoadTasks(const ATaskId: string);
procedure RenderTable;
......@@ -90,6 +100,12 @@ type
[async] procedure HandleAddManagedName(const AFieldName: string; const ARowIndex: Integer; const ANewName: string);
[async] procedure HandleRenameManagedName(const AFieldName, AOldName, ANewName: string);
[async] procedure HandleDeleteManagedName(const AFieldName, AName: string);
procedure ColorPickerInput(Event: TJSEvent);
procedure RichEditorFocus(Event: TJSEvent);
procedure RichEditorSelectionChange(Event: TJSEvent);
procedure ApplyRichTextCommand(const commandName: string; const commandValue: string = '');
procedure UpdateToolbarState;
procedure SetToolbarButtonActive(const buttonId: string; const isActive: Boolean);
public
end;
......@@ -107,7 +123,11 @@ uses
procedure TFTaskItems.WebFormCreate(Sender: TObject);
begin
console.log('TFTaskItems.WebFormCreate fired');
FTaskId := Application.Parameters.Values['task_id'];
if FTaskId = '' then
FTaskId := DMConnection.currentTaskId;
FApplicationOptions := TJSArray.new;
FReportedByOptions := TJSArray.new;
FAssignedToOptions := TJSArray.new;
......@@ -214,7 +234,10 @@ begin
if xdwdsTasks.Eof then
Exit;
newVal := string(TJSObject(el)['value']);
if SameText(fieldName, 'issue') or SameText(fieldName, 'notes') then
newVal := string(el.innerHTML)
else
newVal := string(TJSObject(el)['value']);
console.log('EditorInput: idx=' + IntToStr(idx) + ' field=' + fieldName + ' val=' + newVal);
......@@ -280,6 +303,13 @@ begin
el.addEventListener('input', TJSEventHandler(@EditorInput));
el.addEventListener('blur', TJSEventHandler(@EditorBlur));
el.addEventListener('keydown', TJSEventHandler(@EditorKeyDown));
if el.classList.contains('task-rich-editor') then
begin
el.addEventListener('focus', TJSEventHandler(@RichEditorFocus));
el.addEventListener('mouseup', TJSEventHandler(@RichEditorSelectionChange));
el.addEventListener('keyup', TJSEventHandler(@RichEditorSelectionChange));
end;
end;
nodes := document.querySelectorAll('.task-select');
......@@ -311,6 +341,10 @@ begin
el.addEventListener('click', TJSEventHandler(@RowClick));
end;
el := TJSHTMLElement(document.getElementById('issue_notes_text_color'));
if Assigned(el) then
el.addEventListener('input', TJSEventHandler(@ColorPickerInput));
ApplySelectedRowState;
end;
......@@ -402,6 +436,21 @@ begin
end;
end;
procedure TFTaskItems.btnFmtBoldClick(Sender: TObject);
begin
ApplyRichTextCommand('bold');
end;
procedure TFTaskItems.btnFmtItalicClick(Sender: TObject);
begin
ApplyRichTextCommand('italic');
end;
procedure TFTaskItems.btnFmtUnderlineClick(Sender: TObject);
begin
ApplyRichTextCommand('underline');
end;
[async] procedure TFTaskItems.btnInsertRowClick(Sender: TObject);
begin
Utils.ShowSpinner('spinner');
......@@ -555,16 +604,29 @@ begin
end;
procedure TFTaskItems.btnTextColorClick(Sender: TObject);
var
colorEl: TJSHTMLElement;
begin
colorEl := TJSHTMLElement(document.getElementById('issue_notes_text_color'));
colorEl.click;
end;
procedure TFTaskItems.EnableAutoGrowTextAreas;
begin
asm
(function(){
const host = document.getElementById('tasks_table_host');
if(!host) return;
host.querySelectorAll('textarea.cell-textarea').forEach(ta => {
const fit = () => { ta.style.height = 'auto'; ta.style.height = ta.scrollHeight + 'px'; };
host.querySelectorAll('.task-rich-editor').forEach(function(editor){
const fit = function() {
editor.style.height = 'auto';
editor.style.height = editor.scrollHeight + 'px';
};
fit();
ta.addEventListener('input', fit);
editor.addEventListener('input', fit);
});
})();
end;
......@@ -693,15 +755,6 @@ var
'value="' + HtmlEncode(Value) + '"' + w + '>';
end;
function TextArea(const FieldName, Value: string; const AIdx: Integer): string;
begin
Result :=
'<textarea class="form-control form-control-sm cell-textarea task-editor w-100" ' +
'style="height:31px; min-height:31px; overflow:hidden; resize:none;" ' +
'data-idx="' + IntToStr(AIdx) + '" data-field="' + FieldName + '" ' +
'rows="1">' + HtmlEncode(Value) + '</textarea>';
end;
function ItemNumInput(const Value: Integer; const AIdx: Integer): string;
begin
Result :=
......@@ -786,6 +839,21 @@ var
'</div>';
end;
function RichTextEditor(const FieldName, Value: string; const AIdx: Integer): string;
var
editorId: string;
begin
editorId := 'rtf_' + FieldName + '_' + IntToStr(AIdx);
Result :=
'<div id="' + editorId + '" ' +
'class="form-control form-control-sm task-rich-editor task-editor w-100" ' +
'contenteditable="true" ' +
'data-idx="' + IntToStr(AIdx) + '" ' +
'data-field="' + FieldName + '" ' +
'style="min-height:31px; overflow:hidden;">' + Value + '</div>';
end;
begin
host := TJSHTMLElement(document.getElementById('tasks_table_host'));
if not Assigned(host) then
......@@ -837,8 +905,8 @@ begin
TdNowrap(SelectList('status', xdwdsTasksstatus.AsString, rowIdx, FStatusOptions, False)) +
TdNowrap(DateInput('statusDate', xdwdsTasksstatusDate.AsString, rowIdx)) +
TdNowrap(TextInput('formSection', xdwdsTasksformSection.AsString, rowIdx)) +
TdWrap(TextArea('issue', xdwdsTasksissue.AsString, rowIdx)) +
TdWrap(TextArea('notes', xdwdsTasksnotes.AsString, rowIdx)) +
TdWrap(RichTextEditor('issue', xdwdsTasksissue.AsString, rowIdx)) +
TdWrap(RichTextEditor('notes', xdwdsTasksnotes.AsString, rowIdx)) +
'</tr>';
xdwdsTasks.Next;
......@@ -1580,6 +1648,118 @@ begin
end;
procedure TFTaskItems.ColorPickerInput(Event: TJSEvent);
var
pickerEl: TJSHTMLElement;
colorValue: string;
swatchEl: TJSHTMLElement;
begin
pickerEl := TJSHTMLElement(Event.target);
colorValue := string(TJSObject(pickerEl)['value']);
swatchEl := TJSHTMLElement(document.getElementById('text_color_swatch'));
swatchEl.style.setProperty('background', colorValue);
ApplyRichTextCommand('foreColor', colorValue);
end;
procedure TFTaskItems.RichEditorFocus(Event: TJSEvent);
var
el: TJSHTMLElement;
begin
el := TJSHTMLElement(Event.target);
FActiveRichEditorId := string(el.id);
UpdateToolbarState;
end;
procedure TFTaskItems.RichEditorSelectionChange(Event: TJSEvent);
begin
asm
const sel = window.getSelection();
if (!sel || sel.rangeCount === 0) return;
const range = sel.getRangeAt(0);
const editor = document.getElementById(this.FActiveRichEditorId);
if (!editor) return;
const container = range.commonAncestorContainer;
if (editor.contains(container.nodeType === 1 ? container : container.parentNode)) {
this.FLastSelectionRange = range.cloneRange();
}
end;
UpdateToolbarState;
end;
procedure TFTaskItems.ApplyRichTextCommand(const commandName: string; const commandValue: string = '');
var
editorEl: TJSHTMLElement;
begin
if not xdwdsTasks.Active then
Exit;
if FActiveRichEditorId = '' then
Exit;
editorEl := TJSHTMLElement(document.getElementById(FActiveRichEditorId));
if not Assigned(editorEl) then
Exit;
asm
const sel = window.getSelection();
if (!sel) return;
editorEl.focus();
if (this.FLastSelectionRange) {
sel.removeAllRanges();
sel.addRange(this.FLastSelectionRange);
}
if (commandName === 'foreColor')
document.execCommand('styleWithCSS', false, true)
else
document.execCommand('styleWithCSS', false, false);
if (commandValue)
document.execCommand(commandName, false, commandValue);
else
document.execCommand(commandName, false, null);
if (sel.rangeCount > 0) {
this.FLastSelectionRange = sel.getRangeAt(0).cloneRange();
}
editorEl.setAttribute('data-unsaved-data', '1');
editorEl.dispatchEvent(new Event('input', { bubbles: true }));
end;
UpdateToolbarState;
end;
procedure TFTaskItems.SetToolbarButtonActive(const buttonId: string; const isActive: Boolean);
var
el: TJSHTMLElement;
begin
el := TJSHTMLElement(document.getElementById(buttonId));
if isActive then
el.classList.add('active')
else
el.classList.remove('active');
el.setAttribute('aria-pressed', BoolToStr(isActive, True).ToLower);
end;
procedure TFTaskItems.UpdateToolbarState;
begin
asm
this.SetToolbarButtonActive('btn_fmt_bold', document.queryCommandState('bold'));
this.SetToolbarButtonActive('btn_fmt_italic', document.queryCommandState('italic'));
this.SetToolbarButtonActive('btn_fmt_underline', document.queryCommandState('underline'));
end;
end;
end.
......
......@@ -118,6 +118,65 @@ span.card {
z-index: 1055;
}
.task-toolbar {
border: 1px solid var(--bs-border-color);
border-radius: 0.375rem;
background: var(--bs-body-bg);
white-space: nowrap;
}
.task-toolbar-btn {
min-width: 2rem;
padding: 0.25rem 0.5rem;
}
#lbl_total_rows {
white-space: nowrap;
}
.task-rich-editor {
white-space: pre-wrap;
overflow-wrap: anywhere;
height: auto;
}
.task-rich-editor:focus {
outline: 0;
}
.task-color-btn {
position: relative;
min-width: 2rem;
height: 2rem;
padding: 0.2rem 0.45rem;
}
.task-hidden-color-input {
position: absolute;
inset: 0;
opacity: 0;
pointer-events: none;
width: 1px;
height: 1px;
border: 0;
}
.task-color-glyph {
display: inline-block;
font-weight: 600;
line-height: 1;
}
.task-color-swatch {
position: absolute;
left: 0.35rem;
right: 0.35rem;
bottom: 0.2rem;
height: 0.18rem;
border-radius: 999px;
background: #000000;
}
.task-dd-toggle.status-cannot-duplicate {
--bs-btn-color: #41464b;
......
......@@ -82,13 +82,16 @@ begin
codeParam := Application.Parameters.Values['url_code'];
if (userIdParam = '') or (taskIdParam = '') or (codeParam = '') then
DisplayLoginView(userIdParam, taskIdParam)
begin
DisplayLoginView(userIdParam, taskIdParam);
end
else
begin
AuthService.Logout;
DMConnection.ApiConnection.Connected := False;
if Assigned(FViewMain) then
FViewMain.Free;
DMConnection.currentTaskId := taskIdParam;
Login(userIdParam, taskIdParam, codeParam);
end;
end;
......
......@@ -99,9 +99,10 @@
<VerInfo_MajorVer>0</VerInfo_MajorVer>
<VerInfo_MinorVer>8</VerInfo_MinorVer>
<VerInfo_Release>8</VerInfo_Release>
<TMSWebBrowser>5</TMSWebBrowser>
<TMSUseJSDebugger>2</TMSUseJSDebugger>
<TMSURLParams>?user_id=1019&amp;task_id=4045&amp;url_code=123456</TMSURLParams>
<TMSWebBrowser>1</TMSWebBrowser>
<TMSWebSingleInstance>1</TMSWebSingleInstance>
<TMSUseJSDebugger>2</TMSUseJSDebugger>
<TMSWebOutputPath>..\emT3XDataServer\bin\static</TMSWebOutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2)'!=''">
......
......@@ -104,7 +104,6 @@ object AuthDatabase: TAuthDatabase
Database = 'eTask'
Username = 'root'
Server = '192.168.102.131'
Connected = True
LoginPrompt = False
Left = 71
Top = 133
......@@ -128,12 +127,12 @@ object AuthDatabase: TAuthDatabase
' u.PERSPECTIVE_ID,'
' u.LAST_NAME,'
' u.FIRST_NAME,'
' u.PASSWORD,'
' w.WEB_LOGIN'
'from web_tasks_url w'
'join users u on u.USER_ID = w.USER_ID'
'where u.USER_NAME = :USER_NAME'
' and w.TASK_ID = :TASK_ID'
' and u.PASSWORD = :PASSWORD'
' and w.URL_CODE = :URL_CODE')
Left = 194
Top = 44
......@@ -150,11 +149,6 @@ object AuthDatabase: TAuthDatabase
end
item
DataType = ftUnknown
Name = 'PASSWORD'
Value = nil
end
item
DataType = ftUnknown
Name = 'URL_CODE'
Value = nil
end>
......@@ -198,6 +192,10 @@ object AuthDatabase: TAuthDatabase
FieldName = 'FIRST_NAME'
Size = 25
end
object uqWebLoginPASSWORD: TStringField
FieldName = 'PASSWORD'
Size = 12
end
object uqWebLoginWEB_LOGIN: TStringField
FieldName = 'WEB_LOGIN'
ReadOnly = True
......
......@@ -36,6 +36,7 @@ type
uqWebLoginPERSPECTIVE_ID: TStringField;
uqWebLoginLAST_NAME: TStringField;
uqWebLoginFIRST_NAME: TStringField;
uqWebLoginPASSWORD: TStringField;
uqWebLoginWEB_LOGIN: TStringField;
procedure DataModuleCreate(Sender: TObject);
procedure DataModuleDestroy(Sender: TObject);
......
......@@ -31,6 +31,7 @@ type
function CheckUrlLogin(userId, taskId, urlCode: string): Integer;
function CheckUserLogin(userName, taskId, password: string): Integer;
function HashPassword(const APassword: string): string;
procedure LoadUserFromUrlLoginQuery;
procedure LoadUserFromWebLoginQuery;
public
......@@ -298,16 +299,22 @@ begin
end;
function TAuthService.HashPassword(const APassword: string): string;
begin
Result := LowerCase(THashSHA2.GetHashString(UpperCase(Trim(APassword))));
end;
function TAuthService.CheckUserLogin(userName, taskId, password: string): Integer;
var
webLogin: string;
storedPasswordHash: string;
begin
Logger.Log(3, 'TAuthService.CheckUserLogin(const userId, taskId, password: string): Integer');
Logger.Log(3, 'TAuthService.CheckUserLogin(userName, taskId, password: string): Integer');
authDB.uqWebLogin.Close;
authDB.uqWebLogin.ParamByName('USER_NAME').AsString := userName;
authDB.uqWebLogin.ParamByName('TASK_ID').AsString := taskId;
authDB.uqWebLogin.ParamByName('PASSWORD').AsString := password;
authDB.uqWebLogin.ParamByName('URL_CODE').AsString := '000000';
authDB.uqWebLogin.Open;
......@@ -318,6 +325,14 @@ begin
Exit;
end;
storedPasswordHash := HashPassword(authDB.uqWebLogin.FieldByName('PASSWORD').AsString);
if storedPasswordHash <> LowerCase(Trim(password)) then
begin
Logger.Log(3, '--Web Login failed 0: password hash mismatch');
Result := 0;
Exit;
end;
if authDB.uqWebLoginSTATUS.AsString <> 'ACTIVE' then
begin
Logger.Log(3, '--Web Login failed 1: authDB.uqWebLoginSTATUS.AsString <> ACTIVE');
......
......@@ -2,7 +2,7 @@
MemoLogLevel=4
FileLogLevel=4
webClientVersion=0.8.8
LogFileNum=174
LogFileNum=187
[Database]
Server=192.168.102.131
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment