minimal working
This commit is contained in:
parent
4e36015873
commit
65bf284856
9 changed files with 306 additions and 234 deletions
130
UserEndpoint.ts
130
UserEndpoint.ts
|
@ -11,84 +11,10 @@ import {
|
||||||
IApiRequest,
|
IApiRequest,
|
||||||
IApiResponse,
|
IApiResponse,
|
||||||
} from "@rocket.chat/apps-engine/definition/api";
|
} from "@rocket.chat/apps-engine/definition/api";
|
||||||
const crypto = require("crypto");
|
import { SCIMUser } from "./scim/User";
|
||||||
|
|
||||||
export class UserEndpoint extends ApiEndpoint {
|
export class UserEndpoint extends ApiEndpoint {
|
||||||
public path = "Users/:id";
|
public path = "Users/:id";
|
||||||
// private http: IHttp;
|
|
||||||
// private authHeaders: { [key: string]: string };
|
|
||||||
|
|
||||||
// constructor(app: IApp) {
|
|
||||||
// super(app);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private async updateState(http: IHttp, read: IRead) {
|
|
||||||
// this.http = http;
|
|
||||||
// this.authHeaders = {
|
|
||||||
// "X-User-Id": await read
|
|
||||||
// .getEnvironmentReader()
|
|
||||||
// .getSettings()
|
|
||||||
// .getValueById("rc-user-id"),
|
|
||||||
// "X-Auth-Token": await read
|
|
||||||
// .getEnvironmentReader()
|
|
||||||
// .getSettings()
|
|
||||||
// .getValueById("rc-token"),
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
private async getAuthHeaders(
|
|
||||||
read: IRead
|
|
||||||
): Promise<{ [key: string]: string }> {
|
|
||||||
return {
|
|
||||||
"X-User-Id": await read
|
|
||||||
.getEnvironmentReader()
|
|
||||||
.getSettings()
|
|
||||||
.getValueById("rc-user-id"),
|
|
||||||
"X-Auth-Token": await read
|
|
||||||
.getEnvironmentReader()
|
|
||||||
.getSettings()
|
|
||||||
.getValueById("rc-token"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// private scimToRC(user: ISCIMUser): IUser {
|
|
||||||
// return {
|
|
||||||
// email: user.emails[0].value,
|
|
||||||
// name: user.displayName,
|
|
||||||
// password: "P@ssw0rd",
|
|
||||||
// username: user.userName,
|
|
||||||
// verified: true,
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
private scimToUserUpdate(userId: string, user: ISCIMUser): IUserUpdate {
|
|
||||||
return {
|
|
||||||
userId,
|
|
||||||
data: {
|
|
||||||
email: user.emails[0].value,
|
|
||||||
name: user.displayName,
|
|
||||||
username: user.userName,
|
|
||||||
active: user.active,
|
|
||||||
verified: true,
|
|
||||||
customFields: {
|
|
||||||
scimExternalId: user.externalId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private rcToSCIM(user: IUser): ISCIMUser {
|
|
||||||
return {
|
|
||||||
id: user._id,
|
|
||||||
externalId: "",
|
|
||||||
emails: [
|
|
||||||
{ type: "work", primary: true, value: user.emails[0].address },
|
|
||||||
],
|
|
||||||
displayName: user.name,
|
|
||||||
userName: user.username,
|
|
||||||
active: user.active === undefined ? true : user.active,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async get(
|
public async get(
|
||||||
request: IApiRequest,
|
request: IApiRequest,
|
||||||
|
@ -98,7 +24,7 @@ export class UserEndpoint extends ApiEndpoint {
|
||||||
http: IHttp,
|
http: IHttp,
|
||||||
persis: IPersistence
|
persis: IPersistence
|
||||||
): Promise<IApiResponse> {
|
): Promise<IApiResponse> {
|
||||||
let user: ISCIMUser;
|
let user: SCIMUser;
|
||||||
try {
|
try {
|
||||||
const response = await http.get(
|
const response = await http.get(
|
||||||
`http://localhost:3000/api/v1/users.info?userId=` +
|
`http://localhost:3000/api/v1/users.info?userId=` +
|
||||||
|
@ -113,12 +39,11 @@ export class UserEndpoint extends ApiEndpoint {
|
||||||
if (!response.content) throw new Error("Empty response");
|
if (!response.content) throw new Error("Empty response");
|
||||||
const o = JSON.parse(response.content);
|
const o = JSON.parse(response.content);
|
||||||
if (!o.success) throw new Error(o.error);
|
if (!o.success) throw new Error(o.error);
|
||||||
// user = this.rcToSCIM(o.user);
|
user = SCIMUser.fromRC(o.user);
|
||||||
user = this.rcToSCIM(o.user);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {
|
return {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json+scim",
|
"Content-Type": "application/scim+json",
|
||||||
},
|
},
|
||||||
status: HttpStatusCode.BAD_REQUEST,
|
status: HttpStatusCode.BAD_REQUEST,
|
||||||
content: { message: e.message },
|
content: { message: e.message },
|
||||||
|
@ -126,7 +51,7 @@ export class UserEndpoint extends ApiEndpoint {
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json+scim",
|
"Content-Type": "application/scim+json",
|
||||||
},
|
},
|
||||||
status: HttpStatusCode.FOUND,
|
status: HttpStatusCode.FOUND,
|
||||||
content: user,
|
content: user,
|
||||||
|
@ -141,7 +66,7 @@ export class UserEndpoint extends ApiEndpoint {
|
||||||
http: IHttp,
|
http: IHttp,
|
||||||
persis: IPersistence
|
persis: IPersistence
|
||||||
): Promise<IApiResponse> {
|
): Promise<IApiResponse> {
|
||||||
let user: ISCIMUser;
|
let user: SCIMUser;
|
||||||
try {
|
try {
|
||||||
const response = await http.post(
|
const response = await http.post(
|
||||||
"http://localhost:3000/api/v1/users.update",
|
"http://localhost:3000/api/v1/users.update",
|
||||||
|
@ -153,7 +78,7 @@ export class UserEndpoint extends ApiEndpoint {
|
||||||
content: JSON.stringify(
|
content: JSON.stringify(
|
||||||
this.scimToUserUpdate(
|
this.scimToUserUpdate(
|
||||||
request.params.id,
|
request.params.id,
|
||||||
request.content
|
SCIMUser.fromPlain(request.content)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -161,11 +86,11 @@ export class UserEndpoint extends ApiEndpoint {
|
||||||
if (!response.content) throw new Error("Empty response");
|
if (!response.content) throw new Error("Empty response");
|
||||||
const o = JSON.parse(response.content);
|
const o = JSON.parse(response.content);
|
||||||
if (!o.success) throw new Error(o.error);
|
if (!o.success) throw new Error(o.error);
|
||||||
user = this.rcToSCIM(o.user);
|
user = SCIMUser.fromRC(o.user);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {
|
return {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json+scim",
|
"Content-Type": "application/scim+json",
|
||||||
},
|
},
|
||||||
status: HttpStatusCode.BAD_REQUEST,
|
status: HttpStatusCode.BAD_REQUEST,
|
||||||
content: { message: e.message },
|
content: { message: e.message },
|
||||||
|
@ -173,7 +98,7 @@ export class UserEndpoint extends ApiEndpoint {
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json+scim",
|
"Content-Type": "application/scim+json",
|
||||||
},
|
},
|
||||||
status: HttpStatusCode.FOUND,
|
status: HttpStatusCode.FOUND,
|
||||||
content: user,
|
content: user,
|
||||||
|
@ -209,7 +134,7 @@ export class UserEndpoint extends ApiEndpoint {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {
|
return {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json+scim",
|
"Content-Type": "application/scim+json",
|
||||||
},
|
},
|
||||||
status: HttpStatusCode.BAD_REQUEST,
|
status: HttpStatusCode.BAD_REQUEST,
|
||||||
content: { message: e.message },
|
content: { message: e.message },
|
||||||
|
@ -217,9 +142,40 @@ export class UserEndpoint extends ApiEndpoint {
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json+scim",
|
"Content-Type": "application/scim+json",
|
||||||
},
|
},
|
||||||
status: HttpStatusCode.NO_CONTENT,
|
status: HttpStatusCode.NO_CONTENT,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getAuthHeaders(
|
||||||
|
read: IRead
|
||||||
|
): Promise<{ [key: string]: string }> {
|
||||||
|
return {
|
||||||
|
"X-User-Id": await read
|
||||||
|
.getEnvironmentReader()
|
||||||
|
.getSettings()
|
||||||
|
.getValueById("rc-user-id"),
|
||||||
|
"X-Auth-Token": await read
|
||||||
|
.getEnvironmentReader()
|
||||||
|
.getSettings()
|
||||||
|
.getValueById("rc-token"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private scimToUserUpdate(userId: string, user: SCIMUser): IUserUpdate {
|
||||||
|
return {
|
||||||
|
userId,
|
||||||
|
data: {
|
||||||
|
email: user.getEmail(),
|
||||||
|
name: user.displayName,
|
||||||
|
username: user.userName,
|
||||||
|
active: user.active,
|
||||||
|
verified: true,
|
||||||
|
customFields: {
|
||||||
|
scimExternalId: user.externalId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
225
UsersEndpoint.ts
225
UsersEndpoint.ts
|
@ -11,30 +11,103 @@ import {
|
||||||
IApiRequest,
|
IApiRequest,
|
||||||
IApiResponse,
|
IApiResponse,
|
||||||
} from "@rocket.chat/apps-engine/definition/api";
|
} from "@rocket.chat/apps-engine/definition/api";
|
||||||
const crypto = require("crypto");
|
import crypto = require("crypto");
|
||||||
|
import { SCIMError, SCIMErrorType } from "./scim/Error";
|
||||||
|
import { SCIMListResponse } from "./scim/ListResponse";
|
||||||
|
import { SCIMUser } from "./scim/User";
|
||||||
|
|
||||||
export class UsersEndpoint extends ApiEndpoint {
|
export class UsersEndpoint extends ApiEndpoint {
|
||||||
public path = "Users";
|
public path = "Users";
|
||||||
// private http: IHttp;
|
|
||||||
// private authHeaders: { [key: string]: string };
|
|
||||||
|
|
||||||
// constructor(app: IApp) {
|
public async get(
|
||||||
// super(app);
|
request: IApiRequest,
|
||||||
// }
|
endpoint: IApiEndpointInfo,
|
||||||
|
read: IRead,
|
||||||
|
modify: IModify,
|
||||||
|
http: IHttp,
|
||||||
|
persis: IPersistence
|
||||||
|
): Promise<IApiResponse> {
|
||||||
|
const list = new SCIMListResponse();
|
||||||
|
try {
|
||||||
|
const response = await http.get(
|
||||||
|
`http://localhost:3000/api/v1/users.list?query={"type":{"$eq":"user"}}&fields={"createdAt":1}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
...(await this.getAuthHeaders(read)),
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!response.content) {
|
||||||
|
throw new Error("Empty response");
|
||||||
|
}
|
||||||
|
const o = JSON.parse(response.content);
|
||||||
|
if (!o.success) {
|
||||||
|
throw new Error(o.error);
|
||||||
|
}
|
||||||
|
list.Resources = o.users.map(SCIMUser.fromRC);
|
||||||
|
list.totalResults = o.total;
|
||||||
|
} catch (e) {
|
||||||
|
const err = new SCIMError();
|
||||||
|
err.scimType = SCIMErrorType.INVALID_VALUE;
|
||||||
|
err.detail = e.message;
|
||||||
|
err.status = "400";
|
||||||
|
return err.toApiResponse();
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/scim+json",
|
||||||
|
},
|
||||||
|
status: HttpStatusCode.FOUND,
|
||||||
|
content: list,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// private async updateState(http: IHttp, read: IRead) {
|
public async post(
|
||||||
// this.http = http;
|
request: IApiRequest,
|
||||||
// this.authHeaders = {
|
endpoint: IApiEndpointInfo,
|
||||||
// "X-User-Id": await read
|
read: IRead,
|
||||||
// .getEnvironmentReader()
|
modify: IModify,
|
||||||
// .getSettings()
|
http: IHttp,
|
||||||
// .getValueById("rc-user-id"),
|
persis: IPersistence
|
||||||
// "X-Auth-Token": await read
|
): Promise<IApiResponse> {
|
||||||
// .getEnvironmentReader()
|
let user = request.content;
|
||||||
// .getSettings()
|
try {
|
||||||
// .getValueById("rc-token"),
|
const response = await http.post(
|
||||||
// };
|
`http://localhost:3000/api/v1/users.create`,
|
||||||
// }
|
{
|
||||||
|
headers: {
|
||||||
|
...(await this.getAuthHeaders(read)),
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
content: JSON.stringify(
|
||||||
|
this.scimToUserCreate(SCIMUser.fromPlain(user))
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!response.content) {
|
||||||
|
throw new Error("Empty response");
|
||||||
|
}
|
||||||
|
const o = JSON.parse(response.content);
|
||||||
|
if (!o.success) {
|
||||||
|
throw new Error(o.error);
|
||||||
|
}
|
||||||
|
user = SCIMUser.fromRC(o.user);
|
||||||
|
} catch (e) {
|
||||||
|
const err = new SCIMError();
|
||||||
|
err.scimType = SCIMErrorType.INVALID_VALUE;
|
||||||
|
err.detail = e.message;
|
||||||
|
err.status = "400";
|
||||||
|
return err.toApiResponse();
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/scim+json",
|
||||||
|
},
|
||||||
|
status: HttpStatusCode.CREATED,
|
||||||
|
content: user,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private async getAuthHeaders(
|
private async getAuthHeaders(
|
||||||
read: IRead
|
read: IRead
|
||||||
|
@ -51,17 +124,7 @@ export class UsersEndpoint extends ApiEndpoint {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// private scimToRC(user: ISCIMUser): IUser {
|
private scimToUserCreate(user: SCIMUser): IUserCreate {
|
||||||
// return {
|
|
||||||
// email: user.emails[0].value,
|
|
||||||
// name: user.displayName,
|
|
||||||
// password: "P@ssw0rd",
|
|
||||||
// username: user.userName,
|
|
||||||
// verified: true,
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
private scimToUserCreate(user: ISCIMUser): IUserCreate {
|
|
||||||
return {
|
return {
|
||||||
email: user.emails[0].value,
|
email: user.emails[0].value,
|
||||||
name: user.displayName,
|
name: user.displayName,
|
||||||
|
@ -69,108 +132,8 @@ export class UsersEndpoint extends ApiEndpoint {
|
||||||
password: crypto.randomBytes(64).toString("base64").slice(0, 64),
|
password: crypto.randomBytes(64).toString("base64").slice(0, 64),
|
||||||
verified: true,
|
verified: true,
|
||||||
customFields: {
|
customFields: {
|
||||||
scimExternalId: user.externalId
|
scimExternalId: user.externalId,
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private rcToSCIM(user: IUser): ISCIMUser {
|
|
||||||
return {
|
|
||||||
id: user._id,
|
|
||||||
externalId: "",
|
|
||||||
emails: [
|
|
||||||
{ type: "work", primary: true, value: user.emails[0].address },
|
|
||||||
],
|
|
||||||
displayName: user.name,
|
|
||||||
userName: user.username,
|
|
||||||
active: user.active === undefined ? true : user.active,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async get(
|
|
||||||
request: IApiRequest,
|
|
||||||
endpoint: IApiEndpointInfo,
|
|
||||||
read: IRead,
|
|
||||||
modify: IModify,
|
|
||||||
http: IHttp,
|
|
||||||
persis: IPersistence
|
|
||||||
): Promise<IApiResponse> {
|
|
||||||
let users: Array<ISCIMUser> = [];
|
|
||||||
try {
|
|
||||||
const response = await http.get(
|
|
||||||
`http://localhost:3000/api/v1/users.list?query={"type": {"$eq": "user"}}`,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
...(await this.getAuthHeaders(read)),
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (!response.content) throw new Error("Empty response");
|
|
||||||
const o = JSON.parse(response.content);
|
|
||||||
if (!o.success) throw new Error(o.error);
|
|
||||||
// user = this.rcToSCIM(o.user);
|
|
||||||
users = o.users.map(this.rcToSCIM);
|
|
||||||
} catch (e) {
|
|
||||||
return {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json+scim",
|
|
||||||
},
|
|
||||||
status: HttpStatusCode.BAD_REQUEST,
|
|
||||||
content: { message: e.message },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json+scim",
|
|
||||||
},
|
},
|
||||||
status: HttpStatusCode.FOUND,
|
|
||||||
content: users,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async post(
|
|
||||||
request: IApiRequest,
|
|
||||||
endpoint: IApiEndpointInfo,
|
|
||||||
read: IRead,
|
|
||||||
modify: IModify,
|
|
||||||
http: IHttp,
|
|
||||||
persis: IPersistence
|
|
||||||
): Promise<IApiResponse> {
|
|
||||||
let user = request.content;
|
|
||||||
try {
|
|
||||||
const response = await http.post(
|
|
||||||
"http://localhost:3000/api/v1/users.create",
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
...(await this.getAuthHeaders(read)),
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
content: JSON.stringify(
|
|
||||||
this.scimToUserCreate(request.content)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (!response.content) throw new Error("Empty response");
|
|
||||||
const o = JSON.parse(response.content);
|
|
||||||
if (!o.success) throw new Error(o.error);
|
|
||||||
// user = this.rcToSCIM(o.user);
|
|
||||||
user = this.rcToSCIM(o.user);
|
|
||||||
} catch (e) {
|
|
||||||
return {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json+scim",
|
|
||||||
},
|
|
||||||
status: HttpStatusCode.BAD_REQUEST,
|
|
||||||
content: { message: e.message },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json+scim",
|
|
||||||
},
|
|
||||||
status: HttpStatusCode.CREATED,
|
|
||||||
content: user,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1
rest.ts
1
rest.ts
|
@ -40,4 +40,5 @@ interface IUser {
|
||||||
name: string;
|
name: string;
|
||||||
username: string;
|
username: string;
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
|
16
scim.ts
16
scim.ts
|
@ -1,16 +0,0 @@
|
||||||
interface ISCIMUser {
|
|
||||||
id: string;
|
|
||||||
externalId: string;
|
|
||||||
userName: string;
|
|
||||||
name?: {
|
|
||||||
familyName: string;
|
|
||||||
givenName: string;
|
|
||||||
};
|
|
||||||
displayName: string;
|
|
||||||
active: boolean;
|
|
||||||
emails: Array<{
|
|
||||||
value: string;
|
|
||||||
type: string;
|
|
||||||
primary: boolean;
|
|
||||||
}>;
|
|
||||||
}
|
|
41
scim/Error.ts
Normal file
41
scim/Error.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { IApiResponse } from "@rocket.chat/apps-engine/definition/api";
|
||||||
|
|
||||||
|
export enum SCIMErrorType {
|
||||||
|
INVALID_FILTER = "invalidFilter",
|
||||||
|
TOO_MANY = "tooMany",
|
||||||
|
UNIQUENESS = "uniqueness",
|
||||||
|
MUTABILITY = "mutability",
|
||||||
|
INVALID_SYNTAX = "invalidSyntax",
|
||||||
|
INVALID_PATH = "invalidPath",
|
||||||
|
NO_TARGET = "noTarget",
|
||||||
|
INVALID_VALUE = "invalidValue",
|
||||||
|
INVALID_VERS = "invalidVers",
|
||||||
|
SENSITIVE = "sensitive",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SCIMErrorDetail {
|
||||||
|
TEMPORARY_REDIRECT = "The client is directed to repeat the same HTTP request at the location identified.",
|
||||||
|
PERMANENT_REDIRECT = "The client is directed to repeat the same HTTP request at the location identified.",
|
||||||
|
BAD_REQUEST = "Request is unparsable, syntactically incorrect, or violates schema.",
|
||||||
|
UNAUTHORIZED = "Authorization failure. The authorization header is invalid or missing.",
|
||||||
|
FORBIDDEN = "Operation is not permitted based on the supplied authorization.",
|
||||||
|
NOT_FOUND = "Specified resource or endpoint does not exist.",
|
||||||
|
CONFLICT = "The specified version number does not match the resource's latest version number, or a service provider refused to create a new, duplicate resource.",
|
||||||
|
PRECONDITION_FAILED = "Failed to update. Resource has changed on the server.",
|
||||||
|
PAYLOAD_TOO_LARGE = `{"maxOperations": 1000,"maxPayloadSize": 1048576}`,
|
||||||
|
INTERNAL_SERVER_ERROR = "An internal error.",
|
||||||
|
NOT_IMPLEMENTED = "Service provider does not support the request operation.",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SCIMError {
|
||||||
|
public readonly schemas: ["urn:ietf:params:scim:api:messages:2.0:Error"];
|
||||||
|
public scimType: SCIMErrorType;
|
||||||
|
public detail: SCIMErrorDetail | string;
|
||||||
|
public status: string;
|
||||||
|
public toApiResponse(): IApiResponse {
|
||||||
|
return {
|
||||||
|
status: parseInt(this.status, 10),
|
||||||
|
content: this,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
32
scim/Interfaces.ts
Normal file
32
scim/Interfaces.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { SCIMMeta } from "./Meta";
|
||||||
|
|
||||||
|
export interface ISCIMResource {
|
||||||
|
id: string;
|
||||||
|
externalId: string;
|
||||||
|
meta: SCIMMeta;
|
||||||
|
schemas: Array<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISCIMUserName {
|
||||||
|
familyName?: string;
|
||||||
|
givenName?: string;
|
||||||
|
}
|
||||||
|
export interface ISCIMUserEmail {
|
||||||
|
value: string;
|
||||||
|
primary: boolean;
|
||||||
|
}
|
||||||
|
export interface ISCIMListResponse {
|
||||||
|
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"];
|
||||||
|
totalResults: 2;
|
||||||
|
Resources: [
|
||||||
|
{
|
||||||
|
id: "2819c223-7f76-453a-919d-413861904646";
|
||||||
|
userName: "bjensen";
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "c75ad752-64ae-4823-840d-ffa80929976c";
|
||||||
|
userName: "jsmith";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
11
scim/ListResponse.ts
Normal file
11
scim/ListResponse.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { ISCIMResource } from "./Interfaces";
|
||||||
|
|
||||||
|
export class SCIMListResponse {
|
||||||
|
public readonly schemas = [
|
||||||
|
"urn:ietf:params:scim:api:messages:2.0:ListResponse",
|
||||||
|
];
|
||||||
|
public totalResults: number;
|
||||||
|
public startIndex: number = 1;
|
||||||
|
public itemsPerPage: number;
|
||||||
|
public Resources: Array<ISCIMResource>;
|
||||||
|
}
|
28
scim/Meta.ts
Normal file
28
scim/Meta.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
type ValueGetter = () => string;
|
||||||
|
|
||||||
|
export class SCIMMeta {
|
||||||
|
public resourceType: string;
|
||||||
|
public created: Date;
|
||||||
|
public lastModified: Date;
|
||||||
|
public version: string;
|
||||||
|
private id: ValueGetter;
|
||||||
|
|
||||||
|
constructor(resourceType: string, id: ValueGetter) {
|
||||||
|
this.id = id;
|
||||||
|
this.resourceType = resourceType;
|
||||||
|
const now = Date.now();
|
||||||
|
this.created = new Date(now);
|
||||||
|
this.lastModified = new Date(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get location(): string {
|
||||||
|
return `/${this.resourceType}s/${this.id()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJSON() {
|
||||||
|
return {
|
||||||
|
...this,
|
||||||
|
location: this.location,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
56
scim/User.ts
Normal file
56
scim/User.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import { ISCIMResource, ISCIMUserEmail, ISCIMUserName } from "./Interfaces";
|
||||||
|
import { SCIMMeta } from "./Meta";
|
||||||
|
|
||||||
|
export class SCIMUser implements ISCIMResource {
|
||||||
|
public static fromPlain(plain: SCIMUser): SCIMUser {
|
||||||
|
const user = new SCIMUser();
|
||||||
|
user.id = plain.id;
|
||||||
|
user.externalId = plain.externalId;
|
||||||
|
user.userName = plain.userName;
|
||||||
|
user.displayName = plain.displayName;
|
||||||
|
user.active = plain.active;
|
||||||
|
user.name = plain.name;
|
||||||
|
user.emails = plain.emails;
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromRC(rc: IUser): SCIMUser {
|
||||||
|
const user = new SCIMUser();
|
||||||
|
user.id = rc._id;
|
||||||
|
user.externalId = rc._id;
|
||||||
|
user.setEmail(rc.emails[0].address);
|
||||||
|
user.displayName = rc.name;
|
||||||
|
user.userName = rc.username;
|
||||||
|
user.meta.created = new Date(rc.createdAt);
|
||||||
|
user.meta.lastModified = new Date(rc.createdAt);
|
||||||
|
if (user.active !== undefined) {
|
||||||
|
user.active = user.active;
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly schemas = ["urn:ietf:params:scim:schemas:core:2.0:User"];
|
||||||
|
public meta: SCIMMeta;
|
||||||
|
public id: string;
|
||||||
|
public externalId: string;
|
||||||
|
public userName: string;
|
||||||
|
public displayName: string;
|
||||||
|
public active: boolean;
|
||||||
|
public name: ISCIMUserName = {};
|
||||||
|
public emails: Array<ISCIMUserEmail> = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.meta = new SCIMMeta("User", () => this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getEmail(): string {
|
||||||
|
if (this.emails.length > 0) {
|
||||||
|
return this.emails[0].value;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public setEmail(email: string) {
|
||||||
|
this.emails = [{ primary: true, value: email }];
|
||||||
|
}
|
||||||
|
}
|
Reference in a new issue