Merge branch 'master' into omegazord

This commit is contained in:
Thom Chiovoloni 2019-07-15 10:54:28 -07:00
Родитель cb975b17e2 e740f9907b
Коммит 4eb950f21b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 31F01AEBD799934A
70 изменённых файлов: 977 добавлений и 1815 удалений

Просмотреть файл

@ -1,3 +1,35 @@
# v0.34.0 (_2019-07-10_)
[Full Changelog](https://github.com/mozilla/application-services/compare/v0.33.2...v0.34.0)
## General
- All of our cryptographic primitives are now backed by NSS ([#1349](https://github.com/mozilla/application-services/pull/1349)). This change should be transparent our customers.
If you build application-services, it is recommended to delete the `libs/{desktop, ios, android}` folders and start over using `./build-all.sh [android|desktop|ios]`. [GYP](https://github.com/mogemimi/pomdog/wiki/How-to-Install-GYP) and [ninja](https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages) are required to build these libraries.
## Places
### What's New
- Added `WritableHistoryConnection.acceptResult(searchString, url)` for marking
an awesomebar result as accepted.
([#1332](https://github.com/mozilla/application-services/pull/1332))
- Specifically, `queryAutocomplete` calls for searches that contain
frequently accepted results are more highly ranked.
### Breaking changes
- Android only: The addition of `acceptResult` to `WritableHistoryConnection` is
a breaking change for any custom implementations of `WritableHistoryConnection`
([#1332](https://github.com/mozilla/application-services/pull/1332))
## Push
### Breaking Changes
- `OpenSSLError` has been renamed to the more general `CryptoError`. ([#1349](https://github.com/mozilla/application-services/pull/1349))
# v0.33.2 (_2019-07-04_)
[Full Changelog](https://github.com/mozilla/application-services/compare/v0.33.1...v0.33.2)

Просмотреть файл

@ -2,31 +2,4 @@
# Unreleased Changes
[Full Changelog](https://github.com/mozilla/application-services/compare/v0.33.2...master)
## General
- All of our cryptographic primitives are now backed by NSS. This change should be transparent our customers.
If you build application-services, it is recommended to delete the `libs/{desktop, ios, android}` folders and start over using `./build-all.sh [android|desktop|ios]`.
## Places
### What's New
- Added `WritableHistoryConnection.acceptResult(searchString, url)` for marking
an awesomebar result as accepted.
([#1332](https://github.com/mozilla/application-services/pull/1332))
- Specifically, `queryAutocomplete` calls for searches that contain
frequently accepted results are more highly ranked.
### Breaking changes
- Android only: The addition of `acceptResult` to `WritableHistoryConnection` is
a breaking change for any custom implementations of `WritableHistoryConnection`
([#1332](https://github.com/mozilla/application-services/pull/1332))
## Push
### Breaking Changes
- `OpenSSLError` has been renamed to the more general `CryptoError`.
[Full Changelog](https://github.com/mozilla/application-services/compare/v0.34.0...master)

94
Cargo.lock сгенерированный
Просмотреть файл

@ -112,7 +112,7 @@ dependencies = [
[[package]]
name = "bindgen"
version = "0.49.2"
version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -123,7 +123,7 @@ dependencies = [
"env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
@ -288,7 +288,7 @@ version = "0.1.0"
dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"fxa-client 0.1.0",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"sync15 0.1.0",
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"webbrowser 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -361,7 +361,7 @@ dependencies = [
"cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"publicsuffix 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
@ -571,7 +571,7 @@ name = "dogear"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"smallbitvec 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -621,7 +621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -697,7 +697,7 @@ dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -709,7 +709,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -800,7 +800,7 @@ dependencies = [
"force-viaduct-reqwest 0.1.0",
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"mockiato 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)",
"prost 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -824,7 +824,7 @@ dependencies = [
"ffi-support 0.3.4",
"fxa-client 0.1.0",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"prost 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"viaduct 0.1.0",
@ -871,7 +871,7 @@ dependencies = [
"futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
"http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
@ -968,7 +968,7 @@ dependencies = [
"httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1114,7 +1114,7 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.6"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1135,7 +1135,7 @@ dependencies = [
"fxa-client 0.1.0",
"interrupt 0.1.0",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"more-asserts 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"prettytable-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rusqlite 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1154,7 +1154,7 @@ dependencies = [
"base16 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ffi-support 0.3.4",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"logins 0.1.0",
"rusqlite 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1270,7 +1270,7 @@ dependencies = [
"iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1320,7 +1320,7 @@ dependencies = [
"difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1348,7 +1348,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-sys 0.9.47 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1416,7 +1416,7 @@ dependencies = [
name = "nss_sys"
version = "0.1.0"
dependencies = [
"bindgen 0.49.2 (registry+https://github.com/rust-lang/crates.io-index)",
"bindgen 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1645,7 +1645,7 @@ dependencies = [
"idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"interrupt 0.1.0",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"more-asserts 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1659,6 +1659,7 @@ dependencies = [
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
"sql-support 0.1.0",
"structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
"sync-guid 0.1.0",
"sync15 0.1.0",
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1675,11 +1676,12 @@ dependencies = [
"ffi-support 0.3.4",
"interrupt 0.1.0",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"places 0.1.0",
"prost 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
"sql-support 0.1.0",
"sync-guid 0.1.0",
"sync15 0.1.0",
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"viaduct 0.1.0",
@ -1740,7 +1742,7 @@ dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"multimap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
"prost 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1796,7 +1798,7 @@ dependencies = [
"force-viaduct-reqwest 0.1.0",
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"mockito 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rc_crypto 0.1.0",
"rusqlite 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1815,7 +1817,7 @@ dependencies = [
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ffi-support 0.3.4",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"push 0.1.0",
"rusqlite 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2049,7 +2051,7 @@ dependencies = [
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"ffi-support 0.3.4",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -2135,7 +2137,7 @@ dependencies = [
"http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.12.32 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)",
"native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2304,6 +2306,14 @@ dependencies = [
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_test"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_urlencoded"
version = "0.5.5"
@ -2363,7 +2373,7 @@ dependencies = [
"ffi-support 0.3.4",
"interrupt 0.1.0",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rusqlite 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2420,6 +2430,17 @@ dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sync-guid"
version = "0.1.0"
dependencies = [
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rc_crypto 0.1.0",
"rusqlite 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_test 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sync-test"
version = "0.1.0"
@ -2428,7 +2449,7 @@ dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"fxa-client 0.1.0",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"logins 0.1.0",
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2447,7 +2468,7 @@ dependencies = [
"ffi-support 0.3.4",
"interrupt 0.1.0",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rc_crypto 0.1.0",
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2614,7 +2635,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -2625,7 +2646,7 @@ dependencies = [
"crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2666,7 +2687,7 @@ dependencies = [
"crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2823,7 +2844,7 @@ dependencies = [
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"ffi-support 0.3.4",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"prost 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"prost-build 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"prost-derive 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2854,7 +2875,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2962,7 +2983,7 @@ dependencies = [
"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
"checksum bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9f04a5e50dc80b3d5d35320889053637d15011aed5e66b66b37ae798c65da6f7"
"checksum bindgen 0.49.2 (registry+https://github.com/rust-lang/crates.io-index)" = "846a1fba6535362a01487ef6b10f0275faa12e5c5d835c5c1c627aabc46ccbd6"
"checksum bindgen 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)" = "65a913de3fa2fa95f2c593bb7e33b1be1ce1ce8a83f34b6bb02e6f01400b96cc"
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400"
"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
@ -3061,7 +3082,7 @@ dependencies = [
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c"
"checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff"
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
"checksum log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c275b6ad54070ac2d665eef9197db647b32239c9d244bfb6f041a766d00da5b3"
"checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
@ -3168,6 +3189,7 @@ dependencies = [
"checksum serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "076a696fdea89c19d3baed462576b8f6d663064414b5c793642da8dfeb99475b"
"checksum serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "ef45eb79d6463b22f5f9e16d283798b7c0175ba6050bc25c1a946c122727fe7b"
"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704"
"checksum serde_test 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "6e5889093c6aa447d971c1f152c1127eb96ef0a53c4a3e3082158fe6670b28c6"
"checksum serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a"
"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d"
"checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"

Просмотреть файл

@ -13,6 +13,7 @@ members = [
"components/support/error",
"components/support/ffi",
"components/support/force-viaduct-reqwest",
"components/support/guid",
"components/support/interrupt",
"components/support/rc_crypto",
"components/support/rc_crypto/nss",

Просмотреть файл

@ -23,7 +23,7 @@ sync15 = { path = "../sync15" }
url = "1.7.1"
ffi-support = { path = "../support/ffi" }
viaduct = { path = "../viaduct" }
rc_crypto = { path = "../support/rc_crypto", features = ["ece"] }
rc_crypto = { path = "../support/rc_crypto", features = ["ece", "hawk"] }
error-support = { path = "../support/error" }
[dev-dependencies]
@ -38,6 +38,5 @@ mockiato = "0.8.0"
prost-build = "0.5"
[features]
browserid = ["openssl", "rc_crypto/hawk"]
reqwest = ["viaduct/reqwest"]
default = []

Просмотреть файл

@ -11,7 +11,7 @@ crate-type = ["lib"]
[dependencies]
ffi-support = { path = "../../support/ffi" }
log = "0.4.6"
log = "0.4.7"
lazy_static = "1.3.0"
url = "1.7.1"
prost = "0.5.0"
@ -21,5 +21,4 @@ viaduct = { path = "../../viaduct" }
path = "../"
[features]
browserid = ["fxa-client/browserid"]
reqwest = ["viaduct/reqwest", "fxa-client/reqwest"]

Просмотреть файл

@ -233,7 +233,7 @@ pub extern "C" fn fxa_complete_oauth_flow(
});
}
/// Migrate from a logged-in browserid Firefox Account.
/// Migrate from a logged-in sessionToken Firefox Account.
#[no_mangle]
pub unsafe extern "C" fn fxa_migrate_from_session_token(
handle: u64,

Просмотреть файл

@ -1,128 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::{
error::*,
http_client::browser_id::jwt_utils,
login_sm::{LoginState, LoginStateMachine, MarriedState, ReadyForKeysState, SessionTokenState},
Config, FirefoxAccount, StateV2,
};
use serde_derive::*;
use std::collections::{HashMap, HashSet};
impl FirefoxAccount {
// Initialize state from Firefox Accounts credentials obtained using the
// web flow.
pub fn from_credentials(
content_url: &str,
client_id: &str,
redirect_uri: &str,
credentials: WebChannelResponse,
) -> Result<Self> {
let config = Config::new(content_url, client_id, redirect_uri);
let session_token = hex::decode(credentials.session_token)?;
let key_fetch_token = hex::decode(credentials.key_fetch_token)?;
let unwrap_kb = hex::decode(credentials.unwrap_kb)?;
let login_state_data = ReadyForKeysState::new(
credentials.uid,
credentials.email,
session_token,
key_fetch_token,
unwrap_kb,
);
let login_state = if credentials.verified {
LoginState::EngagedAfterVerified(login_state_data)
} else {
LoginState::EngagedBeforeVerified(login_state_data)
};
Ok(Self::from_state(StateV2 {
config,
login_state,
refresh_token: None,
scoped_keys: HashMap::new(),
last_handled_command: None,
commands_data: HashMap::new(),
device_capabilities: HashSet::new(),
session_token: None,
}))
}
fn advance_to_married(&mut self) -> Result<Option<&MarriedState>> {
self.advance()?;
match self.state.login_state {
LoginState::Married(ref married) => Ok(Some(married)),
_ => Ok(None),
}
}
pub fn advance(&mut self) -> Result<()> {
let state_machine = LoginStateMachine::new(&self.state.config, self.client.clone());
let state = std::mem::replace(&mut self.state.login_state, LoginState::Unknown);
self.state.login_state = state_machine.advance(state)?;
Ok(())
}
pub(crate) fn session_token_from_state(state: &LoginState) -> Option<&[u8]> {
match state {
&LoginState::Separated(_) | LoginState::Unknown => None,
// Despite all these states implementing the same trait we can't treat
// them in a single arm, so this will do for now :/
&LoginState::EngagedBeforeVerified(ref state)
| &LoginState::EngagedAfterVerified(ref state) => Some(state.session_token()),
&LoginState::CohabitingBeforeKeyPair(ref state) => Some(state.session_token()),
&LoginState::CohabitingAfterKeyPair(ref state) => Some(state.session_token()),
&LoginState::Married(ref state) => Some(state.session_token()),
}
}
pub fn generate_assertion(&mut self, audience: &str) -> Result<String> {
let married = match self.advance_to_married()? {
Some(married) => married,
None => return Err(ErrorKind::NotMarried.into()),
};
let key_pair = married.key_pair();
let certificate = married.certificate();
Ok(jwt_utils::create_assertion(
key_pair,
&certificate,
audience,
)?)
}
pub fn get_sync_keys(&mut self) -> Result<SyncKeys> {
let married = match self.advance_to_married()? {
Some(married) => married,
None => return Err(ErrorKind::NotMarried.into()),
};
let sync_key = hex::encode(married.sync_key());
Ok(SyncKeys(sync_key, married.xcs().to_string()))
}
pub fn sign_out(mut self) {
self.client.sign_out();
self.state.login_state = self.state.login_state.into_separated();
}
}
#[derive(Deserialize)]
pub struct WebChannelResponse {
uid: String,
email: String,
verified: bool,
#[serde(rename = "sessionToken")]
session_token: String,
#[serde(rename = "keyFetchToken")]
key_fetch_token: String,
#[serde(rename = "unwrapBKey")]
unwrap_kb: String,
}
impl WebChannelResponse {
pub fn from_json(json: &str) -> Result<WebChannelResponse> {
serde_json::from_str(json).map_err(Into::into)
}
}
pub struct SyncKeys(pub String, pub String);

Просмотреть файл

@ -117,10 +117,6 @@ pub enum ErrorKind {
#[fail(display = "Hex decode error: {}", _0)]
HexDecodeError(#[fail(cause)] hex::FromHexError),
#[cfg(feature = "browserid")]
#[fail(display = "OpenSSL error: {}", _0)]
OpensslError(#[fail(cause)] openssl::error::ErrorStack),
#[fail(display = "Base64 decode error: {}", _0)]
Base64Decode(#[fail(cause)] base64::DecodeError),
@ -170,10 +166,3 @@ error_support::define_error_conversions! {
(HawkError, hawk::Error),
}
}
#[cfg(feature = "browserid")]
error_support::define_error_conversions! {
ErrorKind {
(OpensslError, openssl::error::ErrorStack),
}
}

Просмотреть файл

@ -3,39 +3,49 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::{config::Config, error::*};
use browser_id::{derive_hawk_auth_key_from_session_token, hawk_request::HawkRequestBuilder};
use hex;
use rc_crypto::hawk::{Credentials, Key, PayloadHasher, RequestBuilder, SHA256};
use rc_crypto::{digest, hkdf, hmac};
use serde_derive::*;
use serde_json::json;
use std::collections::HashMap;
use url::Url;
use viaduct::{header_names, status_codes, Method, Request, Response};
pub(crate) mod browser_id;
const HAWK_HKDF_SALT: [u8; 32] = [0b0; 32];
const HAWK_KEY_LENGTH: usize = 32;
#[cfg_attr(test, mockiato::mockable)]
pub trait FxAClient {
fn oauth_token_with_code(
fn oauth_tokens_from_code(
&self,
config: &Config,
code: &str,
code_verifier: &str,
) -> Result<OAuthTokenResponse>;
fn oauth_tokens_from_session_token(
&self,
config: &Config,
session_token: &str,
scopes: &[&str],
) -> Result<OAuthTokenResponse>;
fn oauth_token_with_refresh_token(
&self,
config: &Config,
refresh_token: &str,
scopes: &[&str],
) -> Result<OAuthTokenResponse>;
fn oauth_token_with_session_token(
&self,
config: &Config,
session_token: &str,
scopes: &[&str],
) -> Result<OAuthTokenResponse>;
fn duplicate_session(
&self,
config: &Config,
session_token: &[u8],
session_token: &str,
) -> Result<DuplicateTokenResponse>;
fn oauth_token_from_session_token(
&self,
config: &Config,
session_token: &[u8],
scopes: &[&str],
) -> Result<OAuthTokenResponse>;
fn destroy_oauth_token(&self, config: &Config, token: &str) -> Result<()>;
fn profile(
&self,
@ -69,7 +79,7 @@ pub trait FxAClient {
fn scoped_key_data(
&self,
config: &Config,
session_token: &[u8],
session_token: &str,
scope: &str,
) -> Result<HashMap<String, ScopedKeyDataResponse>>;
}
@ -102,7 +112,9 @@ impl FxAClient for Client {
}))
}
fn oauth_token_with_code(
// For the one-off generation of a `refresh_token` and associated meta from transient credentials.
fn oauth_tokens_from_code(
&self,
config: &Config,
code: &str,
@ -116,6 +128,28 @@ impl FxAClient for Client {
self.make_oauth_token_request(config, body)
}
fn oauth_tokens_from_session_token(
&self,
config: &Config,
session_token: &str,
scopes: &[&str],
) -> Result<OAuthTokenResponse> {
let url = config.token_endpoint()?;
let key = derive_auth_key_from_session_token(&session_token)?;
let body = json!({
"client_id": config.client_id,
"scope": scopes.join(" "),
"grant_type": "fxa-credentials",
"access_type": "offline",
});
let request = HawkRequestBuilder::new(Method::Post, url, &key)
.body(body)
.build()?;
Ok(Self::make_request(request)?.json()?)
}
// For the regular generation of an `access_token` from long-lived credentials.
fn oauth_token_with_refresh_token(
&self,
config: &Config,
@ -131,13 +165,32 @@ impl FxAClient for Client {
self.make_oauth_token_request(config, body)
}
fn oauth_token_with_session_token(
&self,
config: &Config,
session_token: &str,
scopes: &[&str],
) -> Result<OAuthTokenResponse> {
let parameters = json!({
"client_id": config.client_id,
"grant_type": "fxa-credentials",
"scope": scopes.join(" ")
});
let key = derive_auth_key_from_session_token(session_token)?;
let url = config.token_endpoint()?;
let request = HawkRequestBuilder::new(Method::Post, url, &key)
.body(parameters)
.build()?;
Self::make_request(request)?.json().map_err(Into::into)
}
fn duplicate_session(
&self,
config: &Config,
session_token: &[u8],
session_token: &str,
) -> Result<DuplicateTokenResponse> {
let url = config.auth_url_path("v1/session/duplicate")?;
let key = derive_hawk_auth_key_from_session_token(&session_token)?;
let key = derive_auth_key_from_session_token(&session_token)?;
let duplicate_body = json!({
"reason": "migration"
});
@ -148,26 +201,6 @@ impl FxAClient for Client {
Ok(Self::make_request(request)?.json()?)
}
fn oauth_token_from_session_token(
&self,
config: &Config,
session_token: &[u8],
scopes: &[&str],
) -> Result<OAuthTokenResponse> {
let url = config.auth_url_path("v1/oauth/token")?;
let key = derive_hawk_auth_key_from_session_token(&session_token)?;
let body = json!({
"client_id": config.client_id,
"scope": scopes.join(" "),
"grant_type": "fxa-credentials",
"access_type": "offline",
});
let request = HawkRequestBuilder::new(Method::Post, url, &key)
.body(body)
.build()?;
Ok(Self::make_request(request)?.json()?)
}
fn destroy_oauth_token(&self, config: &Config, token: &str) -> Result<()> {
let body = json!({
"token": token,
@ -254,7 +287,7 @@ impl FxAClient for Client {
fn scoped_key_data(
&self,
config: &Config,
session_token: &[u8],
session_token: &str,
scope: &str,
) -> Result<HashMap<String, ScopedKeyDataResponse>> {
let body = json!({
@ -262,7 +295,7 @@ impl FxAClient for Client {
"scope": scope,
});
let url = config.auth_url_path("v1/account/scoped-key-data")?;
let key = derive_hawk_auth_key_from_session_token(session_token)?;
let key = derive_auth_key_from_session_token(session_token)?;
let request = HawkRequestBuilder::new(Method::Post, url, &key)
.body(body)
.build()?;
@ -309,6 +342,79 @@ fn bearer_token(token: &str) -> String {
format!("Bearer {}", token)
}
fn kw(name: &str) -> Vec<u8> {
format!("identity.mozilla.com/picl/v1/{}", name)
.as_bytes()
.to_vec()
}
pub fn derive_auth_key_from_session_token(session_token: &str) -> Result<Vec<u8>> {
let session_token_bytes = hex::decode(session_token)?;
let context_info = kw("sessionToken");
let salt = hmac::SigningKey::new(&digest::SHA256, &HAWK_HKDF_SALT);
let mut out = vec![0u8; HAWK_KEY_LENGTH * 2];
hkdf::extract_and_expand(&salt, &session_token_bytes, &context_info, &mut out)?;
Ok(out)
}
struct HawkRequestBuilder<'a> {
url: Url,
method: Method,
body: Option<String>,
hkdf_sha256_key: &'a [u8],
}
impl<'a> HawkRequestBuilder<'a> {
pub fn new(method: Method, url: Url, hkdf_sha256_key: &'a [u8]) -> Self {
rc_crypto::ensure_initialized();
HawkRequestBuilder {
url,
method,
body: None,
hkdf_sha256_key,
}
}
// This class assumes that the content being sent it always of the type
// application/json.
pub fn body(mut self, body: serde_json::Value) -> Self {
self.body = Some(body.to_string());
self
}
fn make_hawk_header(&self) -> Result<String> {
// Make sure we de-allocate the hash after hawk_request_builder.
let hash;
let method = format!("{}", self.method);
let mut hawk_request_builder = RequestBuilder::from_url(method.as_str(), &self.url)?;
if let Some(ref body) = self.body {
hash = PayloadHasher::hash("application/json", SHA256, &body)?;
hawk_request_builder = hawk_request_builder.hash(&hash[..]);
}
let hawk_request = hawk_request_builder.request();
let token_id = hex::encode(&self.hkdf_sha256_key[0..HAWK_KEY_LENGTH]);
let hmac_key = &self.hkdf_sha256_key[HAWK_KEY_LENGTH..(2 * HAWK_KEY_LENGTH)];
let hawk_credentials = Credentials {
id: token_id,
key: Key::new(hmac_key, SHA256)?,
};
let header = hawk_request.make_header(&hawk_credentials)?;
Ok(format!("Hawk {}", header))
}
pub fn build(self) -> Result<Request> {
let hawk_header = self.make_hawk_header()?;
let mut request =
Request::new(self.method, self.url).header(header_names::AUTHORIZATION, hawk_header)?;
if let Some(body) = self.body {
request = request
.header(header_names::CONTENT_TYPE, "application/json")?
.body(body);
}
Ok(request)
}
}
#[derive(Clone)]
pub struct ResponseAndETag<T> {
pub response: T,

Просмотреть файл

@ -1,335 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::error::*;
#[cfg(feature = "browserid")]
use crate::{
http_client::{self, browser_id::hawk_request::HawkRequestBuilder, OAuthTokenResponse},
util::Xorable,
Config,
};
use rc_crypto::{digest, hkdf, hmac};
#[cfg(feature = "browserid")]
use rsa::RSABrowserIDKeyPair;
#[cfg(feature = "browserid")]
use serde_derive::*;
#[cfg(feature = "browserid")]
use serde_json::json;
#[cfg(feature = "browserid")]
use url::Url;
#[cfg(feature = "browserid")]
use viaduct::{Method, Request};
pub(crate) mod hawk_request;
#[cfg(feature = "browserid")]
pub(crate) mod jwt_utils;
#[cfg(feature = "browserid")]
pub(crate) mod rsa;
const HKDF_SALT: [u8; 32] = [0b0; 32];
const KEY_LENGTH: usize = 32;
#[cfg(feature = "browserid")]
const SIGN_DURATION_MS: u64 = 24 * 60 * 60 * 1000;
#[cfg(feature = "browserid")]
pub trait BrowserIDKeyPair {
fn get_algo(&self) -> String;
fn sign(&self, message: &[u8]) -> Result<Vec<u8>>;
fn verify_message(&self, message: &[u8], signature: &[u8]) -> Result<bool>;
fn to_json(&self, include_private: bool) -> Result<serde_json::Value>;
}
#[cfg(feature = "browserid")]
pub trait FxABrowserIDClient: http_client::FxAClient {
fn sign_out(&self);
fn login(
&self,
config: &Config,
email: &str,
auth_pwd: &str,
get_keys: bool,
) -> Result<LoginResponse>;
fn account_status(&self, config: &Config, uid: &str) -> Result<AccountStatusResponse>;
fn keys(&self, config: &Config, key_fetch_token: &[u8]) -> Result<KeysResponse>;
fn recovery_email_status(
&self,
config: &Config,
session_token: &[u8],
) -> Result<RecoveryEmailStatusResponse>;
fn oauth_token_with_session_token(
&self,
config: &Config,
session_token: &[u8],
scopes: &[&str],
) -> Result<OAuthTokenResponse>;
fn sign(
&self,
config: &Config,
session_token: &[u8],
key_pair: &dyn BrowserIDKeyPair,
) -> Result<SignResponse>;
}
#[cfg(feature = "browserid")]
impl FxABrowserIDClient for http_client::Client {
fn sign_out(&self) {
panic!("Not implemented yet!");
}
fn login(
&self,
config: &Config,
email: &str,
auth_pwd: &str,
get_keys: bool,
) -> Result<LoginResponse> {
let url = config.auth_url_path("v1/account/login")?;
let parameters = json!({
"email": email,
"authPW": auth_pwd
});
let request = Request::post(url)
.query(&[("keys", if get_keys { "true" } else { "false" })])
.json(&parameters);
Self::make_request(request)?.json().map_err(Into::into)
}
fn account_status(&self, config: &Config, uid: &str) -> Result<AccountStatusResponse> {
let url = config.auth_url_path("v1/account/status")?;
let request = Request::get(url).query(&[("uid", uid)]);
Self::make_request(request)?.json().map_err(Into::into)
}
fn keys(&self, config: &Config, key_fetch_token: &[u8]) -> Result<KeysResponse> {
let url = config.auth_url_path("v1/account/keys")?;
let context_info = kw("keyFetchToken");
let key =
derive_hkdf_sha256_key(&key_fetch_token, &HKDF_SALT, &context_info, KEY_LENGTH * 3)?;
let key_request_key = &key[(KEY_LENGTH * 2)..(KEY_LENGTH * 3)];
let request = HawkRequestBuilder::new(Method::Get, url, &key).build()?;
let json: serde_json::Value = Self::make_request(request)?.json()?;
let bundle = match json["bundle"].as_str() {
Some(bundle) => bundle,
None => panic!("Invalid JSON"),
};
let data = hex::decode(bundle)?;
if data.len() != 3 * KEY_LENGTH {
return Err(ErrorKind::BadKeyLength("bundle", 3 * KEY_LENGTH, data.len()).into());
}
let ciphertext = &data[0..(KEY_LENGTH * 2)];
let mac_code = &data[(KEY_LENGTH * 2)..(KEY_LENGTH * 3)];
let context_info = kw("account/keys");
let bytes =
derive_hkdf_sha256_key(key_request_key, &HKDF_SALT, &context_info, KEY_LENGTH * 3)?;
let hmac_key = &bytes[0..KEY_LENGTH];
let xor_key = &bytes[KEY_LENGTH..(KEY_LENGTH * 3)];
let v_key = hmac::VerificationKey::new(&digest::SHA256, hmac_key);
hmac::verify(&v_key, ciphertext, mac_code).map_err(|_| ErrorKind::HmacMismatch)?;
let xored_bytes = ciphertext.xored_with(xor_key)?;
let wrap_kb = xored_bytes[KEY_LENGTH..(KEY_LENGTH * 2)].to_vec();
Ok(KeysResponse { wrap_kb })
}
fn recovery_email_status(
&self,
config: &Config,
session_token: &[u8],
) -> Result<RecoveryEmailStatusResponse> {
let url = config.auth_url_path("v1/recovery_email/status")?;
let key = derive_hawk_auth_key_from_session_token(session_token)?;
let request = HawkRequestBuilder::new(Method::Get, url, &key).build()?;
Self::make_request(request)?.json().map_err(Into::into)
}
fn oauth_token_with_session_token(
&self,
config: &Config,
session_token: &[u8],
scopes: &[&str],
) -> Result<OAuthTokenResponse> {
let audience = get_oauth_audience(&config.oauth_url()?)?;
let key_pair = key_pair(1024)?;
let certificate = self.sign(config, session_token, &key_pair)?.certificate;
let assertion = jwt_utils::create_assertion(&key_pair, &certificate, &audience)?;
let parameters = json!({
"assertion": assertion,
"client_id": config.client_id,
"response_type": "token",
"scope": scopes.join(" ")
});
let key = derive_hawk_auth_key_from_session_token(session_token)?;
let url = config.authorization_endpoint()?;
let request = HawkRequestBuilder::new(Method::Post, url, &key)
.body(parameters)
.build()?;
Self::make_request(request)?.json().map_err(Into::into)
}
fn sign(
&self,
config: &Config,
session_token: &[u8],
key_pair: &dyn BrowserIDKeyPair,
) -> Result<SignResponse> {
let public_key_json = key_pair.to_json(false)?;
let parameters = json!({
"publicKey": public_key_json,
"duration": SIGN_DURATION_MS
});
let key = derive_hawk_auth_key_from_session_token(session_token)?;
let url = config.auth_url_path("v1/certificate/sign")?;
let request = HawkRequestBuilder::new(Method::Post, url, &key)
.body(parameters)
.build()?;
Self::make_request(request)?.json().map_err(Into::into)
}
}
fn kw(name: &str) -> Vec<u8> {
format!("identity.mozilla.com/picl/v1/{}", name)
.as_bytes()
.to_vec()
}
#[cfg(feature = "browserid")]
#[allow(dead_code)]
fn kwe(name: &str, email: &str) -> Vec<u8> {
format!("identity.mozilla.com/picl/v1/{}:{}", name, email)
.as_bytes()
.to_vec()
}
#[cfg(feature = "browserid")]
pub fn key_pair(len: u32) -> Result<RSABrowserIDKeyPair> {
RSABrowserIDKeyPair::generate_random(len)
}
#[cfg(feature = "browserid")]
pub(crate) fn derive_sync_key(kb: &[u8]) -> Result<Vec<u8>> {
let salt = [0u8; 0];
let context_info = kw("oldsync");
derive_hkdf_sha256_key(&kb, &salt, &context_info, KEY_LENGTH * 2)
}
#[cfg(feature = "browserid")]
pub(crate) fn compute_client_state(kb: &[u8]) -> Result<String> {
Ok(hex::encode(
&digest::digest(&digest::SHA256, &kb)?.as_ref()[0..16],
))
}
#[cfg(feature = "browserid")]
fn get_oauth_audience(oauth_url: &Url) -> Result<String> {
let host = oauth_url
.host_str()
.ok_or_else(|| ErrorKind::AudienceURLWithoutHost)?;
match oauth_url.port() {
Some(port) => Ok(format!("{}://{}:{}", oauth_url.scheme(), host, port)),
None => Ok(format!("{}://{}", oauth_url.scheme(), host)),
}
}
pub(crate) fn derive_hawk_auth_key_from_session_token(session_token: &[u8]) -> Result<Vec<u8>> {
let context_info = kw("sessionToken");
Ok(derive_hkdf_sha256_key(
session_token,
&HKDF_SALT,
&context_info,
KEY_LENGTH * 2,
)?)
}
fn derive_hkdf_sha256_key(ikm: &[u8], salt: &[u8], info: &[u8], len: usize) -> Result<Vec<u8>> {
let salt = hmac::SigningKey::new(&digest::SHA256, salt);
let mut out = vec![0u8; len];
hkdf::extract_and_expand(&salt, ikm, info, &mut out)?;
Ok(out)
}
#[cfg(feature = "browserid")]
#[derive(Deserialize)]
pub struct LoginResponse {
pub uid: String,
#[serde(rename = "sessionToken")]
pub session_token: String,
pub verified: bool,
}
#[cfg(feature = "browserid")]
#[derive(Deserialize)]
pub struct RecoveryEmailStatusResponse {
pub email: String,
pub verified: bool,
}
#[cfg(feature = "browserid")]
#[derive(Deserialize)]
pub struct AccountStatusResponse {
pub exists: bool,
}
#[cfg(feature = "browserid")]
#[derive(Deserialize)]
pub struct SignResponse {
#[serde(rename = "cert")]
pub certificate: String,
}
#[cfg(feature = "browserid")]
#[derive(Deserialize)]
pub struct KeysResponse {
// ka: Vec<u8>,
pub wrap_kb: Vec<u8>,
}
#[cfg(feature = "browserid")]
#[cfg(test)]
mod tests {
use super::*;
use ring::{digest, pbkdf2};
fn quick_strech_pwd(email: &str, pwd: &str) -> Vec<u8> {
let salt = kwe("quickStretch", email);
let mut out = [0u8; 32];
pbkdf2::derive(
&digest::SHA256,
std::num::NonZeroU32::new(1000).unwrap(),
&salt,
pwd.as_bytes(),
&mut out,
);
out.to_vec()
}
fn auth_pwd(email: &str, pwd: &str) -> String {
let streched = quick_strech_pwd(email, pwd);
let salt = [0u8; 0];
let context = kw("authPW");
let derived = derive_hkdf_sha256_key(&streched, &salt, &context, 32).unwrap();
hex::encode(derived)
}
#[test]
fn test_quick_strech_pwd() {
let email = "andré@example.org";
let pwd = "pässwörd";
let streched = hex::encode(quick_strech_pwd(email, pwd));
assert_eq!(
streched,
"e4e8889bd8bd61ad6de6b95c059d56e7b50dacdaf62bd84644af7e2add84345d"
);
}
#[test]
fn test_auth_pwd() {
let email = "andré@example.org";
let pwd = "pässwörd";
let auth_pwd = auth_pwd(email, pwd);
assert_eq!(
auth_pwd,
"247b675ffb4c46310bc87e26d712153abe5e1c90ef00a4784594f97ef54f2375"
);
}
}

Просмотреть файл

@ -1,68 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::error::*;
use rc_crypto::hawk::{Credentials, Key, PayloadHasher, RequestBuilder, SHA256};
use url::Url;
use viaduct::{header_names, Method, Request};
const KEY_LENGTH: usize = 32;
pub struct HawkRequestBuilder<'a> {
url: Url,
method: Method,
body: Option<String>,
hkdf_sha256_key: &'a [u8],
}
impl<'a> HawkRequestBuilder<'a> {
pub fn new(method: Method, url: Url, hkdf_sha256_key: &'a [u8]) -> Self {
rc_crypto::ensure_initialized();
HawkRequestBuilder {
url,
method,
body: None,
hkdf_sha256_key,
}
}
// This class assumes that the content being sent it always of the type
// application/json.
pub fn body(mut self, body: serde_json::Value) -> Self {
self.body = Some(body.to_string());
self
}
fn make_hawk_header(&self) -> Result<String> {
// Make sure we de-allocate the hash after hawk_request_builder.
let hash;
let method = format!("{}", self.method);
let mut hawk_request_builder = RequestBuilder::from_url(method.as_str(), &self.url)?;
if let Some(ref body) = self.body {
hash = PayloadHasher::hash("application/json", SHA256, &body)?;
hawk_request_builder = hawk_request_builder.hash(&hash[..]);
}
let hawk_request = hawk_request_builder.request();
let token_id = hex::encode(&self.hkdf_sha256_key[0..KEY_LENGTH]);
let hmac_key = &self.hkdf_sha256_key[KEY_LENGTH..(2 * KEY_LENGTH)];
let hawk_credentials = Credentials {
id: token_id,
key: Key::new(hmac_key, SHA256)?,
};
let header = hawk_request.make_header(&hawk_credentials)?;
Ok(format!("Hawk {}", header))
}
pub fn build(self) -> Result<Request> {
let hawk_header = self.make_hawk_header()?;
let mut request =
Request::new(self.method, self.url).header(header_names::AUTHORIZATION, hawk_header)?;
if let Some(body) = self.body {
request = request
.header(header_names::CONTENT_TYPE, "application/json")?
.body(body);
}
Ok(request)
}
}

Просмотреть файл

@ -1,225 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::{error::*, http_client::browser_id::BrowserIDKeyPair};
use serde_json::{self, json};
use std::time::{SystemTime, UNIX_EPOCH};
const DEFAULT_ASSERTION_ISSUER: &str = "127.0.0.1";
const DEFAULT_ASSERTION_DURATION: u64 = 60 * 60 * 1000;
pub fn create_assertion(
key_pair: &dyn BrowserIDKeyPair,
certificate: &str,
audience: &str,
) -> Result<String> {
let since_epoch = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Something is very wrong.");
let issued_at =
since_epoch.as_secs() * 1000 + u64::from(since_epoch.subsec_nanos()) / 1_000_000;
let expires_at = issued_at + DEFAULT_ASSERTION_DURATION;
let issuer = DEFAULT_ASSERTION_ISSUER;
create_assertion_full(
key_pair,
certificate,
audience,
issuer,
issued_at,
expires_at,
)
}
pub fn create_assertion_full(
key_pair: &dyn BrowserIDKeyPair,
certificate: &str,
audience: &str,
issuer: &str,
issued_at: u64,
expires_at: u64,
) -> Result<String> {
let assertion = SignedJWTBuilder::new(key_pair, issuer, issued_at, expires_at)
.audience(&audience)
.build()?;
Ok(format!("{}~{}", certificate, assertion))
}
struct SignedJWTBuilder<'keypair> {
key_pair: &'keypair dyn BrowserIDKeyPair,
issuer: String,
issued_at: u64,
expires_at: u64,
audience: Option<String>,
payload: Option<serde_json::Value>,
}
impl<'keypair> SignedJWTBuilder<'keypair> {
fn new(
key_pair: &'keypair dyn BrowserIDKeyPair,
issuer: &str,
issued_at: u64,
expires_at: u64,
) -> SignedJWTBuilder<'keypair> {
SignedJWTBuilder {
key_pair,
issuer: issuer.to_owned(),
issued_at,
expires_at,
audience: None,
payload: None,
}
}
fn audience(mut self, audience: &str) -> SignedJWTBuilder<'keypair> {
self.audience = Some(audience.to_owned());
self
}
#[allow(dead_code)]
fn payload(mut self, payload: serde_json::Value) -> SignedJWTBuilder<'keypair> {
self.payload = Some(payload);
self
}
fn build(self) -> Result<String> {
let payload_string = self.get_payload_string()?;
encode_and_sign(&payload_string, self.key_pair)
}
fn get_payload_string(&self) -> Result<String> {
let mut payload = match self.payload {
Some(ref payload) => payload.clone(),
None => json!({}),
};
let obj = match payload.as_object_mut() {
Some(obj) => obj,
None => panic!("The supplied payload was not an object"),
};
if let Some(ref audience) = self.audience {
obj.insert("aud".to_string(), json!(audience));
}
obj.insert("iss".to_string(), json!(self.issuer));
obj.insert("iat".to_string(), json!(self.issued_at));
obj.insert("exp".to_string(), json!(self.expires_at));
Ok(json!(obj).to_string())
}
}
fn encode_and_sign(payload: &str, key_pair: &dyn BrowserIDKeyPair) -> Result<String> {
let headers_str = json!({"alg": key_pair.get_algo()}).to_string();
let encoded_header = base64::encode_config(headers_str.as_bytes(), base64::URL_SAFE_NO_PAD);
let encoded_payload = base64::encode_config(payload.as_bytes(), base64::URL_SAFE_NO_PAD);
let message = format!("{}.{}", encoded_header, encoded_payload);
let signature = key_pair.sign(message.as_bytes())?;
let encoded_signature = base64::encode_config(&signature, base64::URL_SAFE_NO_PAD);
Ok(format!("{}.{}", message, encoded_signature))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::http_client::browser_id::rsa::RSABrowserIDKeyPair;
use crate::http_client::browser_id::BrowserIDKeyPair;
pub fn create_certificate(
serialized_public_key: &serde_json::Value,
email: &str,
issuer: &str,
issued_at: u64,
expires_at: u64,
key_pair: &dyn BrowserIDKeyPair,
) -> Result<String> {
let principal = json!({ "email": email });
let payload = json!({
"principal": principal,
"public-key": serialized_public_key
});
Ok(
SignedJWTBuilder::new(key_pair, issuer, issued_at, expires_at)
.payload(payload)
.build()?,
)
}
fn decode(token: &str, key_pair: &dyn BrowserIDKeyPair) -> Result<String> {
let segments: Vec<&str> = token.split('.').collect();
let message = format!("{}.{}", &segments[0], &segments[1]);
let message_bytes = message.as_bytes();
let signature = base64::decode_config(&segments[2], base64::URL_SAFE_NO_PAD)?;
let verified = key_pair.verify_message(&message_bytes, &signature)?;
if !verified {
return Err(ErrorKind::JWTSignatureValidationFailed.into());
}
let payload = base64::decode_config(&segments[1], base64::URL_SAFE_NO_PAD)?;
String::from_utf8(payload).map_err(Into::into)
}
// These tests are copied directly from Firefox for Android's TestJSONWebTokenUtils.
// They could probably be improved a lot.
fn do_test_encode_decode(key_pair: &dyn BrowserIDKeyPair) {
let payload = json!({"key": "value"}).to_string();
let token = encode_and_sign(&payload, key_pair).unwrap();
let decoded = decode(&token, key_pair).unwrap();
assert_eq!(decoded, payload);
let token_corrupted = format!("{}x", token);
assert!(decode(&token_corrupted, key_pair).is_err());
}
#[test]
fn test_rsa_encode_decode() {
do_test_encode_decode(&RSABrowserIDKeyPair::generate_random(1024).unwrap());
do_test_encode_decode(&RSABrowserIDKeyPair::generate_random(2048).unwrap());
}
#[test]
// These tests were copied from Firefox for Android (TestJSONWebTokenUtils.java).
fn test_rsa_generation() {
let mock_modulus = "15498874758090276039465094105837231567265546373975960480941122651107772824121527483107402353899846252489837024870191707394743196399582959425513904762996756672089693541009892030848825079649783086005554442490232900875792851786203948088457942416978976455297428077460890650409549242124655536986141363719589882160081480785048965686285142002320767066674879737238012064156675899512503143225481933864507793118457805792064445502834162315532113963746801770187685650408560424682654937744713813773896962263709692724630650952159596951348264005004375017610441835956073275708740239518011400991972811669493356682993446554779893834303";
let mock_public_exponent = "65537";
let mock_private_exponent = "6539906961872354450087244036236367269804254381890095841127085551577495913426869112377010004955160417265879626558436936025363204803913318582680951558904318308893730033158178650549970379367915856087364428530828396795995781364659413467784853435450762392157026962694408807947047846891301466649598749901605789115278274397848888140105306063608217776127549926721544215720872305194645129403056801987422794114703255989202755511523434098625000826968430077091984351410839837395828971692109391386427709263149504336916566097901771762648090880994773325283207496645630792248007805177873532441314470502254528486411726581424522838833";
let n = "20332459213245328760269530796942625317006933400814022542511832260333163206808672913301254872114045771215470352093046136365629411384688395020388553744886954869033696089099714200452682590914843971683468562019706059388121176435204818734091361033445697933682779095713376909412972373727850278295874361806633955236862180792787906413536305117030045164276955491725646610368132167655556353974515423042221261732084368978523747789654468953860772774078384556028728800902433401131226904244661160767916883680495122225202542023841606998867411022088440946301191503335932960267228470933599974787151449279465703844493353175088719018221";
let e = "65537";
let d = "9362542596354998418106014928820888151984912891492829581578681873633736656469965533631464203894863562319612803232737938923691416707617473868582415657005943574434271946791143554652502483003923911339605326222297167404896789026986450703532494518628015811567189641735787240372075015553947628033216297520493759267733018808392882741098489889488442349031883643894014316243251108104684754879103107764521172490019661792943030921873284592436328217485953770574054344056638447333651425231219150676837203185544359148474983670261712939626697233692596362322419559401320065488125670905499610998631622562652935873085671353890279911361";
let issuer = "127.0.0.1";
let audience = "http://localhost:8080";
let iat: u64 = 1_352_995_809_210;
let dur: u64 = 60 * 60 * 1000;
let exp: u64 = iat + dur;
let mock_key_pair = RSABrowserIDKeyPair::from_exponents_base10(
mock_modulus,
mock_public_exponent,
mock_private_exponent,
)
.unwrap();
let key_pair_to_sign = RSABrowserIDKeyPair::from_exponents_base10(n, e, d).unwrap();
let certificate = create_certificate(
&key_pair_to_sign.to_json(false).unwrap(),
"test@mockmyid.com",
"mockmyid.com",
iat,
exp,
&mock_key_pair,
)
.unwrap();
let assertion =
create_assertion_full(&key_pair_to_sign, &certificate, audience, issuer, iat, exp)
.unwrap();
let payload = decode(&certificate, &mock_key_pair).unwrap();
let expected_payload = "{\"exp\":1352999409210,\"iat\":1352995809210,\"iss\":\"mockmyid.com\",\"principal\":{\"email\":\"test@mockmyid.com\"},\"public-key\":{\"algorithm\":\"RS\",\"e\":\"65537\",\"n\":\"20332459213245328760269530796942625317006933400814022542511832260333163206808672913301254872114045771215470352093046136365629411384688395020388553744886954869033696089099714200452682590914843971683468562019706059388121176435204818734091361033445697933682779095713376909412972373727850278295874361806633955236862180792787906413536305117030045164276955491725646610368132167655556353974515423042221261732084368978523747789654468953860772774078384556028728800902433401131226904244661160767916883680495122225202542023841606998867411022088440946301191503335932960267228470933599974787151449279465703844493353175088719018221\"}}";
assert_eq!(payload, expected_payload);
let expected_certificate = "eyJhbGciOiJSUzI1NSJ9.eyJleHAiOjEzNTI5OTk0MDkyMTAsImlhdCI6MTM1Mjk5NTgwOTIxMCwiaXNzIjoibW9ja215aWQuY29tIiwicHJpbmNpcGFsIjp7ImVtYWlsIjoidGVzdEBtb2NrbXlpZC5jb20ifSwicHVibGljLWtleSI6eyJhbGdvcml0aG0iOiJSUyIsImUiOiI2NTUzNyIsIm4iOiIyMDMzMjQ1OTIxMzI0NTMyODc2MDI2OTUzMDc5Njk0MjYyNTMxNzAwNjkzMzQwMDgxNDAyMjU0MjUxMTgzMjI2MDMzMzE2MzIwNjgwODY3MjkxMzMwMTI1NDg3MjExNDA0NTc3MTIxNTQ3MDM1MjA5MzA0NjEzNjM2NTYyOTQxMTM4NDY4ODM5NTAyMDM4ODU1Mzc0NDg4Njk1NDg2OTAzMzY5NjA4OTA5OTcxNDIwMDQ1MjY4MjU5MDkxNDg0Mzk3MTY4MzQ2ODU2MjAxOTcwNjA1OTM4ODEyMTE3NjQzNTIwNDgxODczNDA5MTM2MTAzMzQ0NTY5NzkzMzY4Mjc3OTA5NTcxMzM3NjkwOTQxMjk3MjM3MzcyNzg1MDI3ODI5NTg3NDM2MTgwNjYzMzk1NTIzNjg2MjE4MDc5Mjc4NzkwNjQxMzUzNjMwNTExNzAzMDA0NTE2NDI3Njk1NTQ5MTcyNTY0NjYxMDM2ODEzMjE2NzY1NTU1NjM1Mzk3NDUxNTQyMzA0MjIyMTI2MTczMjA4NDM2ODk3ODUyMzc0Nzc4OTY1NDQ2ODk1Mzg2MDc3Mjc3NDA3ODM4NDU1NjAyODcyODgwMDkwMjQzMzQwMTEzMTIyNjkwNDI0NDY2MTE2MDc2NzkxNjg4MzY4MDQ5NTEyMjIyNTIwMjU0MjAyMzg0MTYwNjk5ODg2NzQxMTAyMjA4ODQ0MDk0NjMwMTE5MTUwMzMzNTkzMjk2MDI2NzIyODQ3MDkzMzU5OTk3NDc4NzE1MTQ0OTI3OTQ2NTcwMzg0NDQ5MzM1MzE3NTA4ODcxOTAxODIyMSJ9fQ.a_DXs5LysXoBb6zw3eKVjqIEr8PwXBCqJ0UaLOTNranN18Lw1gAlNDs0wEKvIslvdR3fhWyCm5jRISWTsYlZ8E5XAGwL9LPyFliplxaEVBly-g4mBcZzdDGx37832pwvNHGYnc0qknsjWr0oT8DkZj-ShE3YdVbIlyeGf8191DEJR4aGKccNB2o6itNaa5vrXgMLuZDvXfSDRvE6k2vbQb1wLQQCx_kBwRa6ADmejzVDIqRoKtK7-wCS1zXQzpP3Sa9tOfnKSMHuPkuRTJdrxWHULRkdE0iYmch1YSrGHCtx2kiG09o7YkwH7E53pBSrGcn8mFAdRkNdDrqTdnLV2Q";
assert_eq!(certificate, expected_certificate);
let expected_assertion = "eyJhbGciOiJSUzI1NSJ9.eyJleHAiOjEzNTI5OTk0MDkyMTAsImlhdCI6MTM1Mjk5NTgwOTIxMCwiaXNzIjoibW9ja215aWQuY29tIiwicHJpbmNpcGFsIjp7ImVtYWlsIjoidGVzdEBtb2NrbXlpZC5jb20ifSwicHVibGljLWtleSI6eyJhbGdvcml0aG0iOiJSUyIsImUiOiI2NTUzNyIsIm4iOiIyMDMzMjQ1OTIxMzI0NTMyODc2MDI2OTUzMDc5Njk0MjYyNTMxNzAwNjkzMzQwMDgxNDAyMjU0MjUxMTgzMjI2MDMzMzE2MzIwNjgwODY3MjkxMzMwMTI1NDg3MjExNDA0NTc3MTIxNTQ3MDM1MjA5MzA0NjEzNjM2NTYyOTQxMTM4NDY4ODM5NTAyMDM4ODU1Mzc0NDg4Njk1NDg2OTAzMzY5NjA4OTA5OTcxNDIwMDQ1MjY4MjU5MDkxNDg0Mzk3MTY4MzQ2ODU2MjAxOTcwNjA1OTM4ODEyMTE3NjQzNTIwNDgxODczNDA5MTM2MTAzMzQ0NTY5NzkzMzY4Mjc3OTA5NTcxMzM3NjkwOTQxMjk3MjM3MzcyNzg1MDI3ODI5NTg3NDM2MTgwNjYzMzk1NTIzNjg2MjE4MDc5Mjc4NzkwNjQxMzUzNjMwNTExNzAzMDA0NTE2NDI3Njk1NTQ5MTcyNTY0NjYxMDM2ODEzMjE2NzY1NTU1NjM1Mzk3NDUxNTQyMzA0MjIyMTI2MTczMjA4NDM2ODk3ODUyMzc0Nzc4OTY1NDQ2ODk1Mzg2MDc3Mjc3NDA3ODM4NDU1NjAyODcyODgwMDkwMjQzMzQwMTEzMTIyNjkwNDI0NDY2MTE2MDc2NzkxNjg4MzY4MDQ5NTEyMjIyNTIwMjU0MjAyMzg0MTYwNjk5ODg2NzQxMTAyMjA4ODQ0MDk0NjMwMTE5MTUwMzMzNTkzMjk2MDI2NzIyODQ3MDkzMzU5OTk3NDc4NzE1MTQ0OTI3OTQ2NTcwMzg0NDQ5MzM1MzE3NTA4ODcxOTAxODIyMSJ9fQ.a_DXs5LysXoBb6zw3eKVjqIEr8PwXBCqJ0UaLOTNranN18Lw1gAlNDs0wEKvIslvdR3fhWyCm5jRISWTsYlZ8E5XAGwL9LPyFliplxaEVBly-g4mBcZzdDGx37832pwvNHGYnc0qknsjWr0oT8DkZj-ShE3YdVbIlyeGf8191DEJR4aGKccNB2o6itNaa5vrXgMLuZDvXfSDRvE6k2vbQb1wLQQCx_kBwRa6ADmejzVDIqRoKtK7-wCS1zXQzpP3Sa9tOfnKSMHuPkuRTJdrxWHULRkdE0iYmch1YSrGHCtx2kiG09o7YkwH7E53pBSrGcn8mFAdRkNdDrqTdnLV2Q~eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODAiLCJleHAiOjEzNTI5OTk0MDkyMTAsImlhdCI6MTM1Mjk5NTgwOTIxMCwiaXNzIjoiMTI3LjAuMC4xIn0.Vi9vl8frqV-devCgV5EEfxyP5omfoWYgehcBMPPBtt-rFgylAUMT48gQb4UQlkRuvdUP7bkfc32KPK6lHCrWNKlsX2O0hnry4lTyFp4g2PGRdCdIGkrQ82hrxWpt-s16x_qW2SkcwcauPYMjOmXkuUnWS5Yx-kjEV07fcy-njl-15NZX8sYFO0uocuRsUXMSp5wibBVbDEEkm9IgRoqBPT9SqnpEwO4RBj0Dx16y4t9eKIvbh_3Jpa3GPUGJWP07t7t2w-622Fmoekcf4Bjfsu-NYtMPj_NE_ZnbZ0VFIv6IdPfPsMHUwwCSy-vFh8ZgvD2EVT1fycT1wTS0Puq-dQ";
assert_eq!(assertion, expected_assertion);
}
}

Просмотреть файл

@ -1,321 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use super::BrowserIDKeyPair;
use crate::error::*;
use openssl::{
bn::BigNum,
hash::MessageDigest,
pkey::{PKey, Private},
rsa::{Rsa, RsaPrivateKeyBuilder},
sign::{Signer, Verifier},
};
use serde::{
de::{self, Deserialize, Deserializer, MapAccess, Visitor},
ser::{self, Serialize, SerializeStruct, Serializer},
};
use serde_json::{self, json};
use std::fmt;
pub struct RSABrowserIDKeyPair {
key: PKey<Private>,
}
impl RSABrowserIDKeyPair {
fn from_rsa(rsa: Rsa<Private>) -> Result<RSABrowserIDKeyPair> {
let key = PKey::from_rsa(rsa)?;
Ok(RSABrowserIDKeyPair { key })
}
pub fn generate_random(len: u32) -> Result<RSABrowserIDKeyPair> {
let rsa = Rsa::generate(len)?;
RSABrowserIDKeyPair::from_rsa(rsa)
}
#[allow(dead_code)]
pub fn from_exponents_base10(n: &str, e: &str, d: &str) -> Result<RSABrowserIDKeyPair> {
let n = BigNum::from_dec_str(n)?;
let e = BigNum::from_dec_str(e)?;
let d = BigNum::from_dec_str(d)?;
let rsa = RsaPrivateKeyBuilder::new(n, e, d)?.build();
RSABrowserIDKeyPair::from_rsa(rsa)
}
}
impl BrowserIDKeyPair for RSABrowserIDKeyPair {
fn get_algo(&self) -> String {
format!("RS{}", self.key.bits() / 8)
}
fn sign(&self, message: &[u8]) -> Result<Vec<u8>> {
let mut signer = Signer::new(MessageDigest::sha256(), &self.key)?;
signer.update(message)?;
signer.sign_to_vec().map_err(Into::into)
}
fn verify_message(&self, message: &[u8], signature: &[u8]) -> Result<bool> {
let mut verifier = Verifier::new(MessageDigest::sha256(), &self.key)?;
verifier.update(message)?;
verifier.verify(signature).map_err(Into::into)
}
fn to_json(&self, include_private: bool) -> Result<serde_json::Value> {
if include_private {
panic!("Not implemented!");
}
let rsa = self.key.rsa()?;
let n = format!("{}", rsa.n().to_dec_str()?);
let e = format!("{}", rsa.e().to_dec_str()?);
Ok(json!({
"algorithm": "RS",
"n": n,
"e": e
}))
}
}
impl Clone for RSABrowserIDKeyPair {
fn clone(&self) -> RSABrowserIDKeyPair {
let rsa = self.key.rsa().unwrap().clone();
RSABrowserIDKeyPair::from_rsa(rsa).unwrap() // Yuck
}
}
impl fmt::Debug for RSABrowserIDKeyPair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<rsa_key_pair>")
}
}
impl Serialize for RSABrowserIDKeyPair {
#[allow(clippy::many_single_char_names)] // FIXME
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("RSABrowserIDKeyPair", 2)?;
let rsa = self
.key
.rsa()
.map_err(|err| ser::Error::custom(err.to_string()))?;
let n = rsa
.n()
.to_dec_str()
.map_err(|err| ser::Error::custom(err.to_string()))?;
let e = rsa
.e()
.to_dec_str()
.map_err(|err| ser::Error::custom(err.to_string()))?;
let d = rsa
.d()
.to_dec_str()
.map_err(|err| ser::Error::custom(err.to_string()))?;
state.serialize_field("n", &format!("{}", n))?;
state.serialize_field("e", &format!("{}", e))?;
state.serialize_field("d", &format!("{}", d))?;
if let (Some(p), Some(q)) = (rsa.p(), rsa.q()) {
let p = p
.to_dec_str()
.map_err(|err| ser::Error::custom(err.to_string()))?;
let q = q
.to_dec_str()
.map_err(|err| ser::Error::custom(err.to_string()))?;
state.serialize_field("p", &format!("{}", p))?;
state.serialize_field("q", &format!("{}", q))?;
}
if let (Some(dmp1), Some(dmq1), Some(iqmp)) = (rsa.dmp1(), rsa.dmq1(), rsa.iqmp()) {
let dmp1 = dmp1
.to_dec_str()
.map_err(|err| ser::Error::custom(err.to_string()))?;
let dmq1 = dmq1
.to_dec_str()
.map_err(|err| ser::Error::custom(err.to_string()))?;
let iqmp = iqmp
.to_dec_str()
.map_err(|err| ser::Error::custom(err.to_string()))?;
state.serialize_field("dmp1", &format!("{}", dmp1))?;
state.serialize_field("dmq1", &format!("{}", dmq1))?;
state.serialize_field("iqmp", &format!("{}", iqmp))?;
}
state.end()
}
}
impl<'de> Deserialize<'de> for RSABrowserIDKeyPair {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
enum Field {
N,
E,
D,
P,
Q,
Dmp1,
Dmq1,
Iqmp,
};
impl<'de> Deserialize<'de> for Field {
fn deserialize<D>(deserializer: D) -> std::result::Result<Field, D::Error>
where
D: Deserializer<'de>,
{
struct FieldVisitor;
impl<'de> Visitor<'de> for FieldVisitor {
type Value = Field;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("`n`, `e`, `d`, `p`, `q`, `dmp1`, `dmq1`, `iqmp`")
}
fn visit_str<E>(self, value: &str) -> std::result::Result<Field, E>
where
E: de::Error,
{
match value {
"n" => Ok(Field::N),
"e" => Ok(Field::E),
"d" => Ok(Field::D),
"p" => Ok(Field::P),
"q" => Ok(Field::Q),
"dmp1" => Ok(Field::Dmp1),
"dmq1" => Ok(Field::Dmq1),
"iqmp" => Ok(Field::Iqmp),
_ => Err(de::Error::unknown_field(value, FIELDS)),
}
}
}
deserializer.deserialize_identifier(FieldVisitor)
}
}
struct RSABrowserIDKeyPairVisitor;
impl<'de> Visitor<'de> for RSABrowserIDKeyPairVisitor {
type Value = RSABrowserIDKeyPair;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("struct RSABrowserIDKeyPair")
}
#[allow(clippy::many_single_char_names)] // FIXME
fn visit_map<V>(self, mut map: V) -> std::result::Result<RSABrowserIDKeyPair, V::Error>
where
V: MapAccess<'de>,
{
let mut n = None;
let mut e = None;
let mut d = None;
let mut p = None;
let mut q = None;
let mut dmp1 = None;
let mut dmq1 = None;
let mut iqmp = None;
while let Some(key) = map.next_key()? {
match key {
Field::N => {
if n.is_some() {
return Err(de::Error::duplicate_field("n"));
}
n = Some(map.next_value()?);
}
Field::E => {
if e.is_some() {
return Err(de::Error::duplicate_field("e"));
}
e = Some(map.next_value()?);
}
Field::D => {
if d.is_some() {
return Err(de::Error::duplicate_field("d"));
}
d = Some(map.next_value()?);
}
Field::P => {
if p.is_some() {
return Err(de::Error::duplicate_field("p"));
}
p = Some(map.next_value()?);
}
Field::Q => {
if q.is_some() {
return Err(de::Error::duplicate_field("q"));
}
q = Some(map.next_value()?);
}
Field::Dmp1 => {
if dmp1.is_some() {
return Err(de::Error::duplicate_field("dmp1"));
}
dmp1 = Some(map.next_value()?);
}
Field::Dmq1 => {
if dmq1.is_some() {
return Err(de::Error::duplicate_field("dmq1"));
}
dmq1 = Some(map.next_value()?);
}
Field::Iqmp => {
if iqmp.is_some() {
return Err(de::Error::duplicate_field("iqmp"));
}
iqmp = Some(map.next_value()?);
}
}
}
let n = n.ok_or_else(|| de::Error::missing_field("n"))?;
let n =
BigNum::from_dec_str(n).map_err(|err| de::Error::custom(err.to_string()))?;
let e = e.ok_or_else(|| de::Error::missing_field("e"))?;
let e =
BigNum::from_dec_str(e).map_err(|err| de::Error::custom(err.to_string()))?;
let d = d.ok_or_else(|| de::Error::missing_field("d"))?;
let d =
BigNum::from_dec_str(d).map_err(|err| de::Error::custom(err.to_string()))?;
let mut builder = RsaPrivateKeyBuilder::new(n, e, d)
.map_err(|err| de::Error::custom(err.to_string()))?;
if let (Some(p), Some(q)) = (p, q) {
let p = BigNum::from_dec_str(p)
.map_err(|err| de::Error::custom(err.to_string()))?;
let q = BigNum::from_dec_str(q)
.map_err(|err| de::Error::custom(err.to_string()))?;
builder = builder
.set_factors(p, q)
.map_err(|err| de::Error::custom(err.to_string()))?;
}
if let (Some(dmp1), Some(dmq1), Some(iqmp)) = (dmp1, dmq1, iqmp) {
let dmp1 = BigNum::from_dec_str(dmp1)
.map_err(|err| de::Error::custom(err.to_string()))?;
let dmq1 = BigNum::from_dec_str(dmq1)
.map_err(|err| de::Error::custom(err.to_string()))?;
let iqmp = BigNum::from_dec_str(iqmp)
.map_err(|err| de::Error::custom(err.to_string()))?;
builder = builder
.set_crt_params(dmp1, dmq1, iqmp)
.map_err(|err| de::Error::custom(err.to_string()))?;
}
let rsa = builder.build();
RSABrowserIDKeyPair::from_rsa(rsa).map_err(|err| de::Error::custom(err.to_string()))
}
}
const FIELDS: &[&str] = &["n", "e", "d", "p", "q", "dmp1", "dmq1", "iqmp"];
deserializer.deserialize_struct("RSABrowserIDKeyPair", FIELDS, RSABrowserIDKeyPairVisitor)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_serialize_deserialize() {
let key_pair = RSABrowserIDKeyPair::generate_random(2048).unwrap();
let as_json = serde_json::to_string(&key_pair).unwrap();
let _key_pair: RSABrowserIDKeyPair = serde_json::from_str(&as_json).unwrap();
}
}

Просмотреть файл

@ -5,10 +5,6 @@
#![allow(unknown_lints)]
#![warn(rust_2018_idioms)]
#[cfg(feature = "browserid")]
pub use crate::browser_id::{SyncKeys, WebChannelResponse};
#[cfg(feature = "browserid")]
use crate::login_sm::LoginState;
use crate::{
commands::send_tab::SendTabPayload,
device::{Capability as DeviceCapability, Device},
@ -24,8 +20,6 @@ use std::{
};
use url::Url;
#[cfg(feature = "browserid")]
mod browser_id;
mod commands;
mod config;
pub mod device;
@ -37,8 +31,6 @@ pub mod msg_types {
include!(concat!(env!("OUT_DIR"), "/msg_types.rs"));
}
mod http_client;
#[cfg(feature = "browserid")]
mod login_sm;
mod oauth;
mod profile;
mod scoped_keys;
@ -47,9 +39,6 @@ pub mod send_tab;
mod state_persistence;
mod util;
#[cfg(feature = "browserid")]
type FxAClient = dyn http_client::browser_id::FxABrowserIDClient + Sync + Send;
#[cfg(not(feature = "browserid"))]
type FxAClient = dyn http_client::FxAClient + Sync + Send;
pub struct FirefoxAccount {
@ -67,8 +56,6 @@ pub struct FirefoxAccount {
#[derive(Clone, Serialize, Deserialize)]
pub(crate) struct StateV2 {
config: Config,
#[cfg(feature = "browserid")]
login_state: LoginState,
refresh_token: Option<RefreshToken>,
scoped_keys: HashMap<String, ScopedKey>,
last_handled_command: Option<u64>,
@ -97,8 +84,6 @@ impl FirefoxAccount {
pub fn with_config(config: Config) -> Self {
Self::from_state(StateV2 {
config,
#[cfg(feature = "browserid")]
login_state: LoginState::Unknown,
refresh_token: None,
scoped_keys: HashMap::new(),
last_handled_command: None,

Просмотреть файл

@ -1,294 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::{
error::*,
http_client::{self, browser_id::rsa::RSABrowserIDKeyPair, *},
util::{now, Xorable},
Config,
};
use serde_derive::*;
use std::sync::Arc;
pub struct LoginStateMachine<'a> {
config: &'a Config,
client: Arc<dyn http_client::browser_id::FxABrowserIDClient>,
}
impl<'a> LoginStateMachine<'a> {
pub fn new(
config: &'a Config,
client: Arc<dyn http_client::browser_id::FxABrowserIDClient>,
) -> LoginStateMachine<'_> {
LoginStateMachine { config, client }
}
pub fn advance(&self, from: LoginState) -> Result<LoginState> {
let mut cur_state = from;
loop {
let cur_state_discriminant = std::mem::discriminant(&cur_state);
let new_state = self.advance_one(cur_state)?;
let new_state_discriminant = std::mem::discriminant(&new_state);
cur_state = new_state;
if cur_state_discriminant == new_state_discriminant {
break;
}
}
Ok(cur_state)
}
fn advance_one(&self, from: LoginState) -> Result<LoginState> {
log::info!("advancing from state {:?}", from);
match from {
LoginState::Married(state) => {
let now = now();
log::debug!("Checking key pair and certificate freshness.");
if now > state.token_keys_and_key_pair.key_pair_expires_at {
log::info!("Key pair has expired. Transitioning to CohabitingBeforeKeyPair.");
Ok(LoginState::CohabitingBeforeKeyPair(
state.token_keys_and_key_pair.token_and_keys,
))
} else if now > state.certificate_expires_at {
log::info!("Certificate has expired. Transitioning to CohabitingAfterKeyPair.");
Ok(LoginState::CohabitingAfterKeyPair(
state.token_keys_and_key_pair,
))
} else {
log::info!("Key pair and certificate are fresh; staying Married.");
Ok(LoginState::Married(state)) // same
}
}
LoginState::CohabitingBeforeKeyPair(state) => {
log::debug!("Generating key pair.");
let key_pair = match browser_id::key_pair(2048) {
Ok(key_pair) => key_pair,
Err(_) => {
log::error!("Failed to generate key pair! Transitioning to Separated.");
return Ok(LoginState::Separated(state.base));
}
};
log::info!("Key pair generated! Transitioning to CohabitingAfterKeyPairState.");
let new_state = CohabitingAfterKeyPairState {
token_and_keys: state,
key_pair,
key_pair_expires_at: now() + 30 * 24 * 3600 * 1000,
};
Ok(LoginState::CohabitingAfterKeyPair(new_state))
}
LoginState::CohabitingAfterKeyPair(state) => {
log::debug!("Signing public key.");
let resp = self.client.sign(
&self.config,
&state.token_and_keys.session_token,
&state.key_pair,
);
match resp {
Ok(resp) => {
log::info!("Signed public key! Transitioning to Married.");
let new_state = MarriedState {
token_keys_and_key_pair: state,
certificate: resp.certificate,
certificate_expires_at: now() + 24 * 3600 * 1000,
};
Ok(LoginState::Married(new_state))
}
Err(e) => {
if let ErrorKind::RemoteError { .. } = e.kind() {
log::error!("Server error: {:?}. Transitioning to Separated.", e);
Ok(LoginState::Separated(state.token_and_keys.base))
} else {
log::error!(
"Unknown error: ({:?}). Assuming transient, not transitioning.",
e
);
Ok(LoginState::CohabitingAfterKeyPair(state))
}
}
}
}
LoginState::EngagedBeforeVerified(state) => {
self.handle_ready_for_key_state(LoginState::EngagedBeforeVerified, state)
}
LoginState::EngagedAfterVerified(state) => {
self.handle_ready_for_key_state(LoginState::EngagedAfterVerified, state)
}
LoginState::Separated(_) => Ok(from),
LoginState::Unknown => Ok(from),
}
}
fn handle_ready_for_key_state<F: FnOnce(ReadyForKeysState) -> LoginState>(
&self,
same: F,
state: ReadyForKeysState,
) -> Result<LoginState> {
log::debug!("Fetching keys.");
let resp = self.client.keys(&self.config, &state.key_fetch_token);
match resp {
Ok(resp) => {
let kb = match resp.wrap_kb.xored_with(&state.unwrap_kb) {
Ok(kb) => kb,
Err(_) => {
log::error!("Failed to unwrap keys response! Transitioning to Separated.");
return Ok(same(state));
}
};
log::info!("Unwrapped keys response. Transition to CohabitingBeforeKeyPair.");
let sync_key = browser_id::derive_sync_key(&kb)?;
let xcs = browser_id::compute_client_state(&kb)?;
Ok(LoginState::CohabitingBeforeKeyPair(TokenAndKeysState {
base: state.base,
session_token: state.session_token.to_vec(),
sync_key,
xcs,
}))
}
Err(e) => match e.kind() {
ErrorKind::RemoteError { errno: 104, .. } => {
log::warn!("Account not yet verified, not transitioning.");
Ok(same(state))
}
ErrorKind::RemoteError { .. } => {
log::error!("Server error: {:?}. Transitioning to Separated.", e);
Ok(LoginState::Separated(state.base))
}
_ => {
log::error!(
"Unknown error: ({:?}). Assuming transient, not transitioning.",
e
);
Ok(same(state))
}
},
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum LoginState {
Married(MarriedState),
CohabitingBeforeKeyPair(CohabitingBeforeKeyPairState),
CohabitingAfterKeyPair(CohabitingAfterKeyPairState),
EngagedBeforeVerified(EngagedBeforeVerifiedState),
EngagedAfterVerified(EngagedAfterVerifiedState),
Separated(SeparatedState),
Unknown, // If a client never uses the session_token flows, we will be in this state.
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct MarriedState {
token_keys_and_key_pair: TokenKeysAndKeyPairState,
certificate: String,
certificate_expires_at: u64,
}
pub type CohabitingBeforeKeyPairState = TokenAndKeysState;
pub type CohabitingAfterKeyPairState = TokenKeysAndKeyPairState;
pub type EngagedBeforeVerifiedState = ReadyForKeysState;
pub type EngagedAfterVerifiedState = ReadyForKeysState;
pub type SeparatedState = BaseState;
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ReadyForKeysState {
base: BaseState,
session_token: Vec<u8>,
key_fetch_token: Vec<u8>,
unwrap_kb: Vec<u8>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TokenAndKeysState {
base: BaseState,
session_token: Vec<u8>,
sync_key: Vec<u8>,
xcs: String,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TokenKeysAndKeyPairState {
token_and_keys: TokenAndKeysState,
key_pair: RSABrowserIDKeyPair,
key_pair_expires_at: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct BaseState {
uid: String,
email: String,
}
impl ReadyForKeysState {
pub fn new(
uid: String,
email: String,
session_token: Vec<u8>,
key_fetch_token: Vec<u8>,
unwrap_kb: Vec<u8>,
) -> ReadyForKeysState {
ReadyForKeysState {
base: BaseState { uid, email },
session_token,
key_fetch_token,
unwrap_kb,
}
}
}
pub trait SessionTokenState {
fn session_token(&self) -> &[u8];
}
impl SessionTokenState for ReadyForKeysState {
fn session_token(&self) -> &[u8] {
&self.session_token
}
}
impl SessionTokenState for TokenAndKeysState {
fn session_token(&self) -> &[u8] {
&self.session_token
}
}
impl SessionTokenState for TokenKeysAndKeyPairState {
fn session_token(&self) -> &[u8] {
self.token_and_keys.session_token()
}
}
impl SessionTokenState for MarriedState {
fn session_token(&self) -> &[u8] {
self.token_keys_and_key_pair.session_token()
}
}
impl MarriedState {
pub fn key_pair(&self) -> &RSABrowserIDKeyPair {
&self.token_keys_and_key_pair.key_pair
}
pub fn certificate(&self) -> &str {
&self.certificate
}
pub fn sync_key(&self) -> &[u8] {
&self.token_keys_and_key_pair.token_and_keys.sync_key
}
pub fn xcs(&self) -> &str {
&self.token_keys_and_key_pair.token_and_keys.xcs
}
}
impl LoginState {
pub fn into_separated(self) -> Self {
use self::LoginState::*;
match self {
Married(state) => Separated(state.token_keys_and_key_pair.token_and_keys.base),
CohabitingBeforeKeyPair(state) => Separated(state.base),
CohabitingAfterKeyPair(state) => Separated(state.token_and_keys.base),
EngagedBeforeVerified(state) => Separated(state.base),
EngagedAfterVerified(state) => Separated(state.base),
Separated(state) => Separated(state),
Unknown => panic!("Insane state."),
}
}
}

Просмотреть файл

@ -25,17 +25,15 @@ impl FirefoxAccount {
return Err(ErrorKind::IllegalState("Session Token is already set.").into());
}
// Trade our session token for a refresh token.
self.state.session_token = Some(session_token.to_string());
let session_token = hex::decode(&session_token)?;
let duplicate_session = self
.client
.duplicate_session(&self.state.config, &session_token)?;
let duplicated_session_token = duplicate_session.session_token;
let duplicated_session_token_bytes = hex::decode(duplicated_session_token)?;
let oauth_response = self.client.oauth_token_from_session_token(
self.state.session_token = Some(duplicated_session_token.clone());
let oauth_response = self.client.oauth_tokens_from_session_token(
&self.state.config,
&duplicated_session_token_bytes,
&duplicated_session_token,
&[scopes::PROFILE, scopes::OLD_SYNC],
)?;
self.handle_oauth_response(oauth_response, None)?;
@ -45,9 +43,11 @@ impl FirefoxAccount {
let k_sync = base64::encode_config(&k_sync, base64::URL_SAFE_NO_PAD);
let k_xcs = hex::decode(k_xcs)?;
let k_xcs = base64::encode_config(&k_xcs, base64::URL_SAFE_NO_PAD);
let scoped_key_data =
self.client
.scoped_key_data(&self.state.config, &session_token, scopes::OLD_SYNC)?;
let scoped_key_data = self.client.scoped_key_data(
&self.state.config,
&duplicated_session_token,
scopes::OLD_SYNC,
)?;
let oldsync_key_data = scoped_key_data.get(scopes::OLD_SYNC).ok_or_else(|| {
ErrorKind::IllegalState("The session token doesn't have access to kSync!")
})?;

Просмотреть файл

@ -49,23 +49,14 @@ impl FirefoxAccount {
return Err(ErrorKind::NoCachedToken(scope.to_string()).into());
}
}
None => {
#[cfg(feature = "browserid")]
{
match Self::session_token_from_state(&self.state.login_state) {
Some(session_token) => self.client.oauth_token_with_session_token(
&self.state.config,
session_token,
&[scope],
)?,
None => return Err(ErrorKind::NoCachedToken(scope.to_string()).into()),
}
}
#[cfg(not(feature = "browserid"))]
{
return Err(ErrorKind::NoCachedToken(scope.to_string()).into());
}
}
None => match self.state.session_token {
Some(ref session_token) => self.client.oauth_token_with_session_token(
&self.state.config,
&session_token,
&[scope],
)?,
None => return Err(ErrorKind::NoCachedToken(scope.to_string()).into()),
},
};
let since_epoch = SystemTime::now()
.duration_since(UNIX_EPOCH)
@ -167,7 +158,7 @@ impl FirefoxAccount {
Some(oauth_flow) => oauth_flow,
None => return Err(ErrorKind::UnknownOAuthState.into()),
};
let resp = self.client.oauth_token_with_code(
let resp = self.client.oauth_tokens_from_code(
&self.state.config,
&code,
&oauth_flow.code_verifier,

Просмотреть файл

@ -77,7 +77,6 @@ impl FirefoxAccount {
}
}
#[cfg(not(feature = "browserid"))] // Otherwise gotta impl FxABrowserIDClient too...
#[cfg(test)]
mod tests {
use super::*;

Просмотреть файл

@ -81,8 +81,6 @@ impl From<StateV1> for Result<StateV2> {
state.client_id,
state.redirect_uri,
),
#[cfg(feature = "browserid")]
login_state: super::login_sm::LoginState::Unknown,
refresh_token,
scoped_keys: all_scoped_keys,
last_handled_command: None,
@ -99,8 +97,6 @@ struct StateV1 {
client_id: String,
redirect_uri: String,
config: V1Config,
// #[cfg(feature = "browserid")]
// login_state: LoginState, // Wasn't used anyway
oauth_cache: HashMap<String, V1AuthInfo>,
}

Просмотреть файл

@ -15,7 +15,7 @@ sync15 = { path = "../sync15" }
serde = "1.0.94"
serde_derive = "1.0.94"
serde_json = "1.0.40"
log = "0.4.6"
log = "0.4.7"
lazy_static = "1.1.0"
url = "1.7.1"
failure = "0.1.3"

Просмотреть файл

@ -45,6 +45,9 @@ configurations {
}
dependencies {
// Part of the public API.
api project(':sync15-library')
jnaForTest "net.java.dev.jna:jna:$jna_version@jar"
implementation "net.java.dev.jna:jna:$jna_version@aar"

Просмотреть файл

@ -7,6 +7,7 @@ package mozilla.appservices.logins
import com.sun.jna.Pointer
import mozilla.appservices.logins.rust.PasswordSyncAdapter
import mozilla.appservices.logins.rust.RustError
import mozilla.appservices.sync15.SyncTelemetryPing
import java.util.concurrent.atomic.AtomicLong
/**
@ -92,8 +93,8 @@ class DatabaseLoginsStorage(private val dbPath: String) : AutoCloseable, LoginsS
}
@Throws(LoginsStorageException::class)
override fun sync(syncInfo: SyncUnlockInfo) {
rustCallWithLock { raw, error ->
override fun sync(syncInfo: SyncUnlockInfo): SyncTelemetryPing {
val json = rustCallWithLock { raw, error ->
PasswordSyncAdapter.INSTANCE.sync15_passwords_sync(
raw,
syncInfo.kid,
@ -101,8 +102,9 @@ class DatabaseLoginsStorage(private val dbPath: String) : AutoCloseable, LoginsS
syncInfo.syncKey,
syncInfo.tokenserverURL,
error
)
)?.getAndConsumeRustString()
}
return SyncTelemetryPing.fromJSONString(json)
}
@Throws(LoginsStorageException::class)

Просмотреть файл

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package mozilla.appservices.logins
import mozilla.appservices.sync15.SyncTelemetryPing
class SyncUnlockInfo(
val kid: String,
@ -81,7 +82,7 @@ interface LoginsStorage : AutoCloseable {
* @throws [LoginsStorageException] On unexpected errors (IO failure, rust panics, etc)
*/
@Throws(LoginsStorageException::class)
fun sync(syncInfo: SyncUnlockInfo)
fun sync(syncInfo: SyncUnlockInfo): SyncTelemetryPing
/**
* Delete all locally stored login sync metadata (last sync timestamps, etc).

Просмотреть файл

@ -6,6 +6,7 @@ package mozilla.appservices.logins
import android.util.Log
import java.util.UUID
import mozilla.appservices.sync15.SyncTelemetryPing
private enum class LoginsStorageState {
Unlocked,
@ -88,9 +89,10 @@ class MemoryLoginsStorage(private var list: List<ServerPassword>) : AutoCloseabl
@Synchronized
@Throws(LoginsStorageException::class)
override fun sync(syncInfo: SyncUnlockInfo) {
override fun sync(syncInfo: SyncUnlockInfo): SyncTelemetryPing {
checkUnlocked()
Log.w("MemoryLoginsStorage", "Not syncing because this implementation can not sync")
return SyncTelemetryPing(version = 1, uid = "uid", events = emptyList(), syncs = emptyList())
}
@Synchronized

Просмотреть файл

@ -43,6 +43,7 @@ internal interface PasswordSyncAdapter : Library {
// return json array
fun sync15_passwords_get_all(handle: LoginsDbHandle, error: RustError.ByReference): Pointer?
// Returns a JSON string containing a sync ping.
fun sync15_passwords_sync(
handle: LoginsDbHandle,
key_id: String,
@ -50,7 +51,7 @@ internal interface PasswordSyncAdapter : Library {
sync_key: String,
token_server_url: String,
error: RustError.ByReference
)
): Pointer?
fun sync15_passwords_wipe(handle: LoginsDbHandle, error: RustError.ByReference)
fun sync15_passwords_wipe_local(handle: LoginsDbHandle, error: RustError.ByReference)

Просмотреть файл

@ -95,10 +95,10 @@ pub extern "C" fn sync15_passwords_sync(
sync_key: FfiStr<'_>,
tokenserver_url: FfiStr<'_>,
error: &mut ExternError,
) {
) -> *mut c_char {
log::debug!("sync15_passwords_sync");
ENGINES.call_with_result(error, handle, |state| -> Result<()> {
state.sync(
ENGINES.call_with_result(error, handle, |state| -> Result<_> {
let ping = state.sync(
&sync15::Sync15StorageClientInit {
key_id: key_id.into_string(),
access_token: access_token.into_string(),
@ -106,7 +106,7 @@ pub extern "C" fn sync15_passwords_sync(
},
&sync15::KeyBundle::from_ksync_base64(sync_key.as_str())?,
)?;
Ok(())
Ok(ping)
})
}

Просмотреть файл

@ -136,11 +136,12 @@ open class LoginsStorage {
close()
}
/// Synchronize with the server.
open func sync(unlockInfo: SyncUnlockInfo) throws {
try queue.sync {
/// Synchronize with the server. Returns the sync telemetry "ping" as a JSON
/// string.
open func sync(unlockInfo: SyncUnlockInfo) throws -> String {
return try queue.sync {
let engine = try self.getUnlocked()
try LoginsStoreError.unwrap { err in
let ptr = try LoginsStoreError.unwrap { err in
sync15_passwords_sync(engine,
unlockInfo.kid,
unlockInfo.fxaAccessToken,
@ -148,6 +149,7 @@ open class LoginsStorage {
unlockInfo.tokenserverURL,
err)
}
return String(freeingRustString: ptr)
}
}

Просмотреть файл

@ -52,12 +52,12 @@ char *_Nullable sync15_passwords_get_by_id(Sync15PasswordEngineHandle handle,
char *_Nullable sync15_passwords_get_all(Sync15PasswordEngineHandle handle,
Sync15PasswordsError *_Nonnull error_out);
void sync15_passwords_sync(Sync15PasswordEngineHandle handle,
char const *_Nonnull key_id,
char const *_Nonnull access_token,
char const *_Nonnull sync_key,
char const *_Nonnull token_server_url,
Sync15PasswordsError *_Nonnull error);
char *_Nullable sync15_passwords_sync(Sync15PasswordEngineHandle handle,
char const *_Nonnull key_id,
char const *_Nonnull access_token,
char const *_Nonnull sync_key,
char const *_Nonnull token_server_url,
Sync15PasswordsError *_Nonnull error);
void sync15_passwords_wipe(Sync15PasswordEngineHandle handle,
Sync15PasswordsError *_Nonnull error);

Просмотреть файл

@ -117,6 +117,9 @@ impl PasswordEngine {
self.db.set_global_state(&disk_cached_state)?;
// for b/w compat reasons, we do some dances with the result.
// XXX - note that this means telemetry isn't going to be reported back
// to the app - we need to check with lockwise about whether they really
// need these failures to be reported or whether we can loosen this.
if let Err(e) = result.result {
return Err(e.into());
}

Просмотреть файл

@ -32,6 +32,8 @@ bytes = "0.4.11"
dogear = "0.2.6"
interrupt = { path = "../support/interrupt" }
error-support = { path = "../support/error" }
sync-guid = { path = "../support/guid", features = ["rusqlite_support", "random"]}
[dependencies.rusqlite]
version = "0.19.0"

Просмотреть файл

@ -12,8 +12,9 @@ use places::storage::bookmarks::{
fetch_tree, insert_tree, BookmarkNode, BookmarkRootGuid, BookmarkTreeNode, FetchDepth,
FolderNode, SeparatorNode,
};
use places::types::{BookmarkType, SyncGuid, Timestamp};
use places::types::{BookmarkType, Timestamp};
use places::{ConnectionType, PlacesApi, PlacesDb};
use sync_guid::Guid as SyncGuid;
use failure::Fail;
use serde_derive::*;

Просмотреть файл

@ -24,6 +24,7 @@ bytes = "0.4.12"
viaduct = { path = "../../viaduct" }
interrupt = { path = "../../support/interrupt" }
sql-support = { path = "../../support/sql" }
sync-guid = { path = "../../support/guid" }
[dependencies.sync15]
path = "../../sync15"

Просмотреть файл

@ -15,11 +15,12 @@ use ffi_support::{
use places::error::*;
use places::msg_types::BookmarkNodeList;
use places::storage::bookmarks;
use places::types::{SyncGuid, VisitTransitionSet};
use places::types::VisitTransitionSet;
use places::{storage, ConnectionType, PlacesApi, PlacesDb};
use sql_support::SqlInterruptHandle;
use std::os::raw::c_char;
use std::sync::Arc;
use sync_guid::Guid as SyncGuid;
use places::api::matcher::{self, match_url, search_frecent, SearchParams};
@ -467,7 +468,7 @@ pub extern "C" fn bookmarks_get_tree(
) -> ByteBuffer {
log::debug!("bookmarks_get_tree");
CONNECTIONS.call_with_result(error, handle, |conn| -> places::Result<_> {
let root_id = SyncGuid(guid.into());
let root_id = SyncGuid::from(guid.as_str());
Ok(bookmarks::public_node::fetch_public_tree(conn, &root_id)?)
})
}
@ -481,7 +482,7 @@ pub extern "C" fn bookmarks_get_by_guid(
) -> ByteBuffer {
log::debug!("bookmarks_get_by_guid");
CONNECTIONS.call_with_result(error, handle, |conn| -> places::Result<_> {
let guid = SyncGuid(guid.into());
let guid = SyncGuid::from(guid.as_str());
Ok(bookmarks::public_node::fetch_bookmark(
conn,
&guid,
@ -515,7 +516,7 @@ pub unsafe extern "C" fn bookmarks_insert(
let bookmark: BookmarkNode = prost::Message::decode(buffer)?;
let insertable = bookmark.into_insertable()?;
let guid = bookmarks::insert_bookmark(conn, &insertable)?;
Ok(guid.0)
Ok(guid.into_string())
})
}
@ -540,7 +541,7 @@ pub unsafe extern "C" fn bookmarks_update(
pub extern "C" fn bookmarks_delete(handle: u64, id: FfiStr<'_>, error: &mut ExternError) -> u8 {
log::debug!("bookmarks_delete");
CONNECTIONS.call_with_result(error, handle, |conn| -> places::Result<_> {
let guid = SyncGuid(id.into_string());
let guid = SyncGuid::from(id.as_str());
let did_delete = bookmarks::delete_bookmark(conn, &guid)?;
Ok(did_delete)
})

Просмотреть файл

@ -13,11 +13,11 @@ use crate::storage::{
tags::{validate_tag, ValidatedTag},
URL_LENGTH_MAX,
};
use crate::types::SyncGuid;
use rusqlite::Connection;
use sql_support::{self, ConnExt};
use std::iter;
use sync15::ServerTimestamp;
use sync_guid::Guid as SyncGuid;
use url::Url;
// From Desktop's Ci.nsINavHistoryQueryOptions, but we define it as a str
@ -92,7 +92,7 @@ impl<'a> IncomingApplicator<'a> {
END
)"#,
&[
(":guid", &b.record_id.as_guid().as_ref()),
(":guid", &b.record_id.as_guid().as_str()),
(":parentGuid", &b.parent_record_id.as_ref().map(BookmarkRecordId::as_guid)),
(":serverModified", &(modified.as_millis() as i64)),
(":kind", &SyncedBookmarkKind::Bookmark),
@ -121,7 +121,7 @@ impl<'a> IncomingApplicator<'a> {
WHERE guid = :guid),
(SELECT id FROM moz_tags
WHERE tag = :tag))",
&[(":guid", &b.record_id.as_guid().as_ref()), (":tag", t)],
&[(":guid", &b.record_id.as_guid().as_str()), (":tag", t)],
)?;
}
};
@ -136,7 +136,7 @@ impl<'a> IncomingApplicator<'a> {
VALUES(:guid, :parentGuid, :serverModified, 1, :kind,
:dateAdded, NULLIF(:title, ""))"#,
&[
(":guid", &f.record_id.as_guid().as_ref()),
(":guid", &f.record_id.as_guid().as_str()),
(":parentGuid", &f.parent_record_id.as_ref().map(BookmarkRecordId::as_guid)),
(":serverModified", &(modified.as_millis() as i64)),
(":kind", &SyncedBookmarkKind::Folder),
@ -171,7 +171,7 @@ impl<'a> IncomingApplicator<'a> {
&sql,
iter::once(&f.record_id)
.chain(chunk.iter())
.map(|record_id| record_id.as_guid().as_ref()),
.map(|record_id| record_id.as_guid().as_str()),
)?;
Ok(())
},
@ -273,7 +273,7 @@ impl<'a> IncomingApplicator<'a> {
)
)"#,
&[
(":guid", &q.record_id.as_guid().as_ref()),
(":guid", &q.record_id.as_guid().as_str()),
(":parentGuid", &q.parent_record_id.as_ref().map(BookmarkRecordId::as_guid)),
(":serverModified", &(modified.as_millis() as i64)),
(":kind", &SyncedBookmarkKind::Query),
@ -324,7 +324,7 @@ impl<'a> IncomingApplicator<'a> {
VALUES(:guid, :parentGuid, :serverModified, 1, :kind,
:dateAdded, :title, :feedUrl, :siteUrl, :validity)",
&[
(":guid", &l.record_id.as_guid().as_ref()),
(":guid", &l.record_id.as_guid().as_str()),
(
":parentGuid",
&l.parent_record_id.as_ref().map(BookmarkRecordId::as_guid),
@ -348,7 +348,7 @@ impl<'a> IncomingApplicator<'a> {
VALUES(:guid, :parentGuid, :serverModified, 1, :kind,
:dateAdded)",
&[
(":guid", &s.record_id.as_guid().as_ref()),
(":guid", &s.record_id.as_guid().as_str()),
(
":parentGuid",
&s.parent_record_id.as_ref().map(BookmarkRecordId::as_guid),
@ -469,7 +469,7 @@ mod tests {
#[test]
fn test_apply_folder() {
let children = (1..sql_support::default_max_variable_number() * 2)
.map(|i| SyncGuid(format!("{:A>12}", i)))
.map(|i| SyncGuid::from(format!("{:A>12}", i)))
.collect::<Vec<_>>();
let value = serde_json::to_value(BookmarkItemRecord::from(FolderRecord {
record_id: BookmarkRecordId::from_payload_id("folderAAAAAA".into()),

Просмотреть файл

@ -12,9 +12,9 @@ mod tests;
use crate::db::PlacesDb;
use crate::error::*;
use crate::storage::bookmarks::{BookmarkRootGuid, USER_CONTENT_ROOTS};
use crate::types::SyncGuid;
use rusqlite::types::{ToSql, ToSqlOutput};
use rusqlite::Result as RusqliteResult;
use sync_guid::Guid as SyncGuid;
/// Sets up the syncable roots. All items in `moz_bookmarks_synced` descend
/// from these roots.
@ -33,8 +33,8 @@ pub fn create_synced_bookmark_roots(db: &PlacesDb) -> Result<()> {
INSERT OR IGNORE INTO moz_bookmarks_synced_structure(
guid, parentGuid, position)
VALUES('{guid}', '{parent_guid}', {pos});",
guid = guid.as_ref(),
parent_guid = parent_guid.as_ref(),
guid = guid.as_str(),
parent_guid = parent_guid.as_str(),
kind = SyncedBookmarkKind::Folder as u8,
pos = pos
))?;

Просмотреть файл

@ -4,12 +4,13 @@
use std::fmt;
use crate::{storage::bookmarks::BookmarkRootGuid, types::SyncGuid};
use crate::storage::bookmarks::BookmarkRootGuid;
use serde::{
de::{Deserialize, Deserializer, Visitor},
ser::{Serialize, Serializer},
};
use serde_derive::*;
use sync_guid::Guid as SyncGuid;
/// A bookmark record ID. Bookmark record IDs are the same as Places GUIDs,
/// except for:
@ -35,7 +36,7 @@ impl BookmarkRecordId {
"toolbar" => BookmarkRootGuid::Toolbar.as_guid(),
"unfiled" => BookmarkRootGuid::Unfiled.as_guid(),
"mobile" => BookmarkRootGuid::Mobile.as_guid(),
_ => SyncGuid(payload_id),
_ => SyncGuid::from(payload_id),
})
}
@ -53,7 +54,7 @@ impl BookmarkRecordId {
pub fn into_payload_id(self) -> String {
self.root_payload_id()
.map(Into::into)
.unwrap_or_else(|| (self.0).0)
.unwrap_or_else(|| (self.0).into_string())
}
/// Returns a reference to the GUID for this record ID.

Просмотреть файл

@ -14,7 +14,7 @@ use crate::db::PlacesDb;
use crate::error::*;
use crate::frecency::{calculate_frecency, DEFAULT_FRECENCY_SETTINGS};
use crate::storage::{bookmarks::BookmarkRootGuid, delete_meta, get_meta, put_meta};
use crate::types::{BookmarkType, SyncGuid, SyncStatus, Timestamp};
use crate::types::{BookmarkType, SyncStatus, Timestamp};
use dogear::{
self, AbortSignal, Content, Deletion, Item, MergedDescendant, MergedRoot, TelemetryEvent, Tree,
UploadReason,
@ -30,6 +30,7 @@ use sync15::{
telemetry, CollSyncIds, CollectionRequest, IncomingChangeset, OutgoingChangeset, Payload,
ServerTimestamp, Store, StoreSyncAssociation,
};
use sync_guid::Guid as SyncGuid;
pub const LAST_SYNC_META_KEY: &str = "bookmarks_last_sync_time";
// Note that all engines in this crate should use a *different* meta key
// for the global sync ID, because engines are reset individually.
@ -254,7 +255,7 @@ impl<'a> BookmarksStore<'a> {
(s.syncChangeCounter > 0 OR w.id NOT NULL)",
local_items_fragment = LocalItemsFragment("localItems"),
kind = item_kind_fragment("s.type", UrlOrPlaceIdFragment::Url("h.url")),
root_guid = BookmarkRootGuid::Root.as_guid().as_ref(),
root_guid = BookmarkRootGuid::Root.as_guid().as_str(),
))?;
// Record the child GUIDs of locally changed folders, which we use to
@ -666,7 +667,7 @@ struct Driver {
impl dogear::Driver for Driver {
fn generate_new_guid(&self, _invalid_guid: &dogear::Guid) -> dogear::Result<dogear::Guid> {
Ok(SyncGuid::new().into())
Ok(SyncGuid::random().as_str().into())
}
fn record_telemetry_event(&self, event: TelemetryEvent) {
@ -758,7 +759,7 @@ impl<'a> Merger<'a> {
fn local_row_to_item(&self, row: &Row<'_>) -> Result<Item> {
let guid = row.get::<_, SyncGuid>("guid")?;
let kind = SyncedBookmarkKind::from_u8(row.get("kind")?)?;
let mut item = Item::new(guid.into(), kind.into());
let mut item = Item::new(guid.as_str().into(), kind.into());
// Note that this doesn't account for local clock skew.
let age = self
.local_time
@ -773,7 +774,7 @@ impl<'a> Merger<'a> {
fn remote_row_to_item(&self, row: &Row<'_>) -> Result<Item> {
let guid = row.get::<_, SyncGuid>("guid")?;
let kind = SyncedBookmarkKind::from_u8(row.get("kind")?)?;
let mut item = Item::new(guid.into(), kind.into());
let mut item = Item::new(guid.as_str().into(), kind.into());
// note that serverModified in this table is an int with ms, which isn't
// the format of a ServerTimestamp - so we convert it into a number
// of seconds before creating a ServerTimestamp and doing duration_since.
@ -817,7 +818,7 @@ impl<'a> dogear::Store<Error> for Merger<'a> {
let parent_guid = row.get::<_, SyncGuid>("parentGuid")?;
builder
.item(self.local_row_to_item(&row)?)?
.by_structure(&parent_guid.into())?;
.by_structure(&parent_guid.as_str().into())?;
}
let mut tree = Tree::try_from(builder)?;
@ -831,7 +832,7 @@ impl<'a> dogear::Store<Error> for Merger<'a> {
while let Some(row) = results.next()? {
self.store.interruptee.err_if_interrupted()?;
let guid = row.get::<_, SyncGuid>("guid")?;
tree.note_deleted(guid.into());
tree.note_deleted(guid.as_str().into());
}
Ok(tree)
@ -853,7 +854,7 @@ impl<'a> dogear::Store<Error> for Merger<'a> {
WHERE v.guid IS NULL AND
p.guid <> '{root_guid}' AND
b.syncStatus <> {sync_status}",
root_guid = BookmarkRootGuid::Root.as_guid().as_ref(),
root_guid = BookmarkRootGuid::Root.as_guid().as_str(),
sync_status = SyncStatus::Normal as u8
);
let mut stmt = self.store.db.prepare(&sql)?;
@ -882,7 +883,7 @@ impl<'a> dogear::Store<Error> for Merger<'a> {
}
};
let guid = row.get::<_, SyncGuid>("guid")?;
contents.insert(guid.into(), content);
contents.insert(guid.as_str().into(), content);
}
Ok(contents)
@ -898,7 +899,7 @@ impl<'a> dogear::Store<Error> for Merger<'a> {
FROM moz_bookmarks_synced
WHERE NOT isDeleted AND
guid = '{root_guid}'",
root_guid = BookmarkRootGuid::Root.as_guid().as_ref()
root_guid = BookmarkRootGuid::Root.as_guid().as_str()
);
let mut builder = self
.store
@ -921,7 +922,7 @@ impl<'a> dogear::Store<Error> for Merger<'a> {
WHERE NOT isDeleted AND
guid <> '{root_guid}'
ORDER BY guid",
root_guid = BookmarkRootGuid::Root.as_guid().as_ref()
root_guid = BookmarkRootGuid::Root.as_guid().as_str()
);
let mut stmt = self.store.db.prepare(&sql)?;
let mut results = stmt.query(NO_PARAMS)?;
@ -929,7 +930,7 @@ impl<'a> dogear::Store<Error> for Merger<'a> {
self.store.interruptee.err_if_interrupted()?;
let p = builder.item(self.remote_row_to_item(&row)?)?;
if let Some(parent_guid) = row.get::<_, Option<SyncGuid>>("parentGuid")? {
p.by_parent_guid(parent_guid.into())?;
p.by_parent_guid(parent_guid.as_str().into())?;
}
}
@ -937,7 +938,7 @@ impl<'a> dogear::Store<Error> for Merger<'a> {
"SELECT guid, parentGuid FROM moz_bookmarks_synced_structure
WHERE guid <> '{root_guid}'
ORDER BY parentGuid, position",
root_guid = BookmarkRootGuid::Root.as_guid().as_ref()
root_guid = BookmarkRootGuid::Root.as_guid().as_str()
);
let mut stmt = self.store.db.prepare(&sql)?;
let mut results = stmt.query(NO_PARAMS)?;
@ -946,8 +947,8 @@ impl<'a> dogear::Store<Error> for Merger<'a> {
let guid = row.get::<_, SyncGuid>("guid")?;
let parent_guid = row.get::<_, SyncGuid>("parentGuid")?;
builder
.parent_for(&guid.into())
.by_children(&parent_guid.into())?;
.parent_for(&guid.as_str().into())
.by_children(&parent_guid.as_str().into())?;
}
let mut tree = Tree::try_from(builder)?;
@ -961,7 +962,7 @@ impl<'a> dogear::Store<Error> for Merger<'a> {
while let Some(row) = results.next()? {
self.store.interruptee.err_if_interrupted()?;
let guid = row.get::<_, SyncGuid>("guid")?;
tree.note_deleted(guid.into());
tree.note_deleted(guid.as_str().into());
}
Ok(tree)
@ -983,7 +984,7 @@ impl<'a> dogear::Store<Error> for Merger<'a> {
v.needsMerge AND
b.guid IS NULL AND
s.parentGuid <> '{root_guid}'",
root_guid = BookmarkRootGuid::Root.as_guid().as_ref()
root_guid = BookmarkRootGuid::Root.as_guid().as_str()
);
let mut stmt = self.store.db.prepare(&sql)?;
let mut results = stmt.query(NO_PARAMS)?;
@ -1008,7 +1009,7 @@ impl<'a> dogear::Store<Error> for Merger<'a> {
_ => continue,
};
let guid = row.get::<_, SyncGuid>("guid")?;
contents.insert(guid.into(), content);
contents.insert(guid.as_str().into(), content);
}
Ok(contents)
@ -1064,7 +1065,7 @@ impl<'a> fmt::Display for LocalItemsFragment<'a> {
FROM moz_bookmarks b
JOIN {name} s ON s.id = b.parent)",
name = self.0,
root_guid = BookmarkRootGuid::Root.as_guid().as_ref()
root_guid = BookmarkRootGuid::Root.as_guid().as_str()
)
}
}
@ -1323,7 +1324,7 @@ mod tests {
assert_eq!(node.is_syncable(), true);
let node = tree
.node_for_guid(&BookmarkRootGuid::Unfiled.as_guid().into())
.node_for_guid(&BookmarkRootGuid::Unfiled.as_guid().as_str().into())
.expect("should exist");
assert_eq!(node.needs_merge, true);
assert_eq!(node.validity, Validity::Valid);
@ -1331,7 +1332,7 @@ mod tests {
assert_eq!(node.is_syncable(), true);
let node = tree
.node_for_guid(&BookmarkRootGuid::Menu.as_guid().into())
.node_for_guid(&BookmarkRootGuid::Menu.as_guid().as_str().into())
.expect("should exist");
assert_eq!(node.needs_merge, false);
assert_eq!(node.validity, Validity::Valid);
@ -1339,7 +1340,7 @@ mod tests {
assert_eq!(node.is_syncable(), true);
let node = tree
.node_for_guid(&BookmarkRootGuid::Root.as_guid().into())
.node_for_guid(&BookmarkRootGuid::Root.as_guid().as_str().into())
.expect("should exist");
assert_eq!(node.validity, Validity::Valid);
assert_eq!(node.level(), 0);
@ -1391,21 +1392,21 @@ mod tests {
assert_eq!(node.is_syncable(), true);
let node = tree
.node_for_guid(&BookmarkRootGuid::Unfiled.as_guid().into())
.node_for_guid(&BookmarkRootGuid::Unfiled.as_guid().as_str().into())
.expect("should exist");
assert_eq!(node.needs_merge, true);
assert_eq!(node.level(), 1);
assert_eq!(node.is_syncable(), true);
let node = tree
.node_for_guid(&BookmarkRootGuid::Menu.as_guid().into())
.node_for_guid(&BookmarkRootGuid::Menu.as_guid().as_str().into())
.expect("should exist");
assert_eq!(node.needs_merge, false);
assert_eq!(node.level(), 1);
assert_eq!(node.is_syncable(), true);
let node = tree
.node_for_guid(&BookmarkRootGuid::Root.as_guid().into())
.node_for_guid(&BookmarkRootGuid::Root.as_guid().as_str().into())
.expect("should exist");
assert_eq!(node.needs_merge, false);
assert_eq!(node.level(), 0);

Просмотреть файл

@ -7,9 +7,10 @@ use crate::{
db::PlacesDb,
error::*,
storage::RowId,
types::{SyncGuid, Timestamp},
types::Timestamp,
};
use rusqlite::Row;
use sync_guid::Guid as SyncGuid;
use sql_support::{self, ConnExt};
use sync15::ServerTimestamp;
@ -205,7 +206,7 @@ impl SyncedBookmarkItem {
let parts = t.splitn(2, ':').collect::<Vec<_>>();
(
parts[0].parse::<i64>().unwrap(),
SyncGuid(parts[1].to_owned()),
SyncGuid::from(parts[1].to_owned()),
)
})
.collect::<Vec<_>>()

Просмотреть файл

@ -199,8 +199,9 @@ mod sql_fns {
use crate::api::matcher::{split_after_host_and_port, split_after_prefix};
use crate::hash;
use crate::match_impl::{AutocompleteMatch, MatchBehavior, SearchBehavior};
use crate::types::{SyncGuid, Timestamp};
use crate::types::Timestamp;
use rusqlite::{functions::Context, types::ValueRef, Error, Result};
use sync_guid::Guid as SyncGuid;
// Helpers for define_functions
fn get_raw_str<'a>(ctx: &'a Context<'_>, fname: &'static str, idx: usize) -> Result<&'a str> {
@ -343,7 +344,7 @@ mod sql_fns {
#[inline(never)]
pub fn generate_guid(_ctx: &Context<'_>) -> Result<SyncGuid> {
Ok(SyncGuid::new())
Ok(SyncGuid::random())
}
}

Просмотреть файл

@ -243,7 +243,7 @@ pub fn create(db: &PlacesDb) -> Result<()> {
mod tests {
use super::*;
use crate::db::PlacesDb;
use crate::types::SyncGuid;
use sync_guid::Guid as SyncGuid;
use url::Url;
#[test]
@ -267,7 +267,7 @@ mod tests {
#[test]
fn test_places_no_tombstone() {
let conn = PlacesDb::open_in_memory(ConnectionType::ReadWrite).expect("no memory db");
let guid = SyncGuid::new();
let guid = SyncGuid::random();
conn.execute_named_cached(
"INSERT INTO moz_places (guid, url, url_hash) VALUES (:guid, :url, hash(:url))",
@ -297,7 +297,7 @@ mod tests {
#[test]
fn test_places_tombstone_removal() {
let conn = PlacesDb::open_in_memory(ConnectionType::ReadWrite).expect("no memory db");
let guid = SyncGuid::new();
let guid = SyncGuid::random();
conn.execute_named_cached(
"INSERT INTO moz_places_tombstones VALUES (:guid)",
@ -398,11 +398,11 @@ mod tests {
fn test_bookmark_foreign_count_triggers() {
// create the place.
let conn = PlacesDb::open_in_memory(ConnectionType::ReadWrite).expect("no memory db");
let guid1 = SyncGuid::new();
let guid1 = SyncGuid::random();
let url1 = Url::parse("http://example.com")
.expect("valid url")
.into_string();
let guid2 = SyncGuid::new();
let guid2 = SyncGuid::random();
let url2 = Url::parse("http://example2.com")
.expect("valid url")
.into_string();

Просмотреть файл

@ -11,14 +11,14 @@ use crate::storage::history::history_sync::{
apply_synced_deletion, apply_synced_reconciliation, apply_synced_visits, fetch_outgoing,
fetch_visits, finish_incoming, finish_outgoing, FetchedVisit, FetchedVisitPage, OutgoingInfo,
};
use crate::types::{SyncGuid, Timestamp, VisitTransition};
use crate::valid_guid::is_valid_places_guid;
use crate::types::{Timestamp, VisitTransition};
use interrupt::Interruptee;
use serde_json;
use std::collections::HashSet;
use std::time::{SystemTime, UNIX_EPOCH};
use sync15::telemetry;
use sync15::{IncomingChangeset, OutgoingChangeset, Payload};
use sync_guid::Guid as SyncGuid;
use url::Url;
/// Clamps a history visit date between the current date and the earliest
@ -65,7 +65,7 @@ fn plan_incoming_record(conn: &PlacesDb, record: HistoryRecord, max_visits: usiz
Err(e) => return IncomingPlan::Invalid(e.into()),
};
if !is_valid_places_guid(record.id.as_ref()) {
if !record.id.is_valid_for_places() {
return IncomingPlan::Invalid(InvalidPlaceInfo::InvalidGuid.into());
}
@ -260,7 +260,9 @@ pub fn apply_plan(
for (guid, out_record) in out_infos.drain() {
let payload = match out_record {
OutgoingInfo::Record(record) => Payload::from_record(record)?,
OutgoingInfo::Tombstone => Payload::new_tombstone_with_ttl(guid.0.clone(), HISTORY_TTL),
OutgoingInfo::Tombstone => {
Payload::new_tombstone_with_ttl(guid.as_str().to_string(), HISTORY_TTL)
}
};
log::trace!("outgoing {:?}", payload);
outgoing.changes.push(payload);
@ -451,7 +453,7 @@ mod tests {
// try and add an incoming record with the same URL but different guid.
let record = HistoryRecord {
id: SyncGuid::new(),
id: SyncGuid::random(),
title: "title".into(),
hist_uri: "https://example.com".into(),
sortindex: 0,
@ -475,10 +477,10 @@ mod tests {
// This is testing the case when there are no local visits to that URL.
let _ = env_logger::try_init();
let db = PlacesDb::open_in_memory(ConnectionType::Sync)?;
let guid1 = SyncGuid::new();
let guid1 = SyncGuid::random();
let ts1: Timestamp = (SystemTime::now() - Duration::new(5, 0)).into();
let guid2 = SyncGuid::new();
let guid2 = SyncGuid::random();
let ts2: Timestamp = SystemTime::now().into();
let url = Url::parse("https://example.com")?;
@ -535,10 +537,10 @@ mod tests {
let _ = env_logger::try_init();
let db = PlacesDb::open_in_memory(ConnectionType::Sync)?;
let guid1 = SyncGuid::new();
let guid1 = SyncGuid::random();
let ts1: Timestamp = (SystemTime::now() - Duration::new(5, 0)).into();
let guid2 = SyncGuid::new();
let guid2 = SyncGuid::random();
let ts2: Timestamp = SystemTime::now().into();
let url = Url::parse("https://example.com")?;
@ -597,10 +599,10 @@ mod tests {
let _ = env_logger::try_init();
let db = PlacesDb::open_in_memory(ConnectionType::Sync)?;
let guid1 = SyncGuid::new();
let guid1 = SyncGuid::random();
let ts1: Timestamp = (SystemTime::now() - Duration::new(5, 0)).into();
let guid2 = SyncGuid::new();
let guid2 = SyncGuid::random();
let ts2: Timestamp = SystemTime::now().into();
let url = Url::parse("https://example.com")?;

Просмотреть файл

@ -4,8 +4,8 @@
use super::ServerVisitTimestamp;
use crate::error::*;
use crate::types::SyncGuid;
use serde_derive::*;
use sync_guid::Guid as SyncGuid;
#[derive(Debug, Clone, Hash, PartialEq, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]

Просмотреть файл

@ -23,7 +23,6 @@ pub mod storage;
#[cfg(test)]
mod tests;
mod util;
mod valid_guid;
pub mod msg_types {
include!(concat!(env!("OUT_DIR"), "/msg_types.rs"));

Просмотреть файл

@ -6,7 +6,7 @@ use super::RowId;
use super::{fetch_page_info, new_page_info};
use crate::db::PlacesDb;
use crate::error::*;
use crate::types::{BookmarkType, SyncGuid, SyncStatus, Timestamp};
use crate::types::{BookmarkType, SyncStatus, Timestamp};
use rusqlite::types::ToSql;
use rusqlite::{Connection, Row};
use serde::{
@ -19,6 +19,7 @@ use serde_json::{self, json};
use sql_support::{self, ConnExt};
use std::cmp::{max, min};
use std::collections::HashMap;
use sync_guid::Guid as SyncGuid;
use url::Url;
pub use public_node::PublicNode;
@ -45,7 +46,7 @@ fn create_root(
(SELECT id FROM moz_bookmarks WHERE guid = {:?}),
1, :sync_status)
",
BookmarkRootGuid::Root.as_guid().as_ref()
BookmarkRootGuid::Root.as_guid().as_str()
);
let params: Vec<(&str, &dyn ToSql)> = vec![
(":item_type", &BookmarkType::Folder),
@ -290,7 +291,7 @@ fn insert_bookmark_in_tx(db: &PlacesDb, bm: &InsertableItem) -> Result<SyncGuid>
(:fk, :type, :parent, :position, :title, :dateAdded, :lastModified,
:guid, :syncStatus, :syncChangeCounter)";
let guid = bm.guid().clone().unwrap_or_else(SyncGuid::new);
let guid = bm.guid().clone().unwrap_or_else(SyncGuid::random);
let date_added = bm.date_added().unwrap_or_else(Timestamp::now);
// last_modified can't be before date_added
let last_modified = max(
@ -375,7 +376,7 @@ pub fn delete_bookmark(db: &PlacesDb, guid: &SyncGuid) -> Result<bool> {
fn delete_bookmark_in_tx(db: &PlacesDb, guid: &SyncGuid) -> Result<bool> {
// Can't delete a root.
if let Some(root) = guid.as_root() {
if let Some(root) = BookmarkRootGuid::well_known(&guid.as_str()) {
return Err(InvalidPlaceInfo::CannotUpdateRoot(root).into());
}
let record = match get_raw_bookmark(db, guid)? {
@ -500,7 +501,8 @@ fn update_bookmark_in_tx(
item: &UpdatableItem,
raw: RawBookmark,
) -> Result<()> {
if guid.is_root() {
// if guid is root
if BookmarkRootGuid::well_known(&guid.as_str()).is_some() {
return Err(InvalidPlaceInfo::CannotUpdateRoot(BookmarkRootGuid::Root).into());
}
let existing_parent_guid = raw
@ -905,7 +907,7 @@ mod test_serialize {
#[test]
fn test_tree_serialize() -> Result<()> {
let guid = SyncGuid::new();
let guid = SyncGuid::random();
let tree = BookmarkTreeNode::Folder(FolderNode {
guid: Some(guid.clone()),
date_added: None,
@ -1031,7 +1033,7 @@ fn add_subtree_infos(parent: &SyncGuid, tree: &FolderNode, insert_infos: &mut Ve
.into(),
),
BookmarkTreeNode::Folder(f) => {
let my_guid = f.guid.clone().unwrap_or_else(SyncGuid::new);
let my_guid = f.guid.clone().unwrap_or_else(SyncGuid::random);
// must add the folder before we recurse into children.
insert_infos.push(
InsertableFolder {
@ -1485,10 +1487,10 @@ mod tests {
let _ = env_logger::try_init();
let conn = new_mem_connection();
let guid1 = SyncGuid::new();
let guid2 = SyncGuid::new();
let guid2_1 = SyncGuid::new();
let guid3 = SyncGuid::new();
let guid1 = SyncGuid::random();
let guid2 = SyncGuid::random();
let guid2_1 = SyncGuid::random();
let guid3 = SyncGuid::random();
let jtree = json!({
"guid": &BookmarkRootGuid::Unfiled.as_guid(),

Просмотреть файл

@ -10,7 +10,8 @@ use super::{
use crate::error::{InvalidPlaceInfo, Result};
use crate::msg_types;
use crate::types::{BookmarkType, SyncGuid};
use crate::types::BookmarkType;
use sync_guid::Guid as SyncGuid;
use url::Url;
impl From<BookmarkTreeNode> for PublicNode {
@ -66,17 +67,17 @@ impl From<PublicNode> for msg_types::BookmarkNode {
};
Self {
node_type: Some(n.node_type as i32),
guid: Some(n.guid.0),
guid: Some(n.guid.into_string()),
date_added: Some(n.date_added.0 as i64),
last_modified: Some(n.last_modified.0 as i64),
title: n.title,
url: n.url.map(url::Url::into_string),
parent_guid: n.parent_guid.map(|g| g.0),
parent_guid: n.parent_guid.map(|g| g.into_string()),
position: Some(n.position),
child_guids: n.child_guids.map_or(vec![], |child_guids| {
child_guids
.into_iter()
.map(|m| m.0)
.map(|m| m.into_string())
.collect::<Vec<String>>()
}),
child_nodes: n.child_nodes.map_or(vec![], |nodes| {
@ -243,10 +244,10 @@ impl From<msg_types::BookmarkNode> for BookmarkUpdateInfo {
Self {
// This is a bug in our code on the other side of the FFI,
// so expect should be fine.
guid: SyncGuid(n.guid.expect("Missing guid")),
guid: SyncGuid::from(n.guid.expect("Missing guid")),
title: n.title,
url: n.url,
parent_guid: n.parent_guid.map(SyncGuid),
parent_guid: n.parent_guid.map(SyncGuid::from),
position: n.position,
}
}

Просмотреть файл

@ -29,7 +29,7 @@ impl Default for PublicNode {
// Note: we mainly want `Default::default()` for filling in the
// missing part of struct decls.
node_type: BookmarkType::Separator,
guid: SyncGuid(String::default()),
guid: SyncGuid::from(""),
parent_guid: None,
position: 0,
date_added: Timestamp(0),
@ -310,7 +310,7 @@ mod test {
);
let url = url::Url::parse("https://www.example2.com/a/b/c/d?q=v#abcde")?;
let mut bmks = fetch_bookmarks_by_url(&conns.read, &url)?;
bmks.sort_by_key(|b| b.guid.0.clone());
bmks.sort_by_key(|b| b.guid.as_str().to_string());
assert_eq!(bmks.len(), 2);
assert_eq!(
bmks[0],
@ -395,7 +395,7 @@ mod test {
}),
);
let mut bmks = search_bookmarks(&conns.read, "ample", 10)?;
bmks.sort_by_key(|b| b.guid.0.clone());
bmks.sort_by_key(|b| b.guid.as_str().to_string());
assert_eq!(bmks.len(), 6);
let expect = [
("bookmark1___", "https://www.example1.com/", "", 0),
@ -426,7 +426,7 @@ mod test {
),
];
for (got, want) in bmks.iter().zip(expect.iter()) {
assert_eq!(&got.guid.0, want.0);
assert_eq!(got.guid.as_str(), want.0);
assert_eq!(got.url.as_ref().unwrap(), &url::Url::parse(want.1).unwrap());
assert_eq!(got.title.as_ref().unwrap_or(&String::new()), want.2);
assert_eq!(got.position, want.3);
@ -478,8 +478,8 @@ mod test {
assert_eq!(
child.child_guids.unwrap(),
&[
SyncGuid("bookmark1___".into()),
SyncGuid("bookmark2___".into())
SyncGuid::from("bookmark1___"),
SyncGuid::from("bookmark2___")
]
);
} else {

Просмотреть файл

@ -2,8 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::SyncGuid;
use lazy_static::lazy_static;
use sync_guid::Guid as SyncGuid;
pub const USER_CONTENT_ROOTS: &[BookmarkRootGuid] = &[
BookmarkRootGuid::Menu,
@ -28,23 +28,23 @@ lazy_static! {
static ref GUIDS: [(BookmarkRootGuid, SyncGuid); 5] = [
(
BookmarkRootGuid::Root,
SyncGuid(BookmarkRootGuid::Root.as_str().into())
SyncGuid::from(BookmarkRootGuid::Root.as_str())
),
(
BookmarkRootGuid::Menu,
SyncGuid(BookmarkRootGuid::Menu.as_str().into())
SyncGuid::from(BookmarkRootGuid::Menu.as_str())
),
(
BookmarkRootGuid::Toolbar,
SyncGuid(BookmarkRootGuid::Toolbar.as_str().into())
SyncGuid::from(BookmarkRootGuid::Toolbar.as_str())
),
(
BookmarkRootGuid::Unfiled,
SyncGuid(BookmarkRootGuid::Unfiled.as_str().into())
SyncGuid::from(BookmarkRootGuid::Unfiled.as_str())
),
(
BookmarkRootGuid::Mobile,
SyncGuid(BookmarkRootGuid::Mobile.as_str().into())
SyncGuid::from(BookmarkRootGuid::Mobile.as_str())
),
];
}
@ -71,12 +71,12 @@ impl BookmarkRootGuid {
pub fn well_known(guid: &str) -> Option<Self> {
GUIDS
.iter()
.find(|(_, sync_guid)| sync_guid.0 == guid)
.find(|(_, sync_guid)| sync_guid.as_str() == guid)
.map(|(root, _)| *root)
}
pub fn from_guid(guid: &SyncGuid) -> Option<Self> {
Self::well_known(&guid.0)
Self::well_known(guid.as_ref())
}
}
@ -89,26 +89,26 @@ impl From<BookmarkRootGuid> for SyncGuid {
// Allow comparisons between BookmarkRootGuid and SyncGuids
impl PartialEq<BookmarkRootGuid> for SyncGuid {
fn eq(&self, other: &BookmarkRootGuid) -> bool {
self.0 == other.as_str()
self.as_str().as_bytes() == other.as_str().as_bytes()
}
}
impl PartialEq<SyncGuid> for BookmarkRootGuid {
fn eq(&self, other: &SyncGuid) -> bool {
other.0 == self.as_str()
other.as_str().as_bytes() == self.as_str().as_bytes()
}
}
// Even if we have a reference to &SyncGuid
impl<'a> PartialEq<BookmarkRootGuid> for &'a SyncGuid {
fn eq(&self, other: &BookmarkRootGuid) -> bool {
self.0 == other.as_str()
self.as_str().as_bytes() == other.as_str().as_bytes()
}
}
impl<'a> PartialEq<&'a SyncGuid> for BookmarkRootGuid {
fn eq(&self, other: &&'a SyncGuid) -> bool {
other.0 == self.as_str()
other.as_str().as_bytes() == self.as_str().as_bytes()
}
}

Просмотреть файл

@ -10,11 +10,12 @@ use crate::hash;
use crate::msg_types::{HistoryVisitInfo, HistoryVisitInfos};
use crate::observation::VisitObservation;
use crate::storage::{delete_pending_temp_tables, get_meta, put_meta};
use crate::types::{SyncGuid, SyncStatus, Timestamp, VisitTransition, VisitTransitionSet};
use crate::types::{SyncStatus, Timestamp, VisitTransition, VisitTransitionSet};
use rusqlite::types::ToSql;
use rusqlite::Result as RusqliteResult;
use rusqlite::{Row, NO_PARAMS};
use sql_support::{self, ConnExt};
use sync_guid::Guid as SyncGuid;
use url::Url;
/// When `delete_everything` is called (to perform a permanent local deletion), in
@ -2069,7 +2070,7 @@ mod tests {
apply_synced_visits(
&conn,
&SyncGuid::new(),
&SyncGuid::random(),
&url::Url::parse("http://www.example.com/123").unwrap(),
&None,
&[
@ -2101,7 +2102,7 @@ mod tests {
// Check that we don't insert a place if all visits are too old.
apply_synced_visits(
&conn,
&SyncGuid::new(),
&SyncGuid::random(),
&url::Url::parse("http://www.example.com/1234").unwrap(),
&None,
&[HistoryRecordVisit {

Просмотреть файл

@ -12,13 +12,14 @@ pub mod tags;
use crate::db::PlacesDb;
use crate::error::{ErrorKind, InvalidPlaceInfo, Result};
use crate::msg_types::HistoryVisitInfo;
use crate::types::{SyncGuid, SyncStatus, Timestamp, VisitTransition};
use crate::types::{SyncStatus, Timestamp, VisitTransition};
use rusqlite::types::{FromSql, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use rusqlite::Result as RusqliteResult;
use rusqlite::Row;
use serde_derive::*;
use sql_support::{self, ConnExt};
use std::fmt;
use sync_guid::Guid as SyncGuid;
use url::Url;
/// From https://searchfox.org/mozilla-central/rev/93905b660f/toolkit/components/places/PlacesUtils.jsm#189
@ -146,7 +147,7 @@ fn fetch_page_info(db: &PlacesDb, url: &Url) -> Result<Option<FetchedPageInfo>>
fn new_page_info(db: &PlacesDb, url: &Url, new_guid: Option<SyncGuid>) -> Result<PageInfo> {
let guid = match new_guid {
Some(guid) => guid,
None => SyncGuid::new(),
None => SyncGuid::random(),
};
let url_str = url.as_str();
if url_str.len() > URL_LENGTH_MAX {

Просмотреть файл

@ -8,8 +8,8 @@ use serde_json::Value;
use crate::{
db::PlacesDb,
storage::bookmarks::{fetch_tree, insert_tree, BookmarkTreeNode, FetchDepth},
types::SyncGuid,
};
use sync_guid::Guid as SyncGuid;
use pretty_assertions::assert_eq;

Просмотреть файл

@ -2,8 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::storage::bookmarks::BookmarkRootGuid;
use dogear;
use failure::Fail;
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use rusqlite::Result as RusqliteResult;
@ -16,65 +14,6 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};
mod visit_transition_set;
pub use visit_transition_set::VisitTransitionSet;
// XXX - copied from logins - surprised it's not in `sync`
#[derive(PartialEq, Eq, Hash, Clone, Debug, Serialize, Deserialize)]
pub struct SyncGuid(pub String);
impl SyncGuid {
#[allow(clippy::new_without_default)] // This probably should not be called `new`...
pub fn new() -> Self {
SyncGuid(sync15::random_guid().unwrap())
}
pub fn as_root(&self) -> Option<BookmarkRootGuid> {
BookmarkRootGuid::well_known(&self.0)
}
pub fn is_root(&self) -> bool {
BookmarkRootGuid::well_known(&self.0).is_some()
}
}
impl AsRef<str> for SyncGuid {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl<T> From<T> for SyncGuid
where
T: Into<String>,
{
fn from(x: T) -> SyncGuid {
SyncGuid(x.into())
}
}
impl From<SyncGuid> for dogear::Guid {
fn from(guid: SyncGuid) -> dogear::Guid {
guid.as_ref().into()
}
}
impl ToSql for SyncGuid {
fn to_sql(&self) -> RusqliteResult<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self.0.clone())) // cloning seems wrong?
}
}
impl FromSql for SyncGuid {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
value.as_str().map(|v| SyncGuid(v.to_string()))
}
}
impl fmt::Display for SyncGuid {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
// Typesafe way to manage timestamps.
// We should probably work out how to share this too?
#[derive(

Просмотреть файл

@ -1,57 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/// Returns true for Guids that are valid places guids, and false for all others.
pub fn is_valid_places_guid(s: &str) -> bool {
s.len() == 12 && s.bytes().all(is_valid_places_byte)
}
/// Returns true if the byte `b` is a valid base64url byte.
#[inline]
pub fn is_valid_places_byte(b: u8) -> bool {
BASE64URL_BYTES[b as usize] == 1
}
// This is used to implement the places tests.
const BASE64URL_BYTES: [u8; 256] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_base64url_bytes() {
let mut expect = [0u8; 256];
for b in b'0'..=b'9' {
expect[b as usize] = 1;
}
for b in b'a'..=b'z' {
expect[b as usize] = 1;
}
for b in b'A'..=b'Z' {
expect[b as usize] = 1;
}
expect[b'_' as usize] = 1;
expect[b'-' as usize] = 1;
assert_eq!(&BASE64URL_BYTES[..], &expect[..]);
}
#[test]
fn test_valid_for_places() {
assert!(is_valid_places_guid("aaaabbbbcccc"));
assert!(is_valid_places_guid("09_az-AZ_09-"));
assert!(!is_valid_places_guid("aaaabbbbccccd")); // too long
assert!(!is_valid_places_guid("aaaabbbbccc")); // too short
assert!(!is_valid_places_guid("aaaabbbbccc=")); // right length, bad character
}
}

Просмотреть файл

@ -7,13 +7,14 @@ use places::{
api::places_api::{ConnectionType, PlacesApi},
import::ios_bookmarks::IosBookmarkType,
storage::bookmarks,
Result, SyncGuid, Timestamp,
Result, Timestamp,
};
use rusqlite::Connection;
use sql_support::ConnExt;
use std::collections::HashMap;
use std::path::Path;
use std::sync::atomic::{AtomicUsize, Ordering};
use sync_guid::Guid as SyncGuid;
use tempfile::tempdir;
fn empty_ios_db(path: &Path) -> Result<Connection> {
@ -341,7 +342,7 @@ fn test_import_empty() -> Result<()> {
// XXX SyncGuid is a pain to work with, but apparently dogear::Guid can't turn
// into it because of our blanket into impl... ;_;
fn sync_guid(d: &dogear::Guid) -> SyncGuid {
SyncGuid(d.as_str().to_owned())
SyncGuid::from(d.as_str())
}
#[test]

Просмотреть файл

@ -18,7 +18,7 @@ lazy_static = "1.3.0"
base64 = "0.10.1"
failure = "0.1.5"
failure_derive = "0.1.5"
log = "0.4.6"
log = "0.4.7"
rusqlite = { version = "0.19.0", features = ["bundled"] }
url = "1.7.2"
viaduct = { path = "../viaduct" }

Просмотреть файл

@ -15,7 +15,7 @@ default = []
force_android = []
[dependencies]
log = "0.4.6"
log = "0.4.7"
ffi-support = { path = "../support/ffi" }
lazy_static = "1.3.0"
cfg-if = "0.1.9"

Просмотреть файл

@ -8,7 +8,7 @@ license = "MPL-2.0"
[dependencies]
failure = "0.1.3"
fxa-client = { path = "../../fxa-client" }
log = "0.4.6"
log = "0.4.7"
sync15 = { path = "../../sync15" }
url = "1.7.1"
webbrowser = "0.5.1"

Просмотреть файл

@ -135,7 +135,7 @@ impl<'a> FfiStr<'a> {
/// If the string should be mandatory, you should use
/// [`FfiStr::into_string`] instead. If an owned string is not needed, you
/// may want to use [`FfiStr::as_str`] or [`FfiStr::as_opt_str`] instead,
/// (however, note the differnces in how invalid UTF-8 is handled, should
/// (however, note the differences in how invalid UTF-8 is handled, should
/// this be relevant to your use).
pub fn into_opt_string(self) -> Option<String> {
if !self.cstr.is_null() {
@ -154,7 +154,7 @@ impl<'a> FfiStr<'a> {
/// If the string should *not* be mandatory, you should use
/// [`FfiStr::into_opt_string`] instead. If an owned string is not needed,
/// you may want to use [`FfiStr::as_str`] or [`FfiStr::as_opt_str`]
/// instead, (however, note the differnces in how invalid UTF-8 is handled,
/// instead, (however, note the differences in how invalid UTF-8 is handled,
/// should this be relevant to your use).
#[inline]
pub fn into_string(self) -> String {

Просмотреть файл

@ -0,0 +1,22 @@
[package]
name = "sync-guid"
version = "0.1.0"
authors = ["Thom Chiovoloni <tchiovoloni@mozilla.com>"]
license = "MPL-2.0"
edition = "2018"
[dependencies]
rusqlite = { version = "0.19.0", optional = true }
serde = { version = "1.0.79", optional = true }
rc_crypto = { path = "../rc_crypto", optional = true }
base64 = { version = "0.10.1", optional = true }
[features]
random = ["rc_crypto", "base64"]
rusqlite_support = ["rusqlite"]
serde_support = ["serde"]
# By default we support serde, but not rusqlite.
default = ["serde_support"]
[dev-dependencies]
serde_test = "1.0.79"

Просмотреть файл

@ -0,0 +1,411 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![allow(unknown_lints)]
#![warn(rust_2018_idioms)]
// (It's tempting to avoid the utf8 checks, but they're easy to get wrong, so)
#![deny(unsafe_code)]
#[cfg(feature = "serde_support")]
mod serde_support;
#[cfg(feature = "rusqlite_support")]
mod rusqlite_support;
use std::{fmt, ops, str};
/// This is a type intended to be used to represent the guids used by sync. It
/// has several benefits over using a `String`:
///
/// 1. It's more explicit about what is being stored, and could prevent bugs
/// where a Guid is passed to a function expecting text.
///
/// 2. Guids are guaranteed to be immutable.
///
/// 3. It's optimized for the guids commonly used by sync. In particular, short guids
/// (including the guids which would meet `PlacesUtils.isValidGuid`) do not incur
/// any heap allocation, and are stored inline.
#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Guid(Repr);
// The internal representation of a GUID. Most Sync GUIDs are 12 bytes,
// and contain only base64url characters; we can store them on the stack
// without a heap allocation. However, arbitrary ascii guids of up to length 64
// are possible, in which case we fall back to a heap-allocated string.
//
// This is separate only because making `Guid` an enum would expose the
// internals.
#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
enum Repr {
// see FastGuid for invariants
Fast(FastGuid),
// invariants:
// - _0.len() > MAX_FAST_GUID_LEN
Slow(String),
}
/// Invariants:
///
/// - `len <= MAX_FAST_GUID_LEN`.
/// - `data[0..len]` encodes valid utf8.
/// - `data[len..].iter().all(|&b| b == b'\0')`
///
/// Note: None of these are required for memory safety, just correctness.
#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
struct FastGuid {
len: u8,
data: [u8; MAX_FAST_GUID_LEN],
}
// This is the maximum length (experimentally determined) we can make it before
// `Repr::Fast` is larger than `Guid::Slow` on 32 bit systems. The important
// thing is really that it's not too big, and is above 12 bytes.
const MAX_FAST_GUID_LEN: usize = 14;
impl FastGuid {
#[inline]
fn from_slice(bytes: &[u8]) -> Self {
// Checked by the caller, so debug_assert is fine.
debug_assert!(
can_use_fast(bytes),
"Bug: Caller failed to check can_use_fast: {:?}",
bytes
);
let mut data = [0u8; MAX_FAST_GUID_LEN];
data[0..bytes.len()].copy_from_slice(bytes);
FastGuid {
len: bytes.len() as u8,
data,
}
}
#[inline]
fn as_str(&self) -> &str {
// Note: we only use debug_assert! to enusre valid utf8-ness, so this need
str::from_utf8(self.bytes()).expect("Invalid fast guid bytes!")
}
#[inline]
fn len(&self) -> usize {
self.len as usize
}
#[inline]
fn bytes(&self) -> &[u8] {
&self.data[0..self.len()]
}
}
// Returns:
// - true to use Repr::Fast
// - false to use Repr::Slow
#[inline]
fn can_use_fast<T: ?Sized + AsRef<[u8]>>(bytes: &T) -> bool {
let bytes = bytes.as_ref();
// This is fine as a debug_assert since we'll still panic if it's ever used
// in such a way where it would matter.
debug_assert!(str::from_utf8(bytes).is_ok());
bytes.len() <= MAX_FAST_GUID_LEN
}
impl Guid {
/// Create a guid from a `str`.
#[inline]
pub fn new(s: &str) -> Self {
Guid::from_slice(s.as_ref())
}
/// Create an empty guid. Usable as a constant.
#[inline]
pub const fn empty() -> Self {
Guid(Repr::Fast(FastGuid {
len: 0,
data: [0u8; MAX_FAST_GUID_LEN],
}))
}
/// Create a random guid (of 12 base64url characters). Requires the `random`
/// feature.
#[cfg(feature = "random")]
pub fn random() -> Self {
let mut bytes = [0u8; 9];
// TODO: investigate if we should replace this with something infallible
// (from e.g. `rand`)
rc_crypto::rand::fill(&mut bytes).unwrap();
// Note: only first 12 bytes are used, but remaining are required to
// build the FastGuid
let mut output = [0u8; MAX_FAST_GUID_LEN];
let bytes_written =
base64::encode_config_slice(&bytes, base64::URL_SAFE_NO_PAD, &mut output[..12]);
debug_assert!(bytes_written == 12);
Guid(Repr::Fast(FastGuid {
len: 12,
data: output,
}))
}
/// Convert `b` into a `Guid`.
#[inline]
pub fn from_string(s: String) -> Self {
Guid::from_vec(s.into_bytes())
}
/// Convert `b` into a `Guid`.
#[inline]
pub fn from_slice(b: &[u8]) -> Self {
if can_use_fast(b) {
Guid(Repr::Fast(FastGuid::from_slice(b)))
} else {
Guid::new_slow(b.into())
}
}
/// Convert `v` to a `Guid`, consuming it.
#[inline]
pub fn from_vec(v: Vec<u8>) -> Self {
if can_use_fast(&v) {
Guid(Repr::Fast(FastGuid::from_slice(&v)))
} else {
Guid::new_slow(v)
}
}
/// Get the data backing this `Guid` as a `&[u8]`.
#[inline]
pub fn as_bytes(&self) -> &[u8] {
match &self.0 {
Repr::Fast(rep) => rep.bytes(),
Repr::Slow(rep) => rep.as_ref(),
}
}
/// Get the data backing this `Guid` as a `&str`.
#[inline]
pub fn as_str(&self) -> &str {
match &self.0 {
Repr::Fast(rep) => rep.as_str(),
Repr::Slow(rep) => rep.as_ref(),
}
}
/// Convert this `Guid` into a `String`, consuming it in the process.
#[inline]
pub fn into_string(self) -> String {
match self.0 {
Repr::Fast(rep) => rep.as_str().into(),
Repr::Slow(rep) => rep,
}
}
/// Returns true for Guids that are valid places guids, and false for all others.
pub fn is_valid_for_places(&self) -> bool {
self.len() == 12 && self.bytes().all(Guid::is_valid_places_byte)
}
/// Returns true if the byte `b` is a valid base64url byte.
#[inline]
pub fn is_valid_places_byte(b: u8) -> bool {
BASE64URL_BYTES[b as usize] == 1
}
#[cold]
fn new_slow(v: Vec<u8>) -> Self {
assert!(
!can_use_fast(&v),
"Could use fast for guid (len = {})",
v.len()
);
Guid(Repr::Slow(
String::from_utf8(v).expect("Invalid slow guid bytes!"),
))
}
}
// This is used to implement the places tests.
const BASE64URL_BYTES: [u8; 256] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
impl<'a> From<&'a str> for Guid {
#[inline]
fn from(s: &'a str) -> Guid {
Guid::from_slice(s.as_ref())
}
}
impl<'a> From<&'a [u8]> for Guid {
#[inline]
fn from(s: &'a [u8]) -> Guid {
Guid::from_slice(s)
}
}
impl From<String> for Guid {
#[inline]
fn from(s: String) -> Guid {
Guid::from_string(s)
}
}
impl From<Vec<u8>> for Guid {
#[inline]
fn from(v: Vec<u8>) -> Guid {
Guid::from_vec(v)
}
}
impl From<Guid> for String {
#[inline]
fn from(guid: Guid) -> String {
guid.into_string()
}
}
impl From<Guid> for Vec<u8> {
#[inline]
fn from(guid: Guid) -> Vec<u8> {
guid.into_string().into_bytes()
}
}
impl AsRef<str> for Guid {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<[u8]> for Guid {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl ops::Deref for Guid {
type Target = str;
#[inline]
fn deref(&self) -> &str {
self.as_str()
}
}
// The default Debug impl is pretty unhelpful here.
impl fmt::Debug for Guid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Guid({:?})", self.as_str())
}
}
impl fmt::Display for Guid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
macro_rules! impl_guid_eq {
($($other: ty),+) => {$(
impl<'a> PartialEq<$other> for Guid {
#[inline]
fn eq(&self, other: &$other) -> bool {
PartialEq::eq(AsRef::<[u8]>::as_ref(self), AsRef::<[u8]>::as_ref(other))
}
}
impl<'a> PartialEq<Guid> for $other {
#[inline]
fn eq(&self, other: &Guid) -> bool {
PartialEq::eq(AsRef::<[u8]>::as_ref(self), AsRef::<[u8]>::as_ref(other))
}
}
)+}
}
// Implement direct comparison with some common types from the stdlib.
impl_guid_eq![str, &'a str, String, [u8], &'a [u8], Vec<u8>];
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_base64url_bytes() {
let mut expect = [0u8; 256];
for b in b'0'..=b'9' {
expect[b as usize] = 1;
}
for b in b'a'..=b'z' {
expect[b as usize] = 1;
}
for b in b'A'..=b'Z' {
expect[b as usize] = 1;
}
expect[b'_' as usize] = 1;
expect[b'-' as usize] = 1;
assert_eq!(&BASE64URL_BYTES[..], &expect[..]);
}
#[test]
fn test_valid_for_places() {
assert!(Guid::from("aaaabbbbcccc").is_valid_for_places());
assert!(Guid::from_slice(b"09_az-AZ_09-").is_valid_for_places());
assert!(!Guid::from("aaaabbbbccccd").is_valid_for_places()); // too long
assert!(!Guid::from("aaaabbbbccc").is_valid_for_places()); // too short
assert!(!Guid::from("aaaabbbbccc=").is_valid_for_places()); // right length, bad character
}
#[test]
fn test_comparison() {
assert_eq!(Guid::from("abcdabcdabcd"), "abcdabcdabcd");
assert_ne!(Guid::from("abcdabcdabcd".to_string()), "ABCDabcdabcd");
assert_eq!(Guid::from("abcdabcdabcd"), &b"abcdabcdabcd"[..]); // b"abcdabcdabcd" has type &[u8; 12]...
assert_ne!(Guid::from(&b"abcdabcdabcd"[..]), &b"ABCDabcdabcd"[..]);
assert_eq!(
Guid::from(b"abcdabcdabcd"[..].to_owned()),
"abcdabcdabcd".to_string()
);
assert_ne!(Guid::from("abcdabcdabcd"), "ABCDabcdabcd".to_string());
assert_eq!(
Guid::from("abcdabcdabcd1234"),
Vec::from(b"abcdabcdabcd1234".as_ref())
);
assert_ne!(
Guid::from("abcdabcdabcd4321"),
Vec::from(b"ABCDabcdabcd4321".as_ref())
);
}
#[cfg(feature = "random")]
#[test]
fn test_random() {
use std::collections::HashSet;
// Used to verify uniqueness within our sample of 1000. Could cause
// random failures, but desktop has the same test, and it's never caused
// a problem AFAIK.
let mut seen: HashSet<String> = HashSet::new();
for _ in 0..1000 {
let g = Guid::random();
assert_eq!(g.len(), 12);
assert!(g.is_valid_for_places());
let decoded = base64::decode_config(&g, base64::URL_SAFE_NO_PAD).unwrap();
assert_eq!(decoded.len(), 9);
let no_collision = seen.insert(g.clone().into_string());
assert!(no_collision, "{}", g);
}
}
}

Просмотреть файл

@ -0,0 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![cfg(feature = "rusqlite_support")]
use crate::Guid;
use rusqlite::{
self,
types::{FromSql, FromSqlResult, ToSql, ToSqlOutput, ValueRef},
};
impl ToSql for Guid {
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self.as_str()))
}
}
impl FromSql for Guid {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
value.as_str().map(Guid::from)
}
}

Просмотреть файл

@ -0,0 +1,61 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![cfg(feature = "serde_support")]
use std::fmt;
use serde::{
de::{self, Deserialize, Deserializer, Visitor},
ser::{Serialize, Serializer},
};
use crate::Guid;
struct GuidVisitor;
impl<'de> Visitor<'de> for GuidVisitor {
type Value = Guid;
#[inline]
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a sync guid")
}
#[inline]
fn visit_str<E: de::Error>(self, s: &str) -> Result<Self::Value, E> {
Ok(Guid::from_slice(s.as_ref()))
}
}
impl<'de> Deserialize<'de> for Guid {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(GuidVisitor)
}
}
impl Serialize for Guid {
#[inline]
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
#[cfg(test)]
mod test {
use super::*;
use serde_test::{assert_tokens, Token};
#[test]
fn test_ser_de() {
let guid = Guid::from("asdffdsa12344321");
assert_tokens(&guid, &[Token::Str("asdffdsa12344321")]);
let guid = Guid::from("");
assert_tokens(&guid, &[Token::Str("")]);
let guid = Guid::from(&b"abcd43211234"[..]);
assert_tokens(&guid, &[Token::Str("abcd43211234")]);
}
}

Просмотреть файл

@ -9,7 +9,7 @@ license = "MPL-2.0"
crate-type = ["lib"]
[build-dependencies]
bindgen = "0.49.0"
bindgen = "0.50.0"
serde = "1.0.91"
serde_derive = "1.0.91"
toml = "0.5.0"

Просмотреть файл

@ -5,6 +5,7 @@
package mozilla.appservices.sync15
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import org.json.JSONException

Просмотреть файл

@ -40,7 +40,6 @@ a rc file or similar so they persist between reboots etc.
2. Install `rustup` from https://rustup.rs:
- If you already have it, run `rustup update`
- Run `rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android`
- Run `rustup toolchain add beta` (TODO: this no longer appears necessary).
3. Ensure your clone of `mozilla/application-services` is up to date.
@ -64,7 +63,8 @@ a rc file or similar so they persist between reboots etc.
them somewhere - use the "SDK Manager" to identify this location.
- Set `ANDROID_HOME` to this location and add it to your rc file.
7. Build openssl and sqlcipher
7. Build NSS, OpenSSL and sqlcipher
- Make sure both [GYP](https://github.com/mogemimi/pomdog/wiki/How-to-Install-GYP) and [ninja](https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages) are available on your system.
- `cd path/to/application-services/libs` (Same dir you were just in for step 4)
- `./build-all.sh android` (Go make some coffee or something, this will take
some time as it has to compile sqlcipher and openssl for x86, x86_64, arm, and arm64).

Просмотреть файл

@ -1,6 +1,6 @@
## libs
This directory builds `openssl` for iOS, Android and desktop platforms.
This directory builds the required libraries for iOS, Android and desktop platforms.
### Usage
@ -8,6 +8,7 @@ This directory builds `openssl` for iOS, Android and desktop platforms.
* `./build-all.sh android` - Build for Android
* `./build-all.sh desktop` - Build for Desktop
[GYP](https://github.com/mogemimi/pomdog/wiki/How-to-Install-GYP) and [ninja](https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages) are required to run these scripts.
### Supported architectures

Просмотреть файл

@ -7,25 +7,26 @@
if [ ! -f "$(pwd)/libs/build-all.sh" ]; then
echo "ERROR: bootstrap-desktop.sh should be run from the root directory of the repo"
else
if [ "$(uname -s)" == "Darwin" ]; then
APPSERVICES_PLATFORM_DIR="$(pwd)/libs/desktop/darwin"
else
APPSERVICES_PLATFORM_DIR="$(pwd)/libs/desktop/linux-x86-64"
fi
export SQLCIPHER_LIB_DIR="${APPSERVICES_PLATFORM_DIR}/sqlcipher/lib"
export SQLCIPHER_INCLUDE_DIR="${APPSERVICES_PLATFORM_DIR}/sqlcipher/include"
export OPENSSL_DIR="${APPSERVICES_PLATFORM_DIR}/openssl"
export NSS_DIR="${APPSERVICES_PLATFORM_DIR}/nss"
if [ ! -d "${SQLCIPHER_LIB_DIR}" ] || [ ! -d "${OPENSSL_DIR}" ] || [ ! -d "${NSS_DIR}" ]; then
pushd libs
./build-all.sh desktop
popd
fi;
if [ "$(uname -s)" == "Darwin" ] && [ ! -f "/usr/include/pthread.h" ]; then
# rustc does not include the macOS SDK headers in its include list yet
# (see https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes)
echo "macOS system headers are not installed in /usr/include, please run:"
echo "open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg"
fi
exit 1
fi
if [ "$(uname -s)" == "Darwin" ]; then
APPSERVICES_PLATFORM_DIR="$(pwd)/libs/desktop/darwin"
else
APPSERVICES_PLATFORM_DIR="$(pwd)/libs/desktop/linux-x86-64"
fi
export SQLCIPHER_LIB_DIR="${APPSERVICES_PLATFORM_DIR}/sqlcipher/lib"
export SQLCIPHER_INCLUDE_DIR="${APPSERVICES_PLATFORM_DIR}/sqlcipher/include"
export OPENSSL_DIR="${APPSERVICES_PLATFORM_DIR}/openssl"
export NSS_DIR="${APPSERVICES_PLATFORM_DIR}/nss"
if [ ! -d "${SQLCIPHER_LIB_DIR}" ] || [ ! -d "${OPENSSL_DIR}" ] || [ ! -d "${NSS_DIR}" ]; then
pushd libs
./build-all.sh desktop
popd
fi;
if [ "$(uname -s)" == "Darwin" ] && [ ! -f "/usr/include/pthread.h" ]; then
# rustc does not include the macOS SDK headers in its include list yet
# (see https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes)
echo "macOS system headers are not installed in /usr/include, please run:"
echo "open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg"
fi

Просмотреть файл

@ -33,6 +33,16 @@ PLATFORM="${1}"
abspath () { case "${1}" in /*)printf "%s\\n" "${1}";; *)printf "%s\\n" "${PWD}/${1}";; esac; }
export -f abspath
if ! [ -x "$(command -v gyp)" ]; then
echo 'Error: gyp needs to be installed and executable. See https://github.com/mogemimi/pomdog/wiki/How-to-Install-GYP for install instructions.' >&2
exit 1
fi
if ! [ -x "$(command -v ninja)" ]; then
echo 'Error: ninja needs to be installed and executable. See https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages for install instructions.' >&2
exit 1
fi
OPENSSL="openssl-${OPENSSL_VERSION}"
rm -rf "${OPENSSL}"
if [ ! -e "${OPENSSL}.tar.gz" ]; then

Просмотреть файл

@ -33,7 +33,6 @@ android {
afterEvaluate {
android.sourceSets.debug.jniLibs.srcDirs = android.sourceSets.main.jniLibs.srcDirs
android.sourceSets.release.jniLibs.srcDirs = android.sourceSets.main.jniLibs.srcDirs
// android.sourceSets.main.jniLibs.srcDirs = []
}
configurations {

Просмотреть файл

@ -11,7 +11,7 @@ sync15 = { path = "../../components/sync15", features = ["reqwest"] }
fxa-client = { path = "../../components/fxa-client", features = ["reqwest"] }
url = "1.7.1"
env_logger = "0.6.2"
log = "0.4.6"
log = "0.4.7"
failure = "0.1.3"
rand = "0.7.0"
lazy_static = "1.3.0"