// Implementation of Auth Serice that will eventually retrieve login information
// from the auth database.

unit Auth.ServiceImpl;

interface

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

type
  [ServiceImplementation]
  TAuthService = class(TInterfacedObject, IAuthService)
  strict private
    authDB: TAuthDatabase;
    function GetQuery: TUniQuery;
  private
    userName: string;
    userFullName: string;
    userId: string;
    userAdmin: boolean;
    userPhone: string;
    userEmail: string;
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    property Query: TUniQuery read GetQuery;
    function CheckUser(const user, password: string): Integer;
  public
    function Login(const user, password: string): string;
    function VerifyVersion(version: string): string;
  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;
  authDB := TAuthDatabase.Create(nil);
end;

procedure TAuthService.BeforeDestruction;
begin
  authDB.Free;
  inherited;
end;

function TAuthService.GetQuery: TUniQuery;
begin
  Result := authDB.uq;
end;

function TAuthService.VerifyVersion(version: string): string;
begin
  if( version <> '1.0.0' ) then
  begin
    Logger.Log( 2, 'TLoginService.GetAgenciesConfigList - Error: wrong ver!' );
    result := 'Error - You have the wrong version! Please clear your cache and refresh!';
    Exit;
  end;
  result := '';
end;

function TAuthService.Login(const user, password: string): string;
// Login verification: currently checks if logins are the same or if the user
// is admin, checks for admin password. Eventually will do a database lookup
// instead. If the user has a JWT token they do not need to login.
// Potential error occuring when logging in after you have already logged in
// webcharms.
var
  userState: Integer;
  JWT: TJWT;
begin
  Logger.Log(1, Format( 'AuthService.Login - User: "%s"', [User]));
  userState := CheckUser( user, password );

  if userState = 0 then
    raise EXDataHttpUnauthorized.Create('Invalid username or password');
  if userState = 1 then
    raise EXDataHttpUnauthorized.Create('User does not exist!');
  if userState = 2 then
    raise EXDataHttpUnauthorized.Create('User not active!');

  JWT := TJWT.Create;
  try
    JWT.Claims.JWTId := LowerCase(Copy(TUtils.GuidToVariant(TUtils.NewGuid), 2, 36));
    JWT.Claims.IssuedAt := Now;
    JWT.Claims.Expiration := IncHour(Now, 24);
    JWT.Claims.SetClaimOfType<string>('user_name', userName);
    JWT.Claims.SetClaimOfType<string>('user_fullname', userFullName);
    JWT.Claims.SetClaimOfType<string>('user_id', userId);
    JWT.Claims.SetClaimOfType<boolean>('user_admin', userAdmin);
    JWT.Claims.SetClaimOfType<string>('user_phone', userPhone);
    JWT.Claims.SetClaimOfType<string>('user_email', userEmail);
    Result := TJOSE.SHA256CompactToken(serverConfig.jwtTokenSecret, JWT);
  finally
    JWT.Free;
  end;
end;

function TAuthService.CheckUser(const user, password: string): Integer;
var
  userStr: string;
  SQL: string;
  date_created: string;
  checkString: string;
begin
  //authDB := TAuthDatabase.Create(nil);
  Result := 0;
  //Logger.Log( 3, Format('AuthService.CheckUser - User: "%s"', [user]) );
  SQL := 'select * from users where USER_NAME = ' + QuotedStr(user);
  DoQuery(authDB.uq, SQL);
  if authDB.uq.IsEmpty then
  begin
      Result := 1;   //user does not exist, replace this with 0 for more security
  end
  else if ( authDB.uq.FieldByName('STATUS').AsString <> 'ACTIVE' ) then
    Result := 2 // user is not active
  else
  begin
    //date_created := authDB.uq.FieldByName('date_created').AsString;
    //checkString := THashSHA2.GetHashString(date_created + password, THashSHA2.TSHA2Version.SHA512).ToUpper;
    if password = authDB.uq.FieldByName('PASSWORD').AsString then
    begin
      {userName := user;
      userFullName:= authDB.uq.FieldByName('full_name').AsString;;
      userId := authDB.uq.FieldByName('user_id').AsString;
      userAdmin := authDB.uq.FieldByName('admin').AsBoolean;
      userPhone := authDB.uq.FieldByName('phone_number').AsString;
      userEmail := authDB.uq.FieldByName('email').AsString;

      userStr := '?username=' + userName;
      userStr := userStr + '&fullname=' + userFullName;
      userStr := userStr + '&userid=' + userId;
      userStr := userStr + '&useradmin=' + BoolToStr(userAdmin);}
      //Logger.Log( 3, Format('AuthDB.SetLoginAuditEntry: "%s"', [user]) );
      //AuthDB.SetLoginAuditEntry( userStr );
      Result := 3; // Succcess
    end
    else
      Result := 0; // invalid password
  end;
end;

initialization
  RegisterServiceType(TAuthService);
end.
