-
Notifications
You must be signed in to change notification settings - Fork 217
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
sso: tweak the checkRequiredSSO logic (match "*" at the end, if no ot…
…hers match) – and write tests to nail this down
- Loading branch information
1 parent
7a3da33
commit f024662
Showing
3 changed files
with
93 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { | ||
emailBelongsToDomain, | ||
getEmailDomain, | ||
checkRequiredSSO, | ||
} from "./auth-check-required-sso"; | ||
import { Strategy } from "./types/sso"; | ||
|
||
const SSO = { | ||
display: "", | ||
backgroundColor: "", | ||
public: false, | ||
doNotHide: true, | ||
updateOnLogin: true, | ||
} as const; | ||
|
||
describe("Check Required SSO", () => { | ||
test("getEmailDomain", () => { | ||
expect(getEmailDomain("[email protected]")).toBe("bar.com"); | ||
expect(getEmailDomain("[email protected]")).toBe("bar.co.uk"); | ||
}); | ||
|
||
test("emailBelongsToDomain", () => { | ||
expect(emailBelongsToDomain("foo.com", "foo.com")).toBe(true); | ||
expect(emailBelongsToDomain("bar.foo.com", "foo.com")).toBe(true); | ||
expect(emailBelongsToDomain("foo.com", "bar.com")).toBe(false); | ||
expect(emailBelongsToDomain("foo.com", "foo.co.uk")).toBe(false); | ||
expect(emailBelongsToDomain("foo.com", "foo.com.uk")).toBe(false); | ||
expect(emailBelongsToDomain("foobar.com", "bar.com")).toBe(false); | ||
expect(emailBelongsToDomain("foobar.com", "bar.com")).toBe(false); | ||
expect(emailBelongsToDomain("foobar.com", "*")).toBe(false); | ||
}); | ||
|
||
const foo = { name: "foo", exclusiveDomains: ["foo.co.uk"], ...SSO }; | ||
const bar = { name: "bar", exclusiveDomains: ["*"], ...SSO }; | ||
const baz = { | ||
name: "baz", | ||
exclusiveDomains: ["baz.com", "abc.com"], | ||
...SSO, | ||
}; | ||
|
||
test("checkRequiredSSO", () => { | ||
const strategies: Strategy[] = [foo, baz] as const; | ||
|
||
expect(checkRequiredSSO({ email: "[email protected]", strategies })?.name).toEqual( | ||
"baz", | ||
); | ||
expect( | ||
checkRequiredSSO({ email: "[email protected]", strategies })?.name, | ||
).toEqual("baz"); | ||
expect( | ||
checkRequiredSSO({ email: "[email protected]", strategies })?.name, | ||
).toEqual("foo"); | ||
// no match on naive substring from the right | ||
expect( | ||
checkRequiredSSO({ email: "[email protected]", strategies }), | ||
).toBeUndefined(); | ||
// no catch-all for an unrelated domain, returns no strategy | ||
expect( | ||
checkRequiredSSO({ email: "[email protected]", strategies }), | ||
).toBeUndefined(); | ||
}); | ||
|
||
test("checkRequiredSSO/catchall", () => { | ||
const strategies: Strategy[] = [foo, bar, baz] as const; | ||
|
||
expect(checkRequiredSSO({ email: "[email protected]", strategies })?.name).toEqual( | ||
"baz", | ||
); | ||
expect( | ||
checkRequiredSSO({ email: "[email protected]", strategies })?.name, | ||
).toEqual("baz"); | ||
expect( | ||
checkRequiredSSO({ email: "[email protected]", strategies })?.name, | ||
).toEqual("foo"); | ||
// this is the essential difference to above | ||
expect( | ||
checkRequiredSSO({ email: "[email protected]", strategies })?.name, | ||
).toEqual("bar"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,11 @@ interface Opts { | |
* which is configured to be an "exclusive" domain, then return the Strategy. | ||
* This also matches subdomains, i.e. "[email protected]" is goverend by "baz.edu". | ||
* | ||
* Optionally, if @specificStrategy is set, only that strategy is checked! | ||
* Special case: an sso domain "*" covers all domains, not covered by any other | ||
* exclusive SSO strategy. If there is just one such "*"-SSO strategy, it will deal with all | ||
* accounts. | ||
* | ||
* Optionally, if @specificStrategy is set, only that strategy or "*" is checked! | ||
*/ | ||
export function checkRequiredSSO(opts: Opts): Strategy | undefined { | ||
const { email, strategies, specificStrategy } = opts; | ||
|
@@ -29,11 +33,18 @@ export function checkRequiredSSO(opts: Opts): Strategy | undefined { | |
for (const strategy of strategies) { | ||
if (specificStrategy && specificStrategy !== strategy.name) continue; | ||
for (const ssoDomain of strategy.exclusiveDomains) { | ||
if (ssoDomain === "*") continue; // dealt with below | ||
if (emailBelongsToDomain(emailDomain, ssoDomain)) { | ||
return strategy; | ||
} | ||
} | ||
} | ||
// At this point, we either matched an existing strategy (above) or there is a "*" strategy | ||
for (const strategy of strategies) { | ||
if (strategy.exclusiveDomains.includes("*")) { | ||
return strategy; | ||
} | ||
} | ||
} | ||
|
||
export function getEmailDomain(email: string): string { | ||
|
@@ -43,15 +54,10 @@ export function getEmailDomain(email: string): string { | |
/** | ||
* This checks if the email's domain is either exactly the ssoDomain or a subdomain. | ||
* E.g. for "foo.edu", an email "[email protected]" is covered as well. | ||
* | ||
* Special case: an sso domain "*" covers all domains. This is kind of a complete "take over", | ||
* because all accounts on that instance of CoCalc have to go through that SSO mechanism. | ||
* Note: In that case, it makes no sense to have more than one SSO mechanism configured. | ||
*/ | ||
export function emailBelongsToDomain( | ||
emailDomain: string, | ||
ssoDomain: string, | ||
): boolean { | ||
if (ssoDomain === "*") return true; | ||
return emailDomain === ssoDomain || emailDomain.endsWith(`.${ssoDomain}`); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters