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

Feature: allow client-sso-user to switch to sso-users only when using P4V #3

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
5 changes: 5 additions & 0 deletions loginhook/ExtUtils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ function ExtUtils.verifyHost()
return verify_host == "true"
end

function ExtUtils.allowUserClientP4V()
local allow_user_client_p4v = ExtUtils.iCfgData[ "allow-user-client-p4v-to-sso" ]
return allow_user_client_p4v == "true"
end

function ExtUtils.isClientUser( user )
-- users in the client-sso-users list are required to use P4LOGINSSO
local required = ExtUtils.iCfgData[ "client-sso-users" ]
Expand Down
111 changes: 62 additions & 49 deletions loginhook/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ function InstanceConfigFields()
[ "client-name-identifier" ] = "... Field within JSON web token containing unique user identifier.",
[ "user-identifier" ] = "... Trigger variable used as unique user identifier.",
[ "name-identifier" ] = "... Field within IdP response containing unique user identifier.",
[ "enable-logging" ] = "... Extension will write debug messages to a log if 'true'."
[ "enable-logging" ] = "... Extension will write debug messages to a log if 'true'.",
[ "allow-user-client-p4v-to-sso" ] = "... Extension will fall back to SSO for P4V client rather than P4LOGINSSO if 'true'."
}
end

Expand Down Expand Up @@ -182,6 +183,56 @@ local function validateOAuthResponse( token )
return false, code, err
end

local function webFlow()
-- Get a request id from the service, save it in requestId; do this every time
-- for every user, in case the same user logs in from multiple systems. We
-- will use this request identifier to get the status of the user later.
local userid = utils.userIdentifier( false )
local easy = curl.easy()
local safe_id = easy:escape( userid )
local ok, url, sdata = getData( utils.requestUrl( safe_id ) )
if ok then
requestId = sdata[ "request" ]
-- Save the instanceId used in rule-based routing so that all subsequent
-- requests are routed to the appropriate instance of the service.
instanceId = sdata[ "instanceId" ]
else
utils.debug( {
[ "AuthPreSSO" ] = "error: failed to get request identifier",
[ "http-code" ] = url,
[ "http-error" ] = tostring( sdata )
} )
utils.debug( {
[ "AuthPreSSO" ] = "info: ensure Service-URL is valid",
[ "Service-URL" ] = utils.gCfgData[ "Service-URL" ]
} )
-- At this point we know this user should be using SSO but there was a
-- problem, so try to inform them of what went wrong.
errorMessage = "error connecting to service, check extension logs"
return true, "unused", utils.serviceDownUrl(), false
end
local url = utils.loginUrl( sdata )
-- Return the login URL as a property named `data` for P4PHP clients.
--
-- N.B. Swarm uses a clientprog value of "P4PHP" when performing user login,
-- but the login will be handled differently in that case regardless.
local clientprog = Helix.Core.Server.GetVar( "clientprog" )
if string.find( clientprog, "P4PHP" ) then
utils.debug( { [ "AuthPreSSO" ] = "info: loginURL via 'data' for P4PHP client" } )
return true, url
end
-- Return the login URL as a property named `data` for Helix TeamHub.
if string.find( clientprog, "PilsnerHTHAdapter" ) then
utils.debug( { [ "AuthPreSSO" ] = "info: loginURL via 'data' for PilsnerHTHAdapter" } )
return true, url
end
utils.debug( {
[ "AuthPreSSO" ] = "info: invoking URL " .. url,
[ "user" ] = user
} )
return true, "unused", url, false
end

-- return -> (ret: bool, data: str, invokeURL: str, skipSSO: bool)
-- required: 'ret' if false indicates error
-- optional: 'data' has multiple meanings that depend on the use case
Expand Down Expand Up @@ -209,6 +260,14 @@ function AuthPreSSO()
return false
end
if hasClientUsers and isClientUser then
local currentClientProg = Helix.Core.Server.GetVar( "clientprog" )
if utils.allowUserClientP4V() and currentClientProg:match( "^Helix P4V" ) ~= nil then
utils.debug( {
[ "AuthPreSSO" ] = "info: P4V detected, will fallback to SSO",
[ "user" ] = user
} )
return webFlow()
end
-- This user must authenticate via a P4LOGINSSO program that returns
-- some form of "access token" (JWT) that the service will validate.
utils.debug( {
Expand Down Expand Up @@ -266,53 +325,7 @@ function AuthPreSSO()
return true, "unused", "skipping", true
end
end
-- Get a request id from the service, save it in requestId; do this every time
-- for every user, in case the same user logs in from multiple systems. We
-- will use this request identifier to get the status of the user later.
local userid = utils.userIdentifier( false )
local easy = curl.easy()
local safe_id = easy:escape( userid )
local ok, url, sdata = getData( utils.requestUrl( safe_id ) )
if ok then
requestId = sdata[ "request" ]
-- Save the instanceId used in rule-based routing so that all subsequent
-- requests are routed to the appropriate instance of the service.
instanceId = sdata[ "instanceId" ]
else
utils.debug( {
[ "AuthPreSSO" ] = "error: failed to get request identifier",
[ "http-code" ] = url,
[ "http-error" ] = tostring( sdata )
} )
utils.debug( {
[ "AuthPreSSO" ] = "info: ensure Service-URL is valid",
[ "Service-URL" ] = utils.gCfgData[ "Service-URL" ]
} )
-- At this point we know this user should be using SSO but there was a
-- problem, so try to inform them of what went wrong.
errorMessage = "error connecting to service, check extension logs"
return true, "unused", utils.serviceDownUrl(), false
end
local url = utils.loginUrl( sdata )
-- Return the login URL as a property named `data` for P4PHP clients.
--
-- N.B. Swarm uses a clientprog value of "P4PHP" when performing user login,
-- but the login will be handled differently in that case regardless.
local clientprog = Helix.Core.Server.GetVar( "clientprog" )
if string.find( clientprog, "P4PHP" ) then
utils.debug( { [ "AuthPreSSO" ] = "info: loginURL via 'data' for P4PHP client" } )
return true, url
end
-- Return the login URL as a property named `data` for Helix TeamHub.
if string.find( clientprog, "PilsnerHTHAdapter" ) then
utils.debug( { [ "AuthPreSSO" ] = "info: loginURL via 'data' for PilsnerHTHAdapter" } )
return true, url
end
utils.debug( {
[ "AuthPreSSO" ] = "info: invoking URL " .. url,
[ "user" ] = user
} )
return true, "unused", url, false
return webFlow()
end

local function validateResponse( response )
Expand Down Expand Up @@ -491,4 +504,4 @@ function RunCommand( args )
Helix.Core.Server.ClientOutputText( "unsupported command: " .. cmd .. "\n" )
end
return true
end
end