XRootD
XrdClS3Factory.hh
Go to the documentation of this file.
1 /******************************************************************************/
2 /* Copyright (C) 2025, Pelican Project, Morgridge Institute for Research */
3 /* */
4 /* This file is part of the XrdClS3 client plugin for XRootD. */
5 /* */
6 /* XRootD is free software: you can redistribute it and/or modify it under */
7 /* the terms of the GNU Lesser General Public License as published by the */
8 /* Free Software Foundation, either version 3 of the License, or (at your */
9 /* option) any later version. */
10 /* */
11 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
12 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
13 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
14 /* License for more details. */
15 /* */
16 /* The copyright holder's institutional names and contributor's names may not */
17 /* be used to endorse or promote products derived from this software without */
18 /* specific prior written permission of the institution or contributor. */
19 /******************************************************************************/
20 
21 #ifndef XRDCLS3_S3FACTORY_HH
22 #define XRDCLS3_S3FACTORY_HH
23 
25 
26 #include <mutex>
27 #include <shared_mutex>
28 #include <string>
29 #include <string_view>
30 
31 namespace XrdCl {
32  class Log;
33 }
34 
35 namespace XrdClS3 {
36 
37 const uint64_t kLogXrdClS3 = 73174;
38 
39 class Factory final : public XrdCl::PlugInFactory {
40 public:
41  Factory();
42  virtual ~Factory() {}
43  Factory(const Factory &) = delete;
44 
45  virtual XrdCl::FilePlugIn *CreateFile(const std::string &url) override;
46  virtual XrdCl::FileSystemPlugIn *CreateFileSystem(const std::string &url) override;
47 
48  // Given a S3-style url (s3://bucket/object), return the corresponding HTTPS URL.
49  //
50  // If "obj_result" is not nullptr, then it will be set to the object/key and the resulting HTTPS URL
51  // will not include the key name.
52  static bool GenerateHttpUrl(const std::string &s3_url, std::string &https_url, std::string *obj_result, std::string &err_msg);
53 
54  // Convenience function to extract the hostname from a URL.
55  static std::string_view ExtractHostname(const std::string_view url);
56 
57  // Convenience function to trim the leading and trailing whitespace from a string.
58  static std::string_view TrimView(const std::string_view str);
59 
60 
61  // Generate a V4 signature for the given HTTP URL, returning the
62  // authorization token in auth_token. Returns true on success, false
63  // on failure, with an error message in err_msg.
64  static bool GenerateV4Signature(const std::string &url, const std::string &verb, std::vector<std::pair<std::string, std::string>> &headers, std::string &auth_token, std::string &err_msg);
65 
66  // Given a HTTPS URL representing a S3 object request, return the bucket name.
67  //
68  // Parses the bucket name from the hostname (for virtual-hosted-style URLs) or
69  // from the path (for path-style URLs).
70  static std::string GetBucketFromHttpsUrl(const std::string &url);
71 
72  // Given a bucket name, return the credentials to use.
73  static std::tuple<std::string, std::string, bool> GetCredentialsForBucket(const std::string &bucket, std::string &err_msg);
74 
75  // Helper function to convert the URL to a canonical path form for V4 signing.
76  static std::string PathEncode(const std::string_view url);
77 
78  // Helper function to remove XRootD-specific query parameters from an object name
79  static std::string CleanObjectName(const std::string &object);
80 
81  static const std::string &GetMkdirSentinel() {return m_mkdir_sentinel;}
82 
83  // Setters for the S3 endpoint, service, region, and URL style.
84  // Intended to be used for testing or configuration purposes.
85  static void SetEndpoint(const std::string &endpoint) { m_endpoint = endpoint; }
86  static void SetService(const std::string &service) { m_service = service; }
87  static void SetRegion(const std::string &region) { m_region = region; }
88  static void SetUrlStyle(const std::string &url_style) { m_url_style = url_style; }
89  static void SetDefaultCredentials(const std::string &access_key, const std::string &secret_key) {
90  m_default_creds.m_accesskey = access_key;
91  m_default_creds.m_secretkey = secret_key;
92  }
93  static void SetBucketCredentials(const std::string &bucket, const std::string &access_key, const std::string &secret_key) {
94  m_bucket_location_map[bucket] = {access_key, secret_key};
95  }
96  static void ResetCredCache() {
97  std::unique_lock lock(m_bucket_auth_map_mutex);
98  m_bucket_auth_map.clear();
99  }
100 
101 private:
102 
103  // Iterate through the client configuration and determine the S3
104  // settings
105  static void InitS3Config();
106 
107  // Helper function to canonicalize the query string for V4 signing.
108  static std::string CanonicalizeQueryString(const std::string &url);
109 
110  static bool m_initialized;
111  static XrdCl::Log *m_log;
112  static std::once_flag m_init_once;
113 
114  // Endpoint for the S3 service we will use
115  static std::string m_endpoint;
116  static std::string m_service;
117  static std::string m_region;
118  static std::string m_url_style;
119 
120  // S3 doesn't have the concept of "directories"; if a given name
121  // (some/path) has an object containing it as a prefix
122  // (some/path/foo.txt), then we say it's a directory. Hence, to
123  // "make" a directory, we create a zero-length sentinel file inside
124  // it; this static variable controls the name.
125  static std::string m_mkdir_sentinel;
126 
127  // Struct describing S3 credentials
128  struct Credentials {
129  std::string m_accesskey;
130  std::string m_secretkey;
131  };
132 
133  // Default credentials if not specified for a bucket.
134  static Credentials m_default_creds;
135 
136  // Map from bucket name to credentials to use
137  static std::unordered_map<std::string, Credentials> m_bucket_location_map;
138 
139  // Shared mutex to protect access to the bucket credentials map.
140  static std::shared_mutex m_bucket_auth_map_mutex;
141 
142  // Map for bucket-to-credential info.
143  static std::unordered_map<std::string, std::pair<Credentials, std::chrono::steady_clock::time_point>> m_bucket_auth_map;
144 };
145 
146 }
147 
148 #endif // XRDCLS3_S3FACTORY_HH
static void ResetCredCache()
virtual XrdCl::FilePlugIn * CreateFile(const std::string &url) override
Create a file plug-in for the given URL.
static const std::string & GetMkdirSentinel()
static void SetBucketCredentials(const std::string &bucket, const std::string &access_key, const std::string &secret_key)
static std::string_view ExtractHostname(const std::string_view url)
static void SetEndpoint(const std::string &endpoint)
static std::string PathEncode(const std::string_view url)
static void SetRegion(const std::string &region)
static void SetDefaultCredentials(const std::string &access_key, const std::string &secret_key)
static std::string CleanObjectName(const std::string &object)
static bool GenerateHttpUrl(const std::string &s3_url, std::string &https_url, std::string *obj_result, std::string &err_msg)
static bool GenerateV4Signature(const std::string &url, const std::string &verb, std::vector< std::pair< std::string, std::string >> &headers, std::string &auth_token, std::string &err_msg)
static std::tuple< std::string, std::string, bool > GetCredentialsForBucket(const std::string &bucket, std::string &err_msg)
static void SetService(const std::string &service)
static std::string_view TrimView(const std::string_view str)
virtual XrdCl::FileSystemPlugIn * CreateFileSystem(const std::string &url) override
Create a file system plug-in for the given URL.
Factory(const Factory &)=delete
static void SetUrlStyle(const std::string &url_style)
static std::string GetBucketFromHttpsUrl(const std::string &url)
An interface for file plug-ins.
An interface for file plug-ins.
Handle diagnostics.
Definition: XrdClLog.hh:101
const uint64_t kLogXrdClS3
XrdSysError Log
Definition: XrdConfig.cc:113