Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions packages/encryption/src/Domain/Username/PrivateUsername.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'

const PrivateUserNameV1 = 'StandardNotes-PrivateUsername-V1'

export async function ComputePrivateUsername(
crypto: PureCryptoInterface,
usernameInput: string,
): Promise<string | undefined> {
const result = await crypto.hmac256(
await crypto.sha256(PrivateUserNameV1),
await crypto.sha256(usernameInput.trim().toLowerCase()),
)

if (result == undefined) {
return undefined
}

return result
}
18 changes: 0 additions & 18 deletions packages/encryption/src/Domain/Workspace/PrivateWorkspace.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/encryption/src/Domain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ export * from './Types/EncryptedParameters'
export * from './Types/ItemAuthenticatedData'
export * from './Types/LegacyAttachedData'
export * from './Types/RootKeyEncryptedAuthenticatedData'
export * from './Workspace/PrivateWorkspace'
export * from './Username/PrivateUsername'
6 changes: 3 additions & 3 deletions packages/snjs/lib/Application/Application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import {
WorkspaceManager,
} from '@standardnotes/services'
import { FilesClientInterface } from '@standardnotes/files'
import { ComputePrivateWorkspaceIdentifier } from '@standardnotes/encryption'
import { ComputePrivateUsername } from '@standardnotes/encryption'
import { useBoolean } from '@standardnotes/utils'
import {
BackupFile,
Expand Down Expand Up @@ -272,8 +272,8 @@ export class SNApplication
return this.componentManagerService
}

public computePrivateWorkspaceIdentifier(userphrase: string, name: string): Promise<string | undefined> {
return ComputePrivateWorkspaceIdentifier(this.options.crypto, userphrase, name)
public computePrivateUsername(username: string): Promise<string | undefined> {
return ComputePrivateUsername(this.options.crypto, username)
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/snjs/mocha/test.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
<script type="module" src="002.test.js"></script>
<script type="module" src="003.test.js"></script>
<script type="module" src="004.test.js"></script>
<script type="module" src="workspaces.test.js"></script>
<script type="module" src="username.test.js"></script>
<script type="module" src="app-group.test.js"></script>
<script type="module" src="application.test.js"></script>
<script type="module" src="payload.test.js"></script>
Expand Down
12 changes: 12 additions & 0 deletions packages/snjs/mocha/username.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
chai.use(chaiAsPromised)
const expect = chai.expect

describe('private username', () => {
it('generates private username', async () => {
const username = 'myusername'

const result = await ComputePrivateUsername(new SNWebCrypto(), username)

expect(result).to.equal('9aae57db8dbb233291a49cb7b8ab902336ec785e04f3be70157b8c1669014d0d')
})
})
25 changes: 0 additions & 25 deletions packages/snjs/mocha/workspaces.test.js

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type Props = {
application: WebApplication
viewControllerManager: ViewControllerManager
disabled?: boolean
onPrivateWorkspaceChange?: (isPrivate: boolean, identifier?: string) => void
onPrivateUsernameModeChange?: (isPrivate: boolean, identifier?: string) => void
onStrictSignInChange?: (isStrictSignIn: boolean) => void
children?: ReactNode
}
Expand All @@ -19,54 +19,46 @@ const AdvancedOptions: FunctionComponent<Props> = ({
viewControllerManager,
application,
disabled = false,
onPrivateWorkspaceChange,
onPrivateUsernameModeChange,
onStrictSignInChange,
children,
}) => {
const { server, setServer, enableServerOption, setEnableServerOption } = viewControllerManager.accountMenuController
const [showAdvanced, setShowAdvanced] = useState(false)

const [isPrivateWorkspace, setIsPrivateWorkspace] = useState(false)
const [privateWorkspaceName, setPrivateWorkspaceName] = useState('')
const [privateWorkspaceUserphrase, setPrivateWorkspaceUserphrase] = useState('')
const [isPrivateUsername, setIsPrivateUsername] = useState(false)
const [privateUsername, setPrivateUsername] = useState('')

const [isStrictSignin, setIsStrictSignin] = useState(false)

useEffect(() => {
const recomputePrivateWorkspaceIdentifier = async () => {
const identifier = await application.computePrivateWorkspaceIdentifier(
privateWorkspaceName,
privateWorkspaceUserphrase,
)
const recomputePrivateUsername = async () => {
const identifier = await application.computePrivateUsername(privateUsername)

if (!identifier) {
if (privateWorkspaceName?.length > 0 && privateWorkspaceUserphrase?.length > 0) {
application.alertService.alert('Unable to compute private workspace name.').catch(console.error)
if (privateUsername?.length > 0) {
application.alertService.alert('Unable to compute private username.').catch(console.error)
}
return
}
onPrivateWorkspaceChange?.(true, identifier)
onPrivateUsernameModeChange?.(true, identifier)
}

if (privateWorkspaceName && privateWorkspaceUserphrase) {
recomputePrivateWorkspaceIdentifier().catch(console.error)
if (privateUsername) {
recomputePrivateUsername().catch(console.error)
}
}, [privateWorkspaceName, privateWorkspaceUserphrase, application, onPrivateWorkspaceChange])
}, [privateUsername, application, onPrivateUsernameModeChange])

useEffect(() => {
onPrivateWorkspaceChange?.(isPrivateWorkspace)
}, [isPrivateWorkspace, onPrivateWorkspaceChange])
onPrivateUsernameModeChange?.(isPrivateUsername)
}, [isPrivateUsername, onPrivateUsernameModeChange])

const handleIsPrivateWorkspaceChange = useCallback(() => {
setIsPrivateWorkspace(!isPrivateWorkspace)
}, [isPrivateWorkspace])
const handleIsPrivateUsernameChange = useCallback(() => {
setIsPrivateUsername(!isPrivateUsername)
}, [isPrivateUsername])

const handlePrivateWorkspaceNameChange = useCallback((name: string) => {
setPrivateWorkspaceName(name)
}, [])

const handlePrivateWorkspaceUserphraseChange = useCallback((userphrase: string) => {
setPrivateWorkspaceUserphrase(userphrase)
const handlePrivateUsernameNameChange = useCallback((name: string) => {
setPrivateUsername(name)
}, [])

const handleServerOptionChange: ChangeEventHandler<HTMLInputElement> = useCallback(
Expand Down Expand Up @@ -114,35 +106,28 @@ const AdvancedOptions: FunctionComponent<Props> = ({
<div className="mb-1 flex items-center justify-between">
<Checkbox
name="private-workspace"
label="Private workspace"
checked={isPrivateWorkspace}
label="Private username mode"
checked={isPrivateUsername}
disabled={disabled}
onChange={handleIsPrivateWorkspaceChange}
onChange={handleIsPrivateUsernameChange}
/>
<a href="https://standardnotes.com/help/80" target="_blank" rel="noopener noreferrer" title="Learn more">
<Icon type="info" className="text-neutral" />
</a>
</div>

{isPrivateWorkspace && (
{isPrivateUsername && (
<>
<DecoratedInput
className={{ container: 'mb-2' }}
left={[<Icon type="server" className="text-neutral" />]}
type="text"
placeholder="Userphrase"
value={privateWorkspaceUserphrase}
onChange={handlePrivateWorkspaceUserphraseChange}
disabled={disabled}
/>
<DecoratedInput
className={{ container: 'mb-2' }}
left={[<Icon type="folder" className="text-neutral" />]}
left={[<Icon type="account-circle" className="text-neutral" />]}
type="text"
placeholder="Name"
value={privateWorkspaceName}
onChange={handlePrivateWorkspaceNameChange}
placeholder="Username"
value={privateUsername}
onChange={handlePrivateUsernameNameChange}
disabled={disabled}
spellcheck={false}
autocomplete={false}
/>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const CreateAccount: FunctionComponent<Props> = ({
}) => {
const emailInputRef = useRef<HTMLInputElement>(null)
const passwordInputRef = useRef<HTMLInputElement>(null)
const [isPrivateWorkspace, setIsPrivateWorkspace] = useState(false)
const [isPrivateUsername, setIsPrivateUsername] = useState(false)

useEffect(() => {
if (emailInputRef.current) {
Expand Down Expand Up @@ -98,11 +98,11 @@ const CreateAccount: FunctionComponent<Props> = ({
setPassword('')
}, [setEmail, setMenuPane, setPassword])

const onPrivateWorkspaceChange = useCallback(
(isPrivateWorkspace: boolean, privateWorkspaceIdentifier?: string) => {
setIsPrivateWorkspace(isPrivateWorkspace)
if (isPrivateWorkspace && privateWorkspaceIdentifier) {
setEmail(privateWorkspaceIdentifier)
const onPrivateUsernameChange = useCallback(
(isPrivateUsername: boolean, privateUsernameIdentifier?: string) => {
setIsPrivateUsername(isPrivateUsername)
if (isPrivateUsername && privateUsernameIdentifier) {
setEmail(privateUsernameIdentifier)
}
},
[setEmail],
Expand All @@ -123,7 +123,7 @@ const CreateAccount: FunctionComponent<Props> = ({
<form onSubmit={handleRegisterFormSubmit} className="mb-1 px-3">
<DecoratedInput
className={{ container: 'mb-2' }}
disabled={isPrivateWorkspace}
disabled={isPrivateUsername}
left={[<Icon type="email" className="text-neutral" />]}
onChange={handleEmailChange}
onKeyDown={handleKeyDown}
Expand All @@ -147,7 +147,7 @@ const CreateAccount: FunctionComponent<Props> = ({
<AdvancedOptions
application={application}
viewControllerManager={viewControllerManager}
onPrivateWorkspaceChange={onPrivateWorkspaceChange}
onPrivateUsernameModeChange={onPrivateUsernameChange}
/>
</>
)
Expand Down
16 changes: 8 additions & 8 deletions packages/web/src/javascripts/Components/AccountMenu/SignIn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManag
const [isStrictSignin, setIsStrictSignin] = useState(false)
const [isSigningIn, setIsSigningIn] = useState(false)
const [shouldMergeLocal, setShouldMergeLocal] = useState(true)
const [isPrivateWorkspace, setIsPrivateWorkspace] = useState(false)
const [isPrivateUsername, setIsPrivateUsername] = useState(false)

const emailInputRef = useRef<HTMLInputElement>(null)
const passwordInputRef = useRef<HTMLInputElement>(null)
Expand Down Expand Up @@ -100,11 +100,11 @@ const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManag
})
}, [viewControllerManager, application, email, isEphemeral, isStrictSignin, password, shouldMergeLocal])

const onPrivateWorkspaceChange = useCallback(
(newIsPrivateWorkspace: boolean, privateWorkspaceIdentifier?: string) => {
setIsPrivateWorkspace(newIsPrivateWorkspace)
if (newIsPrivateWorkspace && privateWorkspaceIdentifier) {
setEmail(privateWorkspaceIdentifier)
const onPrivateUsernameChange = useCallback(
(newisPrivateUsername: boolean, privateUsernameIdentifier?: string) => {
setIsPrivateUsername(newisPrivateUsername)
if (newisPrivateUsername && privateUsernameIdentifier) {
setEmail(privateUsernameIdentifier)
}
},
[setEmail],
Expand Down Expand Up @@ -161,7 +161,7 @@ const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManag
onChange={handleEmailChange}
onFocus={resetInvalid}
onKeyDown={handleKeyDown}
disabled={isSigningIn || isPrivateWorkspace}
disabled={isSigningIn || isPrivateUsername}
ref={emailInputRef}
/>
<DecoratedPasswordInput
Expand Down Expand Up @@ -206,7 +206,7 @@ const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManag
viewControllerManager={viewControllerManager}
application={application}
disabled={isSigningIn}
onPrivateWorkspaceChange={onPrivateWorkspaceChange}
onPrivateUsernameModeChange={onPrivateUsernameChange}
onStrictSignInChange={handleStrictSigninChange}
/>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const DecoratedInput = forwardRef(
(
{
autocomplete = false,
spellcheck = true,
className,
disabled = false,
id,
Expand Down Expand Up @@ -68,6 +69,7 @@ const DecoratedInput = forwardRef(
title={title}
type={type}
value={value}
spellCheck={spellcheck}
/>

{right && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { FocusEventHandler, KeyboardEventHandler, ReactNode } from 'react'

export type DecoratedInputProps = {
autocomplete?: boolean
spellcheck?: boolean
className?: {
container?: string
input?: string
Expand Down