Skip to content

Commit

Permalink
feat: support OAuth scope
Browse files Browse the repository at this point in the history
* Rename `Audience` to `OAuth audience`
* Add `OAuth scope` field

Closes #4102
  • Loading branch information
nikku committed Mar 1, 2024
1 parent 0422c9c commit 519764b
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 9 deletions.
2 changes: 2 additions & 0 deletions app/lib/zeebe-api/zeebe-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const RESOURCE_TYPES = {
* @property {'oauth'} type
* @property {string} url
* @property {string} audience
* @property {string} [scope]
* @property {string} clientId
* @property {string} clientSecret
*/
Expand Down Expand Up @@ -323,6 +324,7 @@ class ZeebeAPI {
oAuth: {
url: endpoint.oauthURL,
audience: endpoint.audience,
scope: endpoint.scope,
clientId: endpoint.clientId,
clientSecret: endpoint.clientSecret,
cacheOnDisk: false
Expand Down
54 changes: 53 additions & 1 deletion app/test/spec/zeebe-api/zeebe-api-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,59 @@ describe('ZeebeAPI', function() {
});


describe('OAuth', function() {

it('should pass configuration', async () => {

// given
const deployResourceSpy = sinon.spy();

const ZBClientMock = sinon.spy(function() {
return {
deployResource: deployResourceSpy
};
});

const zeebeAPI = mockZeebeNode({
ZBClient: ZBClientMock
});

// when
await zeebeAPI.deploy({
filePath: 'filePath',
name: 'process.bpmn',
endpoint: {
type: AUTH_TYPES.OAUTH,
url: TEST_URL,
oauthURL: 'oauthURL',
audience: 'audience',
scope: 'scope',
clientId: 'clientId',
clientSecret: 'clientSecret'
}
});

// then
const [ url, config ] = ZBClientMock.getCall(0).args;

// ZBClient is invoked accordingly
expect(url).to.eql(TEST_URL);

expect(config.oAuth).to.include.keys({
audience: 'audience',
clientId: 'clientId',
clientSecret: 'clientSecret',
scope: 'scope',
url: 'oauthURL'
});

// deployment is executed appropriately
expect(deployResourceSpy).to.have.been.calledWith({ name: 'process.bpmn', process: undefined });
});

});


describe('tenant ID', function() {

it('should add tenant ID if exists', async () => {
Expand All @@ -1233,7 +1286,6 @@ describe('ZeebeAPI', function() {
type: AUTH_TYPES.OAUTH,
url: TEST_URL,
oauthURL: 'oauthURL',
audience: 'audience',
clientId: 'clientId',
clientSecret: 'clientSecret'
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export const CONTACT_POINT = 'Cluster endpoint';
export const DEPLOYMENT_NAME_HINT = 'Default value is the file name.';
export const CONTACT_POINT_HINT = 'http://localhost:26500';
export const OAUTH_URL = 'OAuth token URL';
export const AUDIENCE = 'Audience';
export const AUDIENCE = 'OAuth audience';
export const SCOPE = 'OAuth scope';
export const CLIENT_ID = 'Client ID';
export const CLIENT_SECRET = 'Client secret';
export const CLUSTER_URL = 'Cluster URL';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
CONTACT_POINT_HINT,
OAUTH_URL,
AUDIENCE,
SCOPE,
TENANT_ID,
CLIENT_ID,
CLIENT_SECRET,
Expand Down Expand Up @@ -80,6 +81,7 @@ export default class DeploymentPluginOverlay extends React.PureComponent {
contactPoint: validator.validateZeebeContactPoint,
oauthURL: validator.validateOAuthURL,
audience: validator.validateAudience,
scope: validator.validateScope,
clientId: validator.validateClientId,
clientSecret: validator.validateClientSecret,
camundaCloudClientId: validator.validateClientId,
Expand Down Expand Up @@ -138,7 +140,8 @@ export default class DeploymentPluginOverlay extends React.PureComponent {
'clientSecret',
'camundaCloudClientId',
'camundaCloudClientSecret',
'audience'
'audience',
'scope'
].includes(fieldName) && CONNECTION_ERROR_MESSAGES[failureReason];
case ERROR_REASONS.OAUTH_URL:
return fieldName === 'oauthURL' && CONNECTION_ERROR_MESSAGES[failureReason];
Expand All @@ -159,16 +162,21 @@ export default class DeploymentPluginOverlay extends React.PureComponent {
}

scheduleConnectionCheck = formValues => {
this.connectionChecker.check(formValues.endpoint);

const { endpoint } = formValues;

// empty scope shall not be passed
if (!endpoint.scope) {
delete endpoint.scope;
}

// Extract clusterId and clusterRegion as required by zeebeAPI for camundaCloud
if (endpoint.targetType === CAMUNDA_CLOUD && endpoint.camundaCloudClusterUrl) {
endpoint.camundaCloudClusterId = extractClusterId(endpoint.camundaCloudClusterUrl);
endpoint.camundaCloudClusterRegion = extractClusterRegion(endpoint.camundaCloudClusterUrl);
}

this.connectionChecker.check(endpoint);

this.setState({ configValues: formValues });
};

Expand Down Expand Up @@ -205,6 +213,10 @@ export default class DeploymentPluginOverlay extends React.PureComponent {
endpoint.camundaCloudClusterRegion = extractClusterRegion(endpoint.camundaCloudClusterUrl);
}

if (!endpoint.scope) {
delete endpoint.scope;
}

if (endpoint.authType === AUTH_TYPES.NONE) {
delete deployment.tenantId;
}
Expand Down Expand Up @@ -246,7 +258,7 @@ export default class DeploymentPluginOverlay extends React.PureComponent {
form => (
<form onSubmit={ form.handleSubmit }>
<Section>
<Section.Header> { OVERLAY_TITLE } </Section.Header>
<Section.Header>{ OVERLAY_TITLE }</Section.Header>
<Section.Body>
<fieldset className="fieldset">
<div className="fields">
Expand Down Expand Up @@ -339,6 +351,13 @@ export default class DeploymentPluginOverlay extends React.PureComponent {
fieldError={ this.endpointConfigurationFieldError }
validate={ validatorFunctionsByFieldNames.audience }
/>
<Field
name="endpoint.scope"
component={ TextInput }
label={ SCOPE }
fieldError={ this.endpointConfigurationFieldError }
validate={ validatorFunctionsByFieldNames.scope }
/>
</React.Fragment>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ export default class DeploymentPluginValidator {
return this.validateNonEmpty(value, AUDIENCE_MUST_NOT_BE_EMPTY);
};

validateScope = (value) => {
return null;

Check warning on line 58 in client/src/plugins/zeebe-plugin/deployment-plugin/DeploymentPluginValidator.js

View check run for this annotation

Codecov / codecov/patch

client/src/plugins/zeebe-plugin/deployment-plugin/DeploymentPluginValidator.js#L58

Added line #L58 was not covered by tests
};

validateClientId = (value) => {
return this.validateNonEmpty(value, CLIENT_ID_MUST_NOT_BE_EMPTY);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ describe('<DeploymentPluginModal> (Zeebe)', () => {
endpoint: {
targetType: SELF_HOSTED,
authType: AUTH_TYPES.OAUTH,
contactPoint: 'https://google.com',
contactPoint: 'https://google.com'
},
deployment: {
tenantId: 'tenant-1'
Expand Down Expand Up @@ -247,6 +247,75 @@ describe('<DeploymentPluginModal> (Zeebe)', () => {
});


describe('oAuth', () => {

it('should pass config on deploy', done => {

// given
const { wrapper } = createDeploymentPluginModal({
anchor,
onDeploy,
config: {
endpoint: {
targetType: SELF_HOSTED,
authType: AUTH_TYPES.OAUTH,
contactPoint: 'https://google.com',
audience: 'audience'
}
}
});

// when deploy
wrapper.find('form').simulate('submit');

// then
function onDeploy(values) {

const { endpoint } = values;

expect(endpoint.scope).not.to.exists;
expect(endpoint.audience).to.eql('audience');

done();
}
});


it('should pass <scope> on deploy', done => {

// given
const { wrapper } = createDeploymentPluginModal({
anchor,
onDeploy,
config: {
endpoint: {
targetType: SELF_HOSTED,
authType: AUTH_TYPES.OAUTH,
contactPoint: 'https://google.com',
audience: 'audience',
scope: 'scope'
}
}
});

// when deploy
wrapper.find('form').simulate('submit');

// then
function onDeploy(values) {

const { endpoint } = values;

expect(endpoint.scope).to.eql('scope');
expect(endpoint.audience).to.eql('audience');

done();
}
});

});


it('should extract clusterId and clusterRegion', done => {

// given
Expand All @@ -261,8 +330,7 @@ describe('<DeploymentPluginModal> (Zeebe)', () => {
} });

// when
const form = wrapper.find('form');
form.simulate('submit');
wrapper.find('form').simulate('submit');

// then
function onDeploy(values) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1432,6 +1432,7 @@ class TestDeploymentPlugin extends DeploymentPlugin {
contactPoint: '',
oauthURL: 'url',
audience: 'audience',
scope: 'scope',
rememberCredentials: false
} ];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ describe('<DeploymentPluginValidator> (Zeebe)', () => {
});


it.skip('should validate scope');


it('should validate client id', () => {

// given
Expand Down
2 changes: 2 additions & 0 deletions client/src/remote/ZeebeAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ function getEndpointConfiguration(endpoint) {
const {
authType,
audience,
scope,
targetType,
clientId,
clientSecret,
Expand All @@ -101,6 +102,7 @@ function getEndpointConfiguration(endpoint) {
url: contactPoint,
oauthURL,
audience,
scope,
clientId,
clientSecret
};
Expand Down
5 changes: 5 additions & 0 deletions client/src/remote/__tests__/ZeebeAPISpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ describe('<ZeebeAPI>', function() {
const contactPoint = 'contactPoint';
const oauthURL = 'oauthURL';
const audience = 'audience';
const scope = 'scope';
const clientId = 'oauthClientId';
const clientSecret = 'oauthClientSecret';
const endpoint = {
Expand All @@ -77,6 +78,7 @@ describe('<ZeebeAPI>', function() {
contactPoint,
oauthURL,
audience,
scope,
clientId,
clientSecret
};
Expand All @@ -91,6 +93,7 @@ describe('<ZeebeAPI>', function() {
url: contactPoint,
oauthURL,
audience,
scope,
clientId,
clientSecret
}
Expand Down Expand Up @@ -227,6 +230,7 @@ describe('<ZeebeAPI>', function() {
contactPoint,
oauthURL: 'oauthURL',
audience: 'audience',
scope: 'scope',
clientId: 'oauthClientId',
clientSecret: 'oauthClientSecret'
};
Expand All @@ -249,6 +253,7 @@ describe('<ZeebeAPI>', function() {
url: contactPoint,
oauthURL: 'oauthURL',
audience: 'audience',
scope: 'scope',
clientId: 'oauthClientId',
clientSecret: 'oauthClientSecret'
}
Expand Down

0 comments on commit 519764b

Please sign in to comment.