mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-24 16:17:11 +08:00
Improve login errors on missing roles (#61307)
This commit is contained in:
@@ -55,6 +55,14 @@ var (
|
||||
ErrPassswordlessLoginBySSOUser = &trace.AccessDeniedError{
|
||||
Message: "SSO user cannot login using passwordless",
|
||||
}
|
||||
|
||||
// ErrNonExistingRoleAssigned is issued if the user has a role that doesn't exist in
|
||||
// Teleport. We have a check which prevents directly assigned roles to be deleted by auth,
|
||||
// but it can be bypassed in some circumstances. E.g. roles generated by a plugin are
|
||||
// deleted during plugin cleanup.
|
||||
ErrNonExistingRoleAssigned = &trace.AccessDeniedError{
|
||||
Message: "User is assigned one or more roles that no longer exist in Teleport. Ask your cluster admin to verify and remove direct assignments to roles which no longer exist.",
|
||||
}
|
||||
)
|
||||
|
||||
// AuthPreference defines the authentication preferences for a specific
|
||||
|
||||
@@ -343,6 +343,9 @@ func (g *Generator) postProcess(ctx context.Context, state *userloginstate.UserL
|
||||
// Make sure all the roles exist. If they don't, error out.
|
||||
for _, role := range state.Spec.Roles {
|
||||
if _, err := g.access.GetRole(ctx, role); err != nil {
|
||||
if trace.IsNotFound(err) {
|
||||
return trace.Wrap(types.ErrNonExistingRoleAssigned)
|
||||
}
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,11 @@ const (
|
||||
// auth connector's callback was encountered.
|
||||
LoginFailedBadCallbackRedirectURL = "/web/msg/error/login/callback"
|
||||
|
||||
// LoginFailedBadCallbackMissingRoleRedirectURL is a redirect URL when an auth connector
|
||||
// callback failed because it couldn't find the user's calculated role (from auth connector
|
||||
// mapping) in the backend.
|
||||
LoginFailedBadCallbackMissingRoleRedirectURL = "/web/msg/error/login/callback_missing_role"
|
||||
|
||||
// LoginFailedUnauthorizedRedirectURL is a redirect URL for when an SSO authenticates successfully,
|
||||
// but the user has no matching roles in Teleport.
|
||||
LoginFailedUnauthorizedRedirectURL = "/web/msg/error/login/auth"
|
||||
|
||||
+15
-10
@@ -2312,13 +2312,13 @@ func (h *Handler) githubLoginWeb(w http.ResponseWriter, r *http.Request, p httpr
|
||||
req, err := ParseSSORequestParams(r)
|
||||
if err != nil {
|
||||
logger.ErrorContext(r.Context(), "Failed to extract SSO parameters from request", "error", err)
|
||||
return client.LoginFailedRedirectURL
|
||||
return sso.LoginFailedRedirectURL
|
||||
}
|
||||
|
||||
remoteAddr, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
logger.ErrorContext(r.Context(), "Failed to parse request remote address", "error", err)
|
||||
return client.LoginFailedRedirectURL
|
||||
return sso.LoginFailedRedirectURL
|
||||
}
|
||||
|
||||
response, err := h.cfg.ProxyClient.CreateGithubAuthRequest(r.Context(), types.GithubAuthRequest{
|
||||
@@ -2331,7 +2331,7 @@ func (h *Handler) githubLoginWeb(w http.ResponseWriter, r *http.Request, p httpr
|
||||
})
|
||||
if err != nil {
|
||||
logger.ErrorContext(r.Context(), "Error creating auth request", "error", err)
|
||||
return client.LoginFailedRedirectURL
|
||||
return sso.LoginFailedRedirectURL
|
||||
}
|
||||
|
||||
return response.RedirectURL
|
||||
@@ -2404,10 +2404,10 @@ func (h *Handler) githubCallback(w http.ResponseWriter, r *http.Request, p httpr
|
||||
}
|
||||
}
|
||||
if errors.Is(err, auth.ErrGithubNoTeams) {
|
||||
return client.LoginFailedUnauthorizedRedirectURL
|
||||
return sso.LoginFailedUnauthorizedRedirectURL
|
||||
}
|
||||
|
||||
return client.LoginFailedBadCallbackRedirectURL
|
||||
return sso.LoginFailedBadCallbackRedirectURL
|
||||
}
|
||||
|
||||
// if we created web session, set session cookie and redirect to original url
|
||||
@@ -2424,7 +2424,7 @@ func (h *Handler) githubCallback(w http.ResponseWriter, r *http.Request, p httpr
|
||||
|
||||
if err := SSOSetWebSessionAndRedirectURL(w, r, res, true); err != nil {
|
||||
logger.ErrorContext(r.Context(), "Error setting web session.", "error", err)
|
||||
return client.LoginFailedRedirectURL
|
||||
return sso.LoginFailedRedirectURL
|
||||
}
|
||||
|
||||
if dwt := response.Session.GetDeviceWebToken(); dwt != nil {
|
||||
@@ -2443,7 +2443,7 @@ func (h *Handler) githubCallback(w http.ResponseWriter, r *http.Request, p httpr
|
||||
logger.InfoContext(r.Context(), "Callback is redirecting to console login")
|
||||
if len(response.Req.SSHPubKey)+len(response.Req.TLSPubKey) == 0 {
|
||||
logger.ErrorContext(r.Context(), "Not a web or console login request")
|
||||
return client.LoginFailedRedirectURL
|
||||
return sso.LoginFailedRedirectURL
|
||||
}
|
||||
|
||||
redirectURL, err := ConstructSSHResponse(AuthParams{
|
||||
@@ -2459,7 +2459,7 @@ func (h *Handler) githubCallback(w http.ResponseWriter, r *http.Request, p httpr
|
||||
})
|
||||
if err != nil {
|
||||
logger.ErrorContext(r.Context(), "Error constructing ssh response", "error", err)
|
||||
return client.LoginFailedRedirectURL
|
||||
return sso.LoginFailedRedirectURL
|
||||
}
|
||||
|
||||
return redirectURL.String()
|
||||
@@ -3162,6 +3162,11 @@ func (h *Handler) mfaLoginFinishSession(w http.ResponseWriter, r *http.Request,
|
||||
case errors.Is(err, types.ErrPassswordlessLoginBySSOUser):
|
||||
return nil, trace.Wrap(err)
|
||||
|
||||
// Return a friendlier error if the user has assigned a role that doesn't exist in the
|
||||
// backend.
|
||||
case errors.Is(err, types.ErrNonExistingRoleAssigned):
|
||||
return nil, trace.Wrap(err)
|
||||
|
||||
// Obscure all other errors.
|
||||
case err != nil:
|
||||
// log the actual error.
|
||||
@@ -5051,7 +5056,7 @@ func (h *Handler) WithRedirect(fn redirectHandlerFunc) httprouter.Handle {
|
||||
|
||||
redirectURL := fn(w, r, p)
|
||||
if !IsValidRedirectURL(redirectURL) {
|
||||
redirectURL = client.LoginFailedRedirectURL
|
||||
redirectURL = sso.LoginFailedRedirectURL
|
||||
}
|
||||
http.Redirect(w, r, redirectURL, http.StatusFound)
|
||||
}
|
||||
@@ -5065,7 +5070,7 @@ func (h *Handler) WithMetaRedirect(fn redirectHandlerFunc) httprouter.Handle {
|
||||
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||
redirectURL := fn(w, r, p)
|
||||
if !IsValidRedirectURL(redirectURL) {
|
||||
redirectURL = client.LoginFailedRedirectURL
|
||||
redirectURL = sso.LoginFailedRedirectURL
|
||||
}
|
||||
err := app.MetaRedirect(w, redirectURL)
|
||||
if err != nil {
|
||||
|
||||
@@ -28,6 +28,9 @@ export function LoginFailed() {
|
||||
<Route path={cfg.routes.loginErrorCallback}>
|
||||
<LoginFailedComponent message="Unable to process SSO callback." />
|
||||
</Route>
|
||||
<Route path={cfg.routes.loginErrorCallbackMissingRole}>
|
||||
<LoginFailedComponent message="Unable to process SSO callback. The connector has a mapping to role that does not exist. Please contact your SSO administrator." />
|
||||
</Route>
|
||||
<Route path={cfg.routes.loginErrorUnauthorized}>
|
||||
<LoginFailedComponent message="You are not authorized, please contact your SSO administrator." />
|
||||
</Route>
|
||||
|
||||
@@ -39,7 +39,7 @@ import { DesktopSessionContainer as DesktopSession } from './DesktopSession';
|
||||
import { HeadlessRequest } from './HeadlessRequest';
|
||||
import { Login } from './Login';
|
||||
import { LoginClose } from './Login/LoginClose';
|
||||
import { LoginFailedComponent as LoginFailed } from './Login/LoginFailed';
|
||||
import { LoginFailed } from './Login/LoginFailed';
|
||||
import { LoginSuccess } from './Login/LoginSuccess';
|
||||
import { LoginTerminalRedirect } from './Login/LoginTerminalRedirect';
|
||||
import { Main } from './Main';
|
||||
|
||||
@@ -206,6 +206,7 @@ const cfg = {
|
||||
loginErrorLegacy: '/web/msg/error/login_failed',
|
||||
loginError: '/web/msg/error/login',
|
||||
loginErrorCallback: '/web/msg/error/login/callback',
|
||||
loginErrorCallbackMissingRole: '/web/msg/error/login/callback_missing_role',
|
||||
loginErrorUnauthorized: '/web/msg/error/login/auth',
|
||||
samlSloFailed: '/web/msg/error/slo',
|
||||
userInvite: '/web/invite/:tokenId',
|
||||
|
||||
Reference in New Issue
Block a user