UNPKG

@bitblit/ratchet-warden-common

Version:

Typescript library to simplify using simplewebauthn and secondary auth methods over GraphQL

236 lines 9.48 kB
import { WardenContactType } from "../model/warden-contact-type.js"; import { Logger } from "@bitblit/ratchet-common/logger/logger"; import { StringRatchet } from "@bitblit/ratchet-common/lang/string-ratchet"; import { WardenLoginRequestType } from "../model/warden-login-request-type.js"; import { BooleanRatchet } from "@bitblit/ratchet-common/lang/boolean-ratchet"; export class WardenUtils { constructor() { } static extractContactsOfType(req, type) { let rval = null; if (req?.contactMethods) { rval = req.contactMethods.filter((s) => s.type === type).map((s) => s.value); } return rval; } static loginRequestErrors(req) { const rval = []; if (req) { if (req.type) { switch (req.type) { case WardenLoginRequestType.ExpiringToken: if (!req.userId && !WardenUtils.validContact(req.contact)) { rval.push('User ID or contact is required'); } if (!req.expiringToken) { rval.push('Expiring token is required'); } break; case WardenLoginRequestType.ThirdParty: if (req.thirdPartyToken) { if (!req.thirdPartyToken.thirdParty) { rval.push('Third party is required'); } if (!req.thirdPartyToken.token) { rval.push('Third party token is required'); } } else { rval.push('Third party auth is required'); } break; case WardenLoginRequestType.WebAuthn: if (!req.userId && !WardenUtils.validContact(req.contact)) { rval.push('User ID or contact is required'); } if (!req.webAuthn) { rval.push('Web authn is required'); } break; case WardenLoginRequestType.JwtTokenToRefresh: if (!req.jwtTokenToRefresh) { rval.push('JwtToken is required'); } break; default: rval.push('Unknown request type'); } } else { rval.push('Request type is null'); } } else { rval.push('Request is null'); } return rval; } static validLoginRequest(req) { return WardenUtils.loginRequestErrors(req).length === 0; } static stringToWardenContact(input) { let rval = null; const type = WardenUtils.stringToContactType(input); if (type) { rval = { type: type, value: input, }; } else { Logger.error('Failed to convert a string to a contact type', input); } return rval; } static teamRolesToRoles(teamRoles) { const rval = teamRoles?.length ? teamRoles.map((t) => WardenUtils.teamRoleToRoleString(t)) : []; return rval; } static roleStringsToTeamRoles(roles) { const rval = roles?.length ? roles.map((t) => WardenUtils.roleStringToTeamRole(t)) : []; return rval; } static roleStringToTeamRole(role) { let rval = null; if (role && role.indexOf('_/_') >= 0) { const sp = role.split('_/_'); rval = { teamId: sp[0], roleId: sp[1], }; } return rval; } static teamRoleToRoleString(tr) { let rval = null; if (tr?.roleId && tr?.teamId) { rval = tr.teamId + '_/_' + tr.roleId; } return rval; } static stringToContactType(input) { let rval = null; if (StringRatchet.trimToNull(input)) { rval = WardenUtils.stringIsEmailAddress(input) ? WardenContactType.EmailAddress : null; rval = !rval && WardenUtils.stringIsPhoneNumber(input) ? WardenContactType.TextCapablePhoneNumber : rval; } return rval; } static validContact(contact) { let rval = false; if (contact?.type && StringRatchet.trimToNull(contact?.value)) { switch (contact.type) { case WardenContactType.EmailAddress: rval = WardenUtils.stringIsEmailAddress(contact.value); break; case WardenContactType.TextCapablePhoneNumber: rval = WardenUtils.stringIsPhoneNumber(contact.value); break; default: rval = false; } } return rval; } static stringIsEmailAddress(value) { return !!value.match(/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/); } static stringIsPhoneNumber(value) { return !!value.match(/^[\\+]?[(]?[0-9]{3}[)]?[-\\s\\.]?[0-9]{3}[-\\s\\.]?[0-9]{4,6}$/im); } static stripWardenEntryToSummary(we) { const rval = we ? { userId: we.userId, userLabel: we.userLabel, contactMethods: we.contactMethods, webAuthnAuthenticatorSummaries: (we?.webAuthnAuthenticators || []).map((s) => WardenUtils.stripWardenWebAuthnEntryToSummary(s)), } : null; return rval; } static stripWardenWebAuthnEntryToSummary(we) { const rval = we ? { origin: we.origin, applicationName: we.applicationName, deviceLabel: we.deviceLabel, credentialIdBase64: we.credentialIdBase64, } : null; return rval; } static wrapperIsExpired(value) { const rval = value?.userObject?.exp && value.expirationEpochSeconds < Date.now() / 1000; return rval; } static userHasGlobalRole(user, roleId) { return WardenUtils.userHasGlobalRoles(user, [roleId], true); } static userHasRoleOnTeam(user, teamId, roleId) { return WardenUtils.userHasRolesOnTeam(user, teamId, [roleId], true); } static userHasAtLeastOneGlobalRole(user, roleIds) { return WardenUtils.userHasGlobalRoles(user, roleIds, false); } static userHasAtLeastOneRoleOnTeam(user, teamId, roleIds) { return WardenUtils.userHasRolesOnTeam(user, teamId, roleIds, false); } static userHasAllGlobalRoles(user, roleIds) { return WardenUtils.userHasGlobalRoles(user, roleIds, true); } static userHasAllRolesOnTeam(user, teamId, roleIds) { return WardenUtils.userHasRolesOnTeam(user, teamId, roleIds, true); } static userHasGlobalRoles(user, inRoleIds, combineWithAnd) { let rval = false; const roleIds = inRoleIds ? inRoleIds.map(r => StringRatchet.trimToNull(r)?.toLowerCase()) : null; if (user && roleIds && roleIds.length > 0) { const hasMap = user ? roleIds.map(r => !!user.globalRoleIds.find(gr => StringRatchet.trimToEmpty(gr).toLowerCase() === r)) : [false]; rval = combineWithAnd ? BooleanRatchet.allTrue(hasMap) : BooleanRatchet.anyTrue(hasMap); } return rval; } static userHasRolesOnTeam(user, inTeamId, inRoleIds, combineWithAnd) { let rval = false; const teamId = StringRatchet.trimToNull(inTeamId)?.toLowerCase(); const roleIds = inRoleIds ? inRoleIds.map(r => StringRatchet.trimToNull(r)?.toLowerCase()) : null; if (user && teamId && roleIds && roleIds.length > 0) { const hasMap = user ? roleIds.map(r => !!(user?.teamRoleMappings?.find(s => StringRatchet.trimToEmpty(s.teamId).toLowerCase() === teamId && StringRatchet.trimToEmpty(s.roleId).toLowerCase() === r))) : [false]; rval = combineWithAnd ? BooleanRatchet.allTrue(hasMap) : BooleanRatchet.anyTrue(hasMap); } return rval; } static userIsTeamMember(user, inTeamId) { return WardenUtils.userHasAnyRoleOnTeam(user, inTeamId); } static userHasAnyRoleOnTeam(user, inTeamId) { let rval = false; const teamId = StringRatchet.trimToNull(inTeamId)?.toLowerCase(); if (user && teamId) { rval = !!user.teamRoleMappings.find(s => StringRatchet.trimToNull(s.teamId).toLowerCase() === teamId); } return rval; } static usersTeamMemberships(user) { let rval = []; if (user) { const s = new Set(user.teamRoleMappings.map(s => StringRatchet.trimToNull(s.teamId).toLowerCase())); rval = Array.from(s); } return rval; } static wardenUserDecorationFromToken(jwt) { let rval = null; if (jwt) { rval = { userTokenData: jwt.user, proxyUserTokenData: jwt.proxy, userTokenExpirationSeconds: null, globalRoleIds: jwt.globalRoleIds, teamRoleMappings: jwt.teamRoleMappings }; } return rval; } } //# sourceMappingURL=warden-utils.js.map