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.
|
||||
func (a *Auth) getStatusCode(err error) int {
|
||||
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
|
||||
case console.ErrUnauthorized.Has(err), console.ErrTokenExpiration.Has(err), console.ErrRecoveryToken.Has(err), console.ErrLoginCredentials.Has(err):
|
||||
return http.StatusUnauthorized
|
||||
@ -940,8 +940,6 @@ func (a *Auth) getStatusCode(err error) int {
|
||||
return http.StatusConflict
|
||||
case errors.Is(err, errNotImplemented):
|
||||
return http.StatusNotImplemented
|
||||
case console.ErrMFAPasscode.Has(err), console.ErrMFARecoveryCode.Has(err):
|
||||
return http.StatusBadRequest
|
||||
default:
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
@ -966,12 +964,12 @@ func (a *Auth) getUserErrorMessage(err error) string {
|
||||
case console.ErrMFAConflict.Has(err):
|
||||
return "Expected either passcode or recovery code, but got both"
|
||||
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):
|
||||
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):
|
||||
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()
|
||||
case errors.Is(err, errNotImplemented):
|
||||
return "The server is incapable of fulfilling the request"
|
||||
|
@ -57,7 +57,7 @@ func TestAuth(t *testing.T) {
|
||||
"newPassword": user.password + "2",
|
||||
}))
|
||||
|
||||
require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
|
||||
require.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||
_ = body
|
||||
//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 {
|
||||
return ErrUnauthorized.Wrap(ErrMFARecoveryCode.New(mfaRecoveryInvalidErrMsg))
|
||||
return ErrMFARecoveryCode.New(mfaRecoveryInvalidErrMsg)
|
||||
}
|
||||
} else if passcode != "" {
|
||||
valid, err := ValidateMFAPasscode(passcode, user.MFASecretKey, t)
|
||||
|
@ -57,6 +57,7 @@ const (
|
||||
emailNotFoundErrMsg = "There are no users with the specified email"
|
||||
passwordRecoveryTokenIsExpiredErrMsg = "Your password recovery link has expired, please request another one"
|
||||
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"
|
||||
passwordTooLongErrMsg = "Your password must be no longer than %d characters"
|
||||
projectOwnerDeletionForbiddenErrMsg = "%s is a project owner and can not be deleted"
|
||||
@ -94,6 +95,9 @@ var (
|
||||
// ErrLoginCredentials occurs when provided invalid 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 = 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))
|
||||
if err != nil {
|
||||
return ErrUnauthorized.New(credentialsErrMsg)
|
||||
return ErrChangePassword.New(changePasswordErrMsg)
|
||||
}
|
||||
|
||||
if err := ValidatePassword(newPass); err != nil {
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright (C) 2022 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
||||
import { HttpClient } from '@/utils/httpClient';
|
||||
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
|
||||
return new ABTestValues();
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import { BaseGql } from '@/api/baseGql';
|
||||
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
||||
import {
|
||||
AccessGrant,
|
||||
AccessGrantCursor,
|
||||
@ -145,10 +144,6 @@ export class AccessGrantsApiGql extends BaseGql implements AccessGrantsApi {
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('can not delete access grant');
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
import { ErrorBadRequest } from '@/api/errors/ErrorBadRequest';
|
||||
import { ErrorMFARequired } from '@/api/errors/ErrorMFARequired';
|
||||
import { ErrorTooManyRequests } from '@/api/errors/ErrorTooManyRequests';
|
||||
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
||||
import { TokenInfo, UpdatedUser, User, UsersApi } from '@/types/users';
|
||||
import { HttpClient } from '@/utils/httpClient';
|
||||
import { ErrorTokenExpired } from '@/api/errors/ErrorTokenExpired';
|
||||
@ -75,8 +74,6 @@ export class AuthHttpApi implements UsersApi {
|
||||
switch (response.status) {
|
||||
case 400:
|
||||
throw new ErrorBadRequest(errMsg);
|
||||
case 401:
|
||||
throw new ErrorUnauthorized(errMsg);
|
||||
case 429:
|
||||
throw new ErrorTooManyRequests(errMsg);
|
||||
default:
|
||||
@ -97,10 +94,6 @@ export class AuthHttpApi implements UsersApi {
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
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 errMsg = result.error || 'Failed to send password reset link';
|
||||
switch (response.status) {
|
||||
case 404:
|
||||
throw new ErrorUnauthorized(errMsg);
|
||||
case 429:
|
||||
throw new ErrorTooManyRequests(errMsg);
|
||||
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');
|
||||
}
|
||||
|
||||
@ -210,14 +197,8 @@ export class AuthHttpApi implements UsersApi {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (response.status) {
|
||||
case 401: {
|
||||
throw new Error('old password is incorrect, please try again');
|
||||
}
|
||||
default: {
|
||||
throw new Error('can not change password');
|
||||
}
|
||||
}
|
||||
const result = await response.json();
|
||||
throw new Error(result.error);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -236,10 +217,6 @@ export class AuthHttpApi implements UsersApi {
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('can not delete user');
|
||||
}
|
||||
|
||||
@ -279,8 +256,6 @@ export class AuthHttpApi implements UsersApi {
|
||||
switch (response.status) {
|
||||
case 400:
|
||||
throw new ErrorBadRequest(errMsg);
|
||||
case 401:
|
||||
throw new ErrorUnauthorized(errMsg);
|
||||
case 429:
|
||||
throw new ErrorTooManyRequests(errMsg);
|
||||
default:
|
||||
@ -302,10 +277,6 @@ export class AuthHttpApi implements UsersApi {
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('Can not generate MFA secret. Please try again later');
|
||||
}
|
||||
|
||||
@ -326,10 +297,6 @@ export class AuthHttpApi implements UsersApi {
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('Can not enable MFA. Please try again later');
|
||||
}
|
||||
|
||||
@ -352,15 +319,8 @@ export class AuthHttpApi implements UsersApi {
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
if (!response.ok) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
const errMsg = result.error || 'Cannot disable MFA. Please try again later';
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -376,10 +336,6 @@ export class AuthHttpApi implements UsersApi {
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
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) {
|
||||
case 400:
|
||||
throw new ErrorBadRequest(errMsg);
|
||||
case 401:
|
||||
throw new ErrorUnauthorized(errMsg);
|
||||
default:
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
@ -447,10 +401,6 @@ export class AuthHttpApi implements UsersApi {
|
||||
return new Date(await response.json());
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('Unable to refresh session.');
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import { BaseGql } from '@/api/baseGql';
|
||||
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
||||
import { Bucket, BucketCursor, BucketPage, BucketsApi } from '@/types/buckets';
|
||||
import { HttpClient } from '@/utils/httpClient';
|
||||
|
||||
@ -70,10 +69,6 @@ export class BucketsApiGql extends BaseGql implements BucketsApi {
|
||||
const response = await this.client.get(path);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('Can not get bucket names');
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
* ErrorUnauthorized is a custom error type for performing unauthorized operations.
|
||||
*/
|
||||
export class ErrorUnauthorized extends Error {
|
||||
public constructor(message = 'authorization required') {
|
||||
public constructor(message = 'Authorization required') {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
import { ErrorTooManyRequests } from './errors/ErrorTooManyRequests';
|
||||
|
||||
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
||||
import {
|
||||
AccountBalance,
|
||||
Coupon,
|
||||
@ -37,10 +36,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
||||
const response = await this.client.get(path);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('Can not get account balance');
|
||||
}
|
||||
|
||||
@ -66,10 +61,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
||||
return couponType;
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('can not setup account');
|
||||
}
|
||||
|
||||
@ -83,10 +74,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
||||
const response = await this.client.get(path);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('can not get projects charges');
|
||||
}
|
||||
|
||||
@ -124,10 +111,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('can not add credit card');
|
||||
}
|
||||
|
||||
@ -145,10 +128,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('can not remove credit card');
|
||||
}
|
||||
|
||||
@ -163,9 +142,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
||||
const response = await this.client.get(path);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
throw new Error('can not list credit cards');
|
||||
}
|
||||
|
||||
@ -192,10 +168,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
throw new Error('can not list billing history');
|
||||
}
|
||||
|
||||
@ -248,9 +217,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
||||
const response = await this.client.get(path);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
throw new Error('Can not list token payment history');
|
||||
}
|
||||
|
||||
@ -285,33 +251,31 @@ export class PaymentsHttpApi implements PaymentsApi {
|
||||
const response = await this.client.patch(path, couponCode);
|
||||
const errMsg = `Could not apply coupon code "${couponCode}"`;
|
||||
|
||||
if (response.ok) {
|
||||
const coupon = await response.json();
|
||||
|
||||
if (!coupon) {
|
||||
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);
|
||||
}
|
||||
|
||||
return new Coupon(
|
||||
coupon.id,
|
||||
coupon.promoCode,
|
||||
coupon.name,
|
||||
coupon.amountOff,
|
||||
coupon.percentOff,
|
||||
new Date(coupon.addedAt),
|
||||
coupon.expiresAt ? new Date(coupon.expiresAt) : null,
|
||||
coupon.duration,
|
||||
);
|
||||
}
|
||||
|
||||
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:
|
||||
const coupon = await response.json();
|
||||
|
||||
if (!coupon) {
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
|
||||
return new Coupon(
|
||||
coupon.id,
|
||||
coupon.promoCode,
|
||||
coupon.name,
|
||||
coupon.amountOff,
|
||||
coupon.percentOff,
|
||||
new Date(coupon.addedAt),
|
||||
coupon.expiresAt ? new Date(coupon.expiresAt) : null,
|
||||
coupon.duration,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -323,10 +287,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
||||
const path = `${this.ROOT_PATH}/coupon`;
|
||||
const response = await this.client.get(path);
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('cannot retrieve coupon');
|
||||
}
|
||||
|
||||
@ -359,10 +319,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
||||
const response = await this.client.get(path);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('Can not get wallet');
|
||||
}
|
||||
|
||||
@ -385,10 +341,6 @@ export class PaymentsHttpApi implements PaymentsApi {
|
||||
const response = await this.client.post(path, null);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
|
||||
throw new Error('Can not claim new wallet');
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import { BaseGql } from '@/api/baseGql';
|
||||
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
||||
import {
|
||||
DataStamp,
|
||||
Project,
|
||||
@ -144,24 +143,21 @@ export class ProjectsApiGql extends BaseGql implements ProjectsApi {
|
||||
const path = `${this.ROOT_PATH}/${projectId}/usage-limits`;
|
||||
const response = await this.http.get(path);
|
||||
|
||||
if (response.ok) {
|
||||
const limits = await response.json();
|
||||
|
||||
return new ProjectLimits(
|
||||
limits.bandwidthLimit,
|
||||
limits.bandwidthUsed,
|
||||
limits.storageLimit,
|
||||
limits.storageUsed,
|
||||
limits.objectCount,
|
||||
limits.segmentCount,
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error('can not get usage limits');
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
const limits = await response.json();
|
||||
|
||||
return new ProjectLimits(
|
||||
limits.bandwidthLimit,
|
||||
limits.bandwidthUsed,
|
||||
limits.storageLimit,
|
||||
limits.storageUsed,
|
||||
limits.objectCount,
|
||||
limits.segmentCount,
|
||||
);
|
||||
|
||||
throw new Error('can not get usage limits');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -173,22 +169,19 @@ export class ProjectsApiGql extends BaseGql implements ProjectsApi {
|
||||
const path = `${this.ROOT_PATH}/usage-limits`;
|
||||
const response = await this.http.get(path);
|
||||
|
||||
if (response.ok) {
|
||||
const limits = await response.json();
|
||||
if (!response.ok) {
|
||||
throw new Error('can not get total usage limits');
|
||||
|
||||
return new ProjectLimits(
|
||||
limits.bandwidthLimit,
|
||||
limits.bandwidthUsed,
|
||||
limits.storageLimit,
|
||||
limits.storageUsed,
|
||||
);
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
const limits = await response.json();
|
||||
|
||||
throw new Error('can not get total usage limits');
|
||||
return new ProjectLimits(
|
||||
limits.bandwidthLimit,
|
||||
limits.bandwidthUsed,
|
||||
limits.storageLimit,
|
||||
limits.storageUsed,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -205,33 +198,30 @@ export class ProjectsApiGql extends BaseGql implements ProjectsApi {
|
||||
const path = `${this.ROOT_PATH}/${projectId}/daily-usage?from=${since}&to=${before}`;
|
||||
const response = await this.http.get(path);
|
||||
|
||||
if (response.ok) {
|
||||
const usage = await response.json();
|
||||
if (!response.ok) {
|
||||
throw new Error('Can not get project daily usage');
|
||||
|
||||
return new ProjectsStorageBandwidthDaily(
|
||||
usage.storageUsage.map(el => {
|
||||
const date = new Date(el.date);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return new DataStamp(el.value, date);
|
||||
}),
|
||||
usage.allocatedBandwidthUsage.map(el => {
|
||||
const date = new Date(el.date);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return new DataStamp(el.value, date);
|
||||
}),
|
||||
usage.settledBandwidthUsage.map(el => {
|
||||
const date = new Date(el.date);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return new DataStamp(el.value, date);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new ErrorUnauthorized();
|
||||
}
|
||||
const usage = await response.json();
|
||||
|
||||
throw new Error('Can not get project daily usage');
|
||||
return new ProjectsStorageBandwidthDaily(
|
||||
usage.storageUsage.map(el => {
|
||||
const date = new Date(el.date);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return new DataStamp(el.value, date);
|
||||
}),
|
||||
usage.allocatedBandwidthUsage.map(el => {
|
||||
const date = new Date(el.date);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return new DataStamp(el.value, date);
|
||||
}),
|
||||
usage.settledBandwidthUsage.map(el => {
|
||||
const date = new Date(el.date);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return new DataStamp(el.value, date);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public async getSalt(projectId: string): Promise<string> {
|
||||
@ -240,6 +230,7 @@ export class ProjectsApiGql extends BaseGql implements ProjectsApi {
|
||||
if (response.ok) {
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
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);
|
||||
setTimeout(() => {
|
||||
window.location.href = window.location.origin + '/login';
|
||||
}, 3000);
|
||||
}, 2000);
|
||||
} else {
|
||||
nError.result && Vue.prototype.$notify.error(nError.result.error, AnalyticsErrorEventSource.OVERALL_GRAPHQL_ERROR);
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
||||
|
||||
/**
|
||||
* HttpClient is a custom wrapper around fetch api.
|
||||
* Exposes get, post and delete methods for JSON strings.
|
||||
@ -22,7 +24,13 @@ export class HttpClient {
|
||||
'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> {
|
||||
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