Skip to content

Commit

Permalink
Merge pull request #84 from fardarter/master
Browse files Browse the repository at this point in the history
feat: adds azure identity capability
  • Loading branch information
jakeFeldman authored Feb 2, 2024
2 parents 1b80f04 + c55d587 commit 43e86a5
Show file tree
Hide file tree
Showing 4 changed files with 360 additions and 27 deletions.
47 changes: 35 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ To enable the provider, create or edit the file at `./config/plugins.js`.
This is an example `plugins.js` file for Azure storage:

```js

module.exports = ({ env }) => ({
upload: {
config: {
provider: "strapi-provider-upload-azure-storage",
providerOptions: {
authType: env("STORAGE_AUTH_TYPE", "default"),
account: env("STORAGE_ACCOUNT"),
accountKey: env("STORAGE_ACCOUNT_KEY"),//either account key or sas token is enough to make authentication
sasToken: env("STORAGE_ACCOUNT_SAS_TOKEN"),
Expand All @@ -55,19 +55,42 @@ module.exports = ({ env }) => ({
},
});

// For using azure identities, the correct authType is 'msi' or (provide it in the environment variable)

module.exports = ({ env }) => ({
upload: {
config: {
provider: "strapi-provider-upload-azure-storage",
providerOptions: {
authType: 'msi',
account: env("STORAGE_ACCOUNT"),
clientId: env("STORAGE_AZURE_CLIENT_ID"), // optional
serviceBaseURL: env("STORAGE_URL"), // optional
containerName: env("STORAGE_CONTAINER_NAME"),
defaultPath: "assets",
cdnBaseURL: env("STORAGE_CDN_URL"), // optional
defaultCacheControl: env("STORAGE_CACHE_CONTROL"), // optional
removeCN: env("REMOVE_CONTAINER_NAME"), // optional, if you want to remove container name from the URL
},
},
},
});

```

| Property | Required | Description |
| -------- | -------- | -------- |
| account | true | Azure account name |
| accountKey | true | Secret access key |
| sasToken | false | SAS Token, either accountKey or SASToken is required |
| serviceBaseURL | false | Base service URL to be used, optional. Defaults to `https://${account}.blob.core.windows.net` |
| containerName | true | Container name |
| defaultPath | true | The path to use when there is none being specified. Defaults to `assets` |
| cdnBaseURL | false | CDN base url |
| defaultCacheControl | false | Cache-Control header value for all uploaded files |
| removeCN | false | Set to true, to remove container name from azure URL |
| Property | Required | Description |
| ------------------- | ----------------------------------- | --------------------------------------------------------------------------------------------- |
| authType | true | Whether to use a SAS key ("default") or an identity ("msi") |
| account | true | Azure account name |
| accountKey | if 'authType 'default' | Secret access key |
| clientId | false (consumed if 'authType 'msi') | Azure Identity Client ID |
| sasToken | false | SAS Token, either accountKey or SASToken is required if 'authType is 'default' |
| serviceBaseURL | false | Base service URL to be used, optional. Defaults to `https://${account}.blob.core.windows.net` |
| containerName | true | Container name |
| defaultPath | true | The path to use when there is none being specified. Defaults to `assets` |
| cdnBaseURL | false | CDN base url |
| defaultCacheControl | false | Cache-Control header value for all uploaded files |
| removeCN | false | Set to true, to remove container name from azure URL |

### Security Middleware Configuration

Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
"scripts": {
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .js,.ts",
"lint:fix": "eslint . --ext .js,.ts --fix",
"prebuild": "rm -rf dist",
"prepublishOnly": "yarn build"
},
"directories": {
"lib": "./lib"
},
"dependencies": {
"@azure/identity": "^4.0.0",
"@azure/storage-blob": "^12.12.0"
},
"strapi": {
Expand Down
68 changes: 55 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DefaultAzureCredential } from '@azure/identity';
import {
AnonymousCredential,
BlobServiceClient,
Expand All @@ -6,10 +7,25 @@ import {
} from '@azure/storage-blob';
import internal from 'stream';

type Config = {
account: string;
type Config = DefaultConfig | ManagedIdentityConfig;

type DefaultConfig = {
authType: 'default';
accountKey: string;
sasToken: string;
account: string;
serviceBaseURL?: string;
containerName: string;
defaultPath: string;
cdnBaseURL?: string;
defaultCacheControl?: string;
removeCN?: string;
};

type ManagedIdentityConfig = {
authType: 'msi';
clientId?: string;
account: string;
serviceBaseURL?: string;
containerName: string;
defaultPath: string;
Expand Down Expand Up @@ -43,18 +59,36 @@ function getFileName(path: string, file: StrapiFile) {
}

function makeBlobServiceClient(config: Config) {
const account = trimParam(config.account);
const accountKey = trimParam(config.accountKey);
const sasToken = trimParam(config.sasToken);
const serviceBaseURL = getServiceBaseUrl(config);
// if accountKey doesn't contain value return below line
if (sasToken != '') {
const anonymousCredential = new AnonymousCredential();
return new BlobServiceClient(`${serviceBaseURL}${sasToken}`, anonymousCredential);

switch (config.authType) {
case 'default': {
const account = trimParam(config.account);
const accountKey = trimParam(config.accountKey);
const sasToken = trimParam(config.sasToken);
if (sasToken != '') {
const anonymousCredential = new AnonymousCredential();
return new BlobServiceClient(`${serviceBaseURL}${sasToken}`, anonymousCredential);
}
const sharedKeyCredential = new StorageSharedKeyCredential(account, accountKey);
const pipeline = newPipeline(sharedKeyCredential);
return new BlobServiceClient(serviceBaseURL, pipeline);
}
case 'msi': {
const clientId = trimParam(config.clientId);
if (clientId != null && clientId != '') {
return new BlobServiceClient(
serviceBaseURL,
new DefaultAzureCredential({ managedIdentityClientId: clientId })
);
}
return new BlobServiceClient(serviceBaseURL, new DefaultAzureCredential());
}
default: {
const exhaustiveCheck: never = config;
throw new Error(exhaustiveCheck);
}
}
const sharedKeyCredential = new StorageSharedKeyCredential(account, accountKey);
const pipeline = newPipeline(sharedKeyCredential);
return new BlobServiceClient(serviceBaseURL, pipeline);
}

const uploadOptions = {
Expand Down Expand Up @@ -109,12 +143,20 @@ async function handleDelete(
module.exports = {
provider: 'azure',
auth: {
authType: {
label: 'Authentication type (required, either "msi" or "default")',
type: 'text',
},
clientId: {
label: 'Azure Identity ClientId (consumed if authType is "msi" and passed as DefaultAzureCredential({ managedIdentityClientId: clientId }))',
type: 'text',
},
account: {
label: 'Account name (required)',
type: 'text',
},
accountKey: {
label: 'Secret access key (required)',
label: 'Secret access key (required if authType is "default")',
type: 'text',
},
serviceBaseURL: {
Expand Down
Loading

0 comments on commit 43e86a5

Please sign in to comment.