diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 430b6cf1..b11761ed 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -65,15 +65,6 @@ jobs: - name: start smtp server run: docker run -d -p 1080:1080 -p 10025:1025 maildev/maildev --incoming-user admin --incoming-pass password - - name: Install JavaScript Dependencies - run: yarn install - - - name: Build frontend - run: yarn build - - - name: Run the tests - run: yarn test - - name: Install ${{ matrix.version }} uses: actions-rs/toolchain@v1 with: @@ -81,20 +72,15 @@ jobs: profile: minimal override: true - - name: Run migrations - uses: actions-rs/cargo@v1 - with: - command: run - args: --bin tests-migrate -- --build - env: - DATABASE_URL: postgres://postgres:password@localhost:5432/postgres + - name: Build frontend + run: make frontend + + - name: Run the frontend tests + run: make frontend-test - name: Generate coverage file if: matrix.version == '1.51.0' && (github.ref == 'refs/heads/master' || github.event_name == 'pull_request') - uses: actions-rs/tarpaulin@v0.1 - with: - version: '0.15.0' - args: '-t 1200' + run: make xml-test-coverage env: DATABASE_URL: postgres://postgres:password@localhost:5432/postgres # GIT_HASH is dummy value. I guess build.rs is skipped in tarpaulin diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index c926144b..e97cae64 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -38,14 +38,6 @@ jobs: image: mcaptcha/cache ports: - 6379:6379 - # smtp: - # image: maildev/maildev - # ports: - # - 10025:1025 - # - 1080:1080 - # env: - # MAILDEV_INCOMING_USER: admin - # MAILDEV_INCOMING_PASS: password steps: - uses: actions/checkout@v2 @@ -64,13 +56,7 @@ jobs: node-version: '14.x' - name: start smtp server - run: docker run -d -p 1080:1080 -p 10025:1025 maildev/maildev --incoming-user admin --incoming-pass password - - - name: Install JavaScript Dependencies - run: yarn install - - - name: Build Frontend - run: yarn build + run: docker run -d -p 1080:1080 -p 10025:1025 maildev/maildev --incoming-user admin --incoming-pass password - name: Install ${{ matrix.version }} uses: actions-rs/toolchain@v1 @@ -79,37 +65,27 @@ jobs: profile: minimal override: true + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + - name: Run migrations - uses: actions-rs/cargo@v1 - with: - command: run - args: --bin tests-migrate + run: make migrations env: DATABASE_URL: postgres://postgres:password@localhost:5432/postgres - - name: check build - uses: actions-rs/cargo@v1 - with: - command: check - args: --all --bins --examples --tests + - name: build + run: make env: DATABASE_URL: postgres://postgres:password@localhost:5432/postgres - - name: tests - uses: actions-rs/cargo@v1 - timeout-minutes: 40 - with: - command: test - args: --all --all-features --no-fail-fast + - name: run tests + run: make test env: DATABASE_URL: postgres://postgres:password@localhost:5432/postgres - name: generate documentation if: matrix.version == 'stable' && (github.repository == 'mCaptcha/mCaptcha') - uses: actions-rs/cargo@v1 - with: - command: doc - args: --no-deps --workspace --all-features + run: make doc env: DATABASE_URL: postgres://postgres:password@localhost:5432/postgres GIT_HASH: 8e77345f1597e40c2e266cb4e6dee74888918a61 # dummy value diff --git a/Cargo.lock b/Cargo.lock index a9b09d83..2e650a69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -221,7 +221,7 @@ dependencies = [ "actix-web-codegen 0.5.0-beta.3 (registry+https://github.com/rust-lang/crates.io-index)", "ahash", "bytes", - "cfg-if", + "cfg-if 1.0.0", "cookie", "derive_more", "either", @@ -449,7 +449,7 @@ dependencies = [ "actix-service", "base64", "bytes", - "cfg-if", + "cfg-if 1.0.0", "cookie", "derive_more", "futures-core", @@ -579,7 +579,7 @@ dependencies = [ [[package]] name = "cache-buster" version = "0.2.0" -source = "git+https://github.com/realaravinth/cache-buster#d970b7031cd90649e30dc297eb5eba74832153e8" +source = "git+https://github.com/realaravinth/cache-buster#e75ac689d2155092b90d1421ec05ec84c6917350" dependencies = [ "data-encoding", "derive_builder", @@ -600,6 +600,12 @@ dependencies = [ "jobserver", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" @@ -644,6 +650,16 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" +dependencies = [ + "cfg-if 0.1.10", + "wasm-bindgen", +] + [[package]] name = "const_fn" version = "0.4.8" @@ -732,7 +748,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -741,7 +757,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-utils", ] @@ -751,7 +767,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-utils", ] @@ -761,7 +777,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "lazy_static", ] @@ -930,7 +946,7 @@ version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -961,7 +977,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall", "winapi", @@ -973,7 +989,7 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crc32fast", "libc", "miniz_oxide", @@ -1136,7 +1152,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -1147,7 +1163,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi 0.10.2+wasi-snapshot-preview1", ] @@ -1348,7 +1364,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1426,7 +1442,7 @@ checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" dependencies = [ "arrayvec", "bitflags", - "cfg-if", + "cfg-if 1.0.0", "ryu", "static_assertions", ] @@ -1494,7 +1510,7 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1586,6 +1602,20 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "mcaptcha-browser" +version = "0.1.0" +dependencies = [ + "console_error_panic_hook", + "pow_sha256", + "serde 1.0.126", + "serde_derive", + "serde_json", + "wasm-bindgen", + "wasm-bindgen-test", + "wee_alloc", +] + [[package]] name = "md-5" version = "0.9.1" @@ -1603,6 +1633,12 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + [[package]] name = "mime" version = "0.3.16" @@ -1824,7 +1860,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "549430950c79ae24e6d02e0b7404534ecf311d94cc9f861e9e4020187d13d885" dependencies = [ "bitflags", - "cfg-if", + "cfg-if 1.0.0", "foreign-types", "libc", "once_cell", @@ -1867,7 +1903,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "instant", "libc", "redox_syscall", @@ -2408,6 +2444,12 @@ dependencies = [ "parking_lot", ] +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + [[package]] name = "scopeguard" version = "1.1.0" @@ -2543,7 +2585,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c4cfa741c5832d0ef7fab46cabed29c2aae926db0b11bb2069edd8db5e64e16" dependencies = [ "block-buffer", - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest", "opaque-debug", @@ -2562,7 +2604,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" dependencies = [ "block-buffer", - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest", "opaque-debug", @@ -2854,7 +2896,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "rand 0.8.4", "redox_syscall", @@ -3045,7 +3087,7 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "pin-project-lite", "tracing-core", ] @@ -3228,7 +3270,7 @@ version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen-macro", ] @@ -3247,6 +3289,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fba7978c679d53ce2d0ac80c8c175840feb849a161664365d1287b41f2e67f1" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.74" @@ -3276,6 +3330,30 @@ version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" +[[package]] +name = "wasm-bindgen-test" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cab416a9b970464c2882ed92d55b0c33046b08e0bdc9d59b3b718acd4e1bae8" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4543fc6cf3541ef0d98bf720104cc6bd856d7eba449fd2aa365ef4fed0e782" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "web-sys" version = "0.3.51" @@ -3305,6 +3383,18 @@ dependencies = [ "webpki", ] +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + [[package]] name = "whoami" version = "1.1.2" diff --git a/Cargo.toml b/Cargo.toml index c9702ce1..e292ad3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,12 @@ path = "./src/main.rs" name = "tests-migrate" path = "./src/tests-migrate.rs" +[workspace] +members = [ +".", +"browser", +] + [dependencies] #actix-web = "3.3.2" actix-web = "4.0.0-beta.8" diff --git a/Makefile b/Makefile index 00c28463..8933501f 100644 --- a/Makefile +++ b/Makefile @@ -1,29 +1,45 @@ default: frontend cargo build -run: frontend-dev +run: frontend cargo run dev-env: cargo fetch yarn install -docs: +doc: + #yarn doc cargo doc --no-deps --workspace --all-features - -frontend-dev: - yarn build + cd browser && cargo doc --no-deps --workspace --all-features frontend: + cd browser && wasm-pack build --release + yarn install yarn build -test: migrate - cargo test +test: migrations + cd browser && wasm-pack test --release --headless --chrome + cd browser && wasm-pack test --release --headless --firefox + cargo test --all --all-features --no-fail-fast + ${MAKE} frontend-test -xml-test-coverage: migrate +frontend-test: + cd browser && wasm-pack test --release --headless --chrome + cd browser && wasm-pack test --release --headless --firefox + yarn test + +a: + echo a +b: + ${MAKE} a + +xml-test-coverage: migrations + cd browser && cargo tarpaulin -t 1200 --out Xml cargo tarpaulin -t 1200 --out Xml -coverage: migrate +coverage: migrations + cd browser && cargo tarpaulin -t 1200 --out Html cargo tarpaulin -t 1200 --out Html release: frontend @@ -35,13 +51,12 @@ clean: docker-build: docker build -t mcaptcha/mcaptcha:master -t mcaptcha/mcaptcha:latest . + docker-publish: docker-build docker push mcaptcha/mcaptcha:master docker push mcaptcha/mcaptcha:latest - - -migrate: +migrations: cargo run --bin tests-migrate help: @@ -50,10 +65,10 @@ help: @echo ' dev-env - download dependencies' @echo ' docker-build - build docker image' @echo ' docker-publish - build and publish docker image' - @echo ' docs - build documentation' - @echo ' frontend-dev - build static assets in dev mode' + @echo ' doc - build documentation' @echo ' frontend - build static assets in prod mode' - @echo ' migrate - run database migrations' + @echo ' frontend-test - run frontend tests' + @echo ' migrations - run database migrations' @echo ' run - run developer instance' @echo ' test - run unit and integration tests' @echo ' xml-coverage - build test coverage in XML for upload to codecov' diff --git a/browser/.appveyor.yml b/browser/.appveyor.yml new file mode 100644 index 00000000..50910bd6 --- /dev/null +++ b/browser/.appveyor.yml @@ -0,0 +1,11 @@ +install: + - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + - if not defined RUSTFLAGS rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly + - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin + - rustc -V + - cargo -V + +build: false + +test_script: + - cargo test --locked diff --git a/browser/.gitignore b/browser/.gitignore new file mode 100644 index 00000000..009ba71b --- /dev/null +++ b/browser/.gitignore @@ -0,0 +1,14 @@ +/target +tarpaulin-report.html +.env +.env +cobertura.xml +prod/ +node_modules/ +/static-assets/bundle +./templates/**/*.js +/static-assets/bundle/* +src/cache_buster_data.json +coverage +dist/ +docs diff --git a/browser/.travis.yml b/browser/.travis.yml new file mode 100644 index 00000000..7a913256 --- /dev/null +++ b/browser/.travis.yml @@ -0,0 +1,69 @@ +language: rust +sudo: false + +cache: cargo + +matrix: + include: + + # Builds with wasm-pack. + - rust: beta + env: RUST_BACKTRACE=1 + addons: + firefox: latest + chrome: stable + before_script: + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) + - cargo install-update -a + - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f + script: + - cargo generate --git . --name testing + # Having a broken Cargo.toml (in that it has curlies in fields) anywhere + # in any of our parent dirs is problematic. + - mv Cargo.toml Cargo.toml.tmpl + - cd testing + - wasm-pack build + - wasm-pack test --chrome --firefox --headless + + # Builds on nightly. + - rust: nightly + env: RUST_BACKTRACE=1 + before_script: + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) + - cargo install-update -a + - rustup target add wasm32-unknown-unknown + script: + - cargo generate --git . --name testing + - mv Cargo.toml Cargo.toml.tmpl + - cd testing + - cargo check + - cargo check --target wasm32-unknown-unknown + - cargo check --no-default-features + - cargo check --target wasm32-unknown-unknown --no-default-features + - cargo check --no-default-features --features console_error_panic_hook + - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook + - cargo check --no-default-features --features "console_error_panic_hook wee_alloc" + - cargo check --target wasm32-unknown-unknown --no-default-features --features "console_error_panic_hook wee_alloc" + + # Builds on beta. + - rust: beta + env: RUST_BACKTRACE=1 + before_script: + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) + - cargo install-update -a + - rustup target add wasm32-unknown-unknown + script: + - cargo generate --git . --name testing + - mv Cargo.toml Cargo.toml.tmpl + - cd testing + - cargo check + - cargo check --target wasm32-unknown-unknown + - cargo check --no-default-features + - cargo check --target wasm32-unknown-unknown --no-default-features + - cargo check --no-default-features --features console_error_panic_hook + - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook + # Note: no enabling the `wee_alloc` feature here because it requires + # nightly for now. diff --git a/browser/Cargo.lock b/browser/Cargo.lock new file mode 100644 index 00000000..2156030e --- /dev/null +++ b/browser/Cargo.lock @@ -0,0 +1,556 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" +dependencies = [ + "cfg-if 0.1.10", + "wasm-bindgen", +] + +[[package]] +name = "cpufeatures" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef" +dependencies = [ + "libc", +] + +[[package]] +name = "darling" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derive_builder" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73" +dependencies = [ + "derive_builder_core", + "syn", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "js-sys" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "mcaptcha-browser" +version = "0.1.0" +dependencies = [ + "console_error_panic_hook", + "pow_sha256", + "serde", + "serde_derive", + "serde_json", + "wasm-bindgen", + "wasm-bindgen-test", + "wee_alloc", +] + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-complex" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "pow_sha256" +version = "0.2.1" +source = "git+https://github.com/mcaptcha/pow_sha256#807fa7c75284f6d8d488a6f66a3a1b3301f2f412" +dependencies = [ + "bincode", + "derive_builder", + "num", + "serde", + "sha2", +] + +[[package]] +name = "proc-macro2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "serde" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" +dependencies = [ + "block-buffer", + "cfg-if 1.0.0", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "typenum" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "wasm-bindgen" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fba7978c679d53ce2d0ac80c8c175840feb849a161664365d1287b41f2e67f1" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cab416a9b970464c2882ed92d55b0c33046b08e0bdc9d59b3b718acd4e1bae8" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4543fc6cf3541ef0d98bf720104cc6bd856d7eba449fd2aa365ef4fed0e782" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "web-sys" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/browser/Cargo.toml b/browser/Cargo.toml new file mode 100644 index 00000000..7945b5d2 --- /dev/null +++ b/browser/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "mcaptcha-browser" +version = "0.1.0" +authors = ["realaravinth "] +edition = "2018" +license = "MIT OR Apache-2.0" +repository = "https://github.com/mCaptcha/browser" +description = "mCaptcha client library for the web" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +default = ["console_error_panic_hook"] + +[dependencies] +wasm-bindgen = "0.2.63" + +# The `console_error_panic_hook` crate provides better debugging of panics by +# logging them with `console.error`. This is great for development, but requires +# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for +# code size when deploying. +console_error_panic_hook = { version = "0.1.6", optional = true } + +# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size +# compared to the default allocator's ~10K. It is slower than the default +# allocator, however. +# +# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. +wee_alloc = { version = "0.4.5", optional = true } + +serde = "1.0.114" +serde_derive = "1.0.114" +serde_json = "1" + +pow_sha256 = { version = "0.2.1", git = "https://github.com/mcaptcha/pow_sha256" } + +[dev-dependencies] +wasm-bindgen-test = "0.3.13" + +#[profile.release] +## Tell `rustc` to optimize for small code size. +#opt-level = "s" + +[package.metadata.wasm-pack.profile.release] +wasm-opt = false diff --git a/browser/LICENSE_APACHE b/browser/LICENSE_APACHE new file mode 100644 index 00000000..1b5ec8b7 --- /dev/null +++ b/browser/LICENSE_APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/browser/LICENSE_MIT b/browser/LICENSE_MIT new file mode 100644 index 00000000..e58e5ec5 --- /dev/null +++ b/browser/LICENSE_MIT @@ -0,0 +1,25 @@ +Copyright (c) 2018 realaravinth + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/browser/README.md b/browser/README.md new file mode 100644 index 00000000..732b3f20 --- /dev/null +++ b/browser/README.md @@ -0,0 +1,92 @@ +
+ +

PoW JavaScript library

+ +JavaScript library to generate PoW for mCaptcha + +[![0.1.0](https://img.shields.io/badge/Rust_docs-master-dea584)](https://mcaptcha.github.io/browser/rust/mcaptcha_browser/index.html) +[![0.1.0](https://img.shields.io/badge/TypeScript_docs-master-2b7489)](https://mcaptcha.github.io/browser/ts/docs/modules.html) +![Build)]() +[![dependency status](https://deps.rs/repo/github/mCaptcha/browser/status.svg)](https://deps.rs/repo/github/mCaptcha/browser) +
+[![codecov](https://codecov.io/gh/mCaptcha/browser/branch/master/graph/badge.svg)](https://codecov.io/gh/mCaptcha/browser) + +
+ +**NOTE:** wasm compilation currently requires `rustc` nightly and +wasm optimization of this library will have to be done manually at the +moment. Please refer to https://github.com/rustwasm/wasm-pack/issues/886 +for more information. + +### Optimization: + +``` +$ /path/to/wasm-opt pkg/pow_bg.wasm -o pkg/pow_bg.wasm -O --enable-mutable-globals +``` + +My `/path/to/wasm-opt` is `~/.cache/.wasm-pack/wasm-opt-4d7a65327e9363b7/wasm-opt` + +--- + +

Default documentation provided by Rust wasm:

+ +

+ Tutorial + | + Chat +

+ +Built with 🦀🕸 by The Rust and WebAssembly Working Group + + + +## About + +[**📚 Read this template tutorial! 📚**][template-docs] + +This template is designed for compiling Rust libraries into WebAssembly and +publishing the resulting package to NPM. + +Be sure to check out [other `wasm-pack` tutorials online][tutorials] for other +templates and usages of `wasm-pack`. + +[tutorials]: https://rustwasm.github.io/docs/wasm-pack/tutorials/index.html +[template-docs]: https://rustwasm.github.io/docs/wasm-pack/tutorials/npm-browser-packages/index.html + +## 🚴 Usage + +### 🐑 Use `cargo generate` to Clone this Template + +[Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate) + +``` +cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project +cd my-project +``` + +### 🛠️ Build with `wasm-pack build` + +``` +wasm-pack build +``` + +### 🔬 Test in Headless Browsers with `wasm-pack test` + +``` +wasm-pack test --headless --firefox +``` + +### 🎁 Publish to NPM with `wasm-pack publish` + +``` +wasm-pack publish +``` + +## 🔋 Batteries Included + +- [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating + between WebAssembly and JavaScript. +- [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook) + for logging panic messages to the developer console. +- [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized + for small code size. diff --git a/browser/src/lib.rs b/browser/src/lib.rs new file mode 100644 index 00000000..ed974858 --- /dev/null +++ b/browser/src/lib.rs @@ -0,0 +1,154 @@ +/* + * mCaptcha is a PoW based DoS protection software. + * This is the frontend web component of the mCaptcha system + * Copyright © 2021 Aravinth Manivnanan . + * + * Use of this source code is governed by Apache 2.0 or MIT license. + * You shoud have received a copy of MIT and Apache 2.0 along with + * this program. If not, see for + * MIT or for Apache. + */ +//! mCaptcha is a proof of work based Denaial-of-Service attack protection system. +//! This is is a WASM library that you can embed in your frontend code to protect your +//! service. +//! +//! A commercial managed solution is in the works but I'd much rather prefer +//! folks host their own instances as it will make the more decentralized and free. +//! +//! ## Workflow: +//! mCaptcha workflow in the frontend is simple. +//! 1. Call service to get a proof of work(PoW) configuration +//! 2. Call into mCaptcha to get PoW +//! 3. Send PoW to mCaptcha service +//! 4. If proof is valid, the service will return a token to the client +//! 5. Submit token to your backend along with your app data(if any) +//! 6. In backend, validate client's token with mCaptcha service +//! +//! ## Example: +//! +//! generate proof-of-work +//! ```rust +//! fn main() { +//! use mcaptcha_browser::*; +//! use pow_sha256::*; +//! +//! +//! // salt using which PoW should be computed +//! const SALT: &str = "yrandomsaltisnotlongenoug"; +//! // one-time phrase over which PoW should be computed +//! const PHRASE: &str = "ironmansucks"; +//! // and the difficulty factor +//! const DIFFICULTY: u32 = 1000; +//! +//! // currently gen_pow() returns a JSON formated string to better communicate +//! // with JavaScript. See [PoW][pow_sha256::PoW] for schema +//! let serialised_work = gen_pow(SALT.into(), PHRASE.into(), DIFFICULTY); +//! +//! +//! let work: Work = serde_json::from_str(&serialised_work).unwrap(); +//! +//! let work = PoWBuilder::default() +//! .result(work.result) +//! .nonce(work.nonce) +//! .build() +//! .unwrap(); +//! +//! let config = ConfigBuilder::default().salt(SALT.into()).build().unwrap(); +//! assert!(config.is_valid_proof(&work, &PHRASE.to_string())); +//! assert!(config.is_sufficient_difficulty(&work, DIFFICULTY)); +//! } +//! ``` + +use serde::{Deserialize, Serialize}; +use wasm_bindgen::prelude::*; + +// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global +// allocator. +#[cfg(feature = "wee_alloc")] +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + +use pow_sha256::{ConfigBuilder, PoW}; + +#[derive(Deserialize, Serialize)] +pub struct Work { + pub result: String, + pub nonce: u64, +} + +impl From> for Work { + fn from(p: PoW) -> Self { + Work { + result: p.result, + nonce: p.nonce, + } + } +} + +/// generate proof-of-work +/// ```rust +/// fn main() { +/// use mcaptcha_browser::*; +/// use pow_sha256::*; +/// +/// +/// // salt using which PoW should be computed +/// const SALT: &str = "yrandomsaltisnotlongenoug"; +/// // one-time phrase over which PoW should be computed +/// const PHRASE: &str = "ironmansucks"; +/// // and the difficulty factor +/// const DIFFICULTY: u32 = 1000; +/// +/// // currently gen_pow() returns a JSON formated string to better communicate +/// // with JavaScript. See [PoW][pow_sha256::PoW] for schema +/// let serialised_work = gen_pow(SALT.into(), PHRASE.into(), DIFFICULTY); +/// +/// +/// let work: Work = serde_json::from_str(&serialised_work).unwrap(); +/// +/// let work = PoWBuilder::default() +/// .result(work.result) +/// .nonce(work.nonce) +/// .build() +/// .unwrap(); +/// +/// let config = ConfigBuilder::default().salt(SALT.into()).build().unwrap(); +/// assert!(config.is_valid_proof(&work, &PHRASE.to_string())); +/// assert!(config.is_sufficient_difficulty(&work, DIFFICULTY)); +/// } +/// ``` +#[wasm_bindgen] +pub fn gen_pow(salt: String, phrase: String, difficulty_factor: u32) -> String { + let config = ConfigBuilder::default().salt(salt).build().unwrap(); + + let work = config.prove_work(&phrase, difficulty_factor).unwrap(); + let work: Work = work.into(); + + let payload = serde_json::to_string(&work).unwrap(); + payload +} + +#[cfg(test)] +mod tests { + use super::*; + use pow_sha256::PoWBuilder; + + const SALT: &str = "yrandomsaltisnotlongenoug"; + const PHRASE: &str = "ironmansucks"; + const DIFFICULTY: u32 = 1000; + #[test] + fn it_works() { + let serialised_work = gen_pow(SALT.into(), PHRASE.into(), DIFFICULTY); + let work: Work = serde_json::from_str(&serialised_work).unwrap(); + + let work = PoWBuilder::default() + .result(work.result) + .nonce(work.nonce) + .build() + .unwrap(); + + let config = ConfigBuilder::default().salt(SALT.into()).build().unwrap(); + assert!(config.is_valid_proof(&work, &PHRASE.to_string())); + assert!(config.is_sufficient_difficulty(&work, DIFFICULTY)); + } +} diff --git a/browser/static/embeded.html b/browser/static/embeded.html new file mode 100644 index 00000000..5e937f3f --- /dev/null +++ b/browser/static/embeded.html @@ -0,0 +1,49 @@ + + + + + + mcaptcha demo form + + +
+ +
+
+
+ + +
+ +
+ +
+ +
+ + diff --git a/browser/static/index.html b/browser/static/index.html new file mode 100644 index 00000000..0b3475db --- /dev/null +++ b/browser/static/index.html @@ -0,0 +1,124 @@ + + + + + + mCaptcha + + +
+ + + +
+ + + + diff --git a/browser/tests/web.rs b/browser/tests/web.rs new file mode 100644 index 00000000..b16843df --- /dev/null +++ b/browser/tests/web.rs @@ -0,0 +1,42 @@ +/* + * mCaptcha is a PoW based DoS protection software. + * This is the frontend web component of the mCaptcha system + * Copyright © 2021 Aravinth Manivnanan . + * + * Use of this source code is governed by Apache 2.0 or MIT license. + * You shoud have received a copy of MIT and Apache 2.0 along with + * this program. If not, see for + * MIT or for Apache. + */ + +//! Test suite for the Web and headless browsers. + +#![cfg(target_arch = "wasm32")] + +extern crate wasm_bindgen_test; +use wasm_bindgen_test::*; + +wasm_bindgen_test_configure!(run_in_browser); + +#[wasm_bindgen_test] +fn pow_generation_works() { + use mcaptcha_browser::*; + use pow_sha256::*; + + const SALT: &str = "yrandomsaltisnotlongenoug"; + const PHRASE: &str = "ironmansucks"; + const DIFFICULTY: u32 = 1000; + let serialised_work = gen_pow(SALT.into(), PHRASE.into(), DIFFICULTY); + + let work: Work = serde_json::from_str(&serialised_work).unwrap(); + + let work = PoWBuilder::default() + .result(work.result) + .nonce(work.nonce) + .build() + .unwrap(); + + let config = ConfigBuilder::default().salt(SALT.into()).build().unwrap(); + assert!(config.is_valid_proof(&work, &PHRASE.to_string())); + assert!(config.is_sufficient_difficulty(&work, DIFFICULTY)); +} diff --git a/build.rs b/build.rs index b4cf8613..87dcb943 100644 --- a/build.rs +++ b/build.rs @@ -37,20 +37,24 @@ fn main() { } fn cache_bust() { - let types = vec![ - mime::IMAGE_PNG, - mime::IMAGE_SVG, - mime::IMAGE_JPEG, - mime::IMAGE_GIF, - mime::APPLICATION_JAVASCRIPT, - mime::TEXT_CSS, - ]; + // until APPLICATION_WASM gets added to mime crate + // PR: https://github.com/hyperium/mime/pull/138 + // let types = vec![ + // mime::IMAGE_PNG, + // mime::IMAGE_SVG, + // mime::IMAGE_JPEG, + // mime::IMAGE_GIF, + // mime::APPLICATION_JAVASCRIPT, + // mime::TEXT_CSS, + // ]; + + let no_hash = vec!["bundle/6b88f6ccf97567b46745.module.wasm"]; let config = BusterBuilder::default() .source("./static/cache") .result("./assets") - .mime_types(types) .copy(true) + .no_hash(no_hash) .follow_links(true) .build() .unwrap(); diff --git a/src/api/v1/auth.rs b/src/api/v1/auth.rs index 508d1b57..64d0dc81 100644 --- a/src/api/v1/auth.rs +++ b/src/api/v1/auth.rs @@ -226,6 +226,6 @@ async fn signout(id: Identity) -> impl Responder { id.forget(); } HttpResponse::Found() - .append_header((header::LOCATION, "/login")) + .append_header((header::LOCATION, crate::PAGES.auth.login)) .finish() } diff --git a/src/main.rs b/src/main.rs index f03a1776..27b269cb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,6 +63,7 @@ lazy_static! { FILES.get("./static/cache/bundle/bundle.css").unwrap(); pub static ref MOBILE_CSS: &'static str = FILES.get("./static/cache/bundle/mobile.css").unwrap(); + pub static ref VERIFICATIN_WIDGET_JS: &'static str = FILES.get("./static/cache/bundle/verificationWidget.js").unwrap(); pub static ref VERIFICATIN_WIDGET_CSS: &'static str = diff --git a/src/widget/mod.rs b/src/widget/mod.rs index ce81ab74..e766d466 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -14,13 +14,9 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -use std::borrow::Cow; - -use actix_web::body::Body; -use actix_web::{get, http::header, web, HttpResponse, Responder}; +//! User facing CAPTCHA widget +use actix_web::{web, HttpResponse, Responder}; use lazy_static::lazy_static; -use mime_guess::from_path; -use rust_embed::RustEmbed; use sailfish::TemplateOnce; use crate::errors::*; @@ -30,16 +26,12 @@ pub const WIDGET_ROUTES: routes::Widget = routes::Widget::new(); pub mod routes { pub struct Widget { pub verification_widget: &'static str, - pub js: &'static str, - pub wasm: &'static str, } impl Widget { pub const fn new() -> Self { Widget { verification_widget: "/widget", - js: "/widget/bundle.js", - wasm: "/widget/1476099975f2b060264c.module.wasm", } } } @@ -69,37 +61,9 @@ async fn show_widget() -> PageResult { .body(&*INDEX_PAGE)) } -#[derive(RustEmbed)] -#[folder = "static/widget/"] -struct WidgetAssets; - -fn handle_widget_assets(path: &str) -> HttpResponse { - match WidgetAssets::get(path) { - Some(content) => { - let body: Body = match content { - Cow::Borrowed(bytes) => bytes.into(), - Cow::Owned(bytes) => bytes.into(), - }; - - HttpResponse::Ok() - .insert_header(header::CacheControl(vec![ - header::CacheDirective::MaxAge(crate::CACHE_AGE), - ])) - .content_type(from_path(path).first_or_octet_stream().as_ref()) - .body(body) - } - None => HttpResponse::NotFound().body("404 Not Found"), - } -} - -#[get("/widget/{_:.*}")] -pub async fn widget_assets(path: web::Path) -> impl Responder { - handle_widget_assets(&path) -} - +/// widget services pub fn services(cfg: &mut web::ServiceConfig) { cfg.service(show_widget); - cfg.service(widget_assets); } #[cfg(test)] @@ -112,17 +76,6 @@ mod test { #[actix_rt::test] async fn captcha_widget_route_works() { let mut app = get_app!().await; - // let list_sitekey_resp = test::call_service( - // &mut app, - // test::TestRequest::get() - // .uri(crate::WIDGET_ROUTES.verification_widget) - // .to_request(), - // ) - // .await; - // assert_eq!(list_sitekey_resp.status(), StatusCode::OK); - get_works!(app, crate::WIDGET_ROUTES.verification_widget); - get_works!(app, crate::WIDGET_ROUTES.js); - get_works!(app, crate::WIDGET_ROUTES.wasm); } } diff --git a/static/widget/1476099975f2b060264c.module.wasm b/static/widget/1476099975f2b060264c.module.wasm deleted file mode 100644 index 016d531f..00000000 Binary files a/static/widget/1476099975f2b060264c.module.wasm and /dev/null differ diff --git a/static/widget/858fd6c482cc75111d54.module.wasm b/static/widget/858fd6c482cc75111d54.module.wasm deleted file mode 100644 index 183cc571..00000000 Binary files a/static/widget/858fd6c482cc75111d54.module.wasm and /dev/null differ diff --git a/static/widget/bundle.js b/static/widget/bundle.js deleted file mode 100644 index 3bbfe9a0..00000000 --- a/static/widget/bundle.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{"use strict";var e,n,t,r,o,i={180:(e,n,t)=>{t.d(n,{Nj:()=>r,Z6:()=>o,yY:()=>i,yM:()=>a});var r=function(){var e;return function(){if(null==e&&null==(e=new URL(window.location.href).searchParams.get("sitekey")))throw new Error("Define sitekey in query parameter");return e}()},o={getConfig:"/api/v1/pow/config",verififyPoW:"/api/v1/pow/verify"},i=function(){var e;return function(){if(null==e&&null==(e=document.getElementById("widget__verification-checkbox")))throw new Error("mCaptcha button not found)");return e}()},a=function(){var e,n,t,r,o=function(e){return e.style.display="block"},i=function(e){return e.style.display="none"},a=function(){if(null==e){if(null==(e=document.querySelector(".widget__verification-text--before")))throw new Error("before element not found)");return e}},c=function(){if(null==n&&null==(n=document.querySelector(".widget__verification-text--after")))throw new Error("after element not found)");return n},u=function(){if(null==r&&null==(r=document.querySelector(".widget__verification-text--error")))throw new Error("before error not found)");return r},l=function(){if(null==t&&null==(t=document.querySelector(".widget__verification-text--during")))throw new Error("before during not found)");return t};return{before:function(){o(a()),i(c()),i(l()),i(u())},after:function(){i(a()),o(c()),i(l()),i(u())},during:function(){i(a()),i(c()),o(l()),i(u())},error:function(){i(a()),i(c()),i(l()),o(u())}}}},731:(e,n,t)=>{t.d(n,{Z:()=>i});var r=t(525),o=t(180);const i=function(){return e=void 0,n=void 0,i=function(){var e,n,t;return function(e,n){var t,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){return function(i){if(t)throw new TypeError("Generator is already executing.");for(;a;)try{if(t=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!((o=(o=a.trys).length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]{t.a(e,(async e=>{var n=t(720),r=t(731),o=t(792),i=t(243),a=t(180),c=e([n]);n=(c.then?await c:c)[0];var u=!1,l=function(e){return t=void 0,c=void 0,s=function(){var t,c,l,s;return function(e,n){var t,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){return function(i){if(t)throw new TypeError("Generator is already executing.");for(;a;)try{if(t=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!((o=(o=a.trys).length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]{t.a(e,(async e=>{t.d(n,{Z:()=>a});var r=t(838),o=t(180),i=e([r]);r=(i.then?await i:i)[0];const a=function(e){return n=void 0,t=void 0,a=function(){var n,t;return function(e,n){var t,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){return function(i){if(t)throw new TypeError("Generator is already executing.");for(;a;)try{if(t=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!((o=(o=a.trys).length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]{t.d(n,{Z:()=>r});const r=function(e){window.parent.postMessage(e,"*")}},792:(e,n,t)=>{t.d(n,{Z:()=>i});var r=t(525),o=t(180);const i=function(e){return n=void 0,t=void 0,a=function(){var n,t,i,a;return function(e,n){var t,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){return function(i){if(t)throw new TypeError("Generator is already executing.");for(;a;)try{if(t=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!((o=(o=a.trys).length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]{t.d(n,{Z:()=>r});const r=function(e){return{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}}},838:(e,n,t)=>{t.a(e,(async r=>{t.d(n,{I:()=>y});var o=t(716);e=t.hmd(e);var i=r([o]);o=(i.then?await i:i)[0];let a=0,c=null;function u(){return null!==c&&c.buffer===o.memory.buffer||(c=new Uint8Array(o.memory.buffer)),c}let l=new("undefined"==typeof TextEncoder?(0,e.require)("util").TextEncoder:TextEncoder)("utf-8");const s="function"==typeof l.encodeInto?function(e,n){return l.encodeInto(e,n)}:function(e,n){const t=l.encode(e);return n.set(t),{read:e.length,written:t.length}};function f(e,n,t){if(void 0===t){const t=l.encode(e),r=n(t.length);return u().subarray(r,r+t.length).set(t),a=t.length,r}let r=e.length,o=n(r);const i=u();let c=0;for(;c127)break;i[o+c]=n}if(c!==r){0!==c&&(e=e.slice(c)),o=t(o,r,r=c+3*e.length);const n=u().subarray(o+c,o+r);c+=s(e,n).written}return a=c,o}let d=null;function p(){return null!==d&&d.buffer===o.memory.buffer||(d=new Int32Array(o.memory.buffer)),d}let h=new("undefined"==typeof TextDecoder?(0,e.require)("util").TextDecoder:TextDecoder)("utf-8",{ignoreBOM:!0,fatal:!0});function y(e,n,t){try{const w=o.__wbindgen_add_to_stack_pointer(-16);var r=f(e,o.__wbindgen_malloc,o.__wbindgen_realloc),i=a,c=f(n,o.__wbindgen_malloc,o.__wbindgen_realloc),l=a;o.gen_pow(w,r,i,c,l,t);var s=p()[w/4+0],d=p()[w/4+1];return y=s,b=d,h.decode(u().subarray(y,y+b))}finally{o.__wbindgen_add_to_stack_pointer(16),o.__wbindgen_free(s,d)}var y,b}h.decode()}))},716:(e,n,t)=>{e.exports=t.v(n,e.id,"858fd6c482cc75111d54")}},a={};function c(e){var n=a[e];if(void 0!==n)return n.exports;var t=a[e]={id:e,loaded:!1,exports:{}};return i[e](t,t.exports,c),t.loaded=!0,t.exports}e="function"==typeof Symbol?Symbol("webpack then"):"__webpack_then__",n="function"==typeof Symbol?Symbol("webpack exports"):"__webpack_exports__",t=e=>{e&&(e.forEach((e=>e.r--)),e.forEach((e=>e.r--?e.r++:e())))},r=e=>!--e.r&&e(),o=(e,n)=>e?e.push(n):r(n),c.a=(i,a,c)=>{var u,l,s,f=c&&[],d=i.exports,p=!0,h=!1,y=(n,t,r)=>{h||(h=!0,t.r+=n.length,n.map(((n,o)=>n[e](t,r))),h=!1)},b=new Promise(((e,n)=>{s=n,l=()=>(e(d),t(f),f=0)}));b[n]=d,b[e]=(e,n)=>{if(p)return r(e);u&&y(u,e,n),o(f,e),b.catch(n)},i.exports=b,a((i=>{if(!i)return l();var a,c;u=(i=>i.map((i=>{if(null!==i&&"object"==typeof i){if(i[e])return i;if(i.then){var a=[];i.then((e=>{c[n]=e,t(a),a=0}));var c={[e]:(e,n)=>(o(a,e),i.catch(n))};return c}}return{[e]:e=>r(e),[n]:i}})))(i);var s=new Promise(((e,t)=>{(a=()=>e(c=u.map((e=>e[n])))).r=0,y(u,a,t)}));return a.r?s:c})).then(l,s),p=!1},c.d=(e,n)=>{for(var t in n)c.o(n,t)&&!c.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:n[t]})},c.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),c.hmd=e=>((e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set:()=>{throw new Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e),c.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),(()=>{var e;c.g.importScripts&&(e=c.g.location+"");var n=c.g.document;if(!e&&n&&(n.currentScript&&(e=n.currentScript.src),!e)){var t=n.getElementsByTagName("script");t.length&&(e=t[t.length-1].src)}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),c.p=e})(),c.v=(e,n,t,r)=>{var o=fetch(c.p+""+t+".module.wasm");return"function"==typeof WebAssembly.instantiateStreaming?WebAssembly.instantiateStreaming(o,r).then((n=>Object.assign(e,n.instance.exports))):o.then((e=>e.arrayBuffer())).then((e=>WebAssembly.instantiate(e,r))).then((n=>Object.assign(e,n.instance.exports)))},c(404)})(); \ No newline at end of file diff --git a/templates/widget/footer.html b/templates/widget/footer.html index ca99118b..ff0f908f 100644 --- a/templates/widget/footer.html +++ b/templates/widget/footer.html @@ -5,7 +5,5 @@ href="<.= &*crate::VERIFICATIN_WIDGET_CSS .>" /> - - diff --git a/templates/widget/js/const.ts b/templates/widget/js/const.ts new file mode 100644 index 00000000..0dbf7469 --- /dev/null +++ b/templates/widget/js/const.ts @@ -0,0 +1,152 @@ +/* + * mCaptcha is a PoW based DoS protection software. + * This is the frontend web component of the mCaptcha system + * Copyright © 2021 Aravinth Manivnanan . + * + * Use of this source code is governed by Apache 2.0 or MIT license. + * You shoud have received a copy of MIT and Apache 2.0 along with + * this program. If not, see for + * MIT or for Apache. + */ + +/** mcaptcha checkbox ID **/ +export const btnId = 'widget__verification-checkbox'; + +/** get sitekey */ +export const sitekey = () => { + let sitekey; + return (() => { + if (sitekey === null || sitekey === undefined) { + sitekey = new URL(window.location.href).searchParams.get('sitekey'); + if (sitekey === null || sitekey === undefined) { + throw new Error(`Define sitekey in query parameter`); + } + } + return sitekey; + })(); +}; + +/** mCaptcha API routes */ +export const ROUTES = (() => { + const getConfig = '/api/v1/pow/config'; + const verififyPoW = '/api/v1/pow/verify'; + + return { + /** get URL to fetch PoW configuration */ + getConfig, + /** get URL to verify PoW*/ + verififyPoW, + }; +})(); + +/** get mCaptcha verifify checkbox button */ +export const btn = () => { + let btn; + return (() => { + if (btn === null || btn === undefined) { + btn = document.getElementById(btnId); + if (btn === null || btn === undefined) { + throw new Error(`mCaptcha button not found)`); + } + } + return btn; + })(); +}; + +export const messageText = () => { + let beforeClass = 'widget__verification-text--before'; + let duringClass = 'widget__verification-text--during'; + let errorClass = 'widget__verification-text--error'; + let afterClass = 'widget__verification-text--after'; + + let before: HTMLElement; + let after: HTMLElement; + let during: HTMLElement; + let error: HTMLElement; + + /** runner fn to display HTMLElement **/ + const showMsg = (e: HTMLElement) => (e.style.display = 'block'); + /** runner fn to hide HTMLElement **/ + const hideMsg = (e: HTMLElement) => (e.style.display = 'none'); + + /** lazy init and get before elementt **/ + const getBefore = () => { + if (before === null || before === undefined) { + before = document.querySelector(`.${beforeClass}`); + if (before === null || before === undefined) { + throw new Error(`before element not found)`); + } + return before; + } + }; + + /** lazy init and get after elementt **/ + const getAfter = () => { + if (after === null || after === undefined) { + after = document.querySelector(`.${afterClass}`); + if (after === null || after === undefined) { + throw new Error(`after element not found)`); + } + } + + return after; + }; + + /** lazy init and get error elementt **/ + const getError = () => { + if (error === null || error === undefined) { + error = document.querySelector(`.${errorClass}`); + if (error === null || error === undefined) { + throw new Error(`before error not found)`); + } + } + return error; + }; + + /** lazy init and get during elementt **/ + const getDuring = () => { + if (during === null || during === undefined) { + during = document.querySelector(`.${duringClass}`); + if (during === null || during === undefined) { + throw new Error(`before during not found)`); + } + } + + return during; + }; + return { + /** display "before" message **/ + before: () => { + showMsg(getBefore()); + hideMsg(getAfter()); + hideMsg(getDuring()); + hideMsg(getError()); + }, + + /** display "after" message **/ + after: () => { + hideMsg(getBefore()); + showMsg(getAfter()); + hideMsg(getDuring()); + hideMsg(getError()); + }, + + /** display "during" message **/ + during: () => { + hideMsg(getBefore()); + hideMsg(getAfter()); + showMsg(getDuring()); + hideMsg(getError()); + }, + + /** display "error" message **/ + error: () => { + hideMsg(getBefore()); + hideMsg(getAfter()); + hideMsg(getDuring()); + showMsg(getError()); + }, + }; +}; + +export const inputId = 'mcaptcha-response'; diff --git a/templates/widget/js/fetchPoWConfig.ts b/templates/widget/js/fetchPoWConfig.ts new file mode 100644 index 00000000..1a3ae779 --- /dev/null +++ b/templates/widget/js/fetchPoWConfig.ts @@ -0,0 +1,48 @@ +/* + * mCaptcha is a PoW based DoS protection software. + * This is the frontend web component of the mCaptcha system + * Copyright © 2021 Aravinth Manivnanan . + * + * Use of this source code is governed by Apache 2.0 or MIT license. + * You shoud have received a copy of MIT and Apache 2.0 along with + * this program. If not, see for + * MIT or for Apache. + */ + +import genJsonPayload from './utils/genJsonPayload'; +import * as CONST from './const'; + +type GetConfigPayload = { + key: string; +}; + +export type PoWConfig = { + string: string; + difficulty_factor: number; + salt: string; +}; + +/** + * fetch proof-of-work configuration + * @returns {PoWConfig} pow config + * */ +export const fetchPoWConfig = async () => { + try { + const payload: GetConfigPayload = { + key: CONST.sitekey(), + }; + + const res = await fetch(CONST.ROUTES.getConfig, genJsonPayload(payload)); + if (res.ok) { + const config: PoWConfig = await res.json(); + return config; + } else { + const err = await res.json(); + throw new Error(err); + } + } catch (err) { + throw err; + } +}; + +export default fetchPoWConfig; diff --git a/templates/widget/js/index.ts b/templates/widget/js/index.ts new file mode 100644 index 00000000..071409fe --- /dev/null +++ b/templates/widget/js/index.ts @@ -0,0 +1,68 @@ +/* + * mCaptcha is a PoW based DoS protection software. + * This is the frontend web component of the mCaptcha system + * Copyright © 2021 Aravinth Manivnanan . + * + * Use of this source code is governed by Apache 2.0 or MIT license. + * You shoud have received a copy of MIT and Apache 2.0 along with + * this program. If not, see for + * MIT or for Apache. + */ + +import prove from './prove'; +import fetchPoWConfig from './fetchPoWConfig'; +import sendWork from './sendWork'; +import sendToParent from './sendToParent'; +import * as CONST from './const'; + +import '../main.scss'; + +let LOCK = false; + +/** add mcaptcha widget element to DOM */ +export const registerVerificationEventHandler = () => { + const verificationContainer = ( + document.querySelector('.widget__verification-container') + ); + verificationContainer.style.display = 'flex'; + CONST.btn().addEventListener('click', e => solveCaptchaRunner(e)); +}; + +export const solveCaptchaRunner = async (e: Event) => { + if (LOCK) { + e.preventDefault(); + return; + } + + try { + LOCK = true; + if (CONST.btn().checked == false) { + CONST.messageText().before(); + LOCK = false; + return; + } + e.preventDefault(); + // steps: + + // 1. show during + CONST.messageText().during(); + // 1. get config + const config = await fetchPoWConfig(); + // 2. prove work + const proof = await prove(config); + // 3. submit work + const token = await sendWork(proof); + // 4. send token + sendToParent(token); + // 5. mark checkbox checked + CONST.btn().checked = true; + CONST.messageText().after(); + LOCK = false; + } catch (e) { + CONST.messageText().error(); + console.error(e); + LOCK = false; + } +}; + +registerVerificationEventHandler(); diff --git a/templates/widget/js/prove.ts b/templates/widget/js/prove.ts new file mode 100644 index 00000000..3d4c1553 --- /dev/null +++ b/templates/widget/js/prove.ts @@ -0,0 +1,55 @@ +/* + * mCaptcha is a PoW based DoS protection software. + * This is the frontend web component of the mCaptcha system + * Copyright © 2021 Aravinth Manivnanan . + * + * Use of this source code is governed by Apache 2.0 or MIT license. + * You shoud have received a copy of MIT and Apache 2.0 along with + * this program. If not, see for + * MIT or for Apache. + */ + +import {gen_pow} from '../../../browser/pkg/index'; +import {PoWConfig} from './fetchPoWConfig'; +import * as CONST from './const'; + +export type Work = { + result: string; + nonce: number; + string: string; + key: string; +}; + +type WasmWork = { + result: string; + nonce: number; +}; + +/** + * proove work + * @param {PoWConfig} config - the proof-of-work configuration using which + * work needs to be computed + * */ +const prove = async (config: PoWConfig) => { + try { + const proofString = gen_pow( + config.salt, + config.string, + config.difficulty_factor, + ); + const proof: WasmWork = JSON.parse(proofString); + + const res: Work = { + key: CONST.sitekey(), + string: config.string, + nonce: proof.nonce, + result: proof.result, + }; + + return res; + } catch (err) { + throw err; + } +}; + +export default prove; diff --git a/templates/widget/js/sendToParent.ts b/templates/widget/js/sendToParent.ts new file mode 100644 index 00000000..bfdf6c19 --- /dev/null +++ b/templates/widget/js/sendToParent.ts @@ -0,0 +1,24 @@ +/* + * mCaptcha is a PoW based DoS protection software. + * This is the frontend web component of the mCaptcha system + * Copyright © 2021 Aravinth Manivnanan . + * + * Use of this source code is governed by Apache 2.0 or MIT license. + * You shoud have received a copy of MIT and Apache 2.0 along with + * this program. If not, see for + * MIT or for Apache. + */ +import {Token} from './sendWork'; + +/** + * send pow validation token as message to parant of the iframe + * @param {Token} token: token received from mCaptcha service + * upon successful PoW validation + * */ +export const sendToParent = (token: Token) => { + window.parent.postMessage(token, '*'); + // TODO set origin. Make parent send origin as query parameter + // or as a message to iframe +}; + +export default sendToParent; diff --git a/templates/widget/js/sendWork.ts b/templates/widget/js/sendWork.ts new file mode 100644 index 00000000..2c7f2c96 --- /dev/null +++ b/templates/widget/js/sendWork.ts @@ -0,0 +1,42 @@ +/* + * mCaptcha is a PoW based DoS protection software. + * This is the frontend web component of the mCaptcha system + * Copyright © 2021 Aravinth Manivnanan . + * + * Use of this source code is governed by Apache 2.0 or MIT license. + * You shoud have received a copy of MIT and Apache 2.0 along with + * this program. If not, see for + * MIT or for Apache. + */ + +import genJsonPayload from './utils/genJsonPayload'; +import * as CONST from './const'; +import {Work} from './prove'; + +export type Token = { + token: string; +}; + +export const sendWork = async (payload: Work) => { + try { + const res = await fetch(CONST.ROUTES.verififyPoW, genJsonPayload(payload)); + if (res.ok) { + console.debug('work verified'); + const token: Token = await res.json(); + console.debug(`token ${token.token}`); + return token; + } else { + const err = await res.json(); + console.error(`error: ${err.error}`); + throw new Error(err); + } + } catch (err) { + CONST.messageText().error(); + console.error(err); + await new Promise(r => setTimeout(r, 1000)); + window.location.reload(); + throw err; + } +}; + +export default sendWork; diff --git a/templates/widget/js/tests/const.test.ts b/templates/widget/js/tests/const.test.ts new file mode 100644 index 00000000..66521349 --- /dev/null +++ b/templates/widget/js/tests/const.test.ts @@ -0,0 +1,50 @@ +/* + * mCaptcha is a PoW based DoS protection software. + * This is the frontend web component of the mCaptcha system + * Copyright © 2021 Aravinth Manivnanan . + * + * Use of this source code is governed by Apache 2.0 or MIT license. + * You shoud have received a copy of MIT and Apache 2.0 along with + * this program. If not, see for + * MIT or for Apache. + */ +import * as CONST from '../const'; + +import {getBaseHtml, sitekey, checkbox} from './setupTests'; +import * as TESTElements from './setupTests'; + +it('const works', () => { + const body = document.querySelector('body'); + const container = getBaseHtml(); + body.appendChild(container); + expect(CONST.sitekey()).toBe(sitekey); + expect(CONST.btn()).toBe(checkbox); + + // display after + CONST.messageText().after(); + expect(TESTElements.afterMsg.style.display).toBe('block'); + expect(TESTElements.beforeMsg.style.display).toBe('none'); + expect(TESTElements.duringMsg.style.display).toBe('none'); + expect(TESTElements.errorMsg.style.display).toBe('none'); + + // display before + CONST.messageText().before(); + expect(TESTElements.afterMsg.style.display).toBe('none'); + expect(TESTElements.beforeMsg.style.display).toBe('block'); + expect(TESTElements.duringMsg.style.display).toBe('none'); + expect(TESTElements.errorMsg.style.display).toBe('none'); + + // display during + CONST.messageText().during(); + expect(TESTElements.afterMsg.style.display).toBe('none'); + expect(TESTElements.beforeMsg.style.display).toBe('none'); + expect(TESTElements.duringMsg.style.display).toBe('block'); + expect(TESTElements.errorMsg.style.display).toBe('none'); + + // display error + CONST.messageText().error(); + expect(TESTElements.afterMsg.style.display).toBe('none'); + expect(TESTElements.beforeMsg.style.display).toBe('none'); + expect(TESTElements.duringMsg.style.display).toBe('none'); + expect(TESTElements.errorMsg.style.display).toBe('block'); +}); diff --git a/templates/widget/js/tests/setupTests.ts b/templates/widget/js/tests/setupTests.ts new file mode 100644 index 00000000..c61ff110 --- /dev/null +++ b/templates/widget/js/tests/setupTests.ts @@ -0,0 +1,40 @@ +/* + * mCaptcha is a PoW based DoS protection software. + * This is the frontend web component of the mCaptcha system + * Copyright © 2021 Aravinth Manivnanan . + * + * Use of this source code is governed by Apache 2.0 or MIT license. + * You shoud have received a copy of MIT and Apache 2.0 along with + * this program. If not, see for + * MIT or for Apache. + */ +import * as CONST from '../const'; + +export const sitekey = 'imbatman'; + +export const checkbox = document.createElement('input'); +checkbox.type = 'checkbox'; +checkbox.id = CONST.btnId; + +const getMessages = (state: string) => { + const msg = document.createElement('span'); + msg.className = `widget__verification-text--${state}`; + return msg; +}; + +export const beforeMsg = getMessages('before'); +export const afterMsg = getMessages('after'); +export const duringMsg = getMessages('during'); +export const errorMsg = getMessages('error'); + +/** get base HTML with empty mCaptcha container */ +export const getBaseHtml = () => { + const form = document.createElement('form'); + form.appendChild(checkbox); + form.appendChild(beforeMsg); + form.appendChild(duringMsg); + form.appendChild(afterMsg); + form.appendChild(errorMsg); + + return form; +}; diff --git a/templates/widget/js/utils/genJsonPayload.test.ts b/templates/widget/js/utils/genJsonPayload.test.ts new file mode 100644 index 00000000..0524222f --- /dev/null +++ b/templates/widget/js/utils/genJsonPayload.test.ts @@ -0,0 +1,30 @@ +/* + * mCaptcha is a PoW based DoS protection software. + * This is the frontend web component of the mCaptcha system + * Copyright © 2021 Aravinth Manivnanan . + * + * Use of this source code is governed by Apache 2.0 or MIT license. + * You shoud have received a copy of MIT and Apache 2.0 along with + * this program. If not, see for + * MIT or for Apache. + */ + +import genJsonPayload from './genJsonPayload'; + +'use strict'; + +const payload = { + username: 'Jhon', +}; + +const value = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), +}; + +it('getFromUrl workds', () => { + expect(genJsonPayload(payload)).toEqual(value); +}); diff --git a/templates/widget/js/utils/genJsonPayload.ts b/templates/widget/js/utils/genJsonPayload.ts new file mode 100644 index 00000000..8da5220f --- /dev/null +++ b/templates/widget/js/utils/genJsonPayload.ts @@ -0,0 +1,23 @@ +/* + * mCaptcha is a PoW based DoS protection software. + * This is the frontend web component of the mCaptcha system + * Copyright © 2021 Aravinth Manivnanan . + * + * Use of this source code is governed by Apache 2.0 or MIT license. + * You shoud have received a copy of MIT and Apache 2.0 along with + * this program. If not, see for + * MIT or for Apache. + */ + +const genJsonPayload = (payload: any) => { + const value = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }; + return value; +}; + +export default genJsonPayload; diff --git a/webpack.config.js b/webpack.config.js index 470c0251..222a7a14 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -11,7 +11,7 @@ module.exports = { entry: { bundle: './templates/index.ts', mobile: './templates/mobile.ts', - verificationWidget: './templates/widget/index.ts', + verificationWidget: './templates/widget/js/index.ts', }, output: { filename: '[name].js', @@ -45,10 +45,10 @@ module.exports = { plugins: [ new MiniCssExtractPlugin(), -// new WasmPackPlugin({ -// crateDirectory: __dirname, -// outName: "pow.wasm", -// }), + // new WasmPackPlugin({ + // crateDirectory: __dirname, + // outName: "pow.wasm", + // }), ], optimization: { minimizer: [ @@ -57,13 +57,13 @@ module.exports = { new CssMinimizerPlugin(), ], }, -// experiments: { -// // executeModule: true, -// // outputModule: true, -// //syncWebAssembly: true, -// // topLevelAwait: true, -// asyncWebAssembly: true, -// // layers: true, -// // lazyCompilation: true, -// }, + experiments: { + // executeModule: true, + // outputModule: true, + //syncWebAssembly: true, + // topLevelAwait: true, + asyncWebAssembly: true, + // layers: true, + // lazyCompilation: true, + }, };