unit Auth.ServiceImpl;

interface

uses
  XData.Service.Common,
  XData.Server.Module,
  Auth.Service,
  Auth.Database,
  Uni, Data.DB, System.Hash, System.IniFiles, System.JSON;

type
  [ServiceImplementation]
  TAuthService = class(TInterfacedObject, IAuthService)
  strict private
    authDB: TAuthDatabase;
  private
  public
    [HttpPost]function Login(const computer_uid, username, password_hash: string): string;
    function VerifyVersion(ClientVersion: string): TJSONObject;
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
  end;

implementation

uses
  System.SysUtils,
  System.DateUtils,
  System.Generics.Collections,
  Bcl.JOSE.Core.Builder,
  Bcl.JOSE.Core.JWT,
  Aurelius.Global.Utils,
  XData.Sys.Exceptions,
  Common.Logging,
  Common.Config,
  uLibrary;

{ TLoginService }

procedure TAuthService.AfterConstruction;
begin
  inherited;
  try
    authDB := TAuthDatabase.Create(nil);
    Logger.Log(5, 'TAuthService.AfterConstruction - authDB = TAuthDatabase.Create(nil)');
  except
    on E: Exception do
    begin
      Logger.Log(1, 'Error connecting to HSTManager database: ' + E.Message);
      raise EXDataHttpException.Create(500, 'Error connecting to HSTManager database!');
    end;
  end;
end;


procedure TAuthService.BeforeDestruction;
begin
  authDB.Free;
  inherited;
  Logger.Log(5, 'TAuthService.BeforeDestruction - authDB.Free');
end;


function TAuthService.VerifyVersion(clientVersion: string): TJSONObject;
var
  iniFile: TIniFile;
  reqClientVersion: string;
begin
  Result := TJSONObject.Create;
  TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);

  iniFile := TIniFile.Create(ChangeFileExt(ParamStr(0), '.ini'));
  try
    reqClientVersion := iniFile.ReadString('Settings', 'reqClientVersion', '');
    Result.AddPair('reqClientVersion', reqClientVersion);

    if reqClientVersion = '' then
    begin
      Result.AddPair('error', 'reqClientVersion is not configured.');
      Exit;
    end;
    if clientVersion <> reqClientVersion then
    begin
      Result.AddPair('error',
        'Version mismatch' + sLineBreak + '  Client version: ' + clientVersion  +
         sLineBreak + '  Required version: ' + reqClientVersion +
         sLineBreak + 'Please contact system administrator to get the correct version.');
    end;
  finally
    iniFile.Free;
  end;
end;


function TAuthService.Login(const computer_uid, username, password_hash: string): string;
var
  sql, resultStr: string;
  JWT: TJWT;
begin
  Logger.Log(3, Format('AuthService.Login - User: "%s"', [username]));

  sql := 'select * from phone_home where computer_uid = ' + QuotedStr(computer_uid);
  Logger.Log(5, 'TAuthService.Login - sql: ' + sql);
  doQuery( authDB.uq, sql );
  if not authDB.uq.IsEmpty then
  begin
    sql := 'select * from users where user_name = ' + QuotedStr(username) + ' and user_password = ' + QuotedStr(password_hash);
    Logger.Log(5, 'TAuthService.Login - sql: ' + sql);
    doQuery(authDB.uqUser, sql);
    if not authDB.uqUser.IsEmpty then
    begin
      if authDB.uqUseractive.AsInteger = 1 then
      begin
        logger.Log(3, '--login successful');
        resultStr := 'success';
      end
      else
      begin
        logger.Log(3, '--login failed: inactive user');
        resultStr := 'inactive user';
      end;
    end
    else
    begin
      logger.Log(3, '--login failed: invalid user / password');
      resultStr := 'invalid user / password';
    end;
  end
  else
  begin
    logger.Log(3, '--login failed: invalid computer');
    resultStr := 'invalid computer';
  end;

  if resultStr <> 'success' then
  begin
    Logger.Log( 3, 'raise Exception.Create(resultStr)' );
    raise Exception.Create(resultStr);
  end;

  JWT := TJWT.Create;
  try
    JWT.Claims.JWTId := LowerCase(Copy(TUtils.GuidToVariant(TUtils.NewGuid), 2, 36));
    JWT.Claims.SetClaimOfType<string>('USER_ID', authDB.uqUseruser_id.AsString);
    JWT.Claims.IssuedAt := Now;
    JWT.Claims.Expiration := IncHour(Now, 24);
    JWT.Claims.SetClaimOfType<string>('USER_NAME', username);

    Result := TJOSE.SHA256CompactToken( ServerConfig.jwtTokenSecret, JWT );
    Logger.Log(5, 'Returning JWT Token:' + Result)
  finally
    JWT.Free;
  end;

  Logger.Log(3, 'AuthService.Login - Finished');
  Logger.Log(3, '');
end;


initialization
  RegisterServiceType(TAuthService);
end.
