fix(ui): add session timeout notification (#11281)

Co-authored-by: Chris Goller <goller@gmail.com>
pull/11320/head
Delmer 2019-01-18 17:43:00 -05:00 committed by GitHub
parent 354010ca84
commit e2ffc17b21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 62 additions and 17 deletions

View File

@ -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) {

View File

@ -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,
},
},
}

View File

@ -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,
}

View File

@ -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
}

View File

@ -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:

View File

@ -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))

View File

@ -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)