{satellite/console,web/satellite}: get project salt from satellite

Add getSalt to projects api. Add action, GET_SALT, on Store
Projects module to make the api request and return the salt
string everywhere in the web app that generates an access grant.
The Wasm code which is used to create the access grant has been
changed to decode the salt as a base64 encoded string. The names
of the function calls in the changed Wasm code have also been
changed to ensure that access grant creation fails if JS access
grant worker code and Wasm code are not the same version.

https://github.com/storj/storj-private/issues/64

Change-Id: Ia2bc4cbadad84b066ca1882b042a3f0bb13c783a
This commit is contained in:
Cameron 2022-09-13 09:12:14 -04:00 committed by Storj Robot
parent 1887660678
commit 98fed4bc30
19 changed files with 235 additions and 45 deletions

View File

@ -5,7 +5,9 @@ package main
import (
"context"
"crypto/sha256"
"database/sql"
"encoding/base64"
"errors"
"github.com/zeebo/errs"
@ -124,7 +126,10 @@ func GetTestApiKey(satelliteId string) (string, error) {
return "", errs.Wrap(err)
}
accessGrant, err := consolewasm.GenAccessGrant(satelliteId, key.Serialize(), password, projectID.String())
idHash := sha256.Sum256(projectID[:])
base64Salt := base64.StdEncoding.EncodeToString(idHash[:])
accessGrant, err := consolewasm.GenAccessGrant(satelliteId, key.Serialize(), password, base64Salt)
if err != nil {
return "", errs.Wrap(err)
}

View File

@ -4,23 +4,22 @@
package consolewasm
import (
"crypto/sha256"
"encoding/base64"
"storj.io/common/encryption"
"storj.io/common/grant"
"storj.io/common/macaroon"
"storj.io/common/storj"
"storj.io/common/uuid"
)
// GenAccessGrant creates a new access grant and returns it serialized form.
func GenAccessGrant(satelliteNodeURL, apiKey, encryptionPassphrase, projectID string) (string, error) {
func GenAccessGrant(satelliteNodeURL, apiKey, encryptionPassphrase, base64EncodedSalt string) (string, error) {
parsedAPIKey, err := macaroon.ParseAPIKey(apiKey)
if err != nil {
return "", err
}
key, err := DeriveRootKey(encryptionPassphrase, projectID)
key, err := DeriveRootKey(encryptionPassphrase, base64EncodedSalt)
if err != nil {
return "", err
}
@ -41,14 +40,11 @@ func GenAccessGrant(satelliteNodeURL, apiKey, encryptionPassphrase, projectID st
}
// DeriveRootKey derives the root key portion of the access grant.
func DeriveRootKey(encryptionPassphrase, projectID string) (*storj.Key, error) {
id, err := uuid.FromString(projectID)
func DeriveRootKey(encryptionPassphrase, base64EncodedSalt string) (*storj.Key, error) {
const concurrency = 8
saltBytes, err := base64.StdEncoding.DecodeString(base64EncodedSalt)
if err != nil {
return nil, err
}
const concurrency = 8
salt := sha256.Sum256(id[:])
return encryption.DeriveRootKey([]byte(encryptionPassphrase), salt[:], "", concurrency)
return encryption.DeriveRootKey([]byte(encryptionPassphrase), saltBytes, "", concurrency)
}

View File

@ -4,6 +4,13 @@
package consolewasm_test
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"strings"
"testing"
"github.com/stretchr/testify/require"
@ -20,16 +27,27 @@ func TestGenerateAccessGrant(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
client := newTestClient(t, ctx, planet)
user := client.defaultUser()
uplinkPeer := planet.Uplinks[0]
projectID := uplinkPeer.Projects[0].ID
client.login(user.email, user.password)
resp, bodyString := client.request(http.MethodGet, fmt.Sprintf("/projects/%s/salt", projectID.String()), nil)
require.Equal(t, http.StatusOK, resp.StatusCode)
var b64Salt string
require.NoError(t, json.Unmarshal([]byte(bodyString), &b64Salt))
satellitePeer := planet.Satellites[0]
satelliteNodeURL := satellitePeer.NodeURL().String()
uplinkPeer := planet.Uplinks[0]
apiKeyString := uplinkPeer.Projects[0].APIKey
projectID := uplinkPeer.Projects[0].ID.String()
passphrase := "supersecretpassphrase"
wasmAccessString, err := console.GenAccessGrant(satelliteNodeURL, apiKeyString, passphrase, projectID)
wasmAccessString, err := console.GenAccessGrant(satelliteNodeURL, apiKeyString, passphrase, b64Salt)
require.NoError(t, err)
uplinkCliAccess, err := uplinkPeer.Config.RequestAccessWithPassphrase(ctx, satelliteNodeURL, apiKeyString, passphrase)
@ -50,7 +68,7 @@ func TestDefaultAccess(t *testing.T) {
satelliteNodeURL := satellitePeer.NodeURL().String()
uplinkPeer := planet.Uplinks[0]
APIKey := uplinkPeer.APIKey[satellitePeer.ID()]
projectID := uplinkPeer.Projects[0].ID.String()
projectID := uplinkPeer.Projects[0].ID
require.Equal(t, 1, len(uplinkPeer.Projects))
passphrase := "supersecretpassphrase"
@ -58,8 +76,18 @@ func TestDefaultAccess(t *testing.T) {
testfilename := "file.txt"
testdata := []byte("fun data")
client := newTestClient(t, ctx, planet)
user := client.defaultUser()
client.login(user.email, user.password)
resp, bodyString := client.request(http.MethodGet, fmt.Sprintf("/projects/%s/salt", projectID.String()), nil)
require.Equal(t, http.StatusOK, resp.StatusCode)
var b64Salt string
require.NoError(t, json.Unmarshal([]byte(bodyString), &b64Salt))
// Create an access with the console access grant code that allows full access.
access, err := console.GenAccessGrant(satelliteNodeURL, APIKey.Serialize(), passphrase, projectID)
access, err := console.GenAccessGrant(satelliteNodeURL, APIKey.Serialize(), passphrase, b64Salt)
require.NoError(t, err)
newAccess, err := uplink.ParseAccess(access)
require.NoError(t, err)
@ -80,3 +108,94 @@ func TestDefaultAccess(t *testing.T) {
require.NoError(t, uplinkPeer.DeleteBucket(ctx, satellitePeer, testbucket1))
})
}
type testClient struct {
t *testing.T
ctx *testcontext.Context
planet *testplanet.Planet
client *http.Client
}
func newTestClient(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) testClient {
jar, err := cookiejar.New(nil)
require.NoError(t, err)
return testClient{t: t, ctx: ctx, planet: planet, client: &http.Client{Jar: jar}}
}
type registeredUser struct {
email string
password string
}
func (testClient *testClient) request(method string, path string, data io.Reader) (resp Response, body string) {
req, err := http.NewRequestWithContext(testClient.ctx, method, testClient.url(path), data)
require.NoError(testClient.t, err)
req.Header = map[string][]string{
"sec-ch-ua": {`" Not A;Brand";v="99"`, `"Chromium";v="90"`, `"Google Chrome";v="90"`},
"sec-ch-ua-mobile": {"?0"},
"User-Agent": {"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"},
"Content-Type": {"application/json"},
"Accept": {"*/*"},
}
return testClient.do(req)
}
// Response is a wrapper for http.Request to prevent false-positive with bodyclose check.
type Response struct{ *http.Response }
func (testClient *testClient) do(req *http.Request) (_ Response, body string) {
resp, err := testClient.client.Do(req)
require.NoError(testClient.t, err)
data, err := ioutil.ReadAll(resp.Body)
require.NoError(testClient.t, err)
require.NoError(testClient.t, resp.Body.Close())
return Response{resp}, string(data)
}
func (testClient *testClient) url(suffix string) string {
return testClient.planet.Satellites[0].ConsoleURL() + "/api/v0" + suffix
}
func (testClient *testClient) toJSON(v interface{}) io.Reader {
data, err := json.Marshal(v)
require.NoError(testClient.t, err)
return strings.NewReader(string(data))
}
func (testClient *testClient) defaultUser() registeredUser {
user := testClient.planet.Uplinks[0].User[testClient.planet.Satellites[0].ID()]
return registeredUser{
email: user.Email,
password: user.Password,
}
}
func (testClient *testClient) login(email, password string) Response {
resp, body := testClient.request(
http.MethodPost, "/auth/token",
testClient.toJSON(map[string]string{
"email": email,
"password": password,
}))
cookie := findCookie(resp, "_tokenKey")
require.NotNil(testClient.t, cookie)
var tokenInfo struct {
Token string `json:"token"`
}
require.NoError(testClient.t, json.Unmarshal([]byte(body), &tokenInfo))
require.Equal(testClient.t, http.StatusOK, resp.StatusCode)
require.Equal(testClient.t, tokenInfo.Token, cookie.Value)
return resp
}
func findCookie(response Response, name string) *http.Cookie {
for _, c := range response.Cookies() {
if c.Name == name {
return c
}
}
return nil
}

View File

@ -4,8 +4,10 @@
package consolewasm_test
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"testing"
"time"
@ -26,7 +28,7 @@ func TestSetPermissionWithBuckets(t *testing.T) {
uplinkPeer := planet.Uplinks[0]
APIKey := uplinkPeer.APIKey[satellitePeer.ID()]
apiKeyString := APIKey.Serialize()
projectID := uplinkPeer.Projects[0].ID.String()
projectID := uplinkPeer.Projects[0].ID
require.Equal(t, 1, len(uplinkPeer.Projects))
passphrase := "supersecretpassphrase"
@ -80,7 +82,18 @@ func TestSetPermissionWithBuckets(t *testing.T) {
}
restrictedKey, err := consolewasm.SetPermission(apiKeyString, buckets, readOnlyPermission)
require.NoError(t, err)
restrictedAccessGrant, err := consolewasm.GenAccessGrant(satelliteNodeURL, restrictedKey.Serialize(), passphrase, projectID)
client := newTestClient(t, ctx, planet)
user := client.defaultUser()
client.login(user.email, user.password)
resp, bodyString := client.request(http.MethodGet, fmt.Sprintf("/projects/%s/salt", projectID.String()), nil)
require.Equal(t, http.StatusOK, resp.StatusCode)
var b64Salt string
require.NoError(t, json.Unmarshal([]byte(bodyString), &b64Salt))
restrictedAccessGrant, err := consolewasm.GenAccessGrant(satelliteNodeURL, restrictedKey.Serialize(), passphrase, b64Salt)
require.NoError(t, err)
restrictedAccess, err := uplink.ParseAccess(restrictedAccessGrant)
require.NoError(t, err)
@ -126,7 +139,17 @@ func TestSetPermission_Uplink(t *testing.T) {
require.NoError(t, uplinkPeer.Upload(ctx, satellitePeer, testbucket2, testfilename1, testdata))
require.NoError(t, uplinkPeer.Upload(ctx, satellitePeer, testbucket2, testfilename2, testdata))
withAccessKey(ctx, t, planet, passphrase, "only delete", []string{}, consolewasm.Permission{AllowDelete: true}, func(t *testing.T, project *uplink.Project) {
client := newTestClient(t, ctx, planet)
user := client.defaultUser()
client.login(user.email, user.password)
resp, bodyString := client.request(http.MethodGet, fmt.Sprintf("/projects/%s/salt", uplinkPeer.Projects[0].ID.String()), nil)
require.Equal(t, http.StatusOK, resp.StatusCode)
var b64Salt string
require.NoError(t, json.Unmarshal([]byte(bodyString), &b64Salt))
withAccessKey(ctx, t, planet, passphrase, b64Salt, "only delete", []string{}, consolewasm.Permission{AllowDelete: true}, func(t *testing.T, project *uplink.Project) {
// All operation except delete should be restricted
_, err = project.CreateBucket(ctx, testbucket2)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
@ -157,7 +180,7 @@ func TestSetPermission_Uplink(t *testing.T) {
require.Error(t, err)
})
withAccessKey(ctx, t, planet, passphrase, "only list", []string{testbucket1}, consolewasm.Permission{AllowList: true}, func(t *testing.T, project *uplink.Project) {
withAccessKey(ctx, t, planet, passphrase, b64Salt, "only list", []string{testbucket1}, consolewasm.Permission{AllowList: true}, func(t *testing.T, project *uplink.Project) {
// All operation except list inside testbucket1 should be restricted
_, err = project.CreateBucket(ctx, testbucket2)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
@ -186,7 +209,7 @@ func TestSetPermission_Uplink(t *testing.T) {
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
})
withAccessKey(ctx, t, planet, passphrase, "only upload", []string{testbucket1}, consolewasm.Permission{AllowUpload: true}, func(t *testing.T, project *uplink.Project) {
withAccessKey(ctx, t, planet, passphrase, b64Salt, "only upload", []string{testbucket1}, consolewasm.Permission{AllowUpload: true}, func(t *testing.T, project *uplink.Project) {
// All operation except upload to the testbucket1 should be restricted
_, err = project.CreateBucket(ctx, testbucket2)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
@ -215,7 +238,7 @@ func TestSetPermission_Uplink(t *testing.T) {
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
})
withAccessKey(ctx, t, planet, passphrase, "only download", []string{testbucket1}, consolewasm.Permission{AllowDownload: true}, func(t *testing.T, project *uplink.Project) {
withAccessKey(ctx, t, planet, passphrase, b64Salt, "only download", []string{testbucket1}, consolewasm.Permission{AllowDownload: true}, func(t *testing.T, project *uplink.Project) {
// All operation except download from testbucket1 should be restricted
_, err = project.CreateBucket(ctx, testbucket2)
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
@ -244,7 +267,7 @@ func TestSetPermission_Uplink(t *testing.T) {
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
})
withAccessKey(ctx, t, planet, passphrase, "not after", []string{}, consolewasm.Permission{
withAccessKey(ctx, t, planet, passphrase, b64Salt, "not after", []string{}, consolewasm.Permission{
AllowDownload: true,
AllowUpload: true,
AllowList: true,
@ -278,7 +301,7 @@ func TestSetPermission_Uplink(t *testing.T) {
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
})
withAccessKey(ctx, t, planet, passphrase, "not before", []string{}, consolewasm.Permission{
withAccessKey(ctx, t, planet, passphrase, b64Salt, "not before", []string{}, consolewasm.Permission{
AllowDownload: true,
AllowUpload: true,
AllowList: true,
@ -312,7 +335,7 @@ func TestSetPermission_Uplink(t *testing.T) {
require.True(t, errors.Is(err, uplink.ErrPermissionDenied))
})
withAccessKey(ctx, t, planet, passphrase, "all", []string{}, consolewasm.Permission{
withAccessKey(ctx, t, planet, passphrase, b64Salt, "all", []string{}, consolewasm.Permission{
AllowDownload: true,
AllowUpload: true,
AllowList: true,
@ -368,7 +391,7 @@ func getAllBuckets(ctx *testcontext.Context, project *uplink.Project) []*uplink.
return buckets
}
func withAccessKey(ctx context.Context, t *testing.T, planet *testplanet.Planet, passphrase string, testname string, bucket []string, permissions consolewasm.Permission, fn func(t *testing.T, uplink *uplink.Project)) {
func withAccessKey(ctx *testcontext.Context, t *testing.T, planet *testplanet.Planet, passphrase, salt, testname string, bucket []string, permissions consolewasm.Permission, fn func(t *testing.T, uplink *uplink.Project)) {
t.Run(testname, func(t *testing.T) {
upl := planet.Uplinks[0]
sat := planet.Satellites[0]
@ -377,7 +400,7 @@ func withAccessKey(ctx context.Context, t *testing.T, planet *testplanet.Planet,
restrictedKey, err := consolewasm.SetPermission(apikey.Serialize(), bucket, permissions)
require.NoError(t, err)
restrictedGrant, err := consolewasm.GenAccessGrant(sat.NodeURL().String(), restrictedKey.Serialize(), passphrase, upl.Projects[0].ID.String())
restrictedGrant, err := consolewasm.GenAccessGrant(sat.NodeURL().String(), restrictedKey.Serialize(), passphrase, salt)
require.NoError(t, err)
access, err := uplink.ParseAccess(restrictedGrant)

View File

@ -19,8 +19,8 @@ import (
)
func main() {
js.Global().Set("deriveAndEncryptRootKey", deriveAndEncryptRootKey())
js.Global().Set("generateAccessGrant", generateAccessGrant())
js.Global().Set("deriveAndAESEncryptRootKey", deriveAndEncryptRootKey())
js.Global().Set("generateNewAccessGrant", generateAccessGrant())
js.Global().Set("setAPIKeyPermission", setAPIKeyPermission())
js.Global().Set("newPermission", newPermission())
js.Global().Set("restrictGrant", restrictGrant())

View File

@ -234,6 +234,15 @@ export class ProjectsApiGql extends BaseGql implements ProjectsApi {
throw new Error('Can not get project daily usage');
}
public async getSalt(projectId: string): Promise<string> {
const path = `${this.ROOT_PATH}/${projectId}/salt`;
const response = await this.http.get(path);
if (response.ok) {
return await response.json();
}
throw new Error('Can not get project salt');
}
/**
* Fetch owned projects.
*

View File

@ -220,11 +220,13 @@ export default class CreateAccessModal extends Vue {
// creates access credentials
const satelliteNodeURL = MetaUtils.getMetaContent('satellite-nodeurl');
const salt = await this.$store.dispatch(PROJECTS_ACTIONS.GET_SALT, this.$store.getters.selectedProject.id);
this.worker.postMessage({
'type': 'GenerateAccess',
'apiKey': this.restrictedKey,
'passphrase': this.passphrase,
'projectID': this.$store.getters.selectedProject.id,
'salt': salt,
'satelliteNodeURL': satelliteNodeURL,
});

View File

@ -90,6 +90,7 @@ import { RouteConfig } from '@/router';
import { MetaUtils } from '@/utils/meta';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { AnalyticsHttpApi } from '@/api/analytics';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import VButton from '@/components/common/VButton.vue';
import VInput from '@/components/common/VInput.vue';
@ -186,12 +187,13 @@ export default class CreatePassphraseStep extends Vue {
await this.analytics.eventTriggered(AnalyticsEvent.PASSPHRASE_CREATED);
const satelliteNodeURL: string = MetaUtils.getMetaContent('satellite-nodeurl');
const salt = await this.$store.dispatch(PROJECTS_ACTIONS.GET_SALT, this.$store.getters.selectedProject.id);
this.worker.postMessage({
'type': 'GenerateAccess',
'apiKey': this.restrictedKey,
'passphrase': this.passphrase,
'projectID': this.$store.getters.selectedProject.id,
'salt': salt,
'satelliteNodeURL': satelliteNodeURL,
});

View File

@ -29,6 +29,7 @@ import { Component, Vue } from 'vue-property-decorator';
import { RouteConfig } from '@/router';
import { MetaUtils } from '@/utils/meta';
import { AnalyticsHttpApi } from '@/api/analytics';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import VInput from '@/components/common/VInput.vue';
import VButton from '@/components/common/VButton.vue';
@ -98,12 +99,13 @@ export default class EnterPassphraseStep extends Vue {
this.isLoading = true;
const satelliteNodeURL = MetaUtils.getMetaContent('satellite-nodeurl');
const salt = await this.$store.dispatch(PROJECTS_ACTIONS.GET_SALT, this.$store.getters.selectedProject.id);
this.worker.postMessage({
'type': 'GenerateAccess',
'apiKey': this.restrictedKey,
'passphrase': this.passphrase,
'projectID': this.$store.getters.selectedProject.id,
'salt': salt,
'satelliteNodeURL': satelliteNodeURL,
});

View File

@ -56,6 +56,7 @@ import { MetaUtils } from '@/utils/meta';
import { AccessGrant, EdgeCredentials } from '@/types/accessGrants';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { AnalyticsHttpApi } from '@/api/analytics';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import VModal from '@/components/common/VModal.vue';
import VInput from '@/components/common/VInput.vue';
@ -147,12 +148,14 @@ export default class OpenBucketModal extends Vue {
throw new Error(grantEvent.data.error);
}
const salt = await this.$store.dispatch(PROJECTS_ACTIONS.GET_SALT, this.$store.getters.selectedProject.id);
const satelliteNodeURL: string = MetaUtils.getMetaContent('satellite-nodeurl');
this.worker.postMessage({
'type': 'GenerateAccess',
'apiKey': grantEvent.data.value,
'passphrase': this.passphrase,
'projectID': this.$store.getters.selectedProject.id,
'salt': salt,
'satelliteNodeURL': satelliteNodeURL,
});

View File

@ -40,6 +40,7 @@ import { Component, Vue } from 'vue-property-decorator';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { MetaUtils } from '@/utils/meta';
import { AccessGrant, EdgeCredentials } from '@/types/accessGrants';
@ -104,12 +105,13 @@ export default class ShareBucketModal extends Vue {
const cleanAPIKey: AccessGrant = await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.CREATE, LINK_SHARING_AG_NAME);
const satelliteNodeURL = MetaUtils.getMetaContent('satellite-nodeurl');
const salt = await this.$store.dispatch(PROJECTS_ACTIONS.GET_SALT, this.$store.getters.selectedProject.id);
this.worker.postMessage({
'type': 'GenerateAccess',
'apiKey': cleanAPIKey.secret,
'passphrase': this.passphrase,
'projectID': this.$store.getters.selectedProject.id,
'salt': salt,
'satelliteNodeURL': satelliteNodeURL,
});

View File

@ -25,6 +25,7 @@ import { RouteConfig } from '@/router';
import { OBJECTS_ACTIONS } from '@/store/modules/objects';
import { AccessGrant, EdgeCredentials } from '@/types/accessGrants';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { MetaUtils } from '@/utils/meta';
import { AnalyticsHttpApi } from '@/api/analytics';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
@ -169,11 +170,13 @@ export default class BucketCreation extends Vue {
}
const satelliteNodeURL: string = MetaUtils.getMetaContent('satellite-nodeurl');
const salt = await this.$store.dispatch(PROJECTS_ACTIONS.GET_SALT, this.$store.getters.selectedProject.id);
this.worker.postMessage({
'type': 'GenerateAccess',
'apiKey': grantEvent.data.value,
'passphrase': this.passphrase,
'projectID': this.$store.getters.selectedProject.id,
'salt': salt,
'satelliteNodeURL': satelliteNodeURL,
});

View File

@ -91,6 +91,7 @@ import { Component, Vue, Watch } from 'vue-property-decorator';
import { RouteConfig } from '@/router';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { OBJECTS_ACTIONS } from '@/store/modules/objects';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { AccessGrant, EdgeCredentials } from '@/types/accessGrants';
import { MetaUtils } from '@/utils/meta';
import { Validator } from '@/utils/validation';
@ -222,13 +223,15 @@ export default class BucketsView extends Vue {
if (grantEvent.data.error) {
throw new Error(grantEvent.data.error);
}
const salt = await this.$store.dispatch(PROJECTS_ACTIONS.GET_SALT, this.$store.getters.selectedProject.id);
const satelliteNodeURL: string = MetaUtils.getMetaContent('satellite-nodeurl');
this.worker.postMessage({
'type': 'GenerateAccess',
'apiKey': this.grantWithPermissions,
'passphrase': '',
'projectID': this.$store.getters.selectedProject.id,
'salt': salt,
'satelliteNodeURL': satelliteNodeURL,
});

View File

@ -66,6 +66,7 @@ import { OBJECTS_ACTIONS } from '@/store/modules/objects';
import { LocalData } from '@/utils/localData';
import { EdgeCredentials } from '@/types/accessGrants';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { MetaUtils } from '@/utils/meta';
import { AnalyticsHttpApi } from '@/api/analytics';
@ -193,11 +194,13 @@ export default class EncryptData extends Vue {
}
const satelliteNodeURL: string = MetaUtils.getMetaContent('satellite-nodeurl');
const salt = await this.$store.dispatch(PROJECTS_ACTIONS.GET_SALT, this.$store.getters.selectedProject.id);
this.worker.postMessage({
'type': 'GenerateAccess',
'apiKey': grantEvent.data.value,
'passphrase': this.passphrase,
'projectID': this.$store.getters.selectedProject.id,
'salt': salt,
'satelliteNodeURL': satelliteNodeURL,
});

View File

@ -22,6 +22,7 @@ import { Component, Vue } from 'vue-property-decorator';
import { AnalyticsHttpApi } from '@/api/analytics';
import { RouteConfig } from '@/router';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { AccessGrant, EdgeCredentials } from '@/types/accessGrants';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { MetaUtils } from '@/utils/meta';
@ -140,12 +141,13 @@ export default class UploadFile extends Vue {
*/
private async generateCredentials(cleanApiKey: string, path: string, areEndless: boolean): Promise<EdgeCredentials> {
const satelliteNodeURL = MetaUtils.getMetaContent('satellite-nodeurl');
const salt = await this.$store.dispatch(PROJECTS_ACTIONS.GET_SALT, this.$store.getters.selectedProject.id);
this.worker.postMessage({
'type': 'GenerateAccess',
'apiKey': cleanApiKey,
'passphrase': this.passphrase,
'projectID': this.$store.getters.selectedProject.id,
'salt': salt,
'satelliteNodeURL': satelliteNodeURL,
});

View File

@ -29,6 +29,7 @@ export const PROJECTS_ACTIONS = {
CLEAR: 'clearProjects',
GET_LIMITS: 'getProjectLimits',
GET_TOTAL_LIMITS: 'getTotalLimits',
GET_SALT: 'getSalt',
};
export const PROJECTS_MUTATIONS = {
@ -91,6 +92,7 @@ const {
GET_LIMITS,
GET_TOTAL_LIMITS,
FETCH_OWNED,
GET_SALT,
} = PROJECTS_ACTIONS;
const {
@ -331,6 +333,9 @@ export function makeProjectsModule(api: ProjectsApi): StoreModule<ProjectsState,
return limits;
},
[GET_SALT]: async function (_, projectID: string): Promise<string> {
return await api.getSalt(projectID);
},
[CLEAR]: function({ commit }: ProjectsContext): void {
commit(CLEAR_PROJECTS);
},

View File

@ -45,6 +45,14 @@ export interface ProjectsApi {
*/
getLimits(projectId: string): Promise<ProjectLimits>;
/**
* Get project salt
*
* @param projectID - project ID
* throws Error
*/
getSalt(projectID: string): Promise<string>;
/**
* Get project limits.
*

View File

@ -35,7 +35,7 @@ self.onmessage = async function (event) {
const projectID = data.projectID;
const aesKey = data.aesKey;
result = self.deriveAndEncryptRootKey(passphrase, projectID, aesKey);
result = self.deriveAndAESEncryptRootKey(passphrase, projectID, aesKey);
self.postMessage(result);
}
break;
@ -43,11 +43,10 @@ self.onmessage = async function (event) {
{
apiKey = data.apiKey;
const passphrase = data.passphrase;
const projectID = data.projectID;
const salt = data.salt;
const nodeURL = data.satelliteNodeURL;
result = self.generateAccessGrant(nodeURL, apiKey, passphrase, projectID);
result = self.generateNewAccessGrant(nodeURL, apiKey, passphrase, salt);
self.postMessage(result);
}
break;

View File

@ -55,6 +55,10 @@ export class ProjectsApiMock implements ProjectsApi {
return Promise.resolve(this.mockLimits);
}
getSalt(): Promise<string> {
throw new Error('not implemented');
}
getDailyUsage(_projectId: string, _start: Date, _end: Date): Promise<ProjectsStorageBandwidthDaily> {
throw new Error('not implemented');
}