AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

TRestClient digest Authentication?

Ein Thema von taveuni · begonnen am 23. Sep 2021 · letzter Beitrag vom 30. Sep 2021
Antwort Antwort
taveuni

Registriert seit: 3. Apr 2007
Ort: Zürich
526 Beiträge
 
Delphi 11 Alexandria
 
#1

TRestClient digest Authentication?

  Alt 23. Sep 2021, 07:23
Delphi-Version: 5
Hallo zusammen,
Hat schon mal jemand versucht mit den Delphi Komponenten (TRestclient, TRestrequest) einen Rest PUT mit Digest Authentication zu machen? Out of the Box steht diese Authentifizierung nicht zur Verfügung? Auch im allwissenden Internet habe ich nichts gefunden. Ich möchte vermeiden dies mit Indy oder ähnlichem zu lösen.

P.S.: Weshalb steht oben im Post Delphi-Version 5? Ich habe 10.4.2
Die obige Aussage repräsentiert meine persönliche Meinung.
Diese erhebt keinen Anspruch auf Objektivität oder Richtigkeit.
  Mit Zitat antworten Zitat
taveuni

Registriert seit: 3. Apr 2007
Ort: Zürich
526 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: TRestClient digest Authentication?

  Alt 28. Sep 2021, 08:39
Ich erlaube mir dies noch einmal nach oben zu holen. Ist noch niemand auf dieses Problem gestossen (und hat sogar eine Lösung dazu)?
Die obige Aussage repräsentiert meine persönliche Meinung.
Diese erhebt keinen Anspruch auf Objektivität oder Richtigkeit.
  Mit Zitat antworten Zitat
Bbommel

Registriert seit: 27. Jun 2007
Ort: Köln
650 Beiträge
 
Delphi 12 Athens
 
#3

AW: TRestClient digest Authentication?

  Alt 28. Sep 2021, 11:08
Um wenigstens mal zu antworten, wenn auch leider nicht besonders hilfreich : nein, das Problem hatte ich bisher tatsächlich noch nicht. Entweder ganz profane Baisc-Authentication oder direkt OAuth/OAuth2, also mit einem Baraer Token. Beides wird von Delphi ja unterstützt (wobei man bei OAuth2 meistens noch ein bisschen nacharbeiten muss, aber die Grundlagen sind da). Digest hatte ich bisher nicht.
  Mit Zitat antworten Zitat
Benutzerbild von Union
Union

Registriert seit: 18. Mär 2004
Ort: Luxembourg
3.487 Beiträge
 
Delphi 7 Enterprise
 
#4

AW: TRestClient digest Authentication?

  Alt 28. Sep 2021, 17:28
Bekommst Du denn eine 401 response vom Server? Befindet der sich im Intranet oder extern?
Ibi fas ubi proxima merces
sudo /Developer/Library/uninstall-devtools --mode=all
  Mit Zitat antworten Zitat
taveuni

Registriert seit: 3. Apr 2007
Ort: Zürich
526 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: TRestClient digest Authentication?

  Alt 29. Sep 2021, 06:21
Ja ich erhalte 401 mit allen in Delphi vorhandenen Authentifikationen. Es handelt sich um HikVision Peoplecounter Kameras welche alle im Intranet sind. Mit einem Overbyte ICS Http Client - Authentication auf Digest funktioniert es problemlos.
Die obige Aussage repräsentiert meine persönliche Meinung.
Diese erhebt keinen Anspruch auf Objektivität oder Richtigkeit.
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.006 Beiträge
 
Delphi 12 Athens
 
#6

AW: TRestClient digest Authentication?

  Alt 29. Sep 2021, 08:50
In der Regel wird das Standardangebot an REST-Authentifizierungen in neuen Versionen schrittweise erweitert. Mit einem entsprechenden Feature-Request in QP hat man vermutlich die besten Aussichten. Bis dahin kann man das aber auch wohl selbst implementieren.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von Union
Union

Registriert seit: 18. Mär 2004
Ort: Luxembourg
3.487 Beiträge
 
Delphi 7 Enterprise
 
#7

AW: TRestClient digest Authentication?

  Alt 29. Sep 2021, 22:13
Das hat mich jetzt nicht losgelassen. Ich habe etwas bei Indy abgekupfert.
Delphi-Quellcode:
unit REST.Authenticator.Digest;

interface

uses
  System.Classes,
  Data.Bind.ObjectScope,
  REST.Client;

type
  TSubDigestAuthenticationBindSource = class;

  TDigestAuthenticator = class(TCustomAuthenticator)
  protected
    FBindSource: TSubDigestAuthenticationBindSource;
    function CreateBindSource: TBaseObjectBindSource; override;
  private
    FPassword: string;
    FUsername: string;
    FNonceCount: integer;
    FQopOptions : TStringList;
    FRealm : string;
    FNonce : string;
    FOpaque : string;
    FQop : string;
    FMethod : string;
    FAlgorithm : string;
    FUri : string;
    procedure ReadHeader(ARequest : TCustomRESTRequest);
  protected
    procedure SetPassword(const AValue: string);
    procedure SetUsername(const AValue: string);
    procedure DoAuthenticate(ARequest: TCustomRESTRequest); override;
  public
    constructor Create(const AUsername, APassword: string); reintroduce; overload;
    constructor Create(AOwner: TComponent); overload; override;
    destructor Destroy; override;
  published
    property Username: string read FUsername write SetUsername;
    property Password: string read FPassword write SetPassword;
  end;

  TSubDigestAuthenticationBindSource = class(TRESTAuthenticatorBindSource<TDigestAuthenticator>)
  protected
    function CreateAdapterT: TRESTAuthenticatorAdapter<TDigestAuthenticator>; override;
  end;

  TDigestAuthenticatorAdapter = class(TRESTAuthenticatorAdapter<TDigestAuthenticator>)
  protected
    procedure AddFields; override;
  end;

implementation

uses
  System.Sysutils,
  System.Hash,
  Data.Bind.Components,
  REST.Consts,
  REST.Types;


{ TDigestAuthenticator }

constructor TDigestAuthenticator.Create(const AUsername, APassword: string);
begin
  Create(NIL);

  FUsername := AUsername;
  FPassword := APassword;
end;

constructor TDigestAuthenticator.Create(AOwner: TComponent);
begin
  inherited;
  FNonceCount := 1;
  FQopOptions := TStringList.Create;
end;

function TDigestAuthenticator.CreateBindSource: TBaseObjectBindSource;
begin
  FBindSource := TSubDigestAuthenticationBindSource.Create(Self);
  FBindSource.Name := 'BindSource'; { Do not localize }
  FBindSource.SetSubComponent(True);
  FBindSource.Authenticator := Self;

  result := FBindSource;
end;

destructor TDigestAuthenticator.Destroy;
begin
  FQopOptions.Free;
  inherited;
end;

procedure TDigestAuthenticator.DoAuthenticate(ARequest: TCustomRESTRequest);

function Hash(const AString : string) : string;
begin
  Result := THashMD5.GetHashString(AString);
end;

var
  LCNonce : string;
  LA1 : string;
  LA2 : string;
  LResponse : string;
  LResult : string;
begin
  if ARequest.Response.StatusCode <> 401 then Exit;
  
  ReadHeader(ARequest);
  LCNonce := Hash(DateTimeToStr(Now));

  LA1 := Username + ':' + FRealm + ':' + Password; {do not localize}
  LA2 := FMethod + ':' + FUri; {do not localize}
  LResponse := IntToHex(FNonceCount, 8) + ':' + LCNonce + ':' + FQop + ':'; {do not localize}
  LResponse := Hash( Hash(LA1) + ':' + FNonce + ':' + LResponse + Hash(LA2) ); {do not localize}

  LResult := 'Digest ' + {do not localize}
    'username="' + Username + '", ' + {do not localize}
    'realm="' + FRealm + '", ' + {do not localize}
    'nonce="' + FNonce + '", ' + {do not localize}
    'algorithm="' + FAlgorithm + '", ' + {do not localize}
    'uri="' + FUri + '", ';

  LResult := LResult +
    'qop="' + FQop + '", ' + {do not localize}
    'nc=' + IntToHex(FNonceCount, 8) + ', ' + {do not localize}
    'cnonce="' + LCNonce + '", '; {do not localize}

  LResult := LResult + 'response="' + LResponse + '"'; {do not localize}

  if FOpaque <> 'then begin
    LResult := LResult + ', opaque="' + FOpaque + '"'; {do not localize}
  end;

  Inc(FNonceCount);
  ARequest.AddAuthParameter(HTTP_HEADERFIELD_AUTH, LResult, TRESTRequestParameterKind.pkHTTPHEADER,
  [TRESTRequestParameterOption.poDoNotEncode]);
end;

procedure TDigestAuthenticator.ReadHeader(ARequest : TCustomRESTRequest);
  function Unquote(S: String): String;
  var
    I, Len: Integer;
  begin
    Result := S;
    Len := Length(Result);
    I := 2; // skip first quote
    while I <= Len do
    begin
      if Result[I] = '"then begin
        Break;
      end;
      if Result[I] = '\then begin
        Inc(I);
      end;
      Inc(I);
    end;
    Result := Copy(Result, 2, I-2);
  end;
const
  AUTHVALUE = 'WWW-Authenticate';
  DIGESTAUTH = 'Digest';
var
  S : String;
begin
  FQopOptions.Clear;
  S := ARequest.Response.Headers.Values[AUTHVALUE];
  if S.StartsWith(DIGESTAUTH) then
  begin
    S := Copy(S, Length(DIGESTAUTH) + 2);
    FQopOptions.CommaText := S;
    FRealm := UnQuote(FQopOptions.Values['realm']);
    FNonce := UnQuote(FQopOptions.Values['nonce']);
    FOpaque := UnQuote(FQopOptions.Values['opaque']);
    FQop := UnQuote(FQopOptions.Values['qop']);
    FAlgorithm := FQopOptions.Values['algorithm'];

    case ARequest.Method of
      rmPOST: FMethod := 'POST';
      rmPUT: FMethod := 'PUT';
      rmGET: FMethod := 'GET';
      rmDELETE: FMethod := 'DELETE';
      rmPATCH: FMethod := 'PATCH';
    else
      raise ERESTException.Create('Unknown Method');
    end;
    FUri := ARequest.GetFullRequestURL();
    FUri := Copy(FUri, Pos('://', FUri) + 3);
    FUri := Copy(FUri, Pos('/', FUri));
  end;
end;

procedure TDigestAuthenticator.SetPassword(const AValue: string);
begin
 if (AValue <> FPassword) then
  begin
    FPassword := AValue;
    PropertyValueChanged;
  end;
end;

procedure TDigestAuthenticator.SetUsername(const AValue: string);
begin
  if (AValue <> FUsername) then
  begin
    FUsername := AValue;
    PropertyValueChanged;
  end;
end;

{ TDigestAuthenticatorAdapter }

procedure TDigestAuthenticatorAdapter.AddFields;
const
  sUserName = 'UserName';
  sPassword = 'Password';
var
  LGetMemberObject: IGetMemberObject;
begin
  CheckInactive;
  ClearFields;
  if Authenticator <> nil then
  begin
    LGetMemberObject := TBindSourceAdapterGetMemberObject.Create(Self);
    CreateReadWriteField<string>(sUserName, LGetMemberObject, TScopeMemberType.mtText,
      function: string
      begin
        result := Authenticator.Username;
      end,
      procedure(AValue: string)
      begin
        Authenticator.Username := AValue;
      end);
    CreateReadWriteField<string>(sPassword, LGetMemberObject, TScopeMemberType.mtText,
      function: string
      begin
        result := Authenticator.Password;
      end,
      procedure(AValue: string)
      begin
        Authenticator.Password := AValue;
      end);
  end;

end;

{ TSubDigestAuthenticationBindSource }

function TSubDigestAuthenticationBindSource.CreateAdapterT: TRESTAuthenticatorAdapter<TDigestAuthenticator>;
begin
  result := TDigestAuthenticatorAdapter.Create(Self);
end;

end.
Für den Test mit httbin:
Delphi-Quellcode:
unit Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, REST.Client;

type
  TMainForm = class(TForm)
    RequestButton: TButton;
    procedure RequestButtonClick(Sender: TObject);
  private
    { Private-Deklarationen }
    procedure CatchHttpProtocolError(Sender : TCustomRESTRequest);
  public
    { Public-Deklarationen }
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

uses
  REST.Types,
  REST.Authenticator.Digest;


procedure TMainForm.RequestButtonClick(Sender: TObject);
var
  Client : TRestClient;
  Request : TRestRequest;
  Auth : TDigestAuthenticator;
begin
  Client := TRestClient.Create('http://httpbin.org');
  try
    Request := TRestRequest.Create(Client);
    Request.Resource := 'digest-auth/auth/test/user';
    Request.OnHTTPProtocolError := CatchHttpProtocolError;
    Request.Execute;
    if Request.Response.StatusCode = 401 then
    begin
      Auth := TDigestAuthenticator.Create('test', 'user');
      try
        Auth.Authenticate(Request);
        Request.Execute;
      finally
        Auth.Free;
      end;
    end;
    ShowMessage(Request.Response.Content);
  finally
    Client.Free;
  end;
end;

procedure TMainForm.CatchHttpProtocolError(Sender: TCustomRESTRequest);
begin
  if Sender.Response.StatusCode <> 401 then
  begin
    raise ERESTException.Create(Sender.Response.Content);
  end;
end;

end.
Ibi fas ubi proxima merces
sudo /Developer/Library/uninstall-devtools --mode=all
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.006 Beiträge
 
Delphi 12 Athens
 
#8

AW: TRestClient digest Authentication?

  Alt 29. Sep 2021, 22:57
Das hat mich jetzt nicht losgelassen.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
taveuni

Registriert seit: 3. Apr 2007
Ort: Zürich
526 Beiträge
 
Delphi 11 Alexandria
 
#9

AW: TRestClient digest Authentication?

  Alt 30. Sep 2021, 06:04
Das hat mich jetzt nicht losgelassen. Ich habe etwas bei Indy abgekupfert.
Wow! Vielen Vielen Dank!
Die obige Aussage repräsentiert meine persönliche Meinung.
Diese erhebt keinen Anspruch auf Objektivität oder Richtigkeit.
  Mit Zitat antworten Zitat
Benutzerbild von Union
Union

Registriert seit: 18. Mär 2004
Ort: Luxembourg
3.487 Beiträge
 
Delphi 7 Enterprise
 
#10

AW: TRestClient digest Authentication?

  Alt 30. Sep 2021, 08:54
Leider kann man den Authenticator aus verschiedenen Gründen nicht dem Request zuweisen. Der Datenfluss bei der Requestverarbeitung im Rest Client sieht nur eine Authentifzierung vor dem Absenden des Requests vor.
Ibi fas ubi proxima merces
sudo /Developer/Library/uninstall-devtools --mode=all
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:17 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz