web/satellite: reworked Encrypt your data component
Reworked component to have all needed warnings and info. Removed Warning route from objects flow. Updated tests. Change-Id: Iced68aca7fbf4e1f7b7e5302147a51fd0e9115c8
This commit is contained in:
parent
4a530ccffd
commit
98b59fe305
@ -73,8 +73,8 @@ func TestNavigation(t *testing.T) {
|
||||
// project dashboard route
|
||||
page.MustElementR("p", "Objects").MustClick()
|
||||
waitVueTick(page)
|
||||
objectsTitle := page.MustElement("[aria-roledescription=title]").MustText()
|
||||
require.Contains(t, objectsTitle, "Object Browser")
|
||||
objectsTitle := page.MustElement("[aria-roledescription=enc-title]").MustText()
|
||||
require.Contains(t, objectsTitle, "Encryption passphrase")
|
||||
|
||||
// access grants route
|
||||
page.MustElementR("p", "Access").MustClick()
|
||||
@ -124,8 +124,8 @@ func TestNavigation(t *testing.T) {
|
||||
page.MustElementR("p", "Quick Start").MustClick()
|
||||
page.MustElement("[aria-roledescription=objects-route]").MustClick()
|
||||
waitVueTick(page)
|
||||
objectsTitle1 := page.MustElement("[aria-roledescription=title]").MustText()
|
||||
require.Contains(t, objectsTitle1, "Object Browser")
|
||||
objectsTitle1 := page.MustElement("[aria-roledescription=enc-title]").MustText()
|
||||
require.Contains(t, objectsTitle1, "Encryption passphrase")
|
||||
|
||||
// onboarding cli flow route
|
||||
page.MustElementR("p", "Quick Start").MustClick()
|
||||
|
@ -47,20 +47,20 @@ func TestOnboardingWizardBrowser(t *testing.T) {
|
||||
page.MustElementX("(//span[text()=\"Continue in web\"])").MustClick()
|
||||
waitVueTick(page)
|
||||
|
||||
objectBrowserWarning := page.MustElement("[aria-roledescription=sub-title]").MustText()
|
||||
objectBrowserWarning := page.MustElement("[aria-roledescription=objects-title]").MustText()
|
||||
require.Contains(t, objectBrowserWarning, "The object browser uses server side encryption.")
|
||||
page.MustElementX("(//span[text()=\"Continue\"])").MustClick()
|
||||
waitVueTick(page)
|
||||
|
||||
encryptionPassphraseWarningTitle := page.MustElement("[aria-roledescription=warning-title]").MustText()
|
||||
require.Contains(t, encryptionPassphraseWarningTitle, "The object browser uses server side encryption.")
|
||||
require.Contains(t, encryptionPassphraseWarningTitle, "Save your encryption passphrase")
|
||||
customPassphrase := page.MustElement("[aria-roledescription=enter-passphrase-label]")
|
||||
customPassphraseLabel := customPassphrase.MustText()
|
||||
require.Contains(t, customPassphraseLabel, "Enter Your Own Passphrase")
|
||||
require.Contains(t, customPassphraseLabel, "Enter your own passphrase")
|
||||
customPassphrase.MustClick()
|
||||
waitVueTick(page)
|
||||
|
||||
page.MustElement("[aria-roledescription=passphrase] input").MustInput("password123")
|
||||
page.MustElement(".checkmark").MustClick()
|
||||
waitVueTick(page)
|
||||
page.MustElementX("(//span[text()=\"Next >\"])").MustClick()
|
||||
waitVueTick(page)
|
||||
|
||||
|
@ -84,7 +84,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
import * as bip39 from "bip39";
|
||||
import { generateMnemonic } from "bip39";
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { MetaUtils } from '@/utils/meta';
|
||||
@ -144,7 +144,7 @@ export default class CreatePassphraseStep extends Vue {
|
||||
|
||||
this.setWorker();
|
||||
|
||||
this.passphrase = bip39.generateMnemonic();
|
||||
this.passphrase = generateMnemonic();
|
||||
|
||||
this.isLoading = false;
|
||||
}
|
||||
@ -241,7 +241,7 @@ export default class CreatePassphraseStep extends Vue {
|
||||
public onChooseGenerate(): void {
|
||||
if (this.passphrase && this.isGenerateState) return;
|
||||
|
||||
this.passphrase = bip39.generateMnemonic();
|
||||
this.passphrase = generateMnemonic();
|
||||
|
||||
this.isEnterState = false;
|
||||
this.isGenerateState = true;
|
||||
|
@ -4,25 +4,30 @@
|
||||
<template>
|
||||
<div class="encrypt-container">
|
||||
<EncryptIcon />
|
||||
<h1 class="encrypt-container__title">Encrypt your data</h1>
|
||||
<h1 class="encrypt-container__title" aria-roledescription="enc-title">Encryption passphrase</h1>
|
||||
<p class="encrypt-container__info">
|
||||
The encryption passphrase is used to encrypt and access the data that you upload to Storj. We strongly
|
||||
encourage you to use a mnemonic phrase, which is automatically generated on the client-side for you.
|
||||
The encryption passphrase is used to encrypt and access the data that you upload to Storj.
|
||||
</p>
|
||||
<div class="encrypt-container__header">
|
||||
<p class="encrypt-container__header__rec">RECOMMENDED</p>
|
||||
<div class="encrypt-container__header__row">
|
||||
<p class="encrypt-container__header__row__gen" :class="{ active: isGenerate }" @click="setToGenerate">Generate Phrase</p>
|
||||
<div class="encrypt-container__header__row__right">
|
||||
<p class="encrypt-container__header__row__right__enter" :class="{ active: !isGenerate }" aria-roledescription="enter-passphrase-label" @click="setToEnter">
|
||||
Enter Your Own Passphrase
|
||||
<div class="encrypt-container__functional">
|
||||
<div class="encrypt-container__functional__header">
|
||||
<p class="encrypt-container__functional__header__gen" :class="{ active: isGenerate }" @click="setToGenerate">
|
||||
Generate a new passphrase
|
||||
</p>
|
||||
<div class="encrypt-container__functional__header__right" :class="{ active: !isGenerate }">
|
||||
<p
|
||||
class="encrypt-container__functional__header__right__enter"
|
||||
:class="{ active: !isGenerate }"
|
||||
aria-roledescription="enter-passphrase-label"
|
||||
@click="setToEnter"
|
||||
>
|
||||
Enter your own passphrase
|
||||
</p>
|
||||
<VInfo class="encrypt-container__header__row__right__info-button">
|
||||
<VInfo class="encrypt-container__functional__header__right__info-button">
|
||||
<template #icon>
|
||||
<InfoIcon class="encrypt-container__header__row__right__info-button__image" />
|
||||
<InfoIcon class="encrypt-container__functional__header__right__info-button__image" :class="{ active: !isGenerate }" />
|
||||
</template>
|
||||
<template #message>
|
||||
<p class="encrypt-container__header__row__right__info-button__message">
|
||||
<p class="encrypt-container__functional__header__right__info-button__message">
|
||||
We strongly encourage you to use a mnemonic phrase, which is automatically generated on
|
||||
the client-side for you. Alternatively, you can enter your own passphrase.
|
||||
</p>
|
||||
@ -30,32 +35,42 @@
|
||||
</VInfo>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isGenerate" class="encrypt-container__generate">
|
||||
<p class="encrypt-container__generate__value">{{ passphrase }}</p>
|
||||
<VButton
|
||||
class="encrypt-container__generate__button"
|
||||
label="Copy"
|
||||
width="66px"
|
||||
height="30px"
|
||||
is-blue-white="true"
|
||||
:on-press="onCopyClick"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="encrypt-container__enter">
|
||||
<HeaderlessInput
|
||||
placeholder="Enter a passphrase here..."
|
||||
:error="enterError"
|
||||
role-description="passphrase"
|
||||
@setData="setPassphrase"
|
||||
/>
|
||||
</div>
|
||||
<p class="encrypt-container__download" @click="onDownloadClick">Download as a text file</p>
|
||||
<div class="encrypt-container__warning">
|
||||
<h2 class="encrypt-container__warning__title" aria-roledescription="warning-title">The object browser uses server side encryption.</h2>
|
||||
<p class="encrypt-container__warning__msg">
|
||||
If you want to use our product with only end-to-end encryption, you may want to use our command line solution.
|
||||
<div v-if="isGenerate" class="encrypt-container__functional__generate">
|
||||
<p class="encrypt-container__functional__generate__value">{{ passphrase }}</p>
|
||||
<VButton
|
||||
class="encrypt-container__functional__generate__button"
|
||||
label="Copy"
|
||||
width="66px"
|
||||
height="30px"
|
||||
is-blue-white="true"
|
||||
is-uppercase="true"
|
||||
:on-press="onCopyClick"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="encrypt-container__functional__enter">
|
||||
<HeaderlessInput
|
||||
label="Your Passphrase"
|
||||
placeholder="Enter a passphrase here..."
|
||||
:error="enterError"
|
||||
role-description="passphrase"
|
||||
is-password="true"
|
||||
@setData="setPassphrase"
|
||||
/>
|
||||
</div>
|
||||
<h2 class="encrypt-container__functional__warning-title" aria-roledescription="warning-title">
|
||||
Save your encryption passphrase
|
||||
</h2>
|
||||
<p class="encrypt-container__functional__warning-msg">
|
||||
Please note that Storj does not know or store your encryption passphrase. If you lose it, you will not
|
||||
be able to recover your files.
|
||||
</p>
|
||||
<p class="encrypt-container__functional__download" @click="onDownloadClick">Download as a text file</p>
|
||||
<VCheckbox
|
||||
class="encrypt-container__functional__checkbox"
|
||||
label="I understand, and I have saved the passphrase."
|
||||
:is-checkbox-error="isCheckboxError"
|
||||
@setData="setSavingConfirmation"
|
||||
/>
|
||||
</div>
|
||||
<div class="encrypt-container__buttons">
|
||||
<VButton
|
||||
@ -63,7 +78,7 @@
|
||||
height="64px"
|
||||
border-radius="62px"
|
||||
:on-press="onNextButtonClick"
|
||||
:is-disabled="isLoading"
|
||||
:is-disabled="isLoading || !isSavingConfirmed"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -71,7 +86,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
import * as bip39 from "bip39";
|
||||
import { generateMnemonic } from "bip39";
|
||||
|
||||
import { LocalData, UserIDPassSalt } from "@/utils/localData";
|
||||
import { Download } from "@/utils/download";
|
||||
@ -79,9 +94,10 @@ import { Download } from "@/utils/download";
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import VInfo from "@/components/common/VInfo.vue";
|
||||
import HeaderlessInput from "@/components/common/HeaderlessInput.vue";
|
||||
import VCheckbox from "@/components/common/VCheckbox.vue";
|
||||
|
||||
import EncryptIcon from "@/../static/images/objects/encrypt.svg";
|
||||
import InfoIcon from "@/../static/images/common/greyInfo.svg";
|
||||
import InfoIcon from "@/../static/images/common/smallGreyInfo.svg";
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
@ -91,6 +107,7 @@ import InfoIcon from "@/../static/images/common/greyInfo.svg";
|
||||
VInfo,
|
||||
VButton,
|
||||
HeaderlessInput,
|
||||
VCheckbox,
|
||||
},
|
||||
})
|
||||
export default class GeneratePassphrase extends Vue {
|
||||
@ -104,6 +121,8 @@ export default class GeneratePassphrase extends Vue {
|
||||
public isGenerate = true;
|
||||
public enterError = '';
|
||||
public passphrase = '';
|
||||
public isSavingConfirmed = false;
|
||||
public isCheckboxError = false;
|
||||
|
||||
/**
|
||||
* Lifecycle hook after initial render.
|
||||
@ -117,10 +136,14 @@ export default class GeneratePassphrase extends Vue {
|
||||
return;
|
||||
}
|
||||
|
||||
this.passphrase = bip39.generateMnemonic();
|
||||
this.passphrase = generateMnemonic();
|
||||
this.setParentPassphrase(this.passphrase);
|
||||
}
|
||||
|
||||
public setSavingConfirmation(value: boolean): void {
|
||||
this.isSavingConfirmed = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on copy button click logic.
|
||||
* Copies passphrase to clipboard.
|
||||
@ -171,7 +194,7 @@ export default class GeneratePassphrase extends Vue {
|
||||
public setToGenerate(): void {
|
||||
if (this.enterError) this.enterError = '';
|
||||
|
||||
this.passphrase = bip39.generateMnemonic();
|
||||
this.passphrase = generateMnemonic();
|
||||
this.setParentPassphrase(this.passphrase);
|
||||
this.isGenerate = true;
|
||||
}
|
||||
@ -186,6 +209,12 @@ export default class GeneratePassphrase extends Vue {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isSavingConfirmed) {
|
||||
this.isCheckboxError = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await this.onNextClick();
|
||||
}
|
||||
}
|
||||
@ -194,12 +223,15 @@ export default class GeneratePassphrase extends Vue {
|
||||
<style scoped lang="scss">
|
||||
.encrypt-container {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
padding: 60px;
|
||||
padding: 40px 60px 60px 60px;
|
||||
max-width: 500px;
|
||||
background: #fcfcfc;
|
||||
box-shadow: 0 0 32px rgba(0, 0, 0, 0.04);
|
||||
border-radius: 20px;
|
||||
margin: 30px auto 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
@ -207,55 +239,57 @@ export default class GeneratePassphrase extends Vue {
|
||||
line-height: 56px;
|
||||
letter-spacing: 1px;
|
||||
color: #14142b;
|
||||
margin: 35px 0 10px 0;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
&__info {
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
line-height: 28px;
|
||||
letter-spacing: 0.75px;
|
||||
color: #1b2533;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
max-width: 420px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
&__functional {
|
||||
border: 1px solid #e6e9ef;
|
||||
border-radius: 10px;
|
||||
padding: 20px 0;
|
||||
|
||||
&__rec {
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
color: #1b2533;
|
||||
opacity: 0.4;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
&__row {
|
||||
&__header {
|
||||
width: calc(100% - 50px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 25px;
|
||||
border-bottom: 1px solid #e6e9ef;
|
||||
|
||||
&__gen {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
color: #a9b5c1;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 5px solid #fff;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 4px solid #fff;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__right {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 4px solid #fff;
|
||||
cursor: pointer;
|
||||
|
||||
&__enter {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
color: #a9b5c1;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 5px solid #fff;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__info-button {
|
||||
@ -272,59 +306,58 @@ export default class GeneratePassphrase extends Vue {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__generate {
|
||||
margin-top: 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 25px;
|
||||
background: #eff0f7;
|
||||
border-radius: 10px;
|
||||
&__generate {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px 22px;
|
||||
background: #eff0f7;
|
||||
border-radius: 10px;
|
||||
margin: 25px 25px 0 25px;
|
||||
|
||||
&__value {
|
||||
font-size: 16px;
|
||||
line-height: 28px;
|
||||
color: #384b65;
|
||||
&__value {
|
||||
font-size: 14px;
|
||||
line-height: 25px;
|
||||
color: #384b65;
|
||||
}
|
||||
|
||||
&__button {
|
||||
margin-left: 32px;
|
||||
min-width: 66px;
|
||||
}
|
||||
}
|
||||
|
||||
&__button {
|
||||
margin-left: 32px;
|
||||
min-width: 66px;
|
||||
&__enter {
|
||||
margin: 25px 25px 0 25px;
|
||||
}
|
||||
}
|
||||
|
||||
&__enter {
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
&__download {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
color: #0068dc;
|
||||
cursor: pointer;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
&__warning {
|
||||
border: 1px solid #e6e9ef;
|
||||
border-radius: 10px;
|
||||
padding: 25px;
|
||||
|
||||
&__title {
|
||||
&__download {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
color: #df1616;
|
||||
margin-bottom: 10px;
|
||||
color: #0068dc;
|
||||
cursor: pointer;
|
||||
margin: 20px 25px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&__msg {
|
||||
&__warning-title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
color: #1b2533;
|
||||
margin: 25px 25px 10px 25px;
|
||||
}
|
||||
|
||||
&__warning-msg {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #1b2533;
|
||||
margin-bottom: 10px;
|
||||
margin: 0 25px;
|
||||
}
|
||||
|
||||
&__checkbox {
|
||||
margin: 0 25px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,11 +366,6 @@ export default class GeneratePassphrase extends Vue {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 30px;
|
||||
|
||||
&__back,
|
||||
&__skip {
|
||||
margin-right: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -346,6 +374,10 @@ export default class GeneratePassphrase extends Vue {
|
||||
border-color: #0149ff;
|
||||
}
|
||||
|
||||
.active svg rect {
|
||||
fill: #0149ff;
|
||||
}
|
||||
|
||||
::v-deep .info__box__message {
|
||||
min-width: 440px;
|
||||
}
|
||||
|
@ -2,10 +2,13 @@
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<label class="container">
|
||||
<input v-model="checked" class="checkmark-input" type="checkbox" @change="onChange">
|
||||
<span class="checkmark" :class="{'error': isCheckboxError}" />
|
||||
</label>
|
||||
<div class="wrap">
|
||||
<label class="container">
|
||||
<input id="checkbox" v-model="checked" class="checkmark-input" type="checkbox" @change="onChange">
|
||||
<span class="checkmark" :class="{'error': isCheckboxError}" />
|
||||
</label>
|
||||
<label class="label" for="checkbox">{{ label }}</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -17,6 +20,8 @@ import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
export default class VCheckbox extends Vue {
|
||||
@Prop({default: false})
|
||||
private readonly isCheckboxError: boolean;
|
||||
@Prop({default: ''})
|
||||
private readonly label: string;
|
||||
|
||||
private checked = false;
|
||||
|
||||
@ -30,14 +35,19 @@ export default class VCheckbox extends Vue {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding-left: 20px;
|
||||
height: 23px;
|
||||
width: 23px;
|
||||
padding-left: 15px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
cursor: pointer;
|
||||
font-size: 22px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
@ -57,10 +67,11 @@ export default class VCheckbox extends Vue {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border: 2px solid rgba(56, 75, 101, 0.4);
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.checkmark:after {
|
||||
@ -70,8 +81,8 @@ export default class VCheckbox extends Vue {
|
||||
}
|
||||
|
||||
.container .checkmark:after {
|
||||
left: 9px;
|
||||
top: 5px;
|
||||
left: 4px;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid white;
|
||||
@ -86,8 +97,8 @@ export default class VCheckbox extends Vue {
|
||||
}
|
||||
|
||||
.container .checkmark-input:checked ~ .checkmark {
|
||||
border: 2px solid #2196f3;
|
||||
background-color: #2196f3;
|
||||
border: 2px solid #376fff;
|
||||
background-color: #376fff;
|
||||
}
|
||||
|
||||
.checkmark.error {
|
||||
@ -97,4 +108,9 @@ export default class VCheckbox extends Vue {
|
||||
.container .checkmark-input:checked ~ .checkmark:after {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.label {
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
|
@ -3,12 +3,56 @@
|
||||
|
||||
<template>
|
||||
<div class="encrypt">
|
||||
<h1 class="encrypt__title">Objects</h1>
|
||||
<div class="encrypt__msg-container">
|
||||
<p class="encrypt__msg-container__title" aria-roledescription="objects-title">
|
||||
The object browser uses
|
||||
<a
|
||||
class="encrypt__msg-container__title__link"
|
||||
href="https://docs.storj.io/dcs/concepts/encryption-key/design-decision-server-side-encryption"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
server side encryption.
|
||||
</a>
|
||||
</p>
|
||||
<p class="encrypt__msg-container__text">
|
||||
If you want to use our product with only end-to-end encryption, you may want to use our
|
||||
<span class="encrypt__msg-container__text__link" @click="navigateToCLIFlow">command line solution.</span>
|
||||
</p>
|
||||
</div>
|
||||
<GeneratePassphrase
|
||||
:on-next-click="onNextClick"
|
||||
:set-parent-passphrase="setPassphrase"
|
||||
:is-loading="isLoading"
|
||||
/>
|
||||
<div class="encrypt__faq">
|
||||
<h2 class="encrypt__faq__title">FAQ</h2>
|
||||
<FAQBullet
|
||||
title="Why do I need a passphrase to upload?"
|
||||
text="One very important design consideration is that data stored on Storj is encrypted. That means
|
||||
only you have the encryption passphrase for your data. The service doesn't ever have access to or store
|
||||
your encryption passphrase. If you lose your passphrase, you will be unable to recover your data."
|
||||
/>
|
||||
<FAQBullet
|
||||
title="What if I enter a wrong passphrase?"
|
||||
text="There is no wrong passphrase because every passphrase can have access to different files. Entering
|
||||
a new or different passphrase won’t have any effect on the existing files. If you enter your passphrase
|
||||
and don’t see the existing files, it’s most likely you entered a new passphrase and that’s why you can’t
|
||||
see the encrypted data stored with a different passphrase."
|
||||
/>
|
||||
<FAQBullet
|
||||
title="Why I have to enter a passphrase for every bucket?"
|
||||
text="In general, the best practice is to use one encryption passphrase per bucket. If an object with
|
||||
the same path and object name uploaded by two uplinks with encryption keys derived from the same
|
||||
encryption passphrase, the most recent upload will over-write the older object."
|
||||
/>
|
||||
<FAQBullet
|
||||
title="Why there is no “remember passphrase”?"
|
||||
text="There is no wrong passphrase because every passphrase can have access to different files. Entering
|
||||
a new or different passphrase won’t have any effect on the existing files. If you enter a passphrase and
|
||||
don’t see your existing files, try to enter your passphrase again."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -21,17 +65,31 @@ import { OBJECTS_ACTIONS } from '@/store/modules/objects';
|
||||
import { LocalData } from "@/utils/localData";
|
||||
|
||||
import GeneratePassphrase from "@/components/common/GeneratePassphrase.vue";
|
||||
import FAQBullet from "@/components/objects/FAQBullet.vue";
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
GeneratePassphrase,
|
||||
FAQBullet,
|
||||
},
|
||||
})
|
||||
export default class EncryptData extends Vue {
|
||||
public isLoading = false;
|
||||
public passphrase = '';
|
||||
|
||||
/**
|
||||
* Sets passphrase from child component.
|
||||
*/
|
||||
public navigateToCLIFlow(): void {
|
||||
this.$router.push({
|
||||
name: RouteConfig.APIKey.name,
|
||||
params: {
|
||||
backRoute: this.$route.path,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets passphrase from child component.
|
||||
*/
|
||||
@ -86,14 +144,69 @@ export default class EncryptData extends Vue {
|
||||
<style scoped lang="scss">
|
||||
.encrypt {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
padding-bottom: 60px;
|
||||
padding-bottom: 30px;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
line-height: 26px;
|
||||
color: #232b34;
|
||||
&__msg-container {
|
||||
margin: -20px auto 40px auto;
|
||||
max-width: 620px;
|
||||
background-color: #ffd78a;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border-radius: 0 0 10px 10px;
|
||||
padding: 20px 0;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
text-align: center;
|
||||
color: #000;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&__link {
|
||||
color: #000;
|
||||
text-decoration: underline !important;
|
||||
text-underline-position: under;
|
||||
|
||||
&:visited {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
color: #1b2533;
|
||||
max-width: 400px;
|
||||
|
||||
&__link {
|
||||
color: #1b2533;
|
||||
text-decoration: underline;
|
||||
text-underline-position: under;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__faq {
|
||||
max-width: 620px;
|
||||
// display: flex; revert this when FAQ content will be confirmed
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 36px;
|
||||
line-height: 56px;
|
||||
letter-spacing: 1px;
|
||||
color: #14142b;
|
||||
margin: 75px 0 30px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
76
web/satellite/src/components/objects/FAQBullet.vue
Normal file
76
web/satellite/src/components/objects/FAQBullet.vue
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="bullet">
|
||||
<div class="bullet__header" @click="toggle">
|
||||
<h2 class="bullet__header__title">{{ title }}</h2>
|
||||
<MinusIcon v-if="isExpanded" />
|
||||
<PlusIcon v-else />
|
||||
</div>
|
||||
<p v-if="isExpanded" class="bullet__text">{{ text }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
|
||||
import PlusIcon from '@/../static/images/common/plus.svg';
|
||||
import MinusIcon from '@/../static/images/common/minus.svg';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
PlusIcon,
|
||||
MinusIcon,
|
||||
},
|
||||
})
|
||||
export default class FAQBullet extends Vue {
|
||||
@Prop({default: ''})
|
||||
public readonly title: string;
|
||||
@Prop({default: ''})
|
||||
public readonly text: string;
|
||||
|
||||
public isExpanded = false;
|
||||
|
||||
/**
|
||||
* Toggles bullet text visibility.
|
||||
*/
|
||||
public toggle(): void {
|
||||
this.isExpanded = !this.isExpanded;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.bullet {
|
||||
background: #fff;
|
||||
border: 2px solid #d8dee3;
|
||||
border-radius: 16px;
|
||||
padding: 28px 32px;
|
||||
width: calc(100% - 64px);
|
||||
margin-bottom: 30px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
letter-spacing: 0.75px;
|
||||
color: #091c45;
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
margin-top: 12px;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
color: #56606d;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,183 +0,0 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="warning-view">
|
||||
<div class="warning-view__container">
|
||||
<h1 class="warning-view__container__title" aria-roledescription="title">Object Browser</h1>
|
||||
<div class="warning-view__container__message-container">
|
||||
<h2 class="warning-view__container__message-container__sub-title" aria-roledescription="sub-title">The object browser uses server side encryption.</h2>
|
||||
<p class="warning-view__container__message-container__message">
|
||||
If you want to use our product with only end-to-end encryption, you may want to skip this feature.
|
||||
</p>
|
||||
</div>
|
||||
<div class="warning-view__container__checkbox-area">
|
||||
<v-checkbox @setData="setNotAskMeAgain" />
|
||||
<p class="warning-view__container__checkbox-area__message">Do not ask me again</p>
|
||||
</div>
|
||||
<div class="warning-view__container__buttons-area">
|
||||
<VButton
|
||||
class="warning-view__container__buttons-area__left-button"
|
||||
label="Return to dashboard"
|
||||
width="50%"
|
||||
height="48px"
|
||||
:on-press="goToDashboard"
|
||||
:is-blue-white="true"
|
||||
/>
|
||||
<VButton
|
||||
label="Continue"
|
||||
width="50%"
|
||||
height="48px"
|
||||
:on-press="proceed"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import VCheckbox from '@/components/common/VCheckbox.vue';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { LocalData } from '@/utils/localData';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
VCheckbox,
|
||||
VButton,
|
||||
},
|
||||
})
|
||||
export default class WarningView extends Vue {
|
||||
// Used for remembering confirmation of acknowledge.
|
||||
public isNotAskMeAgain = false;
|
||||
|
||||
public setNotAskMeAgain(value: boolean): void {
|
||||
this.isNotAskMeAgain = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if user already acknowledged information and redirects if so.
|
||||
*/
|
||||
public beforeMount(): void {
|
||||
if (LocalData.getServerSideEncryptionAcknowledge()) {
|
||||
this.$router.push(RouteConfig.EncryptData.path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects to project dashboard page.
|
||||
*/
|
||||
public goToDashboard(): void {
|
||||
this.$router.push(RouteConfig.ProjectDashboard.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Proceeds further into file browser flow.
|
||||
*/
|
||||
public proceed(): void {
|
||||
if (this.isNotAskMeAgain) {
|
||||
LocalData.setServerSideEncryptionAcknowledge();
|
||||
}
|
||||
|
||||
this.$router.push(RouteConfig.EncryptData.path);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.warning-view {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-style: normal;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 200px 0 40px 0;
|
||||
|
||||
&__container {
|
||||
background: #fff;
|
||||
border-radius: 6px;
|
||||
padding: 35px 50px;
|
||||
max-width: 515px;
|
||||
|
||||
&__title {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-family: 'font_Bold', sans-serif;
|
||||
font-size: 22px;
|
||||
line-height: 27px;
|
||||
color: #000;
|
||||
margin: 0 0 35px 0;
|
||||
}
|
||||
|
||||
&__message-container {
|
||||
padding: 40px 30px;
|
||||
width: calc(100% - 60px);
|
||||
background: #fff;
|
||||
border: 1px solid #e6e9ef;
|
||||
border-radius: 9px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
&__sub-title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
text-align: center;
|
||||
color: #ce3030;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
&__message {
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
color: #1b2533;
|
||||
word-break: break-word;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__checkbox-area {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
::v-deep.checkmark {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
|
||||
&:after {
|
||||
left: 6px;
|
||||
top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&__message {
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
color: #1b2533;
|
||||
}
|
||||
}
|
||||
|
||||
&__buttons-area {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&__left-button {
|
||||
margin-right: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -24,7 +24,6 @@ import BucketsView from '@/components/objects/BucketsView.vue';
|
||||
import EncryptData from '@/components/objects/EncryptData.vue';
|
||||
import ObjectsArea from '@/components/objects/ObjectsArea.vue';
|
||||
import UploadFile from '@/components/objects/UploadFile.vue';
|
||||
import WarningView from '@/components/objects/WarningView.vue';
|
||||
import OnboardingTourArea from '@/components/onboardingTour/OnboardingTourArea.vue';
|
||||
import OnbCLIStep from '@/components/onboardingTour/steps/CLIStep.vue';
|
||||
import CreateAccessGrantStep from "@/components/onboardingTour/steps/oldFlow/CreateAccessGrantStep.vue";
|
||||
@ -121,7 +120,6 @@ export abstract class RouteConfig {
|
||||
public static AccessGrantGateway = new NavigationLink('gateway', 'Onboarding Access Grant Gateway');
|
||||
|
||||
// objects child paths.
|
||||
public static Warning = new NavigationLink('warning', 'Objects Warning');
|
||||
public static EncryptData = new NavigationLink('encrypt-data', 'Objects Encrypt Data');
|
||||
public static BucketsManagement = new NavigationLink('buckets', 'Buckets Management');
|
||||
public static UploadFile = new NavigationLink('upload/', 'Objects Upload');
|
||||
@ -416,11 +414,6 @@ export const router = new Router({
|
||||
name: RouteConfig.Objects.name,
|
||||
component: ObjectsArea,
|
||||
children: [
|
||||
{
|
||||
path: RouteConfig.Warning.path,
|
||||
name: RouteConfig.Warning.name,
|
||||
component: WarningView,
|
||||
},
|
||||
{
|
||||
path: RouteConfig.EncryptData.path,
|
||||
name: RouteConfig.EncryptData.name,
|
||||
@ -500,7 +493,7 @@ router.beforeEach(async (to, from, next) => {
|
||||
}
|
||||
|
||||
if (navigateToDefaultSubTab(to.matched, RouteConfig.Objects)) {
|
||||
next(RouteConfig.Objects.with(RouteConfig.Warning).path);
|
||||
next(RouteConfig.Objects.with(RouteConfig.EncryptData).path);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -50,14 +50,6 @@ export class LocalData {
|
||||
|
||||
localStorage.setItem(LocalData.userIdPassSalt, JSON.stringify(data));
|
||||
}
|
||||
|
||||
public static getServerSideEncryptionAcknowledge(): boolean {
|
||||
return Boolean(localStorage.getItem(LocalData.serverSideEncryptionAcknowledge));
|
||||
}
|
||||
|
||||
public static setServerSideEncryptionAcknowledge(): void {
|
||||
localStorage.setItem(LocalData.serverSideEncryptionAcknowledge, 'true');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
3
web/satellite/static/images/common/minus.svg
Normal file
3
web/satellite/static/images/common/minus.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.45691 12L21.9224 12" stroke="#0149FF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 223 B |
4
web/satellite/static/images/common/plus.svg
Normal file
4
web/satellite/static/images/common/plus.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 3V21" stroke="#0149FF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3 12L21 12" stroke="#0149FF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 318 B |
4
web/satellite/static/images/common/smallGreyInfo.svg
Normal file
4
web/satellite/static/images/common/smallGreyInfo.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="13" height="14" viewBox="0 0 13 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect y="0.954102" width="13" height="12.8807" rx="6.44037" fill="#A9B5C1"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.50288 10.4956C6.90175 10.4956 7.2251 10.1723 7.2251 9.77338L7.2251 7.40131C7.2251 7.00244 6.90175 6.67909 6.50288 6.67909C6.104 6.67909 5.78065 7.00244 5.78065 7.40131L5.78065 9.77338C5.78065 10.1723 6.104 10.4956 6.50288 10.4956ZM6.50005 5.96355C6.99407 5.96355 7.40283 5.56308 7.40283 5.06906C7.40283 4.57504 6.99407 4.17456 6.50005 4.17456C6.00604 4.17456 5.59728 4.57504 5.59728 5.06906C5.59728 5.56308 6.00604 5.96355 6.50005 5.96355Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 696 B |
@ -1,5 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Checkbox.vue renders correctly 1`] = `<label class="container"><input type="checkbox" class="checkmark-input"> <span class="checkmark"></span></label>`;
|
||||
exports[`Checkbox.vue renders correctly 1`] = `<div class="wrap"><label class="container"><input id="checkbox" type="checkbox" class="checkmark-input"> <span class="checkmark"></span></label> <label for="checkbox" class="label"></label></div>`;
|
||||
|
||||
exports[`Checkbox.vue renders correctly with error 1`] = `<label class="container"><input type="checkbox" class="checkmark-input"> <span class="checkmark error"></span></label>`;
|
||||
exports[`Checkbox.vue renders correctly with error 1`] = `<div class="wrap"><label class="container"><input id="checkbox" type="checkbox" class="checkmark-input"> <span class="checkmark error"></span></label> <label for="checkbox" class="label"></label></div>`;
|
||||
|
Loading…
Reference in New Issue
Block a user