Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support env-defined saml issuer for PPMIs #9455

Merged
merged 2 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ SOCKET_PORT='3001'
# INVITATION_SHORTLINK='example.com'
# If true, all new orgs will default to being enterprise tier. Use for PPMIs
# IS_ENTERPRISE=false
# PPMI single tenant use only. Will set the SAML issuer to this value.
# SAML_ISSUER=''

# AUTHENTICATION
# AUTH_INTERNAL_DISABLED='false'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import {v4 as uuid} from 'uuid'
import zlib from 'zlib'

const getURLWithSAMLRequestParam = (destination: string, slug: string) => {
const issuer = process.env.SAML_ISSUER || `https://${process.env.HOST}/saml-metadata/${slug}`
const template = `
<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_${uuid()}" Version="2.0" IssueInstant="${new Date().toISOString()}" Destination="${destination}" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="https://${
process.env.HOST
}/saml/${slug}">
<saml:Issuer>https://${process.env.HOST}/saml-metadata/${slug}</saml:Issuer>
<saml:Issuer>${issuer}</saml:Issuer>
<samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" AllowCreate="false"/>
</samlp:AuthnRequest>
`
Expand Down
20 changes: 20 additions & 0 deletions packages/server/utils/getSAMLURLFromEmail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ import base64url from 'base64url'
import getSSODomainFromEmail from 'parabol-client/utils/getSSODomainFromEmail'
import {URL} from 'url'
import {DataLoaderWorker} from '../graphql/graphql'
import getKysely from '../postgres/getKysely'

const isSingleTenantSSO =
process.env.AUTH_INTERNAL_DISABLED === 'true' &&
process.env.AUTH_GOOGLE_DISABLED === 'true' &&
process.env.AUTH_MICROSOFT_DISABLED === 'true' &&
process.env.AUTH_SSO_DISABLED === 'false'

const urlWithRelayState = (url: string, isInvited?: boolean | null) => {
if (!isInvited) return url
Expand All @@ -18,6 +25,19 @@ const getSAMLURLFromEmail = async (
) => {
const domainName = getSSODomainFromEmail(email)
if (!domainName) return null
if (isSingleTenantSSO) {
// For PPMI use
const pg = getKysely()
const instanceURLres = await pg
.selectFrom('SAML')
.select('url')
.where('url', 'is not', null)
.limit(1)
.executeTakeFirst()
const instanceURL = instanceURLres?.url
if (!instanceURL) return null
return urlWithRelayState(instanceURL, isInvited)
}
const saml = await dataLoader.get('samlByDomain').load(domainName)
if (!saml) return null
const {url} = saml
Expand Down
Loading