Skip to content

Commit

Permalink
feat: local authentication
Browse files Browse the repository at this point in the history
- introduce passport
- add a passport local strategy for authentication
- introduce Authentication interface to contain the multiple
auth checks (authenticate, checkAuth, logout)
- scram router module for authentication, logout, and auth check
- no op for no auth
- extend auth support to provide additional functions to all modules
for checking auth, logging out

Contributes-to: strimzi#106

Signed-off-by: Nic Townsend <[email protected]>
  • Loading branch information
nictownsend committed Dec 2, 2020
1 parent 2689890 commit ef8a510
Show file tree
Hide file tree
Showing 57 changed files with 1,339 additions and 406 deletions.
134 changes: 67 additions & 67 deletions client/Contexts/Introspect/ExpectationTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,130 +2,130 @@
* Copyright Strimzi authors.
* License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
*/
import {IntrospectionField} from "graphql";
import { IntrospectionField } from 'graphql';

/**
* An expectation is defined per entity we expect e.g. a Topic
*/
export interface Expectation {
/**
* The type is the GraphQL type for this expectation
*/
type: string;
/**
* The fields that we expect this entity to have
*/
fields?: {
/**
* The type is the GraphQL type for this expectation
* The field is defined as a GraphQL type
*/
type: string;
[key: string]: string;
};
/**
* The operations that we expect this entity to have
*/
operations?: {
/**
* The fields that we expect this entity to have
* The operation is validated using a callback
*/
fields?: {
/**
* The field is defined as a GraphQL type
*/
[key: string]: string;
};
[key: string]: OperationCallback;
};
/**
* The subscriptions that we expect this entity to have
*/
subscriptions?: {
/**
* The operations that we expect this entity to have
* The subscription is validated using a callback
*/
operations?: {
/**
* The operation is validated using a callback
*/
[key: string]: OperationCallback;
};
/**
* The subscriptions that we expect this entity to have
*/
subscriptions?: {
/**
* The subscription is validated using a callback
*/
[key: string]: SubscriptionCallback;
};
[key: string]: SubscriptionCallback;
};
}

/**
* A collection of expected entities
*/
export interface Expectations {
[key: string]: Expectation;
[key: string]: Expectation;
}

/**
* An entity expresses whether an expectation has been met or not
*/
export interface Entity {
/**
* The type is the GraphQL type for this expectation
*/
type: string;
/**
* The fields that we expect this entity to have
*/
fields: {
/**
* The type is the GraphQL type for this expectation
* Whether the field met the expectation
*/
type: string;
[key: string]: boolean;
};
/**
* The operations that we expect this entity to have
*/
operations: {
/**
* The fields that we expect this entity to have
* Whether the operation met the expectation
*/
fields: {
/**
* Whether the field met the expectation
*/
[key: string]: boolean;
};
[key: string]: boolean;
};
/**
* The subscriptions that we expect this entity to have
*/
subscriptions: {
/**
* The operations that we expect this entity to have
* Whether the subscription met the expectation
*/
operations: {
/**
* Whether the operation met the expectation
*/
[key: string]: boolean;
};
/**
* The subscriptions that we expect this entity to have
*/
subscriptions: {
/**
* Whether the subscription met the expectation
*/
[key: string]: boolean;
};
[key: string]: boolean;
};
}

/**
* The callback properties for an operation expectation
*/
export interface OperationCallbackProps {
/**
* All the queries defined
*/
queries: { [key: string]: IntrospectionField };
/**
* All the mutations defined
*/
mutations: { [key: string]: IntrospectionField };
/**
* All the queries defined
*/
queries: { [key: string]: IntrospectionField };
/**
* All the mutations defined
*/
mutations: { [key: string]: IntrospectionField };
}

/**
* The callback properties for a subscription expectation
*/
export interface SubscriptionCallbackProps {
/**
* All the subscriptions defined
*/
subscriptions: { [key: string]: IntrospectionField };
/**
* All the subscriptions defined
*/
subscriptions: { [key: string]: IntrospectionField };
}

/**
* The callback for an operation expectation
*/
export interface OperationCallback {
(props: OperationCallbackProps): boolean;
(props: OperationCallbackProps): boolean;
}

/**
* The callback for a subscription expectation
*/
export interface SubscriptionCallback {
(props: SubscriptionCallbackProps): boolean;
(props: SubscriptionCallbackProps): boolean;
}

/**
* A collection of entities
*/
export interface Entities {
[key: string]: Entity;
[key: string]: Entity;
}
128 changes: 65 additions & 63 deletions client/Contexts/Introspect/Introspection.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,82 +2,84 @@
* Copyright Strimzi authors.
* License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
*/
import expectationsBasic from "./mock/expectations-basic";
import expectationsMissingMutation from "./mock/expectations-missing-mutation";
import introspectionBasic from "./mock/mock-introspection";
import expectationsBasic from './mock/expectations-basic';
import expectationsMissingMutation from './mock/expectations-missing-mutation';
import introspectionBasic from './mock/mock-introspection';
import expectionsEmptyTopic from './mock/expectations-empty-topic';
import expectationsMissingType from "./mock/expectations-missing-type"
import introspectionMissingMutation from "./mock/mock-introspection-missing-mutation-type"
import introspectionUnsupportedFieldType from "./mock/mock-introspection-unsupported-field-type";
import introspectionUnindexable from "./mock/mock-introspection-unindexable";
import introspectionMissingMutationBlock from "./mock/mock-introspection-missing-mutation-block";
import introspectionWrongTypeOnTopic from "./mock/mock-introspection-wrong-type-on-topic";
import introspectionWrongTypeOnMutationBlock from "./mock/mock-introspection-wrong-type-on-mutation-block";
import {introspect} from "./Introspection";
import {entitiesBasic} from "./mock/mock-entities";

describe("Basic Introspection", () => {
it("should match", () =>
{
const introspected = introspect(expectationsBasic, introspectionBasic);
expect(introspected).toEqual(entitiesBasic);
});
import expectationsMissingType from './mock/expectations-missing-type';
import introspectionMissingMutation from './mock/mock-introspection-missing-mutation-type';
import introspectionUnsupportedFieldType from './mock/mock-introspection-unsupported-field-type';
import introspectionUnindexable from './mock/mock-introspection-unindexable';
import introspectionMissingMutationBlock from './mock/mock-introspection-missing-mutation-block';
import introspectionWrongTypeOnTopic from './mock/mock-introspection-wrong-type-on-topic';
import introspectionWrongTypeOnMutationBlock from './mock/mock-introspection-wrong-type-on-mutation-block';
import { introspect } from './Introspection';
import { entitiesBasic } from './mock/mock-entities';

describe('Basic Introspection', () => {
it('should match', () => {
const introspected = introspect(expectationsBasic, introspectionBasic);
expect(introspected).toEqual(entitiesBasic);
});
});

describe("Missing Type", () => {
it("should error", () =>
{
expect(() => {introspect(expectationsMissingType, introspectionBasic)}).toThrowError("Unable to find a type for Foo");

});
describe('Missing Type', () => {
it('should error', () => {
expect(() => {
introspect(expectationsMissingType, introspectionBasic);
}).toThrowError('Unable to find a type for Foo');
});
});

describe("Missing Mutation Type", () => {
it("should error", () =>
{
expect(() => {introspect(expectationsMissingMutation, introspectionMissingMutation)}).toThrowError("mutations is empty");

});
describe('Missing Mutation Type', () => {
it('should error', () => {
expect(() => {
introspect(expectationsMissingMutation, introspectionMissingMutation);
}).toThrowError('mutations is empty');
});
});

describe("Unsupported Field Type", () => {
it("should error", () =>
{
expect(() => {introspect(expectationsBasic, introspectionUnsupportedFieldType)}).toThrowError("Unsupported graphql kind UNION for String");

});
describe('Unsupported Field Type', () => {
it('should error', () => {
expect(() => {
introspect(expectationsBasic, introspectionUnsupportedFieldType);
}).toThrowError('Unsupported graphql kind UNION for String');
});
});

describe("Unindexable types", () => {
it("should error", () =>
{
expect(() => {introspect(expectationsBasic, introspectionUnindexable)}).toThrowError("key identified by name must be of type string");

});
describe('Unindexable types', () => {
it('should error', () => {
expect(() => {
introspect(expectationsBasic, introspectionUnindexable);
}).toThrowError('key identified by name must be of type string');
});
});

describe("Missing mutation block", () => {
it("should error", () =>
{
expect(() => {introspect(expectationsBasic, introspectionMissingMutationBlock)}).toThrowError("Unable to find a type for Mutation");

});
describe('Missing mutation block', () => {
it('should error', () => {
expect(() => {
introspect(expectationsBasic, introspectionMissingMutationBlock);
}).toThrowError('Unable to find a type for Mutation');
});
});

describe("Wrong type on mutation block", () => {
it("should error", () =>
{
expect(() => {introspect(expectationsMissingMutation, introspectionWrongTypeOnMutationBlock)}).toThrowError("mutations is empty");

});
describe('Wrong type on mutation block', () => {
it('should error', () => {
expect(() => {
introspect(
expectationsMissingMutation,
introspectionWrongTypeOnMutationBlock
);
}).toThrowError('mutations is empty');
});
});

describe("Non object type", () => {
it("should error", () =>
{
const introspected = introspect(expectionsEmptyTopic, introspectionWrongTypeOnTopic);
expect(introspected);

});
describe('Non object type', () => {
it('should error', () => {
const introspected = introspect(
expectionsEmptyTopic,
introspectionWrongTypeOnTopic
);
expect(introspected);
});
});

4 changes: 1 addition & 3 deletions client/Contexts/Introspect/Introspection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ const indexBy = <T>(array: readonly T[], propName: string) => {
return keyBy(array, (thing) => {
const prop = thing[propName];
if (typeof prop !== 'string') {
throw new Error(
`key identified by ${propName} must be of type string`
);
throw new Error(`key identified by ${propName} must be of type string`);
}
return prop;
});
Expand Down
6 changes: 4 additions & 2 deletions client/Contexts/Introspect/mock/expectations-basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ export default {
},
subscriptions: {
topicsUpdate: ({ subscriptions }) => {
return subscriptions['topicAdded'] &&
return (
subscriptions['topicAdded'] &&
subscriptions['topicAdded'].type.kind === 'OBJECT' &&
subscriptions['topicAdded'].type.name === 'Topic';
subscriptions['topicAdded'].type.name === 'Topic'
);
}, // function that checks for a named subscription which relates to topics
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import { Expectations } from '../ExpectationTypes';
export default {
Topic: {
type: 'Topic', // the GraphQL type name
fields: {}
fields: {},
},
} as Expectations;
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ export default {
operations: {
create: ({ mutations }) => {
if (Object.keys(mutations).length === 0) {
throw new Error("mutations is empty")
throw new Error('mutations is empty');
}
return true;
},
}
},
},
} as Expectations;
Loading

0 comments on commit ef8a510

Please sign in to comment.