fix(ui): add session timeout notification (#11281)
Co-authored-by: Chris Goller <goller@gmail.com>pull/11320/head
parent
354010ca84
commit
e2ffc17b21
|
@ -72,7 +72,7 @@ func (h *AuthenticationHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
|
|||
ctx := r.Context()
|
||||
scheme, err := ProbeAuthScheme(r)
|
||||
if err != nil {
|
||||
ForbiddenError(ctx, err, w)
|
||||
UnauthorizedError(ctx, w)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ func (h *AuthenticationHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
|
|||
return
|
||||
}
|
||||
|
||||
ForbiddenError(ctx, fmt.Errorf("unauthorized"), w)
|
||||
UnauthorizedError(ctx, w)
|
||||
}
|
||||
|
||||
func (h *AuthenticationHandler) extractAuthorization(ctx context.Context, r *http.Request) (context.Context, error) {
|
||||
|
|
|
@ -66,7 +66,7 @@ func TestAuthenticationHandler(t *testing.T) {
|
|||
session: "abc123",
|
||||
},
|
||||
wants: wants{
|
||||
code: http.StatusForbidden,
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -100,7 +100,7 @@ func TestAuthenticationHandler(t *testing.T) {
|
|||
token: "abc123",
|
||||
},
|
||||
wants: wants{
|
||||
code: http.StatusForbidden,
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -111,7 +111,7 @@ func TestAuthenticationHandler(t *testing.T) {
|
|||
},
|
||||
args: args{},
|
||||
wants: wants{
|
||||
code: http.StatusForbidden,
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ func TestAuthenticationHandler_NoAuthRoutes(t *testing.T) {
|
|||
path: "/api/v2/write",
|
||||
},
|
||||
wants: wants{
|
||||
code: http.StatusForbidden,
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -164,6 +164,14 @@ func ForbiddenError(ctx context.Context, err error, w http.ResponseWriter) {
|
|||
}, w)
|
||||
}
|
||||
|
||||
// UnauthorizedError encodes a error message and status code for unauthorized access.
|
||||
func UnauthorizedError(ctx context.Context, w http.ResponseWriter) {
|
||||
EncodeError(ctx, &platform.Error{
|
||||
Code: platform.EUnauthorized,
|
||||
Msg: "unauthorized access",
|
||||
}, w)
|
||||
}
|
||||
|
||||
// statusCode returns the http status code for an error.
|
||||
func statusCode(e kerrors.Error) int {
|
||||
if e.Code > 0 {
|
||||
|
@ -194,6 +202,6 @@ var statusCodePlatformError = map[string]int{
|
|||
platform.ENotFound: http.StatusNotFound,
|
||||
platform.EUnavailable: http.StatusServiceUnavailable,
|
||||
platform.EForbidden: http.StatusForbidden,
|
||||
platform.EUnauthorized: http.StatusForbidden,
|
||||
platform.EUnauthorized: http.StatusUnauthorized,
|
||||
platform.EMethodNotAllowed: http.StatusMethodNotAllowed,
|
||||
}
|
||||
|
|
|
@ -57,20 +57,19 @@ func (h *SessionHandler) handleSignin(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
req, err := decodeSigninRequest(ctx, r)
|
||||
if err != nil {
|
||||
h.Logger.Info("failed to decode request", zap.Error(err))
|
||||
EncodeError(ctx, err, w)
|
||||
UnauthorizedError(ctx, w)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.BasicAuthService.ComparePassword(ctx, req.Username, req.Password); err != nil {
|
||||
// Don't log here, it should already be handled by the service
|
||||
EncodeError(ctx, err, w)
|
||||
UnauthorizedError(ctx, w)
|
||||
return
|
||||
}
|
||||
|
||||
s, e := h.SessionService.CreateSession(ctx, req.Username)
|
||||
if e != nil {
|
||||
EncodeError(ctx, err, w)
|
||||
UnauthorizedError(ctx, w)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -104,13 +103,12 @@ func (h *SessionHandler) handleSignout(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
req, err := decodeSignoutRequest(ctx, r)
|
||||
if err != nil {
|
||||
h.Logger.Info("failed to decode request", zap.Error(err))
|
||||
EncodeError(ctx, err, w)
|
||||
UnauthorizedError(ctx, w)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.SessionService.ExpireSession(ctx, req.Key); err != nil {
|
||||
EncodeError(ctx, err, w)
|
||||
UnauthorizedError(ctx, w)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,12 @@ paths:
|
|||
responses:
|
||||
'204':
|
||||
description: succesfully authenticated
|
||||
'401':
|
||||
description: unauthorized access
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
default:
|
||||
description: unsuccessful authentication
|
||||
content:
|
||||
|
@ -29,6 +35,12 @@ paths:
|
|||
responses:
|
||||
'204':
|
||||
description: session successfully expired
|
||||
'401':
|
||||
description: unauthorized access
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
default:
|
||||
description: unsuccessful session exipry
|
||||
content:
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
// Libraries
|
||||
import React, {ReactElement, PureComponent} from 'react'
|
||||
import {withRouter, WithRouterProps} from 'react-router'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
// Components
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {getMe} from 'src/shared/apis/v2/user'
|
||||
|
||||
// Actions
|
||||
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
||||
|
||||
// Constants
|
||||
import {sessionTimedOut} from 'src/shared/copy/notifications'
|
||||
|
||||
// Types
|
||||
import {RemoteDataState} from 'src/types'
|
||||
|
||||
|
@ -17,7 +24,11 @@ interface OwnProps {
|
|||
children: ReactElement<any>
|
||||
}
|
||||
|
||||
type Props = OwnProps & WithRouterProps
|
||||
interface DispatchProps {
|
||||
notify: typeof notifyAction
|
||||
}
|
||||
|
||||
type Props = OwnProps & WithRouterProps & DispatchProps
|
||||
|
||||
const FETCH_WAIT = 60000
|
||||
|
||||
|
@ -75,6 +86,7 @@ export class Signin extends PureComponent<Props, State> {
|
|||
|
||||
if (pathname !== '/') {
|
||||
returnTo = `?returnTo=${pathname}`
|
||||
this.props.notify(sessionTimedOut())
|
||||
}
|
||||
|
||||
this.props.router.push(`/signin${returnTo}`)
|
||||
|
@ -82,4 +94,11 @@ export class Signin extends PureComponent<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
export default withRouter(Signin)
|
||||
const mdtp: DispatchProps = {
|
||||
notify: notifyAction,
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
mdtp
|
||||
)(withRouter(Signin))
|
||||
|
|
|
@ -110,7 +110,15 @@ class SigninForm extends PureComponent<Props, State> {
|
|||
await signin({username, password})
|
||||
this.handleRedirect()
|
||||
} catch (error) {
|
||||
const message = get(error, 'data.msg', '')
|
||||
const message = get(error, 'response.data.msg', '')
|
||||
const status = get(error, 'response.status', '')
|
||||
|
||||
if (status === 401) {
|
||||
return notify({
|
||||
...copy.SigninError,
|
||||
message: 'Login failed: username or password is invalid',
|
||||
})
|
||||
}
|
||||
|
||||
if (!message) {
|
||||
return notify(copy.SigninError)
|
||||
|
|
Loading…
Reference in New Issue