Skip to content

Commit 12b081c

Browse files
chore: add rds connect utils
1 parent 73cbba2 commit 12b081c

4 files changed

Lines changed: 154 additions & 0 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# coding: utf-8
2+
"""
3+
Feature modules for specific service utilities.
4+
5+
This package contains specialized utility modules for various Volcengine services.
6+
"""
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# coding: utf-8
2+
from volcenginesdkcore.feature.rds.connect_utils import build_auth_token
3+
4+
__all__ = ['build_auth_token']
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# coding: utf-8
2+
"""
3+
RDS MySQL connection authentication utilities.
4+
5+
This module provides utilities for generating authentication tokens for RDS MySQL database connections.
6+
"""
7+
8+
from volcenginesdkcore.signv4 import SignerV4
9+
from volcenginesdkcore.endpoint.providers.default_provider import DefaultEndpointProvider
10+
11+
12+
def build_auth_token(credentials, db_user, instance_id, region, expires=None):
13+
"""
14+
Build an authentication token (presigned URL) for connecting to RDS MySQL database.
15+
16+
:param credentials: CredentialValue object with ak, sk, and optional session_token
17+
:param db_user: Database username
18+
:param instance_id: RDS instance ID
19+
:param region: Service region (e.g., 'cn-beijing')
20+
:param expires: Token expiration time in seconds (default: 900, i.e., 15 minutes)
21+
:return: Presigned URL string for database authentication
22+
:raises ValueError: If required parameters are missing or invalid
23+
"""
24+
# Validate inputs
25+
if credentials is None:
26+
raise ValueError("credentials must not be None")
27+
28+
if not hasattr(credentials, 'ak') or not credentials.ak:
29+
raise ValueError("credentials.ak must not be empty")
30+
31+
if not hasattr(credentials, 'sk') or not credentials.sk:
32+
raise ValueError("credentials.sk must not be empty")
33+
34+
if not db_user:
35+
raise ValueError("db_user must not be empty")
36+
37+
if not instance_id:
38+
raise ValueError("instance_id must not be empty")
39+
40+
if not region:
41+
raise ValueError("region must not be empty")
42+
43+
# Set default expiration time
44+
if expires is None or expires <= 0:
45+
expires = 900 # 15 minutes
46+
47+
# Service configuration
48+
service = 'rds_mysql'
49+
50+
# Get endpoint
51+
endpoint_provider = DefaultEndpointProvider()
52+
resolved_endpoint = endpoint_provider.endpoint_for(service, region)
53+
host = resolved_endpoint.host
54+
55+
# Build query parameters
56+
query = {
57+
'Action': 'ConnectDatabase',
58+
'Version': '2022-01-01',
59+
'X-Expires': str(expires),
60+
'DBUser': db_user,
61+
'InstanceId': instance_id,
62+
}
63+
64+
# Sign the URL
65+
signed_query = SignerV4.sign_url(
66+
path='/',
67+
method='GET',
68+
query=query,
69+
ak=credentials.ak,
70+
sk=credentials.sk,
71+
region=region,
72+
service=service,
73+
session_token=getattr(credentials, 'session_token', None)
74+
)
75+
76+
return signed_query

volcenginesdkcore/signv4.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,71 @@ def get_signing_secret_key_v4(sk, date, region, service):
9191
@staticmethod
9292
def hmac_sha256(key, msg):
9393
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
94+
95+
@staticmethod
96+
def sign_url(path, method, query, ak, sk, region, service, session_token=None):
97+
"""
98+
Generate presigned URL query string (AWS Signature V4)
99+
100+
:param path: Request path
101+
:param method: HTTP method (GET, POST, etc.)
102+
:param query: Query parameters dict
103+
:param ak: Access Key
104+
:param sk: Secret Key
105+
:param region: Service region
106+
:param service: Service name
107+
:param session_token: Optional session token
108+
:return: Query string with signature
109+
"""
110+
format_date = datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
111+
date = format_date[:8]
112+
113+
# Build credential scope
114+
credential_scope = '/'.join([date, region, service, 'request'])
115+
116+
# Add required query parameters
117+
query = dict(query) # Make a copy to avoid modifying original
118+
query['X-Date'] = format_date
119+
query['X-NotSignBody'] = ''
120+
query['X-Credential'] = ak + '/' + credential_scope
121+
query['X-Algorithm'] = 'HMAC-SHA256'
122+
query['X-SignedHeaders'] = ''
123+
query['X-SignedQueries'] = ''
124+
125+
if session_token:
126+
query['X-Security-Token'] = session_token
127+
128+
# Generate X-SignedQueries with all query parameter names
129+
query['X-SignedQueries'] = ';'.join(sorted(query.keys()))
130+
131+
# Build canonical request with empty body
132+
body_hash = hashlib.sha256(b'').hexdigest()
133+
canonical_request = '\n'.join([
134+
method,
135+
path,
136+
SignerV4.canonical_query(query),
137+
'\n', # Empty line for signed headers section
138+
'', # Empty signed headers list
139+
body_hash
140+
])
141+
sdk_core_logger.debug_sign("[sign_url] canonical_request:\n%s", canonical_request)
142+
143+
# Build string to sign
144+
signing_str = '\n'.join([
145+
'HMAC-SHA256',
146+
format_date,
147+
credential_scope,
148+
hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()
149+
])
150+
sdk_core_logger.debug_sign("[sign_url] string_to_sign:\n%s", signing_str)
151+
152+
# Calculate signature
153+
signing_key = SignerV4.get_signing_secret_key_v4(sk, date, region, service)
154+
signature = hmac.new(signing_key, signing_str.encode('utf-8'), hashlib.sha256).hexdigest()
155+
sdk_core_logger.debug_sign("[sign_url] calculated signature: %s", signature)
156+
157+
# Add signature to query
158+
query['X-Signature'] = signature
159+
160+
# Return encoded query string
161+
return urlencode(sorted(query.items()))

0 commit comments

Comments
 (0)