Display detailed errors when login fails (#1069)

Signed-off-by: Lenin Alevski <alevsk.8772@gmail.com>

Co-authored-by: Alex <33497058+bexsoft@users.noreply.github.com>
Co-authored-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
This commit is contained in:
Lenin Alevski
2021-09-21 20:48:24 -07:00
committed by GitHub
parent e7665fae85
commit 137ff41be2
6 changed files with 35 additions and 45 deletions

View File

@@ -344,8 +344,6 @@ const AddReplicationModal = ({
name="deleteMarker"
label="Delete Marker"
onChange={(e) => {
console.log(e);
console.log(e.target.checked);
setRepDeleteMarker(e.target.checked);
}}
value={repDeleteMarker}

View File

@@ -26,6 +26,7 @@ import { setErrorSnackMessage } from "../../../../actions";
import { snackBarMessage } from "../../../../types";
interface IMainErrorProps {
customStyle?: any;
classes: any;
snackBar: snackBarMessage;
displayErrorMessage: typeof setErrorSnackMessage;
@@ -130,6 +131,7 @@ const MainError = ({
classes,
snackBar,
displayErrorMessage,
customStyle,
}: IMainErrorProps) => {
const [detailsOpen, setDetailsOpen] = useState<boolean>(false);
const [displayErrorMsg, setDisplayErrorMsg] = useState<boolean>(false);
@@ -171,6 +173,7 @@ const MainError = ({
className={`${classes.mainErrorContainer} ${
displayErrorMsg ? classes.mainErrorShow : ""
}`}
style={customStyle}
onMouseOver={stopHideTimer}
onMouseLeave={() => startHideTimer(closeErrorMessage)}
>

View File

@@ -29,19 +29,18 @@ import {
Theme,
withStyles,
} from "@material-ui/core/styles";
import request from "superagent";
import ErrorIcon from "@material-ui/icons/Error";
import Button from "@material-ui/core/Button";
import TextField from "@material-ui/core/TextField";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import { ILoginDetails, loginStrategyType } from "./types";
import { SystemState } from "../../types";
import { userLoggedIn } from "../../actions";
import { setErrorSnackMessage, userLoggedIn } from "../../actions";
import { ErrorResponseHandler } from "../../common/types";
import api from "../../common/api";
import history from "../../history";
import RefreshIcon from "../../icons/RefreshIcon";
import MainError from "../Console/Common/MainError/MainError";
const styles = (theme: Theme) =>
createStyles({
@@ -180,13 +179,14 @@ const mapState = (state: SystemState) => ({
loggedIn: state.loggedIn,
});
const connector = connect(mapState, { userLoggedIn });
const connector = connect(mapState, { userLoggedIn, setErrorSnackMessage });
// The inferred type will look like:
// {isOn: boolean, toggleOn: () => void}
interface ILoginProps {
userLoggedIn: typeof userLoggedIn;
setErrorSnackMessage: typeof setErrorSnackMessage;
classes: any;
}
@@ -198,16 +198,21 @@ interface LoginStrategyPayload {
[key: string]: any;
}
const Login = ({ classes, userLoggedIn }: ILoginProps) => {
const Login = ({
classes,
userLoggedIn,
setErrorSnackMessage,
}: ILoginProps) => {
const [accessKey, setAccessKey] = useState<string>("");
const [jwt, setJwt] = useState<string>("");
const [secretKey, setSecretKey] = useState<string>("");
const [error, setError] = useState<ErrorResponseHandler | null>(null);
const [loginStrategy, setLoginStrategy] = useState<ILoginDetails>({
loginStrategy: loginStrategyType.unknown,
redirect: "",
});
const [loginSending, setLoginSending] = useState<boolean>(false);
const [loadingFetchConfiguration, setLoadingFetchConfiguration] =
useState<boolean>(false);
const loginStrategyEndpoints: LoginStrategyRoutes = {
form: "/api/v1/login",
@@ -219,51 +224,39 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
};
const fetchConfiguration = () => {
setLoadingFetchConfiguration(true);
api
.invoke("GET", "/api/v1/login")
.then((loginDetails: ILoginDetails) => {
setLoginStrategy(loginDetails);
setError(null);
if (
loginDetails.loginStrategy === "redirect" &&
loginDetails.redirect !== ""
) {
//location.href = loginDetails.redirect;
}
setLoadingFetchConfiguration(false);
})
.catch((err: ErrorResponseHandler) => {
setError(err);
setErrorSnackMessage(err);
setLoadingFetchConfiguration(false);
});
};
const formSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setLoginSending(true);
request
.post(
loginStrategyEndpoints[loginStrategy.loginStrategy] || "/api/v1/login"
api
.invoke(
"POST",
loginStrategyEndpoints[loginStrategy.loginStrategy] || "/api/v1/login",
loginStrategyPayload[loginStrategy.loginStrategy]
)
.send(loginStrategyPayload[loginStrategy.loginStrategy])
.then((res: any) => {
const bodyResponse = res.body;
if (bodyResponse.error) {
setLoginSending(false);
// throw will be moved to catch block once bad login returns 403
throw bodyResponse.error;
}
})
.then(() => {
// We set the state in redux
userLoggedIn(true);
if (loginStrategy.loginStrategy === loginStrategyType.form) {
localStorage.setItem("userLoggedIn", btoa(accessKey));
}
history.push("/");
})
.catch((err) => {
setLoginSending(false);
setError({ detailedError: "", errorMessage: err.message });
setErrorSnackMessage(err);
});
};
@@ -409,12 +402,12 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
default:
loginComponent = (
<div className={classes.loaderAlignment}>
{error === null ? (
{loadingFetchConfiguration ? (
<CircularProgress className={classes.loadingLoginStrategy} />
) : (
<React.Fragment>
<div>
<p>An error has ocurred, the backend cannot be reached.</p>
<p>An error has occurred, the backend cannot be reached.</p>
</div>
<div>
<Button
@@ -436,13 +429,8 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
return (
<React.Fragment>
{error !== null && (
<div className={classes.errorBlock}>
<ErrorIcon fontSize="small" className={classes.errorIconStyle} />{" "}
{error.errorMessage}
</div>
)}
<Paper className={classes.paper}>
<MainError customStyle={{ marginTop: -140 }} />
<Grid container className={classes.mainContainer}>
<Grid item xs={7} className={classes.theOcean}>
<div className={classes.oceanBg} />

View File

@@ -1,6 +1,6 @@
import { createMuiTheme } from "@material-ui/core";
import { createTheme } from "@material-ui/core";
const theme = createMuiTheme({
const theme = createTheme({
palette: {
primary: {
light: "#073052",

View File

@@ -11,7 +11,7 @@ import (
)
var (
// ErrorGeneric is a heneric error message
// ErrorGeneric is a generic error message
ErrorGeneric = errors.New("an error occurred, please try again")
errInvalidCredentials = errors.New("invalid Login")
errorGenericInvalidSession = errors.New("invalid session")
@@ -33,6 +33,7 @@ var (
errLicenseNotFound = errors.New("license not found")
errAvoidSelfAccountDelete = errors.New("logged in user cannot be deleted by itself")
errAccessDenied = errors.New("access denied")
errOauth2Provider = errors.New("error contacting the external identity provider")
)
// Tiering errors
@@ -134,7 +135,7 @@ func prepareError(err ...error) *models.Error {
errorCode = 401
errorMessage = errorGenericInvalidSession.Error()
}
// if we received a second error take that as friendly message but dont override the code
// if we received a second error take that as friendly message but don't override the code
if len(err) > 1 && err[1] != nil {
LogError("friendly error: %v", err[1].Error())
errorMessage = err[1].Error()

View File

@@ -163,11 +163,11 @@ func getLoginResponse(lr *models.LoginRequest) (*models.LoginResponse, *models.E
// prepare console credentials
consolCreds, err := getConsoleCredentials(ctx, *lr.AccessKey, *lr.SecretKey)
if err != nil {
return nil, prepareError(errInvalidCredentials, nil, err)
return nil, prepareError(err, errInvalidCredentials, err)
}
sessionID, err := login(consolCreds)
if err != nil {
return nil, prepareError(errInvalidCredentials, nil, err)
return nil, prepareError(err, errInvalidCredentials, err)
}
// serialize output
loginResponse := &models.LoginResponse{
@@ -186,7 +186,7 @@ func getLoginDetailsResponse() (*models.LoginDetails, *models.Error) {
// initialize new oauth2 client
oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, GetConsoleHTTPClient())
if err != nil {
return nil, prepareError(err)
return nil, prepareError(err, errOauth2Provider)
}
// Validate user against IDP
identityProvider := &auth.IdentityProvider{Client: oauth2Client}