1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.opencastproject.transcription.microsoft.azure;
23
24 import org.apache.commons.codec.binary.Base64;
25 import org.apache.commons.codec.digest.HmacAlgorithms;
26 import org.apache.commons.codec.digest.HmacUtils;
27 import org.apache.commons.lang3.StringUtils;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import java.net.URLEncoder;
32 import java.nio.charset.StandardCharsets;
33 import java.nio.file.Paths;
34 import java.text.SimpleDateFormat;
35 import java.time.Instant;
36 import java.time.temporal.ChronoUnit;
37 import java.util.ArrayList;
38 import java.util.Date;
39 import java.util.List;
40 import java.util.TimeZone;
41
42 import javax.crypto.Mac;
43
44 public class MicrosoftAzureAuthorization {
45
46 private static final Logger logger = LoggerFactory.getLogger(MicrosoftAzureStorageClient.class);
47 public static final String AZURE_STORAGE_VERSION = "2022-11-02";
48 public static final String AZURE_BLOB_STORE_URL_SUFFIX = "blob.core.windows.net";
49
50 private final String azureStorageAccountName;
51 private final String azureAccountAccessKey;
52
53 public MicrosoftAzureAuthorization(String azureStorageAccountName, String azureAccountAccessKey)
54 throws MicrosoftAzureStorageClientException {
55 this.azureStorageAccountName = StringUtils.trimToEmpty(azureStorageAccountName);
56 this.azureAccountAccessKey = StringUtils.trimToEmpty(azureAccountAccessKey);
57 if (StringUtils.isEmpty(azureStorageAccountName)) {
58 throw new MicrosoftAzureStorageClientException("Azure storage account name not set.");
59 }
60 if (StringUtils.isEmpty(azureAccountAccessKey)) {
61 throw new MicrosoftAzureStorageClientException("Azure storage account key not set.");
62 }
63 }
64
65 public String getAzureStorageAccountName() {
66 return azureStorageAccountName;
67 }
68
69 String generateAccountSASToken(String signedPermissions, String signedResourceType,
70 Date signedStart, Date signedExpiry, String signedIP, String signedEncryptionScope) {
71
72 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
73 df.setTimeZone(TimeZone.getTimeZone("UTC"));
74 List<String> queryArgs = new ArrayList<>();
75 StringBuilder stringBuilder = new StringBuilder();
76
77
78
79
80
81
82
83
84
85
86
87
88 stringBuilder.append(azureStorageAccountName + "\n");
89 stringBuilder.append(StringUtils.trimToEmpty(signedPermissions) + "\n");
90 queryArgs.add("sp=" + StringUtils.trimToEmpty(signedPermissions));
91 stringBuilder.append("b" + "\n");
92 queryArgs.add("ss=b");
93 stringBuilder.append(StringUtils.trimToEmpty(signedResourceType) + "\n");
94 queryArgs.add("srt=" + StringUtils.trimToEmpty(signedResourceType));
95 Date startDate = signedStart;
96 if (startDate == null) {
97 startDate = Date.from(Instant.now().minus(15, ChronoUnit.MINUTES));
98 }
99 stringBuilder.append(df.format(startDate) + "\n");
100 queryArgs.add("st=" + df.format(startDate));
101 Date endDate = signedExpiry;
102 if (endDate == null) {
103 endDate = Date.from(Instant.now().plus(1, ChronoUnit.DAYS));
104 }
105 stringBuilder.append(df.format(endDate) + "\n");
106 queryArgs.add("se=" + df.format(endDate));
107 stringBuilder.append(StringUtils.trimToEmpty(signedIP) + "\n");
108 if (StringUtils.isNotBlank(signedIP)) {
109 queryArgs.add("sip=" + StringUtils.trimToEmpty(signedIP));
110 }
111 stringBuilder.append("https" + "\n");
112 queryArgs.add("spr=https");
113 stringBuilder.append(AZURE_STORAGE_VERSION + "\n");
114 queryArgs.add("sv=" + AZURE_STORAGE_VERSION);
115 stringBuilder.append(StringUtils.trimToEmpty(signedEncryptionScope) + "\n");
116 if (StringUtils.isNotBlank(signedEncryptionScope)) {
117 queryArgs.add("ses=" + StringUtils.trimToEmpty(signedEncryptionScope));
118 }
119 String stringToSign = stringBuilder.toString();
120
121 Mac initializedMac = HmacUtils.getInitializedMac(HmacAlgorithms.HMAC_SHA_256,
122 Base64.decodeBase64(azureAccountAccessKey));
123 byte[] signedString = initializedMac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
124 String signature = Base64.encodeBase64String(signedString);
125 queryArgs.add("sig=" + URLEncoder.encode(signature, StandardCharsets.UTF_8));
126 return StringUtils.joinWith("&", queryArgs.toArray());
127 }
128
129 String generateServiceSasToken(String signedPermissions, Date signedStart, Date signedExpiry, String resource,
130 String signedResource) {
131 return generateServiceSasToken(signedPermissions, signedStart, signedExpiry, resource, null, null, null,
132 signedResource, null, null, null, null, null, null, null, null);
133 }
134
135 String generateServiceSasToken(String signedPermissions, Date signedStart, Date signedExpiry, String resource,
136 String signedIdentifier, String signedIP, String signedVersion, String signedResource,
137 String signedDirectoryDepth, String signedSnapshotTime, String signedEncryptionScope,
138 String rscc, String rscd, String rsce, String rscl, String rsct) {
139
140
141 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
142 df.setTimeZone(TimeZone.getTimeZone("UTC"));
143 List<String> queryArgs = new ArrayList<>();
144 StringBuilder stringBuilder = new StringBuilder();
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164 stringBuilder.append(StringUtils.trimToEmpty(signedPermissions) + "\n");
165 queryArgs.add("sp=" + StringUtils.trimToEmpty(signedPermissions));
166
167 Date startDate = signedStart;
168 if (startDate == null) {
169 startDate = Date.from(Instant.now().minus(15, ChronoUnit.MINUTES));
170 }
171 stringBuilder.append(df.format(startDate) + "\n");
172 queryArgs.add("st=" + df.format(startDate));
173
174 Date endDate = signedExpiry;
175 if (endDate == null) {
176 endDate = Date.from(Instant.now().plus(1, ChronoUnit.DAYS));
177 }
178 stringBuilder.append(df.format(endDate) + "\n");
179 queryArgs.add("se=" + df.format(endDate));
180
181 String canonicalizedResource = Paths.get("/blob", azureStorageAccountName, StringUtils.trimToEmpty(resource))
182 .normalize().toString();
183 if (StringUtils.endsWith(canonicalizedResource,"/")) {
184 canonicalizedResource = StringUtils.substring(canonicalizedResource, 0, canonicalizedResource.length() - 1);
185 }
186 stringBuilder.append(canonicalizedResource + "\n");
187
188 stringBuilder.append(StringUtils.trimToEmpty(signedIdentifier) + "\n");
189 if (StringUtils.isNotBlank(signedIdentifier)) {
190 queryArgs.add("si=" + StringUtils.trimToEmpty(signedIdentifier));
191 }
192
193 stringBuilder.append(StringUtils.trimToEmpty(signedIP) + "\n");
194 if (StringUtils.isNotBlank(signedIP)) {
195 queryArgs.add("sip=" + StringUtils.trimToEmpty(signedIP));
196 }
197
198 stringBuilder.append("https" + "\n");
199 queryArgs.add("spr=https");
200
201 if (StringUtils.isNotBlank(signedVersion)) {
202 stringBuilder.append(StringUtils.trimToEmpty(signedVersion) + "\n");
203 queryArgs.add("sv=" + StringUtils.trimToEmpty(signedVersion));
204 } else {
205 stringBuilder.append(AZURE_STORAGE_VERSION + "\n");
206 queryArgs.add("sv=" + AZURE_STORAGE_VERSION);
207 }
208
209 stringBuilder.append(StringUtils.trimToEmpty(signedResource) + "\n");
210 if (StringUtils.isNotBlank(signedResource)) {
211 queryArgs.add("sr=" + StringUtils.trimToEmpty(signedResource));
212 }
213
214 if (StringUtils.isNotBlank(signedDirectoryDepth)) {
215 queryArgs.add("sdd=" + StringUtils.trimToEmpty(signedDirectoryDepth));
216 }
217 stringBuilder.append(StringUtils.trimToEmpty(signedSnapshotTime) + "\n");
218
219
220
221
222 stringBuilder.append(StringUtils.trimToEmpty(signedEncryptionScope) + "\n");
223 if (StringUtils.isNotBlank(signedEncryptionScope)) {
224 queryArgs.add("ses=" + StringUtils.trimToEmpty(signedEncryptionScope));
225 }
226
227 stringBuilder.append(StringUtils.trimToEmpty(rscc) + "\n");
228 if (StringUtils.isNotBlank(rscc)) {
229 queryArgs.add("rscc=" + StringUtils.trimToEmpty(rscc));
230 }
231
232 stringBuilder.append(StringUtils.trimToEmpty(rscd) + "\n");
233 if (StringUtils.isNotBlank(rscd)) {
234 queryArgs.add("rscd=" + StringUtils.trimToEmpty(rscd));
235 }
236
237 stringBuilder.append(StringUtils.trimToEmpty(rsce) + "\n");
238 if (StringUtils.isNotBlank(rsce)) {
239 queryArgs.add("rsce=" + StringUtils.trimToEmpty(rsce));
240 }
241
242 stringBuilder.append(StringUtils.trimToEmpty(rscl) + "\n");
243 if (StringUtils.isNotBlank(rscl)) {
244 queryArgs.add("rscl=" + StringUtils.trimToEmpty(rscl));
245 }
246
247 stringBuilder.append(StringUtils.trimToEmpty(rsct));
248 if (StringUtils.isNotBlank(rsct)) {
249 queryArgs.add("rsct=" + StringUtils.trimToEmpty(rsct));
250 }
251 String stringToSign = stringBuilder.toString();
252
253 Mac initializedMac = HmacUtils.getInitializedMac(HmacAlgorithms.HMAC_SHA_256,
254 Base64.decodeBase64(azureAccountAccessKey));
255 byte[] signedString = initializedMac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
256 String signature = Base64.encodeBase64String(signedString);
257 queryArgs.add("sig=" + URLEncoder.encode(signature, StandardCharsets.UTF_8));
258 return StringUtils.joinWith("&", queryArgs.toArray());
259 }
260 }