UPD: MacCloud/S3: step-85: S3 refactor

This commit is contained in:
rich2014 2025-04-29 21:28:06 +08:00
commit 93eb7dfd83
4 changed files with 196 additions and 126 deletions

View file

@ -15,19 +15,60 @@ uses
type
{ TAWSAuthSessionParams }
TAWSAuthSessionParams = record
config: TAWSConfig;
resultProcessFunc: TCloudDriverResultProcessFunc;
region: String;
endPoint: String;
defaultBucket: String;
end;
{ TAWSSigner }
TAWSSigner = class
strict private
_params: TAWSAuthSessionParams;
_config: TAWSConfig;
_accessKey: TAWSAccessKey;
_request: NSMutableURLRequest;
protected
constructor Create(
const params: TAWSAuthSessionParams;
const accessKey: TAWSAccessKey;
const request: NSMutableURLRequest );
protected
function buildDateYYYYMMDDString: NSString;
function buildTimeStampString: NSString;
function buildAuthString: NSString;
function buildSignature: NSString;
function buildSigningString: NSString;
function buildSigningKey: NSData;
function buildScopeString: NSString;
function buildCredentialString: NSString;
function buildHmacAlgorithmString: NSString;
end;
{ TAWSAuthSession }
TAWSAuthSession = class( TCloudDriverAuthSession )
strict protected
_params: TAWSAuthSessionParams;
_config: TAWSConfig;
_accessKey: TAWSAccessKey;
_signer: TAWSSigner;
private
procedure addNeededHeader( const request: NSMutableURLRequest );
public
constructor Create( const config: TAWSConfig );
constructor Create( const params: TAWSAuthSessionParams );
destructor Destroy; override;
procedure setAccessKey( const accessKey: TAWSAccessKey );
function clone( const driver: TCloudDriver ): TCloudDriverAuthSession; override;
procedure setAuthHeader( const http: TMiniHttpClient ); override;
public
property config: TAWSConfig read _config;
property params: TAWSAuthSessionParams read _params;
property accessKey: TAWSAccessKey read _accessKey write setAccessKey;
end;
implementation
@ -48,34 +89,6 @@ type
class function build( const request: NSMutableURLRequest ): NSString;
end;
{ TSignUtil }
TSignUtil = class
strict private
class function buildDateYYYYMMDDString( const request: NSMutableURLRequest ): NSString;
class function buildTimeStampString( const request: NSMutableURLRequest ): NSString;
protected
class function buildAuthString(
const config: TAWSConfig;
const request: NSMutableURLRequest ): NSString;
class function buildSignature(
const config: TAWSConfig;
const request: NSMutableURLRequest ): NSString;
class function buildSigningString(
const config: TAWSConfig;
const request: NSMutableURLRequest ): NSString;
class function buildSigningKey(
const config: TAWSConfig;
const request: NSMutableURLRequest ): NSData;
class function buildScopeString(
const config: TAWSConfig;
const request: NSMutableURLRequest ): NSString;
class function buildCredentialString(
const config: TAWSConfig;
const request: NSMutableURLRequest ): NSString;
class function buildHmacAlgorithmString( const config: TAWSConfig ): NSString;
end;
{ TAWSAuthSession }
procedure TAWSAuthSession.addNeededHeader( const request: NSMutableURLRequest );
@ -100,43 +113,86 @@ begin
addHostHeaderIfNeeded;
end;
constructor TAWSAuthSession.Create(const config: TAWSConfig);
constructor TAWSAuthSession.Create( const params: TAWSAuthSessionParams );
begin
_config:= config;
_config.credentialPrefixAndSecret:= _config.credentialPrefix + _config.accessKeySecret;
_params:= params;
_config:= params.config;
end;
destructor TAWSAuthSession.Destroy;
begin
FreeAndNil( _accessKey );
end;
function TAWSAuthSession.clone( const driver: TCloudDriver ): TCloudDriverAuthSession;
var
session: TAWSAuthSession;
begin
Result:= TAWSAuthSession.Create( _config );
session:= TAWSAuthSession.Create( _params );
session.setAccessKey( TAWSCloudDriver(driver).getAccessKey );
Result:= session;
end;
procedure TAWSAuthSession.setAuthHeader( const http: TMiniHttpClient );
var
request: NSMutableURLRequest;
authString: NSString;
signer: TAWSSigner = nil;
begin
request:= http.request;
self.addNeededHeader( request );
authString:= TSignUtil.buildAuthString( _config, request );
http.addHeader( HttpConst.Header.Authorization, authString );
try
request:= http.request;
self.addNeededHeader( request );
signer:= TAWSSigner.Create( params, _accessKey, request );
authString:= signer.buildAuthString;
http.addHeader( HttpConst.Header.Authorization, authString );
finally
FreeAndNil( signer );
end;
end;
{ TSigningKey }
procedure TAWSAuthSession.setAccessKey(const accessKey: TAWSAccessKey);
var
oldAccessKey: TAWSAccessKey;
begin
oldAccessKey:= _accessKey;
_accessKey:= accessKey;
oldAccessKey.Free;
end;
class function TSignUtil.buildAuthString(
const config: TAWSConfig;
const request: NSMutableURLRequest ): NSString;
{ TAWSSigner }
constructor TAWSSigner.Create(
const params: TAWSAuthSessionParams;
const accessKey: TAWSAccessKey;
const request: NSMutableURLRequest );
begin
_params:= params;
_config:= _params.config;
_accessKey:= accessKey;
_request:= request;
end;
function TAWSSigner.buildDateYYYYMMDDString: NSString;
begin
Result:= self.buildTimeStampString.substringToIndex( 8 );
end;
function TAWSSigner.buildTimeStampString: NSString;
begin
Result:= NSString( _request.allHTTPHeaderFields.objectForKey(HttpConst.Header.Date) );
end;
function TAWSSigner.buildAuthString: NSString;
var
hmacAlgorithm: NSString;
credential: NSString;
signedHeader: NSString;
signature: NSString;
begin
hmacAlgorithm:= buildHmacAlgorithmString( config );
credential:= buildCredentialString( config, request );
signedHeader:= TCanonicalRequestUtil.buildSignedHeadersString( request );
signature:= buildSignature( config, request );
hmacAlgorithm:= buildHmacAlgorithmString;
credential:= buildCredentialString;
signedHeader:= TCanonicalRequestUtil.buildSignedHeadersString( _request );
signature:= buildSignature;
Result:= NSString.stringWithFormat( NSSTR('%@ Credential=%@,SignedHeaders=%@,Signature=%@'),
hmacAlgorithm,
credential,
@ -144,37 +200,31 @@ begin
signature );
end;
class function TSignUtil.buildSignature(
const config: TAWSConfig;
const request: NSMutableURLRequest ): NSString;
function TAWSSigner.buildSignature: NSString;
var
signingkeyData: NSData;
signingString: NSString;
begin
signingkeyData:= buildSigningKey( config, request );
signingString:= buildSigningString( config, request );
signingkeyData:= buildSigningKey;
signingString:= buildSigningString;
Result:= THashUtil.HmacSha256AndHexStr( signingkeyData, signingString );
end;
class function TSignUtil.buildSigningString(
const config: TAWSConfig;
const request: NSMutableURLRequest ): NSString;
function TAWSSigner.buildSigningString: NSString;
var
canonicalRequestString: NSString;
canonicalRequestSha256HexString: NSString;
begin
canonicalRequestString:= TCanonicalRequestUtil.build(request);
canonicalRequestString:= TCanonicalRequestUtil.build( _request );
canonicalRequestSha256HexString:= THashUtil.sha256AndHexStr( canonicalRequestString );
Result:= NSString.stringWithFormat( NSSTR('%@'#10'%@'#10'%@'#10'%@'),
buildHmacAlgorithmString( config ),
buildTimeStampString(request),
buildScopeString(config,request),
buildHmacAlgorithmString,
buildTimeStampString,
buildScopeString,
canonicalRequestSha256HexString );
end;
class function TSignUtil.buildSigningKey(
const config: TAWSConfig;
const request: NSMutableURLRequest ): NSData;
function TAWSSigner.buildSigningKey: NSData;
var
dateString: NSString;
keyString: NSString;
@ -183,43 +233,31 @@ var
dateRegionKey: NSData;
dateRegionServiceKey: NSData;
begin
dateString:= self.buildDateYYYYMMDDString(request);
keyString:= StringToNSString( config.credentialPrefixAndSecret );
dateString:= buildDateYYYYMMDDString;
keyString:= StringToNSString( _config.credentialPrefix + _accessKey.secret );
key:= keyString.dataUsingEncoding( NSUTF8StringEncoding );
dateKey:= THashUtil.HmacSha256( key, dateString );
dateRegionKey:= THashUtil.HmacSha256( dateKey, NSSTR(config.credentialRegion) );
dateRegionServiceKey:= THashUtil.HmacSha256( dateRegionKey, NSSTR(config.credentialService) );
Result:= THashUtil.HmacSha256( dateRegionServiceKey, NSSTR(config.credentialRequest) );
dateRegionKey:= THashUtil.HmacSha256( dateKey, NSSTR(_params.region) );
dateRegionServiceKey:= THashUtil.HmacSha256( dateRegionKey, NSSTR(_config.credentialService) );
Result:= THashUtil.HmacSha256( dateRegionServiceKey, NSSTR(_config.credentialRequest) );
end;
class function TSignUtil.buildDateYYYYMMDDString( const request: NSMutableURLRequest ): NSString;
function TAWSSigner.buildHmacAlgorithmString: NSString;
begin
Result:= self.buildTimeStampString(request).substringToIndex( 8 );
Result:= NSSTR( _config.credentialVersionAlgorithm );
end;
class function TSignUtil.buildTimeStampString( const request: NSMutableURLRequest ): NSString;
begin
Result:= NSString( request.allHTTPHeaderFields.objectForKey(HttpConst.Header.Date) );
end;
class function TSignUtil.buildHmacAlgorithmString( const config: TAWSConfig ): NSString;
begin
Result:= NSSTR( config.credentialVersionAlgorithm );
end;
class function TSignUtil.buildScopeString(
const config: TAWSConfig;
const request: NSMutableURLRequest ): NSString;
function TAWSSigner.buildScopeString: NSString;
var
signDate: NSString;
signRegion: NSString;
signService: NSString;
signProduct: NSString;
begin
signDate:= buildDateYYYYMMDDString( request );
signService:= NSSTR( config.credentialService );
signRegion:= NSSTR( config.credentialRegion );
signProduct:= NSSTR( config.credentialRequest );
signDate:= buildDateYYYYMMDDString;
signService:= NSSTR( _config.credentialService );
signRegion:= NSSTR( _params.region );
signProduct:= NSSTR( _config.credentialRequest );
Result:= NSString.stringWithFormat( NSSTR('%@/%@/%@/%@'),
signDate,
signRegion,
@ -227,13 +265,11 @@ begin
signProduct );
end;
class function TSignUtil.buildCredentialString(
const config: TAWSConfig;
const request: NSMutableURLRequest ): NSString;
function TAWSSigner.buildCredentialString: NSString;
begin
Result:= NSString.stringWithFormat( NSSTR('%@/%@'),
NSSTR( config.accessKeyID),
TSignUtil.buildScopeString(config,request) );
NSSTR( _accessKey.id ),
buildScopeString );
end;
{ TCanonicalRequest }

View file

@ -5,7 +5,8 @@ unit uAWSCore;
interface
uses
Classes, SysUtils;
Classes, SysUtils,
uCloudDriver;
type
@ -13,15 +14,30 @@ type
TAWSConfig = class
public
accessKeyID: String;
accessKeySecret: String;
endPoint: String;
credentialVersionAlgorithm: String;
credentialPrefix: String;
credentialRegion: String;
credentialService: String;
credentialRequest: String;
credentialPrefixAndSecret: String;
end;
{ TAWSAccessKey }
TAWSAccessKey = class
private
_id: String;
_secret: String;
public
constructor Create( const id: String; const secret: String );
property id: String read _id;
property secret: String read _secret;
end;
{ TAWSCloudDriver }
TAWSCloudDriver = class( TCloudDriver )
public
function getAccessKey: TAWSAccessKey; virtual; abstract;
procedure setAccessKey( const accessKey: TAWSAccessKey ); virtual; abstract;
end;
{ TAWSConstHeader }
@ -49,5 +65,13 @@ const
implementation
{ TAWSAccessKey }
constructor TAWSAccessKey.Create( const id: String; const secret: String );
begin
_id:= id;
_secret:= secret;
end;
end.

View file

@ -21,18 +21,17 @@ uses
type
{ TS3Config }
TS3AccessKey = TAWSAccessKey;
TS3Config = class( TAWSConfig )
public
bucket: String;
end;
TS3Config = TAWSConfig;
TS3AuthSessionParams = TAWSAuthSessionParams;
{ TS3ListFolderSession }
TS3ListFolderSession = class( TCloudDriverListFolderSession )
private
_config: TS3Config;
_params: TS3AuthSessionParams;
_continuationToken: String;
private
procedure analyseListResult( const listString: String );
@ -82,7 +81,7 @@ type
{ TS3Client }
TS3Client = class( TCloudDriver )
TS3Client = class( TAWSCloudDriver )
protected
_config: TS3Config;
_authSession: TAWSAuthSession;
@ -95,6 +94,8 @@ type
function authorize: Boolean; override;
procedure unauthorize; override;
function authorized: Boolean; override;
function getAccessKey: TAWSAccessKey; override;
procedure setAccessKey(const accessKey: TAWSAccessKey); override;
public
procedure download(
const serverPath: String;
@ -156,7 +157,7 @@ var
var
message: String;
begin
message:= 'Box Error';
message:= 'S3 Error';
if e.Message <> EmptyStr then
message:= message + ': ' + e.Message;
TLogUtil.logError( message );
@ -194,6 +195,8 @@ var
for xmlContent in xmlContents do begin
name:= TXmlUtil.getString( xmlContent, 'Key' );
name:= name.Substring( prefixLength );
if name = EmptyStr then
continue;
size:= TXmlUtil.getInteger( xmlContent, 'Size' );
modificationTime:= ISO8601ToDate( TXmlUtil.getString(xmlContent,'LastModified') );
cloudFile:= TCloudFile.Create;
@ -245,7 +248,7 @@ var
queryItems: TQueryItemsDictonary;
begin
try
urlString:= 'https://' + _config.bucket + '.' + _config.endPoint + '/?';
urlString:= 'https://' + _params.defaultBucket + '.' + _params.endPoint + '/?';
queryItems:= TQueryItemsDictonary.Create;
queryItems.Add( 'list-type', '2' );
queryItems.Add( 'delimiter', '/' );
@ -276,7 +279,7 @@ constructor TS3ListFolderSession.Create( const authSession: TCloudDriverAuthSess
var
truePath: String;
begin
_config:= TS3Config( TAWSAuthSession(authSession).config );
_params:= TAWSAuthSession(authSession).params;
truePath:= path;
if truePath.StartsWith( '/' ) then
truePath:= truePath.Substring( 1 );
@ -292,13 +295,13 @@ end;
procedure TS3DownloadSession.download;
var
http: TMiniHttpClient = nil;
config: TS3Config;
params: TS3AuthSessionParams;
urlString: String;
cloudDriverResult: TCloudDriverResult = nil;
begin
try
config:= TS3Config( TAWSAuthSession(_authSession).config );
urlString:= 'https://' + config.bucket + '.' + config.endPoint + THttpClientUtil.urlEncode(_serverPath);
params:= TAWSAuthSession(_authSession).params;
urlString:= 'https://' + params.defaultBucket + '.' + params.endPoint + THttpClientUtil.urlEncode(_serverPath);
http:= TMiniHttpClient.Create( urlString, HttpConst.Method.GET );
http.addHeader( AWSConst.HEADER.CONTENT_SHA256, AWSConst.HEADER.CONTENT_SHA256_DEFAULT_VALUE );
_authSession.setAuthHeader( http );
@ -318,13 +321,13 @@ end;
procedure TS3UploadSession.upload;
var
http: TMiniHttpClient = nil;
config: TS3Config;
params: TS3AuthSessionParams;
urlString: String;
cloudDriverResult: TCloudDriverResult = nil;
begin
try
config:= TS3Config( TAWSAuthSession(_authSession).config );
urlString:= 'https://' + config.bucket + '.' + config.endPoint + THttpClientUtil.urlEncode(_serverPath);
params:= TAWSAuthSession(_authSession).params;
urlString:= 'https://' + params.defaultBucket + '.' + params.endPoint + THttpClientUtil.urlEncode(_serverPath);
http:= TMiniHttpClient.Create( urlString, HttpConst.Method.PUT );
http.addHeader( AWSConst.HEADER.CONTENT_SHA256, AWSConst.HEADER.CONTENT_SHA256_DEFAULT_VALUE );
_authSession.setAuthHeader( http );
@ -344,13 +347,13 @@ end;
procedure TS3CreateFolderSession.createFolder;
var
http: TMiniHttpClient = nil;
config: TS3Config;
params: TS3AuthSessionParams;
urlString: String;
cloudDriverResult: TCloudDriverResult = nil;
begin
try
config:= TS3Config( TAWSAuthSession(_authSession).config );
urlString:= 'https://' + config.bucket + '.' + config.endPoint + THttpClientUtil.urlEncode(_path+'/');
params:= TAWSAuthSession(_authSession).params;
urlString:= 'https://' + params.defaultBucket + '.' + params.endPoint + THttpClientUtil.urlEncode(_path+'/');
http:= TMiniHttpClient.Create( urlString, HttpConst.Method.PUT );
http.addHeader( AWSConst.HEADER.CONTENT_SHA256, AWSConst.HEADER.CONTENT_SHA256_DEFAULT_VALUE );
_authSession.setAuthHeader( http );
@ -370,13 +373,13 @@ end;
procedure TS3DeleteSession.delete;
var
http: TMiniHttpClient = nil;
config: TS3Config;
params: TS3AuthSessionParams;
urlString: String;
cloudDriverResult: TCloudDriverResult = nil;
begin
try
config:= TS3Config( TAWSAuthSession(_authSession).config );
urlString:= 'https://' + config.bucket + '.' + config.endPoint + THttpClientUtil.urlEncode(_path);
params:= TAWSAuthSession(_authSession).params;
urlString:= 'https://' + params.defaultBucket + '.' + params.endPoint + THttpClientUtil.urlEncode(_path);
if _isFolder then
urlString:= urlString + '/';
http:= TMiniHttpClient.Create( urlString, HttpConst.Method.DELETE );
@ -398,15 +401,15 @@ end;
procedure TS3CopyMoveSession.doCopyFile;
var
http: TMiniHttpClient = nil;
config: TS3Config;
params: TS3AuthSessionParams;
urlString: String;
cloudDriverResult: TCloudDriverResult = nil;
sourceHeaderString: String;
begin
try
config:= TS3Config( TAWSAuthSession(_authSession).config );
urlString:= 'https://' + config.bucket + '.' + config.endPoint + THttpClientUtil.urlEncode(_toPath);
sourceHeaderString:= '/' + config.bucket + THttpClientUtil.urlEncode(_fromPath);
params:= TAWSAuthSession(_authSession).params;
urlString:= 'https://' + params.defaultBucket + '.' + params.endPoint + THttpClientUtil.urlEncode(_toPath);
sourceHeaderString:= '/' + params.defaultBucket + THttpClientUtil.urlEncode(_fromPath);
http:= TMiniHttpClient.Create( urlString, HttpConst.Method.PUT );
http.addHeader( AWSConst.HEADER.CONTENT_SHA256, AWSConst.HEADER.CONTENT_SHA256_DEFAULT_VALUE );
http.addHeader( AWSConst.HEADER.COPY_SOURCE, sourceHeaderString );
@ -437,12 +440,10 @@ end;
constructor TS3Client.Create( const config: TS3Config );
begin
_config:= config;
_authSession:= TAWSAuthSession.Create( config );
end;
destructor TS3Client.Destroy;
begin
FreeAndNil( _config );
FreeAndNil( _authSession );
end;
@ -465,6 +466,16 @@ begin
Result:= True;
end;
function TS3Client.getAccessKey: TAWSAccessKey;
begin
Result:= _authSession.accessKey;
end;
procedure TS3Client.setAccessKey(const accessKey: TAWSAccessKey);
begin
_authSession.accessKey:= accessKey;
end;
procedure TS3Client.download(
const serverPath: String;
const localPath: String;

View file

@ -380,8 +380,7 @@ type
_config: TTokenCloudDriverConfig;
_authSession: TCloudDriverOAuth2Session;
public
constructor Create(
const config: TTokenCloudDriverConfig );
constructor Create( const config: TTokenCloudDriverConfig );
destructor Destroy; override;
public
function authorize: Boolean; override;