Skip to content

Commit

Permalink
Fix Welcome with new Database, close #22
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack-Works committed Jun 12, 2019
1 parent 40af4cb commit 830cbde
Show file tree
Hide file tree
Showing 22 changed files with 155 additions and 94 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"react-router-dom": "^5.0.0",
"react-scripts": "^3.0.1",
"reflect-metadata": "^0.1.13",
"serialijse": "^0.1.3",
"storybook-addon-material-ui": "^0.9.0-alpha.19",
"tiny-secp256k1": "^1.1.2",
"ts-loader": "^6.0.1",
Expand Down
3 changes: 3 additions & 0 deletions public/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@
"service_others_key_not_found": {
"message": "$1's public key not found!"
},
"service_not_setup_yet": {
"message": "You have not set up Maskbook yet!"
},
"service_publish_post_aes_key_failed": {
"message": "Publish AES key failed!"
},
Expand Down
3 changes: 3 additions & 0 deletions public/_locales/zh/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@
"service_others_key_not_found": {
"message": "$1 的公鑰無法找到!"
},
"service_not_setup_yet": {
"message": "您還沒有進行初始設置,在您沒有導入或生成您的密鑰之前,我們暫時無法爲您解密此消息!"
},
"service_publish_post_aes_key_failed": {
"message": "未能發佈 AES 密鑰。"
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default function Dashboard(props: Props) {
<main>
{props.identities.map(x => (
<Identity
key={x.identifier.toString()}
key={x.identifier.toText()}
person={x}
onClick={() => props.onProfileClick(x.identifier)}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/components/DataSource/PeopleRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Person } from '../../database'
const ref = new ValueRef<Person[]>([])
Services.People.queryPeople('facebook.com').then(p => (ref.value = p))
MessageCenter.on('newPerson', p => {
const old = ref.value.filter(x => x.identifier.toString() !== p.identifier.toString())
const old = ref.value.filter(x => x.identifier.toText() !== p.identifier.toText())
ref.value = [...old, p]
})
export function usePeople() {
Expand Down
6 changes: 3 additions & 3 deletions src/components/InjectedComponents/AdditionalPostBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react'
import { useAsync } from '../../utils/components/AsyncComponent'
import { usePeople } from '../DataSource/PeopleRef'
import { SelectPeopleUI } from './SelectPeople'
import { myUsername, getUsername } from '../../extension/content-script/injections/LiveSelectors'
import { myUsername, getPersonIdentifierAtFacebook } from '../../extension/content-script/injections/LiveSelectors'
import { useRef } from 'react'
import { useCapturedInput } from '../../utils/hooks/useCapturedEvents'
import { Avatar } from '../../utils/components/Avatar'
Expand Down Expand Up @@ -84,7 +84,7 @@ export function AdditionalPostBoxUI(props: Props) {
}

export function AdditionalPostBox() {
const username = getUsername()
const username = getPersonIdentifierAtFacebook()
const [identity, setIdentity] = React.useState<Person | undefined>(undefined)
// ! TODO: Query my identity //
// useAsync(() => ).then(setIdentity)
Expand All @@ -94,7 +94,7 @@ export function AdditionalPostBox() {
pasteIntoPostBox(fullPost, geti18nString('additional_post_box__encrypted_failed'))
Services.Crypto.publishPostAESKey(token)
}, [])
if (!username) {
if (username.isUnknown) {
return <AdditionalPostBoxUI people={usePeople()} onRequestPost={onRequestPost} />
}
return <AdditionalPostBoxUI people={usePeople()} myself={identity} onRequestPost={onRequestPost} />
Expand Down
2 changes: 1 addition & 1 deletion src/components/Welcomes/0.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default function Welcome({ create, restore, close }: Props) {
<Paper elevation={2} className={classes.paper}>
<nav className={classes.nav}>
<Button onClick={close} disableFocusRipple disableRipple className={classes.navButton}>
{geti18nString('welcome_0_title')}
{geti18nString('welcome_0_close_button')}
<Close className={classes.navButtonIcon} />
</Button>
</nav>
Expand Down
14 changes: 7 additions & 7 deletions src/database/avatar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ const db = openDB<AvatarDB>('maskbook-avatar-cache', 1, {
*/
export async function storeAvatarDB(id: IdentityWithAvatar, avatar: ArrayBuffer) {
const meta: AvatarMetadataRecord = {
identifier: id.toString(),
identifier: id.toText(),
lastUpdateTime: new Date(),
lastAccessTime: new Date(),
}
const t = (await db).transaction(['avatars', 'metadata'], 'readwrite')
const a = t.objectStore('avatars').put(avatar, id.toString())
const a = t.objectStore('avatars').put(avatar, id.toText())
await t.objectStore('metadata').put(meta)
await a
return
Expand All @@ -51,7 +51,7 @@ export async function storeAvatarDB(id: IdentityWithAvatar, avatar: ArrayBuffer)
*/
export async function queryAvatarDB(id: IdentityWithAvatar): Promise<ArrayBuffer | null> {
const t = (await db).transaction('avatars')
const result = await t.objectStore('avatars').get(id.toString())
const result = await t.objectStore('avatars').get(id.toText())
if (result) {
updateAvatarMetaDB(id, { lastAccessTime: new Date() }).catch(e => {
console.warn('Update last use record failed', e)
Expand All @@ -64,7 +64,7 @@ export async function queryAvatarDB(id: IdentityWithAvatar): Promise<ArrayBuffer
*/
export async function updateAvatarMetaDB(id: IdentityWithAvatar, newMeta: Partial<AvatarMetadataRecord>) {
const t = (await db).transaction('metadata', 'readwrite')
const meta = await t.objectStore('metadata').get(id.toString())
const meta = await t.objectStore('metadata').get(id.toText())
const newRecord = Object.assign({}, meta, newMeta)
await t.objectStore('metadata').put(newRecord)
}
Expand Down Expand Up @@ -100,7 +100,7 @@ export async function isAvatarOutdatedDB(
deadline: Date = new Date(Date.now() - 1000 * 60 * 60 * 24 * (attribute === 'lastAccessTime' ? 30 : 7)),
): Promise<boolean> {
const t = (await db).transaction('metadata')
const meta = await t.objectStore('metadata').get(identifier.toString())
const meta = await t.objectStore('metadata').get(identifier.toText())
if (!meta) return true
if (deadline > meta[attribute]) return true
return false
Expand All @@ -112,8 +112,8 @@ export async function deleteAvatarsDB(ids: IdentityWithAvatar[]) {
const t = (await db).transaction(['avatars', 'metadata'], 'readwrite')
const promises: Promise<void>[] = []
for (const id of ids) {
const a = t.objectStore('avatars').delete(id.toString())
const b = t.objectStore('metadata').delete(id.toString())
const a = t.objectStore('avatars').delete(id.toText())
const b = t.objectStore('metadata').delete(id.toText())
promises.push(a, b)
}
return
Expand Down
2 changes: 1 addition & 1 deletion src/database/helpers/avatar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const avatarCache = new Map<string, string>()
* Get a (cached) blob url for an identifier.
*/
export async function getAvatarBlobURL(identifier: PersonIdentifier | GroupIdentifier): Promise<string | undefined> {
const id = identifier.toString()
const id = identifier.toText()
if (avatarCache.has(id)) return avatarCache.get(id)!
const buffer = await queryAvatarDB(identifier)
if (!buffer) return undefined
Expand Down
5 changes: 3 additions & 2 deletions src/database/helpers/person.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ const calculateFingerprint = memoize(async function(_key: CryptoKey) {
/**
* @deprecated
*/
export async function getMyPrivateKeyAtFacebook() {
export async function getMyPrivateKeyAtFacebook(): Promise<null | CryptoKey> {
const x = await getMyIdentitiesDB()
const y = x.find(y => y.identifier.network === 'facebook.com' && y.privateKey)
return y!.privateKey
if (y) return y.privateKey
return null
}
26 changes: 21 additions & 5 deletions src/database/people.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ async function outDb({ identifier, publicKey, privateKey, ...rest }: PersonRecor
async function toDb({ publicKey, privateKey, ...rest }: PersonRecord): Promise<PersonRecordInDatabase> {
const result: PersonRecordInDatabase = {
...rest,
identifier: rest.identifier.toString(),
identifier: rest.identifier.toText(),
network: rest.identifier.network,
}
if (publicKey) result.publicKey = await CryptoKeyToJsonWebKey(publicKey)
Expand Down Expand Up @@ -158,7 +158,7 @@ export async function queryPeopleDB(
*/
export async function queryPersonDB(id: PersonIdentifier): Promise<null | PersonRecord> {
const t = (await db).transaction('people', 'readonly')
const result = await t.objectStore('people').get(id.toString())
const result = await t.objectStore('people').get(id.toText())
if (!result) return null
return outDb(result)
}
Expand All @@ -168,7 +168,7 @@ export async function queryPersonDB(id: PersonIdentifier): Promise<null | Person
*/
export async function updatePersonDB(person: Partial<PersonRecord> & Pick<PersonRecord, 'identifier'>): Promise<void> {
const t = (await db).transaction('people', 'readwrite')
const full = await t.objectStore('people').get(person.identifier.toString())
const full = await t.objectStore('people').get(person.identifier.toText())
if (!full) throw new Error('Person is not in the db')
const o: PersonRecordInDatabase = { ...full, ...(await toDb(person as PersonRecord)) }
// TODO: Send new person message
Expand All @@ -180,7 +180,7 @@ export async function updatePersonDB(person: Partial<PersonRecord> & Pick<Person
*/
export async function removePersonDB(people: PersonIdentifier[]): Promise<void> {
const t = (await db).transaction('people', 'readwrite')
for (const person of people) await t.objectStore('people').delete(person.toString())
for (const person of people) await t.objectStore('people').delete(person.toText())
return
}
//#endregion
Expand All @@ -191,7 +191,7 @@ export async function removePersonDB(people: PersonIdentifier[]): Promise<void>
*/
export async function queryMyIdentityAtDB(id: PersonIdentifier): Promise<null | PersonRecordPublicPrivate> {
const t = (await db).transaction('myself')
const result = await t.objectStore('myself').get(id.toString())
const result = await t.objectStore('myself').get(id.toText())
if (!result) return null
return outDb(result) as Promise<PersonRecordPublicPrivate>
}
Expand All @@ -213,6 +213,22 @@ export async function getMyIdentitiesDB(): Promise<PersonRecordPublicPrivate[]>
const result = await t.objectStore('myself').getAll()
return Promise.all(result.map(outDb)) as Promise<PersonRecordPublicPrivate[]>
}
/**
* Generate a new identity
*/
export async function generateMyIdentityDB(identifier: PersonIdentifier): Promise<void> {
const now = await getMyIdentitiesDB()
if (now.some(id => id.identifier.equals(identifier))) return
const key = await crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'K-256' }, true, ['deriveKey'])
await storeMyIdentityDB({
groups: [],
identifier,
relation: [],
relationLastCheckTime: new Date(),
publicKey: key.publicKey,
privateKey: key.privateKey,
})
}
//#endregion
//#region LocalKeys
function generateAESKey(exportable: boolean) {
Expand Down
8 changes: 4 additions & 4 deletions src/database/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function outDb(db: PostDBRecordV40): PostOutDBRecordV40 {
}
}
function toDb(out: PostOutDBRecordV40): PostDBRecordV40 {
return { ...out, identifier: out.identifier.toString() }
return { ...out, identifier: out.identifier.toText() }
}
interface PostOutDBRecordV40 extends Omit<PostDBRecordV40, 'identifier'> {
identifier: PostIdentifier
Expand Down Expand Up @@ -41,18 +41,18 @@ const db = openDB<PostDB>('maskbook-post-v2', 1, {
})
export async function storePostCryptoKeyDB(record: PostOutDBRecordV40): Promise<void> {
const t = (await db).transaction('post', 'readwrite')
const o: typeof record = { ...record, identifier: record.identifier.toString() as any }
const o: typeof record = { ...record, identifier: record.identifier.toText() as any }
await t.objectStore('post').put(toDb(record))
}
export async function queryPostCryptoKeyDB(record: PostIdentifier): Promise<PostOutDBRecordV40 | null> {
const t = (await db).transaction('post')
const result = await t.objectStore('post').get(record.toString())
const result = await t.objectStore('post').get(record.toText())
if (result) return outDb(result)
return null
}
export async function deletePostCryptoKeyDB(record: PostIdentifier) {
const t = (await db).transaction('post', 'readwrite')
await t.objectStore('post').delete(record.toString())
await t.objectStore('post').delete(record.toText())
}
export async function backupPostCryptoKeyDB() {
throw new Error('Not implemented')
Expand Down
32 changes: 22 additions & 10 deletions src/database/type.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { serializable } from '../utils/type-transform/Serialization'

type Identifiers = 'person' | 'group' | 'post'
const fromString = Symbol()
function noSlash(str: string) {
function noSlash(str?: string) {
if (!str) return
if (str.split('/')[1]) throw new TypeError('Cannot contain / in a part of identifier')
}
export abstract class Identifier {
// Don't remove this property, or this class will compatible with type `string`
abstract readonly type: 'post' | 'group' | 'person'
abstract toString(): string
public equals(other: Identifier) {
return this === other || this.toText() === other.toText()
}
abstract toText(): string
static fromString<T extends Identifier>(id: T): T
static fromString(id: string): Identifier | null
static fromString<T extends Identifier>(id: string | T): Identifier | null {
Expand All @@ -24,8 +28,13 @@ export abstract class Identifier {
}
}
}

@serializable('PersonIdentifier')
export class PersonIdentifier extends Identifier {
readonly type = 'person'
static unknown = new PersonIdentifier('localhost', '$unknown')
get isUnknown() {
return this.equals(PersonIdentifier.unknown)
}
/**
* @param network - Network belongs to
* @param userId - User ID
Expand All @@ -35,7 +44,7 @@ export class PersonIdentifier extends Identifier {
noSlash(network)
noSlash(userId)
}
toString() {
toText() {
return `person:${this.network}/${this.userId}`
}
static [fromString](str: string) {
Expand All @@ -44,14 +53,15 @@ export class PersonIdentifier extends Identifier {
return new PersonIdentifier(network, userId)
}
}

@serializable('GroupIdentifier')
export class GroupIdentifier extends Identifier {
readonly type = 'group'
constructor(public network: string, public groupId: string, public virtual = false) {
super()
noSlash(network)
noSlash(groupId)
}
toString() {
toText() {
return `group:${this.network}/${this.groupId}/${this.virtual ? 'virtual' : 'real'}`
}
static [fromString](str: string) {
Expand All @@ -60,6 +70,8 @@ export class GroupIdentifier extends Identifier {
return new GroupIdentifier(network, groupId, virtual === 'virtual')
}
}

@serializable('PostIdentifier')
export class PostIdentifier<T extends Identifier = Identifier> extends Identifier {
readonly type = 'post'
/**
Expand All @@ -70,8 +82,8 @@ export class PostIdentifier<T extends Identifier = Identifier> extends Identifie
super()
noSlash(postId)
}
toString() {
return `post:${this.postId}/${this.identifier.toString()}`
toText() {
return `post:${this.postId}/${this.identifier.toText()}`
}
static [fromString](str: string) {
const [postId, ...identifier] = str.split('/')
Expand Down
4 changes: 3 additions & 1 deletion src/extension/background-script/CryptoService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export async function encryptTo(content: string, to: Person[]): Promise<[Encrypt

// tslint:disable-next-line: deprecation
const mine = await getMyPrivateKeyAtFacebook()
if (!mine) throw new TypeError('Not inited yet')
const {
encryptedContent: encryptedText,
version,
Expand Down Expand Up @@ -134,6 +135,7 @@ export async function decryptFrom(
if (!byKey) return { error: geti18nString('service_others_key_not_found', by.userId) }
// tslint:disable-next-line: deprecation
const mine = await getMyPrivateKeyAtFacebook()
if (!mine) return { error: geti18nString('service_not_setup_yet') }
try {
const unverified = ['2/4', ownersAESKeyEncrypted, salt, encryptedText].join('|')
if (by === whoAmI) {
Expand Down Expand Up @@ -266,7 +268,7 @@ export async function appendShareTarget(
-40,
AESKey,
// tslint:disable-next-line: deprecation
await getMyPrivateKeyAtFacebook(),
(await getMyPrivateKeyAtFacebook())!,
toKey,
)
publishPostAESKey_Service(postIdentifier, othersAESKeyEncrypted)
Expand Down

0 comments on commit 830cbde

Please sign in to comment.