web/satellite: unauthorized error (401) interception for http requests
Implemented interception for http requests. We redirect user to login page on every 401 response. Issue: https://github.com/storj/storj/issues/5339 Change-Id: Icba4fc0031cb2b4e682a1be078cdcf95b7fa6bfe
This commit is contained in:
parent
e598c2b3b1
commit
2ebdc5ff2f
@ -932,7 +932,7 @@ func (a *Auth) serveJSONError(w http.ResponseWriter, err error) {
|
|||||||
// getStatusCode returns http.StatusCode depends on console error class.
|
// getStatusCode returns http.StatusCode depends on console error class.
|
||||||
func (a *Auth) getStatusCode(err error) int {
|
func (a *Auth) getStatusCode(err error) int {
|
||||||
switch {
|
switch {
|
||||||
case console.ErrValidation.Has(err), console.ErrCaptcha.Has(err), console.ErrMFAMissing.Has(err):
|
case console.ErrValidation.Has(err), console.ErrCaptcha.Has(err), console.ErrMFAMissing.Has(err), console.ErrMFAPasscode.Has(err), console.ErrMFARecoveryCode.Has(err), console.ErrChangePassword.Has(err):
|
||||||
return http.StatusBadRequest
|
return http.StatusBadRequest
|
||||||
case console.ErrUnauthorized.Has(err), console.ErrTokenExpiration.Has(err), console.ErrRecoveryToken.Has(err), console.ErrLoginCredentials.Has(err):
|
case console.ErrUnauthorized.Has(err), console.ErrTokenExpiration.Has(err), console.ErrRecoveryToken.Has(err), console.ErrLoginCredentials.Has(err):
|
||||||
return http.StatusUnauthorized
|
return http.StatusUnauthorized
|
||||||
@ -940,8 +940,6 @@ func (a *Auth) getStatusCode(err error) int {
|
|||||||
return http.StatusConflict
|
return http.StatusConflict
|
||||||
case errors.Is(err, errNotImplemented):
|
case errors.Is(err, errNotImplemented):
|
||||||
return http.StatusNotImplemented
|
return http.StatusNotImplemented
|
||||||
case console.ErrMFAPasscode.Has(err), console.ErrMFARecoveryCode.Has(err):
|
|
||||||
return http.StatusBadRequest
|
|
||||||
default:
|
default:
|
||||||
return http.StatusInternalServerError
|
return http.StatusInternalServerError
|
||||||
}
|
}
|
||||||
@ -966,12 +964,12 @@ func (a *Auth) getUserErrorMessage(err error) string {
|
|||||||
case console.ErrMFAConflict.Has(err):
|
case console.ErrMFAConflict.Has(err):
|
||||||
return "Expected either passcode or recovery code, but got both"
|
return "Expected either passcode or recovery code, but got both"
|
||||||
case console.ErrMFAPasscode.Has(err):
|
case console.ErrMFAPasscode.Has(err):
|
||||||
return "The MFA passcode is not valid or has expired. You have just used up one of your login attempts"
|
return "The MFA passcode is not valid or has expired"
|
||||||
case console.ErrMFARecoveryCode.Has(err):
|
case console.ErrMFARecoveryCode.Has(err):
|
||||||
return "The MFA recovery code is not valid or has been previously used. You have just used up one of your login attempts"
|
return "The MFA recovery code is not valid or has been previously used"
|
||||||
case console.ErrLoginCredentials.Has(err):
|
case console.ErrLoginCredentials.Has(err):
|
||||||
return "Your login credentials are incorrect, please try again"
|
return "Your login credentials are incorrect, please try again"
|
||||||
case console.ErrValidation.Has(err):
|
case console.ErrValidation.Has(err), console.ErrChangePassword.Has(err):
|
||||||
return err.Error()
|
return err.Error()
|
||||||
case errors.Is(err, errNotImplemented):
|
case errors.Is(err, errNotImplemented):
|
||||||
return "The server is incapable of fulfilling the request"
|
return "The server is incapable of fulfilling the request"
|
||||||
|
@ -57,7 +57,7 @@ func TestAuth(t *testing.T) {
|
|||||||
"newPassword": user.password + "2",
|
"newPassword": user.password + "2",
|
||||||
}))
|
}))
|
||||||
|
|
||||||
require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
|
require.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||||
_ = body
|
_ = body
|
||||||
//TODO: require.Contains(t, body, "password was incorrect")
|
//TODO: require.Contains(t, body, "password was incorrect")
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ func (s *Service) DisableUserMFA(ctx context.Context, passcode string, t time.Ti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
return ErrUnauthorized.Wrap(ErrMFARecoveryCode.New(mfaRecoveryInvalidErrMsg))
|
return ErrMFARecoveryCode.New(mfaRecoveryInvalidErrMsg)
|
||||||
}
|
}
|
||||||
} else if passcode != "" {
|
} else if passcode != "" {
|
||||||
valid, err := ValidateMFAPasscode(passcode, user.MFASecretKey, t)
|
valid, err := ValidateMFAPasscode(passcode, user.MFASecretKey, t)
|
||||||
|
@ -57,6 +57,7 @@ const (
|
|||||||
emailNotFoundErrMsg = "There are no users with the specified email"
|
emailNotFoundErrMsg = "There are no users with the specified email"
|
||||||
passwordRecoveryTokenIsExpiredErrMsg = "Your password recovery link has expired, please request another one"
|
passwordRecoveryTokenIsExpiredErrMsg = "Your password recovery link has expired, please request another one"
|
||||||
credentialsErrMsg = "Your login credentials are incorrect, please try again"
|
credentialsErrMsg = "Your login credentials are incorrect, please try again"
|
||||||
|
changePasswordErrMsg = "Your old password is incorrect, please try again"
|
||||||
passwordTooShortErrMsg = "Your password needs to be at least %d characters long"
|
passwordTooShortErrMsg = "Your password needs to be at least %d characters long"
|
||||||
passwordTooLongErrMsg = "Your password must be no longer than %d characters"
|
passwordTooLongErrMsg = "Your password must be no longer than %d characters"
|
||||||
projectOwnerDeletionForbiddenErrMsg = "%s is a project owner and can not be deleted"
|
projectOwnerDeletionForbiddenErrMsg = "%s is a project owner and can not be deleted"
|
||||||
@ -94,6 +95,9 @@ var (
|
|||||||
// ErrLoginCredentials occurs when provided invalid login credentials.
|
// ErrLoginCredentials occurs when provided invalid login credentials.
|
||||||
ErrLoginCredentials = errs.Class("login credentials")
|
ErrLoginCredentials = errs.Class("login credentials")
|
||||||
|
|
||||||
|
// ErrChangePassword occurs when provided old password is incorrect.
|
||||||
|
ErrChangePassword = errs.Class("change password")
|
||||||
|
|
||||||
// ErrEmailUsed is error type that occurs on repeating auth attempts with email.
|
// ErrEmailUsed is error type that occurs on repeating auth attempts with email.
|
||||||
ErrEmailUsed = errs.Class("email used")
|
ErrEmailUsed = errs.Class("email used")
|
||||||
|
|
||||||
@ -1306,7 +1310,7 @@ func (s *Service) ChangePassword(ctx context.Context, pass, newPass string) (err
|
|||||||
|
|
||||||
err = bcrypt.CompareHashAndPassword(user.PasswordHash, []byte(pass))
|
err = bcrypt.CompareHashAndPassword(user.PasswordHash, []byte(pass))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrUnauthorized.New(credentialsErrMsg)
|
return ErrChangePassword.New(changePasswordErrMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ValidatePassword(newPass); err != nil {
|
if err := ValidatePassword(newPass); err != nil {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (C) 2022 Storj Labs, Inc.
|
// Copyright (C) 2022 Storj Labs, Inc.
|
||||||
// See LICENSE for copying information.
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
|
||||||
import { HttpClient } from '@/utils/httpClient';
|
import { HttpClient } from '@/utils/httpClient';
|
||||||
import { ABHitAction, ABTestApi, ABTestValues } from '@/types/abtesting';
|
import { ABHitAction, ABTestApi, ABTestValues } from '@/types/abtesting';
|
||||||
|
|
||||||
@ -29,10 +28,6 @@ export class ABHttpApi implements ABTestApi {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
// use default values on error
|
// use default values on error
|
||||||
return new ABTestValues();
|
return new ABTestValues();
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// See LICENSE for copying information.
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
import { BaseGql } from '@/api/baseGql';
|
import { BaseGql } from '@/api/baseGql';
|
||||||
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
|
||||||
import {
|
import {
|
||||||
AccessGrant,
|
AccessGrant,
|
||||||
AccessGrantCursor,
|
AccessGrantCursor,
|
||||||
@ -145,10 +144,6 @@ export class AccessGrantsApiGql extends BaseGql implements AccessGrantsApi {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('can not delete access grant');
|
throw new Error('can not delete access grant');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
import { ErrorBadRequest } from '@/api/errors/ErrorBadRequest';
|
import { ErrorBadRequest } from '@/api/errors/ErrorBadRequest';
|
||||||
import { ErrorMFARequired } from '@/api/errors/ErrorMFARequired';
|
import { ErrorMFARequired } from '@/api/errors/ErrorMFARequired';
|
||||||
import { ErrorTooManyRequests } from '@/api/errors/ErrorTooManyRequests';
|
import { ErrorTooManyRequests } from '@/api/errors/ErrorTooManyRequests';
|
||||||
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
|
||||||
import { TokenInfo, UpdatedUser, User, UsersApi } from '@/types/users';
|
import { TokenInfo, UpdatedUser, User, UsersApi } from '@/types/users';
|
||||||
import { HttpClient } from '@/utils/httpClient';
|
import { HttpClient } from '@/utils/httpClient';
|
||||||
import { ErrorTokenExpired } from '@/api/errors/ErrorTokenExpired';
|
import { ErrorTokenExpired } from '@/api/errors/ErrorTokenExpired';
|
||||||
@ -75,8 +74,6 @@ export class AuthHttpApi implements UsersApi {
|
|||||||
switch (response.status) {
|
switch (response.status) {
|
||||||
case 400:
|
case 400:
|
||||||
throw new ErrorBadRequest(errMsg);
|
throw new ErrorBadRequest(errMsg);
|
||||||
case 401:
|
|
||||||
throw new ErrorUnauthorized(errMsg);
|
|
||||||
case 429:
|
case 429:
|
||||||
throw new ErrorTooManyRequests(errMsg);
|
throw new ErrorTooManyRequests(errMsg);
|
||||||
default:
|
default:
|
||||||
@ -97,10 +94,6 @@ export class AuthHttpApi implements UsersApi {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Can not logout. Please try again later');
|
throw new Error('Can not logout. Please try again later');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,8 +118,6 @@ export class AuthHttpApi implements UsersApi {
|
|||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
const errMsg = result.error || 'Failed to send password reset link';
|
const errMsg = result.error || 'Failed to send password reset link';
|
||||||
switch (response.status) {
|
switch (response.status) {
|
||||||
case 404:
|
|
||||||
throw new ErrorUnauthorized(errMsg);
|
|
||||||
case 429:
|
case 429:
|
||||||
throw new ErrorTooManyRequests(errMsg);
|
throw new ErrorTooManyRequests(errMsg);
|
||||||
default:
|
default:
|
||||||
@ -185,10 +176,6 @@ export class AuthHttpApi implements UsersApi {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('can not get user data');
|
throw new Error('can not get user data');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,14 +197,8 @@ export class AuthHttpApi implements UsersApi {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (response.status) {
|
const result = await response.json();
|
||||||
case 401: {
|
throw new Error(result.error);
|
||||||
throw new Error('old password is incorrect, please try again');
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
throw new Error('can not change password');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -236,10 +217,6 @@ export class AuthHttpApi implements UsersApi {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('can not delete user');
|
throw new Error('can not delete user');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,8 +256,6 @@ export class AuthHttpApi implements UsersApi {
|
|||||||
switch (response.status) {
|
switch (response.status) {
|
||||||
case 400:
|
case 400:
|
||||||
throw new ErrorBadRequest(errMsg);
|
throw new ErrorBadRequest(errMsg);
|
||||||
case 401:
|
|
||||||
throw new ErrorUnauthorized(errMsg);
|
|
||||||
case 429:
|
case 429:
|
||||||
throw new ErrorTooManyRequests(errMsg);
|
throw new ErrorTooManyRequests(errMsg);
|
||||||
default:
|
default:
|
||||||
@ -302,10 +277,6 @@ export class AuthHttpApi implements UsersApi {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Can not generate MFA secret. Please try again later');
|
throw new Error('Can not generate MFA secret. Please try again later');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,10 +297,6 @@ export class AuthHttpApi implements UsersApi {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Can not enable MFA. Please try again later');
|
throw new Error('Can not enable MFA. Please try again later');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,16 +319,9 @@ export class AuthHttpApi implements UsersApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (!response.ok) {
|
|
||||||
const errMsg = result.error || 'Cannot disable MFA. Please try again later';
|
const errMsg = result.error || 'Cannot disable MFA. Please try again later';
|
||||||
switch (response.status) {
|
|
||||||
case 401:
|
|
||||||
throw new ErrorUnauthorized(errMsg);
|
|
||||||
default:
|
|
||||||
throw new Error(errMsg);
|
throw new Error(errMsg);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to generate user's MFA recovery codes.
|
* Used to generate user's MFA recovery codes.
|
||||||
@ -376,10 +336,6 @@ export class AuthHttpApi implements UsersApi {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Can not generate MFA recovery codes. Please try again later');
|
throw new Error('Can not generate MFA recovery codes. Please try again later');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,8 +382,6 @@ export class AuthHttpApi implements UsersApi {
|
|||||||
switch (response.status) {
|
switch (response.status) {
|
||||||
case 400:
|
case 400:
|
||||||
throw new ErrorBadRequest(errMsg);
|
throw new ErrorBadRequest(errMsg);
|
||||||
case 401:
|
|
||||||
throw new ErrorUnauthorized(errMsg);
|
|
||||||
default:
|
default:
|
||||||
throw new Error(errMsg);
|
throw new Error(errMsg);
|
||||||
}
|
}
|
||||||
@ -447,10 +401,6 @@ export class AuthHttpApi implements UsersApi {
|
|||||||
return new Date(await response.json());
|
return new Date(await response.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Unable to refresh session.');
|
throw new Error('Unable to refresh session.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// See LICENSE for copying information.
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
import { BaseGql } from '@/api/baseGql';
|
import { BaseGql } from '@/api/baseGql';
|
||||||
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
|
||||||
import { Bucket, BucketCursor, BucketPage, BucketsApi } from '@/types/buckets';
|
import { Bucket, BucketCursor, BucketPage, BucketsApi } from '@/types/buckets';
|
||||||
import { HttpClient } from '@/utils/httpClient';
|
import { HttpClient } from '@/utils/httpClient';
|
||||||
|
|
||||||
@ -70,10 +69,6 @@ export class BucketsApiGql extends BaseGql implements BucketsApi {
|
|||||||
const response = await this.client.get(path);
|
const response = await this.client.get(path);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Can not get bucket names');
|
throw new Error('Can not get bucket names');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
* ErrorUnauthorized is a custom error type for performing unauthorized operations.
|
* ErrorUnauthorized is a custom error type for performing unauthorized operations.
|
||||||
*/
|
*/
|
||||||
export class ErrorUnauthorized extends Error {
|
export class ErrorUnauthorized extends Error {
|
||||||
public constructor(message = 'authorization required') {
|
public constructor(message = 'Authorization required') {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
import { ErrorTooManyRequests } from './errors/ErrorTooManyRequests';
|
import { ErrorTooManyRequests } from './errors/ErrorTooManyRequests';
|
||||||
|
|
||||||
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
|
||||||
import {
|
import {
|
||||||
AccountBalance,
|
AccountBalance,
|
||||||
Coupon,
|
Coupon,
|
||||||
@ -37,10 +36,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
|||||||
const response = await this.client.get(path);
|
const response = await this.client.get(path);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Can not get account balance');
|
throw new Error('Can not get account balance');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,10 +61,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
|||||||
return couponType;
|
return couponType;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('can not setup account');
|
throw new Error('can not setup account');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,10 +74,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
|||||||
const response = await this.client.get(path);
|
const response = await this.client.get(path);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('can not get projects charges');
|
throw new Error('can not get projects charges');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,10 +111,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('can not add credit card');
|
throw new Error('can not add credit card');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,10 +128,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('can not remove credit card');
|
throw new Error('can not remove credit card');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,9 +142,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
|||||||
const response = await this.client.get(path);
|
const response = await this.client.get(path);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
throw new Error('can not list credit cards');
|
throw new Error('can not list credit cards');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,10 +168,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('can not make credit card default');
|
throw new Error('can not make credit card default');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,9 +182,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
|||||||
const response = await this.client.get(path);
|
const response = await this.client.get(path);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
throw new Error('can not list billing history');
|
throw new Error('can not list billing history');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,9 +217,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
|||||||
const response = await this.client.get(path);
|
const response = await this.client.get(path);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
throw new Error('Can not list token payment history');
|
throw new Error('Can not list token payment history');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +251,15 @@ export class PaymentsHttpApi implements PaymentsApi {
|
|||||||
const response = await this.client.patch(path, couponCode);
|
const response = await this.client.patch(path, couponCode);
|
||||||
const errMsg = `Could not apply coupon code "${couponCode}"`;
|
const errMsg = `Could not apply coupon code "${couponCode}"`;
|
||||||
|
|
||||||
if (response.ok) {
|
if (!response.ok) {
|
||||||
|
switch (response.status) {
|
||||||
|
case 429:
|
||||||
|
throw new ErrorTooManyRequests('You\'ve exceeded limit of attempts, try again in 5 minutes');
|
||||||
|
default:
|
||||||
|
throw new Error(errMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const coupon = await response.json();
|
const coupon = await response.json();
|
||||||
|
|
||||||
if (!coupon) {
|
if (!coupon) {
|
||||||
@ -304,16 +278,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (response.status) {
|
|
||||||
case 429:
|
|
||||||
throw new ErrorTooManyRequests('You\'ve exceeded limit of attempts, try again in 5 minutes');
|
|
||||||
case 401:
|
|
||||||
throw new ErrorUnauthorized(errMsg);
|
|
||||||
default:
|
|
||||||
throw new Error(errMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getCoupon returns the coupon applied to the user.
|
* getCoupon returns the coupon applied to the user.
|
||||||
*
|
*
|
||||||
@ -323,10 +287,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
|||||||
const path = `${this.ROOT_PATH}/coupon`;
|
const path = `${this.ROOT_PATH}/coupon`;
|
||||||
const response = await this.client.get(path);
|
const response = await this.client.get(path);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('cannot retrieve coupon');
|
throw new Error('cannot retrieve coupon');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,10 +319,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
|||||||
const response = await this.client.get(path);
|
const response = await this.client.get(path);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Can not get wallet');
|
throw new Error('Can not get wallet');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,10 +341,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
|||||||
const response = await this.client.post(path, null);
|
const response = await this.client.post(path, null);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Can not claim new wallet');
|
throw new Error('Can not claim new wallet');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// See LICENSE for copying information.
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
import { BaseGql } from '@/api/baseGql';
|
import { BaseGql } from '@/api/baseGql';
|
||||||
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
|
||||||
import {
|
import {
|
||||||
DataStamp,
|
DataStamp,
|
||||||
Project,
|
Project,
|
||||||
@ -144,7 +143,10 @@ export class ProjectsApiGql extends BaseGql implements ProjectsApi {
|
|||||||
const path = `${this.ROOT_PATH}/${projectId}/usage-limits`;
|
const path = `${this.ROOT_PATH}/${projectId}/usage-limits`;
|
||||||
const response = await this.http.get(path);
|
const response = await this.http.get(path);
|
||||||
|
|
||||||
if (response.ok) {
|
if (!response.ok) {
|
||||||
|
throw new Error('can not get usage limits');
|
||||||
|
}
|
||||||
|
|
||||||
const limits = await response.json();
|
const limits = await response.json();
|
||||||
|
|
||||||
return new ProjectLimits(
|
return new ProjectLimits(
|
||||||
@ -155,13 +157,7 @@ export class ProjectsApiGql extends BaseGql implements ProjectsApi {
|
|||||||
limits.objectCount,
|
limits.objectCount,
|
||||||
limits.segmentCount,
|
limits.segmentCount,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('can not get usage limits');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,7 +169,11 @@ export class ProjectsApiGql extends BaseGql implements ProjectsApi {
|
|||||||
const path = `${this.ROOT_PATH}/usage-limits`;
|
const path = `${this.ROOT_PATH}/usage-limits`;
|
||||||
const response = await this.http.get(path);
|
const response = await this.http.get(path);
|
||||||
|
|
||||||
if (response.ok) {
|
if (!response.ok) {
|
||||||
|
throw new Error('can not get total usage limits');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
const limits = await response.json();
|
const limits = await response.json();
|
||||||
|
|
||||||
return new ProjectLimits(
|
return new ProjectLimits(
|
||||||
@ -184,13 +184,6 @@ export class ProjectsApiGql extends BaseGql implements ProjectsApi {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('can not get total usage limits');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get project daily usage for specific date range.
|
* Get project daily usage for specific date range.
|
||||||
*
|
*
|
||||||
@ -205,7 +198,11 @@ export class ProjectsApiGql extends BaseGql implements ProjectsApi {
|
|||||||
const path = `${this.ROOT_PATH}/${projectId}/daily-usage?from=${since}&to=${before}`;
|
const path = `${this.ROOT_PATH}/${projectId}/daily-usage?from=${since}&to=${before}`;
|
||||||
const response = await this.http.get(path);
|
const response = await this.http.get(path);
|
||||||
|
|
||||||
if (response.ok) {
|
if (!response.ok) {
|
||||||
|
throw new Error('Can not get project daily usage');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
const usage = await response.json();
|
const usage = await response.json();
|
||||||
|
|
||||||
return new ProjectsStorageBandwidthDaily(
|
return new ProjectsStorageBandwidthDaily(
|
||||||
@ -227,19 +224,13 @@ export class ProjectsApiGql extends BaseGql implements ProjectsApi {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
throw new ErrorUnauthorized();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Can not get project daily usage');
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getSalt(projectId: string): Promise<string> {
|
public async getSalt(projectId: string): Promise<string> {
|
||||||
const path = `${this.ROOT_PATH}/${projectId}/salt`;
|
const path = `${this.ROOT_PATH}/${projectId}/salt`;
|
||||||
const response = await this.http.get(path);
|
const response = await this.http.get(path);
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return await response.json();
|
return await response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Can not get project salt');
|
throw new Error('Can not get project salt');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ const errorLink = onError(({ graphQLErrors, networkError }) => {
|
|||||||
Vue.prototype.$notify.error('Session token expired', AnalyticsErrorEventSource.OVERALL_SESSION_EXPIRED_ERROR);
|
Vue.prototype.$notify.error('Session token expired', AnalyticsErrorEventSource.OVERALL_SESSION_EXPIRED_ERROR);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = window.location.origin + '/login';
|
window.location.href = window.location.origin + '/login';
|
||||||
}, 3000);
|
}, 2000);
|
||||||
} else {
|
} else {
|
||||||
nError.result && Vue.prototype.$notify.error(nError.result.error, AnalyticsErrorEventSource.OVERALL_GRAPHQL_ERROR);
|
nError.result && Vue.prototype.$notify.error(nError.result.error, AnalyticsErrorEventSource.OVERALL_GRAPHQL_ERROR);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (C) 2019 Storj Labs, Inc.
|
// Copyright (C) 2019 Storj Labs, Inc.
|
||||||
// See LICENSE for copying information.
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HttpClient is a custom wrapper around fetch api.
|
* HttpClient is a custom wrapper around fetch api.
|
||||||
* Exposes get, post and delete methods for JSON strings.
|
* Exposes get, post and delete methods for JSON strings.
|
||||||
@ -22,7 +24,13 @@ export class HttpClient {
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
};
|
};
|
||||||
|
|
||||||
return await fetch(path, request);
|
const response = await fetch(path, request);
|
||||||
|
if (response.status === 401) {
|
||||||
|
await this.handleUnauthorized();
|
||||||
|
throw new ErrorUnauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,4 +75,31 @@ export class HttpClient {
|
|||||||
public async delete(path: string): Promise<Response> {
|
public async delete(path: string): Promise<Response> {
|
||||||
return this.sendJSON('DELETE', path, null);
|
return this.sendJSON('DELETE', path, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles unauthorized actions.
|
||||||
|
* Call logout and redirect to login.
|
||||||
|
*/
|
||||||
|
private async handleUnauthorized(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const logoutPath = '/api/v0/auth/logout';
|
||||||
|
const request: RequestInit = {
|
||||||
|
method: 'POST',
|
||||||
|
body: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
request.headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
|
await fetch(logoutPath, request);
|
||||||
|
// eslint-disable-next-line no-empty
|
||||||
|
} catch (error) {}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!window.location.href.includes('/login')) {
|
||||||
|
window.location.href = window.location.origin + '/login';
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user