-
Notifications
You must be signed in to change notification settings - Fork 3
Added on-prem IDM Deployment type #21
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
base: main
Are you sure you want to change the base?
Changes from 6 commits
10d8ec8
842addf
c756f58
706ffc6
de6b8e5
b0ff7e5
f214452
c6dda67
cb11e18
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ import { createHash, randomBytes } from 'crypto'; | |
| import url from 'url'; | ||
| import { v4 } from 'uuid'; | ||
|
|
||
| import { step } from '../api/AuthenticateApi'; | ||
| import { step, stepIdm } from '../api/AuthenticateApi'; | ||
| import { getServerInfo, getServerVersionInfo } from '../api/ServerInfoApi'; | ||
| import Constants from '../shared/Constants'; | ||
| import { State } from '../shared/State'; | ||
|
|
@@ -155,12 +155,23 @@ let adminClientId = fidcClientId; | |
| * @returns {string} cookie name | ||
| */ | ||
| async function determineCookieName(state: State): Promise<string> { | ||
| const data = await getServerInfo({ state }); | ||
| let cookieName = null; | ||
| try { | ||
| const data = await getServerInfo({ state }); | ||
| cookieName = data.cookieName; | ||
| } catch (e) { | ||
| if ( | ||
| e.response?.status !== 401 || | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In what scenarios would we get a 401 or Access Denied?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch, I don't think we should get a 401 here since the endpoint doesn't require any credentials, so we can remove that. We do need to keep the try-catch though since there is no server info endpoint for IDM, and we don't want the program to fail because of it. @skootrivir Could you remove this if check? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't we already know the deployment type by the time this call is being made? If so, it seems like it would be better to check the state to see if this is an IDM deployment.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it's the first time the connection is created, we don't know the deployment type, so this is necessary. I remember we tried to find a way to re-order things so that we try and get the cookie name after determining the deployment type, but it's not possible because we need the cookie name for determining the deployment type (at least for AM, Cloud, and ForgeOps deployments) since it is used in the requests that it makes. |
||
| e.response?.data.message !== 'Access Denied' | ||
| ) { | ||
| throw e; | ||
| } | ||
| } | ||
| debugMessage({ | ||
| message: `AuthenticateOps.determineCookieName: cookieName=${data.cookieName}`, | ||
| message: `AuthenticateOps.determineCookieName: cookieName=${cookieName}`, | ||
| state, | ||
| }); | ||
| return data.cookieName; | ||
| return cookieName; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -335,6 +346,7 @@ async function determineDeploymentType(state: State): Promise<string> { | |
| return deploymentType; | ||
|
|
||
| case Constants.CLASSIC_DEPLOYMENT_TYPE_KEY: | ||
| case Constants.IDM_DEPLOYMENT_TYPE_KEY: | ||
phalestrivir marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| debugMessage({ | ||
| message: `AuthenticateOps.determineDeploymentType: end [type=${deploymentType}]`, | ||
| state, | ||
|
|
@@ -409,10 +421,35 @@ async function determineDeploymentType(state: State): Promise<string> { | |
| }); | ||
| deploymentType = Constants.FORGEOPS_DEPLOYMENT_TYPE_KEY; | ||
| } else { | ||
| verboseMessage({ | ||
| message: `Classic deployment`['brightCyan'] + ` detected.`, | ||
| state, | ||
| }); | ||
| try { | ||
| const idmresponse = await stepIdm({ | ||
|
||
| body: {}, | ||
| config: {}, | ||
| state, | ||
| }); | ||
| if ( | ||
| idmresponse.status === 200 && | ||
| idmresponse.data?.authorization.authLogin | ||
| ) { | ||
| verboseMessage({ | ||
| message: | ||
| `Ping Identity IDM deployment`['brightCyan'] + | ||
| ` detected.`, | ||
| state, | ||
| }); | ||
| deploymentType = Constants.IDM_DEPLOYMENT_TYPE_KEY; | ||
| } else { | ||
| verboseMessage({ | ||
| message: `Classic deployment`['brightCyan'] + ` detected.`, | ||
| state, | ||
| }); | ||
| } | ||
| } catch { | ||
|
||
| verboseMessage({ | ||
| message: `Classic deployment`['brightCyan'] + ` detected.`, | ||
| state, | ||
| }); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -471,7 +508,18 @@ async function getFreshUserSessionToken({ | |
| 'X-OpenAM-Password': state.getPassword(), | ||
| }, | ||
| }; | ||
| let response = await step({ body: {}, config, state }); | ||
| let response; | ||
| try { | ||
| response = await step({ body: {}, config, state }); | ||
| } catch (e) { | ||
| if ( | ||
| e.response?.status !== 401 || | ||
| e.response?.data.message !== 'Access Denied' | ||
| ) { | ||
| throw e; | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| let skip2FA = null; | ||
| let steps = 0; | ||
|
|
@@ -553,6 +601,7 @@ async function getUserSessionToken( | |
| otpCallbackHandler: otpCallback, | ||
| state, | ||
| }); | ||
| if (!token) return token; | ||
|
||
| token.from_cache = false; | ||
| debugMessage({ | ||
| message: `AuthenticateOps.getUserSessionToken: fresh`, | ||
|
|
@@ -940,6 +989,7 @@ async function determineDeploymentTypeAndDefaultRealmAndVersion( | |
| state, | ||
| }); | ||
| state.setDeploymentType(await determineDeploymentType(state)); | ||
| if (state.getDeploymentType() === Constants.IDM_DEPLOYMENT_TYPE_KEY) return; | ||
| determineDefaultRealm(state); | ||
| debugMessage({ | ||
| message: `AuthenticateOps.determineDeploymentTypeAndDefaultRealmAndVersion: realm=${state.getRealm()}, type=${state.getDeploymentType()}`, | ||
|
|
@@ -1162,7 +1212,7 @@ export async function getTokens({ | |
| }); | ||
| const token = await getUserSessionToken(callbackHandler, state); | ||
| if (token) state.setUserSessionTokenMeta(token); | ||
| if (usingConnectionProfile && !token.from_cache) { | ||
| if (usingConnectionProfile && (!token || !token.from_cache)) { | ||
| saveConnectionProfile({ host: state.getHost(), state }); | ||
| } | ||
| await determineDeploymentTypeAndDefaultRealmAndVersion(state); | ||
|
|
@@ -1191,6 +1241,14 @@ export async function getTokens({ | |
| else { | ||
| throw new FrodoError(`Incomplete or no credentials`); | ||
| } | ||
| // Return IDM tokens for IDM deployment type | ||
| if (state.getDeploymentType() === Constants.IDM_DEPLOYMENT_TYPE_KEY) { | ||
| return { | ||
| subject: state.getUsername(), | ||
| host: state.getHost(), | ||
| realm: state.getRealm() ? state.getRealm() : 'root', | ||
| }; | ||
| } | ||
| if ( | ||
| state.getCookieValue() || | ||
| (state.getUseBearerTokenForAmApis() && state.getBearerToken()) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally I prefer consistency, but in this case I don't think the function name stepIdm makes sense. I think something like loginIdm or authenticateIdm would make more sense since IDM authentication doesn't have multiple steps like AM. Did I miss something here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the reason he named it that is because there is a
stepfunction that does the usual AM authentication calls, so he called this onestepIdmsince it is for IDM authentication which is why I didn't think too much of it. However, I agree thatstepIdmis not the best name for it, since the reason the AM one is calledstepis because it is stepping through a journey to authenticate the Admin user when we create connection profiles. For IDM, we don't have steps to authenticate, it's just part of any request that is made to IDM to include the username/password to authenticate, so this would be a good change for you to make @skootrivir, assuming we can't find a better alternative to determine if the deployment is an IDM deployment as mentioned in the next comment.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will re-name this to 'authenticateIdm'