IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Web & réseau Delphi Discussion :

[D10.4] Récupérer une url pour télécharger un fichier depuis un conteneur azure ?


Sujet :

Web & réseau Delphi

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert
    Avatar de Lung
    Profil pro
    Analyste-programmeur
    Inscrit en
    Mai 2002
    Messages
    2 704
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste-programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 704
    Par défaut [D10.4] Récupérer une url pour télécharger un fichier depuis un conteneur azure ?
    Existe t'il une fonction pour récupérer une url, utilisable par n'importe qui, pour télécharger un fichier depuis un conteneur azure ?


    Pour ajouter un fichier dans un nouveau blob d'un conteneur azure, je n'ai pas de pb.
    Ca fonctionne bien :
    - TAzureConnectionInfo pour se connecter.
    - TAzureBlobService pour naviguer dans les conteneurs, et PutBlockBlob() pour envoyer le fichier.

    Énumérer les blobs d'un conteneur avec ListBlobs(), permet d'accéder à quelques informations des blobs, dont TAzureBlob.Url.
    Mais cette url n'est valable que si le conteneur n'est pas privé (le paramètre "niveau d'accès anonyme" du conteneur différent de "private"), ce qui n'est pas sécurisé.

    Toutes les recherches que j'ai faites indiquent la même chose : il faut construire soit-même une url avec un certains nombre de paramètres, dont une signature constituée de la permission + #10 + une date.

    https://<nom_du_compte>.blob.core.windows.net/<nom_du_container>/<nom_du_fichier>?sv=<version_service>&ss=<service>&srt=<type_de_ressource>&sp=<permissions>&se=<expiration>&st=<heure_de_début>&spr=<protocoles>&sig=<signature>
    J'ai beau essayer aléatoirement d'ajouter des paramètres, ou de bricoler cette signature, j'obtiens diverses erreurs.
    La dernière en date : "Make sure the value of Authorization header is formed correctly including the signature".

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    function TFrm_AzureConnect.GenerateBlobSAS(const accountName, accountKey, containerName, blobName: String; const permissions: String; const expiry: TDateTime): String;
    var
      http: TIdHTTP;
      url, signature: String;
      hmac: TIdHMACSHA1;   // TIdHMACSHA256
      signatureBytes: TIdBytes;   // TBytes;
    begin
      http := TIdHTTP.Create(nil);
      try
        http.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(http);
        http.Request.CustomHeaders.Add('x-ms-version:2019-12-12');
     
        url := Format('https://%s.blob.core.windows.net/%s/%s', [accountName, containerName, blobName]);
        signature := Format('%s'#10'%s', [permissions, FormatDateTime('yyyy-mm-dd', expiry)]);
     
        hmac := TIdHMACSHA1.Create;
        try
            hmac.Key := ConvertirTableauTIdBytes(TEncoding.ASCII.GetBytes(accountKey));
            signatureBytes := hmac.HashValue(ConvertirAnsiStringTIdBytes(AnsiString(signature)));
            signature := TNetEncoding.Base64.EncodeBytesToString(signatureBytes);
        finally
          hmac.Free;
        end;
        url := url + '?ss=b&srt=sco&sr=c&spr=https&st=' + FormatDateTime('yyyy-mm-dd', Now) + 'T' + FormatDateTime('hh:nn:ss', Now) + 'Z&se=' + FormatDateTime('yyyy-mm-dd', expiry) + 'T' + FormatDateTime('hh:nn:ss', expiry) + 'Z&sp=' + permissions + '&sig=' + signature;
     
        try
          Result := http.Get(url);
        except
          on E: Exception do
          begin
            RichEdit.Lines.Add('      # ' + E.Message);
            Exit;
          end;
        end;
      finally
        http.Free;
      end;
    end;
    Ce qui me surprend, c'est qu'il n'y ait pas une simple fonction à appeler pour faire ça.
    L'urgent est fait, l'impossible est en cours, pour les miracles prévoir un délai. :bug: ___ "http://club.developpez.com/regles/#LIII-A"Écrivez dans un français correct !!

    C++Builder 5 - Delphi 6#2 Entreprise - Delphi 2007 Entreprise - Delphi 2010 Architecte - Delphi XE Entreprise - Delphi XE7 Entreprise - Delphi 10 Entreprise - Delphi 10.4.2 Entreprise - Delphi 11.3 Entreprise - Visual studio 2022
    OpenGL 2.1 - Oracle 10g - Paradox - Interbase (XE) - PostgreSQL (15.7)

  2. #2
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 104
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 104
    Par défaut
    Cela ressemble aux "SAS Token" comme dans https://learn.microsoft.com/fr-fr/az...abs=Containers ?
    SAS = Shared Access Signatures

    Tu n'as pas un header du genre

    Code header http : Sélectionner tout - Visualiser dans une fenêtre à part
    Authorization: SharedKey <AccountName>:<Signature>

    Voir https://learn.microsoft.com/en-us/re...ith-shared-key

    Et plutôt un HMAC-SHA256 qu'un SHA1

    AnsiString ? Cela ne devrait pas être converti plutôt en UTF8 (ce qui est similaire si permissions ne contient que des char 7 bits)
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  3. #3
    Membre Expert
    Avatar de Lung
    Profil pro
    Analyste-programmeur
    Inscrit en
    Mai 2002
    Messages
    2 704
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste-programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 704
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Cela ressemble aux "SAS Token" comme dans https://learn.microsoft.com/fr-fr/az...abs=Containers ?
    SAS = Shared Access Signatures
    C'est bien ça.


    Citation Envoyé par ShaiLeTroll Voir le message
    Tu n'as pas un header du genre
    Code header http : Sélectionner tout - Visualiser dans une fenêtre à part
    Authorization: SharedKey <AccountName>:<Signature>
    Voir https://learn.microsoft.com/en-us/re...ith-shared-key
    Je suis tombé sur une page qui parlait de ça, mais je ne sais pas techniquement ce qu'il faut en faire.
    Il faudrait écrire quoi, dans quelle propriété de http.Request ?


    Citation Envoyé par ShaiLeTroll Voir le message
    Et plutôt un HMAC-SHA256 qu'un SHA1
    J'étais aussi tombé sur une page qui parlait de ça, sauf que quand je remplace TIdHMACSHA1 par TIdHMACSHA256, j'ai une erreur à l'exécution :
    Le projet __ a déclenché la classe d'exception EIdHMACHashNotAvailable avec le message 'L'algorithme de hachage HMAC "SHA-256" n'est pas disponible'.



    Citation Envoyé par ShaiLeTroll Voir le message
    AnsiString ? Cela ne devrait pas être converti plutôt en UTF8 (ce qui est similaire si permissions ne contient que des char 7 bits)
    En fait, c'est le dernier bout de code trouvé parmi tous mes nombreux essais qui proposait ça.
    Mais effectivement, permissions ne contient que le caractère "r".


    J'ai l'impression qu'en C#, ça semble beaucoup plus simple. Une simple fonction à appeler : BlobBaseClient.GenerateSasUri (si j'ai bien tout compris ).
    https://learn.microsoft.com/en-us/do...w=azure-dotnet
    L'urgent est fait, l'impossible est en cours, pour les miracles prévoir un délai. :bug: ___ "http://club.developpez.com/regles/#LIII-A"Écrivez dans un français correct !!

    C++Builder 5 - Delphi 6#2 Entreprise - Delphi 2007 Entreprise - Delphi 2010 Architecte - Delphi XE Entreprise - Delphi XE7 Entreprise - Delphi 10 Entreprise - Delphi 10.4.2 Entreprise - Delphi 11.3 Entreprise - Visual studio 2022
    OpenGL 2.1 - Oracle 10g - Paradox - Interbase (XE) - PostgreSQL (15.7)

  4. #4
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 104
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 104
    Par défaut
    Pour le header Authorization, tu as toi même la connaissance http.Request.CustomHeaders mais, comme c'est différente documentation, cela semble étrange de devoir cumuler les mêmes données en header que dans l'url


    En C#, c'est Microsoft qui écrit ses clients pour ses propres protocoles, heureusement que c'est plus simple.

    Pour le Hash, j'utilise la RTL et non Indy, par exemple pour un JSON signé
    THashSHA2 en SHA256
    Pour la partie signature, là j'appelle directement libeay32.dll (voir mon TSLTPEMKeyLoader et TSLTSignatureGenerator ) en ayant prévu un couplage faible

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
     
      // stringify and encode data
      VJSON := TJSONObject.Create();
      try
        with VJSON do
        begin
          AddPair('...', ...);
     
        end;
     
        with TBase64Encoding.Create(0) do
        try
          VPayload := EncodeBytesToString(TEncoding.UTF8.GetBytes(VJSON.ToJSON()));
        finally
          Free();
        end;
      finally
        VJSON.Free();
      end;
     
      VDigestMessage := THashSHA2.GetHashBytes(VPayload, SHA256);
      with TBase64Encoding.Create(0) do
      try
        VMessage := TEncoding.ASCII.GetString(Encode(VDigestMessage));
      finally
        Free();
      end;
     
      VDigestSalted := VMessage + ASalt;
     
      // Sign the message with Private certificate
      VSignature := ASigner.Sign(VDigestSalted); // couplage faible TSigner est abstrait, là il fonctionne via PEM + libeay32.dll


    Attention au TBase64Encoding et son CharsPerLine à 76 par défaut, cela doit nuire à ton URL d'utiliser directement TNetEncoding.Base64.EncodeBytesToString qui doit contenir des CRLF ... typiquement le base64 du MIME ou des clés RSA souvent présentées ainsi (quoi que plutôt aligné sur 64)

    Avec CharsPerLine différent de Zéro, tu obtiens un base64 typique de ce genre de présentation :
    Nom : word-image.png
Affichages : 87
Taille : 43,3 Ko
    C'est exactement ce genre de clé que gère mon TSLTPEMKeyLoader


    Dans une URL, des CRLF, il y a de quoi rendre les données totalement incompréhensible.



    Enfin, dans une URL, le Base64 c'est potentiellement à échapper surtout les == de bourrage

    Donc
    soit c'est un "base64url" (RFC 4648) voir TBase64URLEncoding,
    soit c'est un Base64 classique, il te faut donc ajouter un TURI.URLEncode().



    J'ai géré ce cas en D10

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // https://docwiki.embarcadero.com/RADStudio/Alexandria/en/What%27s_New : New TBase64URLEncoding encoding and TNetEncoding.Base64URL property
    const
      CompilerVersionAlexandria = 35.0;
      CompilerVersionSeattle = 30.0;
     
    {$IF CompilerVersion < CompilerVersionAlexandria}
    type
      TBase64URLEncoding = class(TBase64Encoding)
      protected
        function DoDecode(const Input: string): string; override;
        function DoEncode(const Input: string): string; override;
     
        function DoEncodeBytesToString(const Input: array of Byte): string; overload; override;
      end;
    {$IFEND}
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    { TBase64URLEncoding }
     
    {$IF CompilerVersion < CompilerVersionAlexandria}
    //------------------------------------------------------------------------------
    function TBase64URLEncoding.DoDecode(const Input: string): string;
    begin
      Result := Input + StringOfChar('=', (4 - Length(Input) mod 4) mod 4);
      Result := StringReplace(Result, '-', '+', [rfReplaceAll]);
      Result := StringReplace(Result, '_', '/', [rfReplaceAll]);
      Result := inherited DoEncode(Result);
    end;
     
    //------------------------------------------------------------------------------
    function TBase64URLEncoding.DoEncode(const Input: string): string;
    begin
      Result := inherited DoEncode(Input);
      Result := StringReplace(Result, '+', '-', [rfReplaceAll]);
      Result := StringReplace(Result, '/', '_', [rfReplaceAll]);
      Result := StringReplace(Result, '=', '', [rfReplaceAll]);
    end;
     
    //------------------------------------------------------------------------------
    function TBase64URLEncoding.DoEncodeBytesToString(const Input: array of Byte): string;
    begin
      Result := inherited DoEncodeBytesToString(Input);
      Result := StringReplace(Result, '+', '-', [rfReplaceAll]);
      Result := StringReplace(Result, '/', '_', [rfReplaceAll]);
      Result := StringReplace(Result, '=', '', [rfReplaceAll]);
    end;
     
    {$IFEND}
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

Discussions similaires

  1. spécifier une URL pour une dépendance
    Par Methode dans le forum Maven
    Réponses: 1
    Dernier message: 30/01/2009, 20h45
  2. Réponses: 5
    Dernier message: 06/01/2009, 17h11
  3. Une URL pour chaque partie de mon swf (URL Rewriting)
    Par Touillette85 dans le forum ActionScript 1 & ActionScript 2
    Réponses: 3
    Dernier message: 11/12/2008, 14h26
  4. Réponses: 1
    Dernier message: 09/10/2007, 21h53
  5. [JpGraph] Passage d'un tableau en paramètre dans une URL pour JPgraph
    Par crazydiver_e2 dans le forum Bibliothèques et frameworks
    Réponses: 4
    Dernier message: 16/01/2006, 16h15

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo