mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2025-04-30 15:40:41 +02:00
Compare commits
143 Commits
release/0.
...
developmen
Author | SHA1 | Date | |
---|---|---|---|
314145084e | |||
5e0d615ada | |||
e4b7aa8766 | |||
bbf0e77571 | |||
40ca2acdb7 | |||
3e29df8b2c | |||
fdde8a933c | |||
|
c8f0337c1e | ||
|
151c04d9df | ||
|
1d2ba9eddc | ||
|
ee21f74d2d | ||
|
06083a63e3 | ||
57a0436ca1 | |||
|
8b15acf983 | ||
|
40ba114e61 | ||
|
c2c34ede67 | ||
|
2b0fe0e868 | ||
|
fbfb76c34e | ||
|
971dee36fd | ||
|
41983e6039 | ||
|
ca25cd83d0 | ||
|
9805f4ee04 | ||
|
66877159f0 | ||
|
a9143b0cdd | ||
|
165b269d4f | ||
|
e35e2b7334 | ||
|
98c2e3fd01 | ||
|
27f5413e3d | ||
|
55e9bf6e2b | ||
|
4cdbfd8925 | ||
|
7a85667a44 | ||
|
cf3853263a | ||
|
946a08c19c | ||
|
e42a32934a | ||
|
24c02fccff | ||
|
beecb54d38 | ||
|
0716a75ee6 | ||
|
0380e02f3f | ||
|
0d2cd6f376 | ||
|
410ed8cb33 | ||
|
7a941c3338 | ||
|
8776fa3ca2 | ||
|
5c4cb32d1a | ||
|
1971515601 | ||
|
0ed53f5cc9 | ||
|
47524ef038 | ||
|
a8cc1be87d | ||
|
95ee4228bd | ||
|
3cf152a164 | ||
|
29bfe61a2c | ||
|
ec1cac9443 | ||
|
f3278fcf05 | ||
|
e3423c7786 | ||
|
9346e433e1 | ||
|
aeaae4cd7b | ||
|
218a316571 | ||
|
70c94feced | ||
|
1fc13405e8 | ||
|
a79293add1 | ||
|
7a0a50dc3f | ||
|
c9a8ef7db4 | ||
|
e1377d0f79 | ||
|
257fbf5506 | ||
|
1ff3f2afb7 | ||
|
13bbe2bee9 | ||
|
fac0a9ba94 | ||
|
57fb279092 | ||
|
77e0935945 | ||
|
e7358838d5 | ||
|
3075e1c027 | ||
|
7e113bab47 | ||
|
ff727b6d97 | ||
|
2f5f7cb0d7 | ||
|
567df800f3 | ||
|
9a86bae45a | ||
|
18d69063fd | ||
|
2d8d6f9938 | ||
|
aef36fa3d4 | ||
|
287ca9806d | ||
|
35c9f45f6d | ||
|
ee0593dc6f | ||
|
8a35818b4f | ||
|
df7bd80d06 | ||
|
58f40d98ed | ||
|
728c33f444 | ||
|
a66303566a | ||
|
6d8d1384d9 | ||
|
9100811c50 | ||
|
5f2214abe9 | ||
|
17fd08b7e5 | ||
|
a43c38c118 | ||
|
84a4e9791e | ||
|
e7828cd7f5 | ||
|
7861568ca1 | ||
|
2cb7a28967 | ||
|
a436b93e56 | ||
|
50b4394cfd | ||
|
360d6bfced | ||
|
0f264bed0e | ||
|
cd052fcaf0 | ||
|
78bd75ae05 | ||
|
523c091284 | ||
|
7784313a95 | ||
|
66c8ed4a8c | ||
|
689c2b5353 | ||
|
aa71c6bf4a | ||
|
d35477c806 | ||
|
f905b1f375 | ||
|
25df5bf5b2 | ||
|
c435f76d08 | ||
|
d591daa884 | ||
|
bfde6c03dc | ||
|
67ff33ba79 | ||
|
b30dc0033b | ||
|
3c9777e3cf | ||
|
d132b8f172 | ||
|
938e1ade28 | ||
|
2a1e4c59bc | ||
|
2479a6972d | ||
|
59736c088d | ||
|
ae94ba0be6 | ||
|
67b46a85bb | ||
|
eb8aa5a352 | ||
|
26608bdf21 | ||
|
96bed54c29 | ||
|
38869c6623 | ||
|
a5f5209371 | ||
|
cb63d3fef8 | ||
|
c4d74115f8 | ||
|
57b98cf15e | ||
|
4265393c30 | ||
|
08d7512d01 | ||
|
d6858ab5a5 | ||
|
3078d5dab8 | ||
|
481649c8d2 | ||
|
f1c726f672 | ||
|
ce204b9bc1 | ||
|
be5a600abf | ||
|
9e2be12fbd | ||
|
76f59d7196 | ||
|
5f7397588a | ||
|
212f657289 | ||
|
2d9f30b55b |
@ -1,2 +0,0 @@
|
|||||||
[build]
|
|
||||||
rustflags = ["-C", "target-cpu=native"]
|
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,5 +1 @@
|
|||||||
/target
|
/target
|
||||||
**/*.rs.bk
|
|
||||||
tags
|
|
||||||
/.idea/
|
|
||||||
|
|
||||||
|
445
.gitlab-ci.yml
445
.gitlab-ci.yml
@ -1,121 +1,350 @@
|
|||||||
# Official language image. Look for the different tagged releases at:
|
# Define slightly different stages.
|
||||||
# https://hub.docker.com/r/library/rust/tags/
|
# Additionally, lint the code before anything else to fail more quickly
|
||||||
image: "rust:latest"
|
stages:
|
||||||
|
- lint
|
||||||
|
- check
|
||||||
|
- build
|
||||||
|
- test
|
||||||
|
- release
|
||||||
|
- dockerify
|
||||||
|
|
||||||
# Optional: Pick zero or more services to be used on all builds.
|
default:
|
||||||
# Only needed when using a docker container to run your tests in.
|
image: "registry.gitlab.com/fabinfra/rust-builder:latest"
|
||||||
# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service
|
tags:
|
||||||
# services:
|
- linux
|
||||||
# - mysql:latest
|
- docker
|
||||||
# - redis:latest
|
- fabinfra
|
||||||
# - postgres:latest
|
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
GIT_SUBMODULE_STRATEGY: recursive
|
GIT_SUBMODULE_STRATEGY: recursive
|
||||||
# CARGO_HOME: $CI_PROJECT_DIR/cargo
|
CARGO_HOME: $CI_PROJECT_DIR/cargo
|
||||||
APT_CACHE_DIR: $CI_PROJECT_DIR/apt
|
APT_CACHE_DIR: $CI_PROJECT_DIR/apt
|
||||||
|
FF_USE_FASTZIP: "true" # enable fastzip - a faster zip implementation that also supports level configuration.
|
||||||
|
ARTIFACT_COMPRESSION_LEVEL: fast # can also be set to fastest, fast, slow and slowest. If just enabling fastzip is not enough try setting this to fastest or fast.
|
||||||
|
CACHE_COMPRESSION_LEVEL: fastest # same as above, but for caches
|
||||||
|
TRANSFER_METER_FREQUENCY: 5s # will display transfer progress every 5 seconds for artifacts and remote caches.
|
||||||
|
|
||||||
# install build dependencies
|
|
||||||
before_script:
|
|
||||||
- apt-get update -yqq
|
|
||||||
- apt-get install -o dir::cache::archives="$APT_CACHE_DIR" -yqq --no-install-recommends capnproto build-essential cmake clang libclang-dev libgsasl7-dev
|
|
||||||
- rustup update
|
|
||||||
- rustup component add rustfmt
|
|
||||||
- rustup component add clippy
|
|
||||||
|
|
||||||
# Use clippy to lint the project
|
|
||||||
lint:clippy:
|
|
||||||
allow_failure: true
|
|
||||||
script:
|
|
||||||
- rustc --version && cargo --version # Print version info for debugging
|
|
||||||
- cargo clippy -- -D warnings
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
- development
|
|
||||||
- merge_requests
|
|
||||||
tags:
|
|
||||||
- linux
|
|
||||||
- docker
|
|
||||||
|
|
||||||
# Use rustfmt to check formating of the project
|
|
||||||
lint:fmt:
|
|
||||||
allow_failure: true
|
|
||||||
script:
|
|
||||||
- rustc --version && cargo --version # Print version info for debugging
|
|
||||||
- cargo fmt -- --check # TODO: Do we want to enforce formating?
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
- development
|
|
||||||
- merge_requests
|
|
||||||
tags:
|
|
||||||
- linux
|
|
||||||
- docker
|
|
||||||
|
|
||||||
# Use cargo to test the project
|
|
||||||
test:cargo:
|
|
||||||
script:
|
|
||||||
- rustc --version && cargo --version # Print version info for debugging
|
|
||||||
- cargo test --workspace --verbose
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
- development
|
|
||||||
- merge_requests
|
|
||||||
tags:
|
|
||||||
- linux
|
|
||||||
- docker
|
|
||||||
|
|
||||||
build:docker-master:
|
|
||||||
image:
|
|
||||||
name: gcr.io/kaniko-project/executor:v1.6.0-debug
|
|
||||||
entrypoint: [""]
|
|
||||||
before_script:
|
|
||||||
- ''
|
|
||||||
script:
|
|
||||||
- mkdir -p /kaniko/.docker
|
|
||||||
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
|
|
||||||
- /kaniko/executor --force --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:latest
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
tags:
|
|
||||||
- linux
|
|
||||||
- docker
|
|
||||||
|
|
||||||
build:docker-releases:
|
|
||||||
image:
|
|
||||||
name: gcr.io/kaniko-project/executor:v1.6.0-debug
|
|
||||||
entrypoint: [""]
|
|
||||||
before_script:
|
|
||||||
- ''
|
|
||||||
script:
|
|
||||||
- mkdir -p /kaniko/.docker
|
|
||||||
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
|
|
||||||
- /kaniko/executor --force --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
|
|
||||||
only:
|
|
||||||
- tags
|
|
||||||
tags:
|
|
||||||
- linux
|
|
||||||
- docker
|
|
||||||
|
|
||||||
build:docker-development:
|
|
||||||
image:
|
|
||||||
name: gcr.io/kaniko-project/executor:v1.6.0-debug
|
|
||||||
entrypoint: [""]
|
|
||||||
before_script:
|
|
||||||
- ''
|
|
||||||
script:
|
|
||||||
- mkdir -p /kaniko/.docker
|
|
||||||
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
|
|
||||||
- /kaniko/executor --force --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:dev-latest
|
|
||||||
only:
|
|
||||||
- development
|
|
||||||
tags:
|
|
||||||
- linux
|
|
||||||
- docker
|
|
||||||
|
|
||||||
# cache dependencies and build environment to speed up setup
|
# cache dependencies and build environment to speed up setup
|
||||||
cache:
|
cache:
|
||||||
key: "$CI_COMMIT_REF_SLUG"
|
key: "$CI_COMMIT_REF_SLUG"
|
||||||
paths:
|
paths:
|
||||||
- apt/
|
- apt/
|
||||||
# - cargo/
|
- cargo/
|
||||||
- target/
|
- target/
|
||||||
|
|
||||||
|
|
||||||
|
.lints:
|
||||||
|
stage: lint
|
||||||
|
allow_failure: true
|
||||||
|
only:
|
||||||
|
- merge_requests
|
||||||
|
|
||||||
|
# Use clippy lints
|
||||||
|
lint:clippy:
|
||||||
|
extends: .lints
|
||||||
|
script:
|
||||||
|
- cargo clippy -V
|
||||||
|
- echo -e "\e[0Ksection_start:`date +%s`:clippy_output\r\e[0Kcargo clippy output"
|
||||||
|
- cargo clippy -- --no-deps
|
||||||
|
- echo -e "\e[0Ksection_end:`date +%s`:clippy_output\r\e[0K"
|
||||||
|
|
||||||
|
# Use rustfmt to check formating
|
||||||
|
lint:fmt:
|
||||||
|
extends: .lints
|
||||||
|
script:
|
||||||
|
- cargo fmt --version
|
||||||
|
- echo -e "\e[0Ksection_start:`date +%s`:rustfmt_output\r\e[0KChanges suggested by rustfmt"
|
||||||
|
- cargo fmt --check -- -v
|
||||||
|
- echo -e "\e[0Ksection_end:`date +%s`:rustfmt_output\r\e[0K"
|
||||||
|
|
||||||
|
# Check if the code builds on rust stable
|
||||||
|
stable:check:
|
||||||
|
stage: check
|
||||||
|
only:
|
||||||
|
- main
|
||||||
|
- development
|
||||||
|
- merge_requests
|
||||||
|
script:
|
||||||
|
- rustc +stable --version && cargo --version
|
||||||
|
- echo -e "\e[0Ksection_start:`date +%s`:build_output\r\e[0KOutput of cargo check"
|
||||||
|
- cargo check --verbose
|
||||||
|
- echo -e "\e[0Ksection_end:`date +%s`:build_output\r\e[0K"
|
||||||
|
|
||||||
|
# Check if the code builds on rust stable on armv7
|
||||||
|
stable:check:armhf:
|
||||||
|
stage: check
|
||||||
|
only:
|
||||||
|
- main
|
||||||
|
- development
|
||||||
|
- merge_requests
|
||||||
|
before_script:
|
||||||
|
- mkdir -p $CARGO_HOME
|
||||||
|
- cp cargo-cross-config $CARGO_HOME/config.toml
|
||||||
|
script:
|
||||||
|
- rustc +stable --version && cargo --version
|
||||||
|
- echo -e "\e[0Ksection_start:`date +%s`:build_output\r\e[0KOutput of cargo check with target armv7-unknown-linux-gnueabihf"
|
||||||
|
- cargo check --verbose --target armv7-unknown-linux-gnueabihf
|
||||||
|
- echo -e "\e[0Ksection_end:`date +%s`:build_output\r\e[0K"
|
||||||
|
|
||||||
|
# Check if the code builds on rust stable on arm64
|
||||||
|
stable:check:arm64:
|
||||||
|
stage: check
|
||||||
|
only:
|
||||||
|
- main
|
||||||
|
- development
|
||||||
|
- merge_requests
|
||||||
|
before_script:
|
||||||
|
- mkdir -p $CARGO_HOME
|
||||||
|
- cp cargo-cross-config $CARGO_HOME/config.toml
|
||||||
|
script:
|
||||||
|
- rustc +stable --version && cargo --version
|
||||||
|
- echo -e "\e[0Ksection_start:`date +%s`:build_output\r\e[0KOutput of cargo check with target aarch64-unknown-linux-gnu"
|
||||||
|
- cargo check --verbose --target aarch64-unknown-linux-gnu
|
||||||
|
- echo -e "\e[0Ksection_end:`date +%s`:build_output\r\e[0K"
|
||||||
|
|
||||||
|
# Check if the code builds on rust stable
|
||||||
|
stable:build:amd64:
|
||||||
|
stage: build
|
||||||
|
only:
|
||||||
|
- main
|
||||||
|
- development
|
||||||
|
- merge_requests
|
||||||
|
script:
|
||||||
|
- rustc +stable --version && cargo --version
|
||||||
|
- echo -e "\e[0Ksection_start:`date +%s`:build_output\r\e[0KOutput of cargo build with target x86_64-unknown-linux-gnu"
|
||||||
|
- cargo build --release --target x86_64-unknown-linux-gnu
|
||||||
|
- echo -e "\e[0Ksection_end:`date +%s`:build_output\r\e[0K"
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- target/x86_64-unknown-linux-gnu/release/bffhd
|
||||||
|
|
||||||
|
|
||||||
|
# Check if the code builds on rust stable on armv7
|
||||||
|
stable:build:armhf:
|
||||||
|
stage: build
|
||||||
|
only:
|
||||||
|
- main
|
||||||
|
- development
|
||||||
|
before_script:
|
||||||
|
- mkdir -p $CARGO_HOME
|
||||||
|
- cp cargo-cross-config $CARGO_HOME/config.toml
|
||||||
|
script:
|
||||||
|
- rustc +stable --version && cargo --version
|
||||||
|
- echo -e "\e[0Ksection_start:`date +%s`:build_output\r\e[0KOutput of cargo build with target armv7-unknown-linux-gnueabihf"
|
||||||
|
- cargo build --release --target armv7-unknown-linux-gnueabihf
|
||||||
|
- echo -e "\e[0Ksection_end:`date +%s`:build_output\r\e[0K"
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- target/armv7-unknown-linux-gnueabihf/release/bffhd
|
||||||
|
|
||||||
|
# Check if the code builds on rust stable on arm64
|
||||||
|
stable:build:arm64:
|
||||||
|
stage: build
|
||||||
|
only:
|
||||||
|
- main
|
||||||
|
- development
|
||||||
|
before_script:
|
||||||
|
- mkdir -p $CARGO_HOME
|
||||||
|
- cp cargo-cross-config $CARGO_HOME/config.toml
|
||||||
|
script:
|
||||||
|
- rustc +stable --version && cargo --version
|
||||||
|
- echo -e "\e[0Ksection_start:`date +%s`:build_output\r\e[0KOutput of cargo build with target aarch64-unknown-linux-gnu"
|
||||||
|
- cargo build --release --target aarch64-unknown-linux-gnu
|
||||||
|
- echo -e "\e[0Ksection_end:`date +%s`:build_output\r\e[0K"
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- target/aarch64-unknown-linux-gnu/release/bffhd
|
||||||
|
|
||||||
|
stable:test:
|
||||||
|
stage: build
|
||||||
|
needs: ["stable:check"]
|
||||||
|
only:
|
||||||
|
- main
|
||||||
|
- development
|
||||||
|
- merge_requests
|
||||||
|
script:
|
||||||
|
- echo -e "\e[0Ksection_start:`date +%s`:build_output\r\e[0KOutput of cargo test --no-run"
|
||||||
|
- cargo test --verbose --no-run --workspace
|
||||||
|
|
||||||
|
.tests:
|
||||||
|
stage: test
|
||||||
|
needs: ["stable:test"]
|
||||||
|
script:
|
||||||
|
- cargo test --workspace $TEST_TARGET -- -Z unstable-options --format json --report-time | cargo2junit > report.xml
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
junit:
|
||||||
|
- report.xml
|
||||||
|
only:
|
||||||
|
- main
|
||||||
|
- development
|
||||||
|
- merge_requests
|
||||||
|
|
||||||
|
# Run unit tests
|
||||||
|
unit test 1:3:
|
||||||
|
variables:
|
||||||
|
TEST_TARGET: "--lib"
|
||||||
|
extends: .tests
|
||||||
|
|
||||||
|
unit test 2:3:
|
||||||
|
variables:
|
||||||
|
TEST_TARGET: "--bins"
|
||||||
|
extends: .tests
|
||||||
|
|
||||||
|
unit test 3:3:
|
||||||
|
variables:
|
||||||
|
TEST_TARGET: "--examples"
|
||||||
|
extends: .tests
|
||||||
|
|
||||||
|
upload_binaries:
|
||||||
|
stage: release
|
||||||
|
image: curlimages/curl:latest
|
||||||
|
before_script: []
|
||||||
|
cache: []
|
||||||
|
dependencies:
|
||||||
|
- stable:build:amd64
|
||||||
|
- stable:build:armhf
|
||||||
|
- stable:build:arm64
|
||||||
|
script:
|
||||||
|
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file target/aarch64-unknown-linux-gnu/release/bffhd "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/bffhd/${CI_COMMIT_TAG}/bffhd_${VERSION}_linux_arm64"'
|
||||||
|
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file target/x86_64-unknown-linux-gnu/release/bffhd "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/bffhd/${CI_COMMIT_TAG}/bffhd_${VERSION}_linux_amd64"'
|
||||||
|
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file target/armv7-unknown-linux-gnueabihf/release/bffhd "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/bffhd/${VERSION}/bffhd_${VERSION}_linux_arm"'
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_TAG =~ "release/.*"
|
||||||
|
when: never
|
||||||
|
- if: $CI_COMMIT_BRANCH == "main"
|
||||||
|
|
||||||
|
release_prepare:
|
||||||
|
stage: release
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_TAG =~ "release/.*"
|
||||||
|
when: never
|
||||||
|
- if: $CI_COMMIT_BRANCH == "main"
|
||||||
|
script:
|
||||||
|
- VERSION="cargo metadata --format-version 1 | jq -C '.packages | .[] | select(.name == "diflouroborane") | .version' -r"
|
||||||
|
- echo $VERSION > release.env
|
||||||
|
artifacts:
|
||||||
|
reports:
|
||||||
|
dotenv: release.env
|
||||||
|
|
||||||
|
release_job:
|
||||||
|
stage: release
|
||||||
|
needs:
|
||||||
|
- job: release_prepare
|
||||||
|
artifacts: true
|
||||||
|
image: registry.gitlab.com/gitlab-org/release-cli:latest
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_TAG =~ "release/.*"
|
||||||
|
when: never
|
||||||
|
- if: $CI_COMMIT_BRANCH == "main"
|
||||||
|
script:
|
||||||
|
- echo "Creating GitLab release…"
|
||||||
|
release:
|
||||||
|
name: "BFFH $VERSION"
|
||||||
|
description: "GitLab CI auto-created release"
|
||||||
|
tag_name: "release/$VERSION"
|
||||||
|
assets:
|
||||||
|
links:
|
||||||
|
- name: 'bffhd AMD64'
|
||||||
|
url: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/bffhd/${VERSION}/bffhd_${VERSION}_linux_amd64"
|
||||||
|
- name: 'bffhd ARMv7'
|
||||||
|
url: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/bffhd/${VERSION}/bffhd_${VERSION}_linux_arm"
|
||||||
|
- name: 'bffhd ARM64'
|
||||||
|
url: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/bffhd/${VERSION}/bffhd_${VERSION}_linux_arm64"
|
||||||
|
|
||||||
|
build:docker-releases:
|
||||||
|
stage: dockerify
|
||||||
|
image: jdrouet/docker-with-buildx:latest
|
||||||
|
dependencies:
|
||||||
|
- stable:build:amd64
|
||||||
|
- stable:build:armhf
|
||||||
|
- stable:build:arm64
|
||||||
|
tags:
|
||||||
|
- linux
|
||||||
|
- docker
|
||||||
|
- fabinfra
|
||||||
|
variables:
|
||||||
|
DOCKER_HOST: tcp://docker:2375/
|
||||||
|
DOCKER_DRIVER: overlay2
|
||||||
|
DOCKER_TLS_CERTDIR: ""
|
||||||
|
TRIVY_NO_PROGRESS: "true"
|
||||||
|
TRIVY_CACHE_DIR: ".trivycache/"
|
||||||
|
services:
|
||||||
|
- docker:dind
|
||||||
|
before_script:
|
||||||
|
- export TRIVY_VERSION=$(wget -qO - "https://api.github.com/repos/aquasecurity/trivy/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
|
||||||
|
- echo $TRIVY_VERSION
|
||||||
|
- wget --no-verbose https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz -O - | tar -zxvf -
|
||||||
|
script:
|
||||||
|
- docker login $CI_REGISTRY -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
|
||||||
|
- docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||||
|
- docker buildx create --name cibuilder --driver docker-container --use
|
||||||
|
- docker buildx ls
|
||||||
|
- docker buildx inspect --bootstrap
|
||||||
|
- docker buildx build --platform linux/arm/v7,linux/arm64,linux/amd64 -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG .
|
||||||
|
- docker buildx build --load --platform linux/amd64 -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG .
|
||||||
|
# Build report
|
||||||
|
- ./trivy image --exit-code 0 --format template --template "@contrib/gitlab.tpl" -o gl-container-scanning-report.json $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
|
||||||
|
# Print report
|
||||||
|
- ./trivy image --exit-code 0 --severity HIGH $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
|
||||||
|
# Fail on severe vulnerabilities
|
||||||
|
- ./trivy image --exit-code 1 --severity CRITICAL $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
|
||||||
|
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- .trivycache/
|
||||||
|
artifacts:
|
||||||
|
reports:
|
||||||
|
container_scanning: gl-container-scanning-report.json
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_TAG =~ "release/.*"
|
||||||
|
when: never
|
||||||
|
|
||||||
|
build:docker-development:
|
||||||
|
stage: dockerify
|
||||||
|
image: jdrouet/docker-with-buildx:latest
|
||||||
|
dependencies:
|
||||||
|
- stable:build:amd64
|
||||||
|
- stable:build:armhf
|
||||||
|
- stable:build:arm64
|
||||||
|
tags:
|
||||||
|
- linux
|
||||||
|
- docker
|
||||||
|
- fabinfra
|
||||||
|
variables:
|
||||||
|
DOCKER_HOST: tcp://docker:2375/
|
||||||
|
DOCKER_DRIVER: overlay2
|
||||||
|
DOCKER_TLS_CERTDIR: ""
|
||||||
|
TRIVY_NO_PROGRESS: "true"
|
||||||
|
TRIVY_CACHE_DIR: ".trivycache/"
|
||||||
|
services:
|
||||||
|
- docker:dind
|
||||||
|
before_script:
|
||||||
|
- export TRIVY_VERSION=$(wget -qO - "https://api.github.com/repos/aquasecurity/trivy/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
|
||||||
|
- echo $TRIVY_VERSION
|
||||||
|
- wget --no-verbose https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz -O - | tar -zxvf -
|
||||||
|
script:
|
||||||
|
- docker login $CI_REGISTRY -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
|
||||||
|
- docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||||
|
- docker buildx create --name cibuilder --driver docker-container --use
|
||||||
|
- docker buildx ls
|
||||||
|
- docker buildx inspect --bootstrap
|
||||||
|
- docker buildx build --platform linux/arm/v7,linux/arm64,linux/amd64 -t $CI_REGISTRY_IMAGE:development .
|
||||||
|
- docker buildx build --load --platform linux/amd64 -t $CI_REGISTRY_IMAGE:development .
|
||||||
|
# Build report
|
||||||
|
- ./trivy image --exit-code 0 --format template --template "@contrib/gitlab.tpl" -o gl-container-scanning-report.json $CI_REGISTRY_IMAGE:development
|
||||||
|
# Print report
|
||||||
|
- ./trivy image --exit-code 0 --severity HIGH $CI_REGISTRY_IMAGE:development
|
||||||
|
# Fail on severe vulnerabilities
|
||||||
|
- ./trivy image --exit-code 1 --severity CRITICAL $CI_REGISTRY_IMAGE:development
|
||||||
|
- docker push $CI_REGISTRY_IMAGE:development
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- .trivycache/
|
||||||
|
artifacts:
|
||||||
|
reports:
|
||||||
|
container_scanning: gl-container-scanning-report.json
|
||||||
|
only:
|
||||||
|
- development
|
||||||
|
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,5 +1,17 @@
|
|||||||
# Revision history for Difluoroborane
|
# Revision history for Difluoroborane
|
||||||
|
|
||||||
|
## 0.4.3 -- 2025-02-11
|
||||||
|
|
||||||
|
* Adds binary version of FabFire authenitcation protocol
|
||||||
|
* Adds commands to dump and restore the full database as a TOML text file (`--dump-db` and `--load-db`)
|
||||||
|
* allows compilation with current stable Rust (1.84)
|
||||||
|
- Attention: The database format still relies on Rust data layout, so when updating the compiler, the database must be transfered as TOML dump.
|
||||||
|
Therefore, the `rust-toolchain.toml` file pinning `rustc` to version `1.66` is still in place.
|
||||||
|
* resolves a crash (use after free) when disconnecting a client.
|
||||||
|
* resolves some compiler warnings
|
||||||
|
|
||||||
|
## 0.4.2 -- TODO
|
||||||
|
|
||||||
## 0.4.1 -- 2022-04-24
|
## 0.4.1 -- 2022-04-24
|
||||||
|
|
||||||
* Initial full implementation of the FabAccess 0.3 API, "Spigots of Berlin".
|
* Initial full implementation of the FabAccess 0.3 API, "Spigots of Berlin".
|
||||||
|
@ -2,79 +2,38 @@
|
|||||||
|
|
||||||
Thank you for your interest in helping out the FabAccess system!
|
Thank you for your interest in helping out the FabAccess system!
|
||||||
|
|
||||||
You found a bug, an exploit or a feature that doesn't work like it's documented? Please tell us
|
You found a bug, an exploit or a feature that doesn't work like it's documented? Please tell us about it, see [Issues](#issues)
|
||||||
about it, see [Issues](#issues)
|
|
||||||
|
|
||||||
You have a feature request? Great, check out the paragraph on [Feature Requests](#feature-requests)
|
You have a feature request? Great, check out the paragraph on [Feature Requests](#feature-requests)
|
||||||
|
|
||||||
## Issues
|
## Issues
|
||||||
|
|
||||||
While we try to not have any bugs or exploits or documentation bugs we're not perfect either. Thanks
|
While we try to not have any bugs or exploits or documentation bugs we're not perfect either. Thanks for helping us out!
|
||||||
for helping us out!
|
|
||||||
|
|
||||||
We have labels that help us sort issues better, so if you know what would be the correct ones,
|
We have labels that help us sort issues better, so if you know what would be the correct ones, please tag your issue with one or multiple keywords. See [Labels](https://gitlab.com/fabinfra/fabaccess/bffh/-/labels) to get an overview of all keywords and their use case.
|
||||||
please tag your issue:
|
|
||||||
- `documentation` if it's an documentation issue, be it lacking docs or even worse wrong docs.
|
|
||||||
- `bug` is for software bugs, unexpected behaviour, crashes and so on.
|
|
||||||
- `exploit` for any bugs that may be used as RCE, to escalate priviledges or some-such.
|
|
||||||
Don't worry if you aren't sure about the correct labels, an issue opened with no labels is much
|
|
||||||
better than no knowing about the issue!
|
|
||||||
|
|
||||||
Especially for bugs and exploits, please mark your issue as "confidential" if you think it impacts
|
Especially for **bugs** and **exploits**, please mark your issue as "confidential" if you think it impacts the `stable` branch. If you're not sure, mark it as confidential anyway. It's easier to publish information than it is to un-publish information. You may also contact as by [mail](https://fab-access.org/impressum).
|
||||||
the `stable` branch. If you're not sure, mark it as confidential anyway. It's easier to publish
|
|
||||||
information than it is to un-publish information.
|
|
||||||
|
|
||||||
If you found an exploit and it's high-impact enough that you do not want to open an issue but
|
|
||||||
instead want direct contact with the developers, you can find public keys respectively fingerprints
|
|
||||||
for GPG, XMPP+OMEMO and Matrix+MegOlm in the git repository as blobs with tags assigned to them.
|
|
||||||
|
|
||||||
You can import the gpg key for dequbed either from the repository like so:
|
|
||||||
```
|
|
||||||
$ git cat-file -p keys/dequbed/gpg | gpg --import-key
|
|
||||||
```
|
|
||||||
Or from your local trusted gpg keyserver, and/or verify it using [keybase](https://keybase.io/dequbed)
|
|
||||||
This key is also used to sign the other tags so to verify them you can run e.g.
|
|
||||||
```
|
|
||||||
$ git tag -v keys/dequbed/xmpp+omemo
|
|
||||||
```
|
|
||||||
|
|
||||||
## Feature Requests
|
## Feature Requests
|
||||||
|
|
||||||
We also like new feature requests of course!
|
We also like new feature requests of course!
|
||||||
But before you open an issue in this repo for a feature request, please first check a few things:
|
But before you open an issue in this repo for a feature request, please first check a few things:
|
||||||
1. Is it a feature that needs to be implemented in more than just the backend server? For example,
|
|
||||||
is it something also having a GUI-component or something that you want to be able to do via the
|
1. Is it a feature that needs to be implemented in more than just the backend server? For example, is it something also having a GUI-component or something that you want to be able to do via the API? If so it's better suited over at the
|
||||||
API? If so it's better suited over at the
|
[Lastenheft](https://gitlab.com/fabinfra/fabaccess_lastenheft) because that's where the required coordination for that will end up happening
|
||||||
[Lastenheft](https://gitlab.com/fabinfra/fabaccess_lastenheft) because that's where the required
|
2. Who else needs that feature? Is this something super specific to your environment/application or something that others will want too? If it's something that's relevant for more people please also tell us that in the feature request.
|
||||||
coordination for that will end up happening
|
3. Can you already get partway or all the way there using what's there already? If so please also tell us what you're currently doing and what doesn't work or why you dislike your current solution.
|
||||||
2. Who else needs that feature? Is this something super specific to your environment/application or
|
|
||||||
something that others will want too? If it's something that's relevant for more people please
|
|
||||||
also tell us that in the feature request.
|
|
||||||
3. Can you already get partway or all the way there using what's there already? If so please also
|
|
||||||
tell us what you're currently doing and what doesn't work or why you dislike your current
|
|
||||||
solution.
|
|
||||||
|
|
||||||
## Contributing Code
|
## Contributing Code
|
||||||
|
|
||||||
To help develop Diflouroborane you will need a Rust toolchain. I heavily recommend installing
|
To help develop Difluoroborane you will need a Rust toolchain. We heavily recommend installing [rustup](https://rustup.rs) even if your distribution provides a recent enough rustc, simply because it allows to easily switch compilers between several versions of both stable and nightly. It also allows you to download the respective stdlib crate, giving you the option of an offline reference.
|
||||||
[rustup](https://rustup.rs) even if your distribution provides a recent enough rustc, simply because
|
|
||||||
it allows to easily switch compilers between several versions of both stable and nightly. It also
|
|
||||||
allows you to download the respective stdlib crate, giving you the option of an offline reference.
|
|
||||||
|
|
||||||
We use a stable release branch / moving development workflow. This means that all *new* development
|
We use a stable release branch / moving development workflow. This means that all *new* development should happen on the `development` branch which is regularly merged into `stable` as releases. The exception of course are bug- and hotfixes that can target whichever branch.
|
||||||
should happen on the `development` branch which is regularly merged into `stable` as releases. The
|
|
||||||
exception of course are bug- and hotfixes that can target whichever branch.
|
|
||||||
|
|
||||||
If you want to add a new feature please work off the development branch. We suggest you create
|
If you want to add a new feature please work off the development branch. We suggest you create yourself a feature branch, e.g. using
|
||||||
yourself a feature branch, e.g. using `git switch development; git checkout -b
|
|
||||||
feature/my-cool-feature`.
|
|
||||||
Using a feature branch keeps your local `development` branch clean, making it easier to later rebase
|
|
||||||
your feature branch onto it before you open a pull/merge request.
|
|
||||||
|
|
||||||
When you want feedback on your current progress or are ready to have it merged upstream open a merge
|
```git switch development; git checkout -b feature/my-cool-feature```
|
||||||
request. Don't worry we don't bite! ^^
|
|
||||||
|
|
||||||
|
Using a feature branch keeps your local `development` branch clean, making it easier to later rebase your feature branch onto it before you open a pull/merge request.
|
||||||
|
|
||||||
## Tests
|
When you want feedback on your current progress or are ready to have it merged upstream open a merge request. Don't worry, we don't bite! ^^
|
||||||
|
|
||||||
Sadly, still very much `// TODO:`. We're working on it! :/
|
|
||||||
|
2125
Cargo.lock
generated
2125
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
39
Cargo.toml
39
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "diflouroborane"
|
name = "difluoroborane"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
authors = [ "dequbed <me@dequbed.space>"
|
authors = [ "dequbed <me@dequbed.space>"
|
||||||
, "Kai Jan Kriegel <kai@kjkriegel.de>"
|
, "Kai Jan Kriegel <kai@kjkriegel.de>"
|
||||||
, "Joseph Langosch <thejoklla@gmail.com>"
|
, "Joseph Langosch <thejoklla@gmail.com>"
|
||||||
@ -19,10 +19,14 @@ lto = "thin"
|
|||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
path = "bffhd/lib.rs"
|
path = "bffhd/lib.rs"
|
||||||
|
# Don't run unit tests on `cargo test --tests`, only run integration tests.
|
||||||
|
test = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "bffhd"
|
name = "bffhd"
|
||||||
path = "bin/bffhd/main.rs"
|
path = "bin/bffhd/main.rs"
|
||||||
|
# Don't run unit tests on `cargo test --tests`, only run integration tests.
|
||||||
|
test = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2.101"
|
libc = "0.2.101"
|
||||||
@ -33,7 +37,11 @@ pin-utils = "0.1.0"
|
|||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
futures-lite = "1.12.0"
|
futures-lite = "1.12.0"
|
||||||
async-net = "1.6.1"
|
async-net = "1.6.1"
|
||||||
anyhow = "1.0.56"
|
async-io = "1.7.0"
|
||||||
|
async-process = "1.4.0"
|
||||||
|
backtrace = "0.3.65"
|
||||||
|
miette = { version = "4.7.1", features = ["fancy"] }
|
||||||
|
thiserror = "1.0.31"
|
||||||
toml = "0.5.8"
|
toml = "0.5.8"
|
||||||
|
|
||||||
# Well-known paths/dirs for e.g. cache
|
# Well-known paths/dirs for e.g. cache
|
||||||
@ -41,6 +49,8 @@ dirs = "4.0.0"
|
|||||||
|
|
||||||
# Runtime
|
# Runtime
|
||||||
executor = { path = "runtime/executor" }
|
executor = { path = "runtime/executor" }
|
||||||
|
lightproc = { path = "runtime/lightproc" }
|
||||||
|
console = { path = "runtime/console" }
|
||||||
|
|
||||||
# Catch&Handle POSIX process signals
|
# Catch&Handle POSIX process signals
|
||||||
signal-hook = "0.3.13"
|
signal-hook = "0.3.13"
|
||||||
@ -56,7 +66,7 @@ ptr_meta = "0.1"
|
|||||||
rkyv_typename = "0.7"
|
rkyv_typename = "0.7"
|
||||||
rkyv_dyn = "0.7"
|
rkyv_dyn = "0.7"
|
||||||
inventory = "0.1"
|
inventory = "0.1"
|
||||||
linkme = "0.2.10"
|
linkme = "0.3"
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
|
||||||
# Password hashing for internal users
|
# Password hashing for internal users
|
||||||
@ -64,9 +74,9 @@ rust-argon2 = "0.8.3"
|
|||||||
rand = "0.8.4"
|
rand = "0.8.4"
|
||||||
|
|
||||||
# Async aware logging and tracing
|
# Async aware logging and tracing
|
||||||
tracing = "0.1.28"
|
tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.2.25", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "registry", "std"] }
|
||||||
tracing-futures = { version = "0.2.5", features = ["futures-03"] }
|
tracing-futures = { version = "0.2", features = ["futures-03"] }
|
||||||
|
|
||||||
# API
|
# API
|
||||||
api = { path = "api" }
|
api = { path = "api" }
|
||||||
@ -74,7 +84,8 @@ capnp = "0.14"
|
|||||||
capnp-rpc = "0.14.1"
|
capnp-rpc = "0.14.1"
|
||||||
|
|
||||||
# API Authentication
|
# API Authentication
|
||||||
desfire = "0.2.0-alpha1"
|
desfire = "0.2.0-alpha3"
|
||||||
|
|
||||||
hex = { version = "0.4.3", features = ["serde"] }
|
hex = { version = "0.4.3", features = ["serde"] }
|
||||||
|
|
||||||
futures-signals = "0.3.22"
|
futures-signals = "0.3.22"
|
||||||
@ -99,15 +110,19 @@ async-compat = "0.2.1"
|
|||||||
url = "2.2.2"
|
url = "2.2.2"
|
||||||
rustls-native-certs = "0.6.1"
|
rustls-native-certs = "0.6.1"
|
||||||
|
|
||||||
|
shadow-rs = "0.11"
|
||||||
|
|
||||||
[dependencies.rsasl]
|
[dependencies.rsasl]
|
||||||
git = "https://github.com/dequbed/rsasl.git"
|
version = "2.2.0"
|
||||||
rev = "0b5012d0"
|
|
||||||
default_features = false
|
default_features = false
|
||||||
features = ["unstable_custom_mechanism", "provider", "registry_static", "plain"]
|
features = ["unstable_custom_mechanism", "provider", "registry_static", "config_builder", "plain"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
futures-test = "0.3.16"
|
futures-test = "0.3.16"
|
||||||
tempfile = "3.2"
|
tempfile = "3.2"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
shadow-rs = "0.11"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["modules/*", "api"]
|
members = ["runtime/*", "modules/*", "api"]
|
||||||
|
28
Dockerfile
28
Dockerfile
@ -1,21 +1,23 @@
|
|||||||
# Setup build image for multistage build
|
FROM --platform=$BUILDPLATFORM alpine:latest as copy
|
||||||
FROM rust:bullseye as builder
|
ARG TARGETPLATFORM
|
||||||
# install build deps
|
RUN case "$TARGETPLATFORM" in \
|
||||||
RUN apt-get update && apt-get upgrade -y
|
"linux/arm/v7") echo armv7-unknown-linux-gnueabihf > /rust_target.txt ;; \
|
||||||
RUN apt-get install -yqq --no-install-recommends capnproto
|
"linux/arm/v6") echo arm-unknown-linux-gnueabihf > /rust_target.txt ;; \
|
||||||
|
"linux/arm64") echo aarch64-unknown-linux-gnu > /rust_target.txt ;; \
|
||||||
|
"linux/amd64") echo x86_64-unknown-linux-gnu > /rust_target.txt ;; \
|
||||||
|
*) exit 1 ;; \
|
||||||
|
esac
|
||||||
|
|
||||||
WORKDIR /usr/src/bffh
|
WORKDIR /usr/src/bffh
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN cargo build --release
|
RUN cp target/$(cat /rust_target.txt)/release/bffhd ./bffhd.bin
|
||||||
|
|
||||||
|
|
||||||
# Setup deployable image
|
# Setup deployable image
|
||||||
FROM debian:bullseye-slim
|
FROM ubuntu:22.04
|
||||||
# Install runtime deps
|
RUN apt-get update && apt-get upgrade -y
|
||||||
#RUN apt-get update && apt-get upgrade -yqq
|
RUN apt-get install -yqq --no-install-recommends python3 python3-pip
|
||||||
COPY --from=builder /usr/src/bffh/target/release/bffhd /usr/local/bin/bffhd
|
RUN pip3 install paho-mqtt
|
||||||
#COPY --from=builder /usr/src/bffh/examples/bffh.dhall /etc/diflouroborane.dhall
|
COPY --from=copy /usr/src/bffh/bffhd.bin /usr/local/bin/bffhd
|
||||||
# RUN diflouroborane --print-default > /etc/diflouroborane.toml
|
|
||||||
VOLUME /etc/bffh/
|
VOLUME /etc/bffh/
|
||||||
VOLUME /var/lib/bffh/
|
VOLUME /var/lib/bffh/
|
||||||
VOLUME /usr/local/lib/bffh/adapters/
|
VOLUME /usr/local/lib/bffh/adapters/
|
||||||
|
35
INSTALL.md
35
INSTALL.md
@ -1,35 +0,0 @@
|
|||||||
## Installation
|
|
||||||
|
|
||||||
Currently there are no distribution packages available.
|
|
||||||
However installation is reasonably straight-forward, since Diflouroborane compiles into a single
|
|
||||||
mostly static binary with few dependencies.
|
|
||||||
|
|
||||||
At the moment only Linux is supported. If you managed to compile Diflouroborane please open an issue
|
|
||||||
outlining your steps or add a merge request expanding this part. Thanks!
|
|
||||||
|
|
||||||
### Requirements
|
|
||||||
|
|
||||||
General requirements; scroll down for distribution-specific instructions
|
|
||||||
|
|
||||||
- GNU SASL (libgsasl).
|
|
||||||
* If you want to compile Diflouroborane from source you will potentially also need development
|
|
||||||
headers
|
|
||||||
- capnproto
|
|
||||||
- rustc stable / nightly >= 1.48
|
|
||||||
* If your distribution does not provide a recent enough rustc, [rustup](https://rustup.rs/) helps
|
|
||||||
installing a local toolchain and keeping it up to date.
|
|
||||||
|
|
||||||
###### Arch Linux:
|
|
||||||
```shell
|
|
||||||
$ pacman -S gsasl rust capnproto
|
|
||||||
```
|
|
||||||
|
|
||||||
### Compiling from source
|
|
||||||
|
|
||||||
Diflouroborane uses Cargo, so compilation boils down to:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ cargo build --release
|
|
||||||
```
|
|
||||||
|
|
||||||
The compiled binary can then be found in `./target/release/diflouroborane`
|
|
12
README.md
12
README.md
@ -1,8 +1,8 @@
|
|||||||
# FabAccess Diflouroborane
|
# FabAccess Difluoroborane
|
||||||
|
|
||||||
Diflouroborane (shorter: BFFH, the chemical formula for Diflouroborane) is the server part of
|
Difluoroborane (shorter: BFFH, the chemical formula for Difluoroborane) is the server part of
|
||||||
FabAccess.
|
FabAccess.
|
||||||
It provides a server-side implementation of the [FabAccess API](/fabinfra/fabaccess/fabaccess-api).
|
It provides a server-side implementation of the [FabAccess API](https://gitlab.com/fabinfra/fabaccess/fabaccess-api).
|
||||||
|
|
||||||
## What is this?
|
## What is this?
|
||||||
|
|
||||||
@ -13,14 +13,14 @@ to be used for all other things one would like to give exclusive access to even
|
|||||||
dangerous or expensive to use (think 3D printers, smart lightbulbs, meeting rooms).
|
dangerous or expensive to use (think 3D printers, smart lightbulbs, meeting rooms).
|
||||||
|
|
||||||
FabAccess uses a Client/Server architecture with a [Cap'n Proto](https://capnproto.org/) API. You
|
FabAccess uses a Client/Server architecture with a [Cap'n Proto](https://capnproto.org/) API. You
|
||||||
can find the API schema files over [in their own repository](/fabinfra/fabaccess/fabaccess-api).
|
can find the API schema files over [in their own repository](https://gitlab.com/fabinfra/fabaccess/fabaccess-api).
|
||||||
The reference client is [Borepin](/fabinfra/fabaccess/borepin), written in C#/Xamarin to be able to
|
The reference client is [Borepin](https://gitlab.com/fabinfra/fabaccess/borepin), written in C#/Xamarin to be able to
|
||||||
be ported to as many platforms as possible.
|
be ported to as many platforms as possible.
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
See [INSTALL.md](INSTALL.md)
|
See [https://fab-access.org/install](https://fab-access.org/install)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "api"
|
name = "api"
|
||||||
version = "0.3.1"
|
version = "0.3.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
publish = false
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
generated = []
|
generated = []
|
||||||
@ -17,4 +16,4 @@ capnpc = "0.14.4"
|
|||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
capnpc = "0.14.4"
|
capnpc = "0.14.4"
|
||||||
# Used in build.rs to iterate over all files in schema/
|
# Used in build.rs to iterate over all files in schema/
|
||||||
walkdir = "2.3.2"
|
walkdir = "2.3.2"
|
50
api/build.rs
50
api/build.rs
@ -1,14 +1,14 @@
|
|||||||
use walkdir::{WalkDir, DirEntry};
|
use walkdir::{DirEntry, WalkDir};
|
||||||
|
|
||||||
fn is_hidden(entry: &DirEntry) -> bool {
|
fn is_hidden(entry: &DirEntry) -> bool {
|
||||||
entry.file_name()
|
entry
|
||||||
|
.file_name()
|
||||||
.to_str()
|
.to_str()
|
||||||
.map(|s| s.starts_with('.'))
|
.map(|s| s.starts_with('.'))
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "gen_static"))]
|
fn generate_api() {
|
||||||
fn main() {
|
|
||||||
println!("cargo:rerun-if-changed=schema");
|
println!("cargo:rerun-if-changed=schema");
|
||||||
let mut compile_command = ::capnpc::CompilerCommand::new();
|
let mut compile_command = ::capnpc::CompilerCommand::new();
|
||||||
compile_command
|
compile_command
|
||||||
@ -22,47 +22,21 @@ fn main() {
|
|||||||
.filter_map(Result::ok) // Filter all entries that access failed on
|
.filter_map(Result::ok) // Filter all entries that access failed on
|
||||||
.filter(|e| !e.file_type().is_dir()) // Filter directories
|
.filter(|e| !e.file_type().is_dir()) // Filter directories
|
||||||
// Filter non-schema files
|
// Filter non-schema files
|
||||||
.filter(|e| e.file_name()
|
.filter(|e| {
|
||||||
.to_str()
|
e.file_name()
|
||||||
.map(|s| s.ends_with(".capnp"))
|
.to_str()
|
||||||
.unwrap_or(false)
|
.map(|s| s.ends_with(".capnp"))
|
||||||
)
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
{
|
{
|
||||||
println!("Collecting schema file {}", entry.path().display());
|
println!("Collecting schema file {}", entry.path().display());
|
||||||
compile_command
|
compile_command.file(entry.path());
|
||||||
.file(entry.path());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Compiling schemas...");
|
println!("Compiling schemas...");
|
||||||
compile_command.run().expect("Failed to generate API code");
|
compile_command.run().expect("Failed to generate API code");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gen_static")]
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("cargo:rerun-if-changed=schema");
|
generate_api();
|
||||||
let mut compile_command = ::capnpc::CompilerCommand::new();
|
|
||||||
compile_command
|
|
||||||
.src_prefix("schema")
|
|
||||||
.output_path("src/schema")
|
|
||||||
.default_parent_module(vec!["schema".to_string()]);
|
|
||||||
|
|
||||||
for entry in WalkDir::new("schema")
|
|
||||||
.max_depth(2)
|
|
||||||
.into_iter()
|
|
||||||
.filter_entry(|e| !is_hidden(e))
|
|
||||||
.filter_map(Result::ok) // Filter all entries that access failed on
|
|
||||||
.filter(|e| !e.file_type().is_dir()) // Filter directories
|
|
||||||
// Filter non-schema files
|
|
||||||
.filter(|e| e.file_name()
|
|
||||||
.to_str()
|
|
||||||
.map(|s| s.ends_with(".capnp"))
|
|
||||||
.unwrap_or(false)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
println!("Collecting schema file {}", entry.path().display());
|
|
||||||
compile_command
|
|
||||||
.file(entry.path());
|
|
||||||
}
|
|
||||||
|
|
||||||
compile_command.run().expect("Failed to generate extra API code");
|
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 244eb9bd1b59cfcf36c067ff0d6208c0992766ba
|
Subproject commit f3f53dafb6b7d23a19947f2a32d4ed5ee4e91d22
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
//! FabAccess generated API bindings
|
//! FabAccess generated API bindings
|
||||||
//!
|
//!
|
||||||
//! This crate contains slightly nicer and better documented bindings for the FabAccess API.
|
//! This crate contains slightly nicer and better documented bindings for the FabAccess API.
|
||||||
|
@ -1,72 +1,41 @@
|
|||||||
pub use capnpc::schema_capnp;
|
pub use capnpc::schema_capnp;
|
||||||
|
|
||||||
|
|
||||||
#[cfg(feature = "generated")]
|
|
||||||
pub mod authenticationsystem_capnp {
|
pub mod authenticationsystem_capnp {
|
||||||
include!(concat!(env!("OUT_DIR"), "/authenticationsystem_capnp.rs"));
|
include!(concat!(env!("OUT_DIR"), "/authenticationsystem_capnp.rs"));
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "generated"))]
|
|
||||||
pub mod authenticationsystem_capnp;
|
|
||||||
|
|
||||||
#[cfg(feature = "generated")]
|
|
||||||
pub mod connection_capnp {
|
pub mod connection_capnp {
|
||||||
include!(concat!(env!("OUT_DIR"), "/connection_capnp.rs"));
|
include!(concat!(env!("OUT_DIR"), "/connection_capnp.rs"));
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "generated"))]
|
|
||||||
pub mod connection_capnp;
|
|
||||||
|
|
||||||
#[cfg(feature = "generated")]
|
|
||||||
pub mod general_capnp {
|
pub mod general_capnp {
|
||||||
include!(concat!(env!("OUT_DIR"), "/general_capnp.rs"));
|
include!(concat!(env!("OUT_DIR"), "/general_capnp.rs"));
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "generated"))]
|
|
||||||
pub mod general_capnp;
|
|
||||||
|
|
||||||
#[cfg(feature = "generated")]
|
|
||||||
pub mod machine_capnp {
|
pub mod machine_capnp {
|
||||||
include!(concat!(env!("OUT_DIR"), "/machine_capnp.rs"));
|
include!(concat!(env!("OUT_DIR"), "/machine_capnp.rs"));
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "generated"))]
|
|
||||||
pub mod machine_capnp;
|
|
||||||
|
|
||||||
#[cfg(feature = "generated")]
|
|
||||||
pub mod machinesystem_capnp {
|
pub mod machinesystem_capnp {
|
||||||
include!(concat!(env!("OUT_DIR"), "/machinesystem_capnp.rs"));
|
include!(concat!(env!("OUT_DIR"), "/machinesystem_capnp.rs"));
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "generated"))]
|
|
||||||
pub mod machinesystem_capnp;
|
|
||||||
|
|
||||||
#[cfg(feature = "generated")]
|
|
||||||
pub mod permissionsystem_capnp {
|
pub mod permissionsystem_capnp {
|
||||||
include!(concat!(env!("OUT_DIR"), "/permissionsystem_capnp.rs"));
|
include!(concat!(env!("OUT_DIR"), "/permissionsystem_capnp.rs"));
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "generated"))]
|
|
||||||
pub mod permissionsystem_capnp;
|
|
||||||
|
|
||||||
#[cfg(feature = "generated")]
|
|
||||||
pub mod role_capnp {
|
pub mod role_capnp {
|
||||||
include!(concat!(env!("OUT_DIR"), "/role_capnp.rs"));
|
include!(concat!(env!("OUT_DIR"), "/role_capnp.rs"));
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "generated"))]
|
|
||||||
pub mod role_capnp;
|
|
||||||
|
|
||||||
#[cfg(feature = "generated")]
|
|
||||||
pub mod space_capnp {
|
pub mod space_capnp {
|
||||||
include!(concat!(env!("OUT_DIR"), "/space_capnp.rs"));
|
include!(concat!(env!("OUT_DIR"), "/space_capnp.rs"));
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "generated"))]
|
|
||||||
pub mod space_capnp;
|
|
||||||
|
|
||||||
#[cfg(feature = "generated")]
|
|
||||||
pub mod user_capnp {
|
pub mod user_capnp {
|
||||||
include!(concat!(env!("OUT_DIR"), "/user_capnp.rs"));
|
include!(concat!(env!("OUT_DIR"), "/user_capnp.rs"));
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "generated"))]
|
|
||||||
pub mod user_capnp;
|
|
||||||
|
|
||||||
#[cfg(feature = "generated")]
|
|
||||||
pub mod usersystem_capnp {
|
pub mod usersystem_capnp {
|
||||||
include!(concat!(env!("OUT_DIR"), "/usersystem_capnp.rs"));
|
include!(concat!(env!("OUT_DIR"), "/usersystem_capnp.rs"));
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "generated"))]
|
|
||||||
pub mod usersystem_capnp;
|
|
||||||
|
@ -1,996 +0,0 @@
|
|||||||
// @generated by the capnpc-rust plugin to the Cap'n Proto schema compiler.
|
|
||||||
// DO NOT EDIT.
|
|
||||||
// source: authenticationsystem.capnp
|
|
||||||
|
|
||||||
|
|
||||||
pub mod response {
|
|
||||||
pub use self::Which::{Failed,Challenge,Successful};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
pub fn has_challenge(&self) -> bool {
|
|
||||||
if self.reader.get_data_field::<u16>(1) != 1 { return false; }
|
|
||||||
!self.reader.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn which(self) -> ::core::result::Result<WhichReader<'a,>, ::capnp::NotInSchema> {
|
|
||||||
match self.reader.get_data_field::<u16>(1) {
|
|
||||||
0 => {
|
|
||||||
::core::result::Result::Ok(Failed(
|
|
||||||
::capnp::traits::FromStructReader::new(self.reader)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
::core::result::Result::Ok(Challenge(
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
2 => {
|
|
||||||
::core::result::Result::Ok(Successful(
|
|
||||||
::capnp::traits::FromStructReader::new(self.reader)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
x => ::core::result::Result::Err(::capnp::NotInSchema(x))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_failed(self, ) -> crate::schema::authenticationsystem_capnp::response::failed::Builder<'a> {
|
|
||||||
self.builder.set_data_field::<u16>(1, 0);
|
|
||||||
self.builder.set_data_field::<u16>(0, 0u16);
|
|
||||||
self.builder.get_pointer_field(0).clear();
|
|
||||||
::capnp::traits::FromStructBuilder::new(self.builder)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_challenge(&mut self, value: ::capnp::data::Reader<'_>) {
|
|
||||||
self.builder.set_data_field::<u16>(1, 1);
|
|
||||||
self.builder.get_pointer_field(0).set_data(value);
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_challenge(self, size: u32) -> ::capnp::data::Builder<'a> {
|
|
||||||
self.builder.set_data_field::<u16>(1, 1);
|
|
||||||
self.builder.get_pointer_field(0).init_data(size)
|
|
||||||
}
|
|
||||||
pub fn has_challenge(&self) -> bool {
|
|
||||||
if self.builder.get_data_field::<u16>(1) != 1 { return false; }
|
|
||||||
!self.builder.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_successful(self, ) -> crate::schema::authenticationsystem_capnp::response::successful::Builder<'a> {
|
|
||||||
self.builder.set_data_field::<u16>(1, 2);
|
|
||||||
self.builder.get_pointer_field(0).clear();
|
|
||||||
self.builder.get_pointer_field(1).clear();
|
|
||||||
::capnp::traits::FromStructBuilder::new(self.builder)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn which(self) -> ::core::result::Result<WhichBuilder<'a,>, ::capnp::NotInSchema> {
|
|
||||||
match self.builder.get_data_field::<u16>(1) {
|
|
||||||
0 => {
|
|
||||||
::core::result::Result::Ok(Failed(
|
|
||||||
::capnp::traits::FromStructBuilder::new(self.builder)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
::core::result::Result::Ok(Challenge(
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
2 => {
|
|
||||||
::core::result::Result::Ok(Successful(
|
|
||||||
::capnp::traits::FromStructBuilder::new(self.builder)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
x => ::core::result::Result::Err(::capnp::NotInSchema(x))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 1, pointers: 2 };
|
|
||||||
pub const TYPE_ID: u64 = 0xe76b_4158_bdde_4934;
|
|
||||||
}
|
|
||||||
pub enum Which<A0,A1,A2> {
|
|
||||||
Failed(A0),
|
|
||||||
Challenge(A1),
|
|
||||||
Successful(A2),
|
|
||||||
}
|
|
||||||
pub type WhichReader<'a,> = Which<crate::schema::authenticationsystem_capnp::response::failed::Reader<'a>,::capnp::Result<::capnp::data::Reader<'a>>,crate::schema::authenticationsystem_capnp::response::successful::Reader<'a>>;
|
|
||||||
pub type WhichBuilder<'a,> = Which<crate::schema::authenticationsystem_capnp::response::failed::Builder<'a>,::capnp::Result<::capnp::data::Builder<'a>>,crate::schema::authenticationsystem_capnp::response::successful::Builder<'a>>;
|
|
||||||
|
|
||||||
#[repr(u16)]
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum Error {
|
|
||||||
Aborted = 0,
|
|
||||||
BadMechanism = 1,
|
|
||||||
InvalidCredentials = 2,
|
|
||||||
Failed = 3,
|
|
||||||
}
|
|
||||||
impl ::capnp::traits::FromU16 for Error {
|
|
||||||
#[inline]
|
|
||||||
fn from_u16(value: u16) -> ::core::result::Result<Error, ::capnp::NotInSchema> {
|
|
||||||
match value {
|
|
||||||
0 => ::core::result::Result::Ok(Error::Aborted),
|
|
||||||
1 => ::core::result::Result::Ok(Error::BadMechanism),
|
|
||||||
2 => ::core::result::Result::Ok(Error::InvalidCredentials),
|
|
||||||
3 => ::core::result::Result::Ok(Error::Failed),
|
|
||||||
n => ::core::result::Result::Err(::capnp::NotInSchema(n)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::capnp::traits::ToU16 for Error {
|
|
||||||
#[inline]
|
|
||||||
fn to_u16(self) -> u16 { self as u16 }
|
|
||||||
}
|
|
||||||
impl ::capnp::traits::HasTypeId for Error {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { 0xe87e_ae86_8b88_cfc1u64 }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod failed {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_code(self) -> ::core::result::Result<crate::schema::authenticationsystem_capnp::response::Error,::capnp::NotInSchema> {
|
|
||||||
::capnp::traits::FromU16::from_u16(self.reader.get_data_field::<u16>(0))
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_additional_data(self) -> ::capnp::Result<::capnp::data::Reader<'a>> {
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
pub fn has_additional_data(&self) -> bool {
|
|
||||||
!self.reader.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_code(self) -> ::core::result::Result<crate::schema::authenticationsystem_capnp::response::Error,::capnp::NotInSchema> {
|
|
||||||
::capnp::traits::FromU16::from_u16(self.builder.get_data_field::<u16>(0))
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_code(&mut self, value: crate::schema::authenticationsystem_capnp::response::Error) {
|
|
||||||
self.builder.set_data_field::<u16>(0, value as u16)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_additional_data(self) -> ::capnp::Result<::capnp::data::Builder<'a>> {
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_additional_data(&mut self, value: ::capnp::data::Reader<'_>) {
|
|
||||||
self.builder.get_pointer_field(0).set_data(value);
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_additional_data(self, size: u32) -> ::capnp::data::Builder<'a> {
|
|
||||||
self.builder.get_pointer_field(0).init_data(size)
|
|
||||||
}
|
|
||||||
pub fn has_additional_data(&self) -> bool {
|
|
||||||
!self.builder.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 1, pointers: 2 };
|
|
||||||
pub const TYPE_ID: u64 = 0xd726_d467_66b2_fd0c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod successful {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_session(self) -> ::capnp::Result<crate::schema::connection_capnp::session::Reader<'a>> {
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
pub fn has_session(&self) -> bool {
|
|
||||||
!self.reader.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_additional_data(self) -> ::capnp::Result<::capnp::data::Reader<'a>> {
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(1), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
pub fn has_additional_data(&self) -> bool {
|
|
||||||
!self.reader.get_pointer_field(1).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_session(self) -> ::capnp::Result<crate::schema::connection_capnp::session::Builder<'a>> {
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_session(&mut self, value: crate::schema::connection_capnp::session::Reader<'_>) -> ::capnp::Result<()> {
|
|
||||||
::capnp::traits::SetPointerBuilder::set_pointer_builder(self.builder.get_pointer_field(0), value, false)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_session(self, ) -> crate::schema::connection_capnp::session::Builder<'a> {
|
|
||||||
::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), 0)
|
|
||||||
}
|
|
||||||
pub fn has_session(&self) -> bool {
|
|
||||||
!self.builder.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_additional_data(self) -> ::capnp::Result<::capnp::data::Builder<'a>> {
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(1), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_additional_data(&mut self, value: ::capnp::data::Reader<'_>) {
|
|
||||||
self.builder.get_pointer_field(1).set_data(value);
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_additional_data(self, size: u32) -> ::capnp::data::Builder<'a> {
|
|
||||||
self.builder.get_pointer_field(1).init_data(size)
|
|
||||||
}
|
|
||||||
pub fn has_additional_data(&self) -> bool {
|
|
||||||
!self.builder.get_pointer_field(1).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
pub fn get_session(&self) -> crate::schema::connection_capnp::session::Pipeline {
|
|
||||||
::capnp::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field(0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 1, pointers: 2 };
|
|
||||||
pub const TYPE_ID: u64 = 0xbf3b_c966_6eea_ffa0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub mod authentication {
|
|
||||||
#![allow(unused_variables)]
|
|
||||||
pub type StepParams<> = ::capnp::capability::Params<crate::schema::authenticationsystem_capnp::authentication::step_params::Owned>;
|
|
||||||
pub type StepResults<> = ::capnp::capability::Results<crate::schema::authenticationsystem_capnp::response::Owned>;
|
|
||||||
pub type AbortParams<> = ::capnp::capability::Params<crate::schema::authenticationsystem_capnp::authentication::abort_params::Owned>;
|
|
||||||
pub type AbortResults<> = ::capnp::capability::Results<crate::schema::authenticationsystem_capnp::authentication::abort_results::Owned>;
|
|
||||||
|
|
||||||
pub struct Client {
|
|
||||||
pub client: ::capnp::capability::Client,
|
|
||||||
}
|
|
||||||
impl ::capnp::capability::FromClientHook for Client {
|
|
||||||
fn new(hook: Box<dyn (::capnp::private::capability::ClientHook)>) -> Client {
|
|
||||||
Client { client: ::capnp::capability::Client::new(hook), }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Client; type Builder = Client; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Client; }
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Client<> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, _default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Client<>> {
|
|
||||||
::core::result::Result::Ok(::capnp::capability::FromClientHook::new(reader.get_capability()?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Client<> {
|
|
||||||
fn init_pointer(_builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Client<> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Client<>> {
|
|
||||||
::core::result::Result::Ok(::capnp::capability::FromClientHook::new(builder.get_capability()?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <> ::capnp::traits::SetPointerBuilder for Client<> {
|
|
||||||
fn set_pointer_builder(pointer: ::capnp::private::layout::PointerBuilder<'_>, from: Client<>, _canonicalize: bool) -> ::capnp::Result<()> {
|
|
||||||
pointer.set_capability(from.client.hook);
|
|
||||||
::core::result::Result::Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::capnp::traits::HasTypeId for Client {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl Clone for Client {
|
|
||||||
fn clone(&self) -> Client {
|
|
||||||
Client { client: ::capnp::capability::Client::new(self.client.hook.add_ref()), }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Client {
|
|
||||||
pub fn step_request(&self) -> ::capnp::capability::Request<crate::schema::authenticationsystem_capnp::authentication::step_params::Owned,crate::schema::authenticationsystem_capnp::response::Owned> {
|
|
||||||
self.client.new_call(_private::TYPE_ID, 0, None)
|
|
||||||
}
|
|
||||||
pub fn abort_request(&self) -> ::capnp::capability::Request<crate::schema::authenticationsystem_capnp::authentication::abort_params::Owned,crate::schema::authenticationsystem_capnp::authentication::abort_results::Owned> {
|
|
||||||
self.client.new_call(_private::TYPE_ID, 1, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub trait Server<> {
|
|
||||||
fn step(&mut self, _: StepParams<>, _: StepResults<>) -> ::capnp::capability::Promise<(), ::capnp::Error> { ::capnp::capability::Promise::err(::capnp::Error::unimplemented("method not implemented".to_string())) }
|
|
||||||
fn abort(&mut self, _: AbortParams<>, _: AbortResults<>) -> ::capnp::capability::Promise<(), ::capnp::Error> { ::capnp::capability::Promise::err(::capnp::Error::unimplemented("method not implemented".to_string())) }
|
|
||||||
}
|
|
||||||
pub struct ServerDispatch<_T,> {
|
|
||||||
pub server: _T,
|
|
||||||
}
|
|
||||||
impl <_S: Server + 'static, > ::capnp::capability::FromServer<_S> for Client {
|
|
||||||
type Dispatch = ServerDispatch<_S, >;
|
|
||||||
fn from_server(s: _S) -> ServerDispatch<_S, > {
|
|
||||||
ServerDispatch { server: s, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl <_T: Server> ::core::ops::Deref for ServerDispatch<_T> {
|
|
||||||
type Target = _T;
|
|
||||||
fn deref(&self) -> &_T { &self.server}
|
|
||||||
}
|
|
||||||
impl <_T: Server> ::core::ops::DerefMut for ServerDispatch<_T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut _T { &mut self.server}
|
|
||||||
}
|
|
||||||
impl <_T: Server> ::capnp::capability::Server for ServerDispatch<_T> {
|
|
||||||
fn dispatch_call(&mut self, interface_id: u64, method_id: u16, params: ::capnp::capability::Params<::capnp::any_pointer::Owned>, results: ::capnp::capability::Results<::capnp::any_pointer::Owned>) -> ::capnp::capability::Promise<(), ::capnp::Error> {
|
|
||||||
match interface_id {
|
|
||||||
_private::TYPE_ID => ServerDispatch::<_T, >::dispatch_call_internal(&mut self.server, method_id, params, results),
|
|
||||||
_ => { ::capnp::capability::Promise::err(::capnp::Error::unimplemented("Method not implemented.".to_string())) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl <_T :Server> ServerDispatch<_T> {
|
|
||||||
pub fn dispatch_call_internal(server: &mut _T, method_id: u16, params: ::capnp::capability::Params<::capnp::any_pointer::Owned>, results: ::capnp::capability::Results<::capnp::any_pointer::Owned>) -> ::capnp::capability::Promise<(), ::capnp::Error> {
|
|
||||||
match method_id {
|
|
||||||
0 => server.step(::capnp::private::capability::internal_get_typed_params(params), ::capnp::private::capability::internal_get_typed_results(results)),
|
|
||||||
1 => server.abort(::capnp::private::capability::internal_get_typed_params(params), ::capnp::private::capability::internal_get_typed_results(results)),
|
|
||||||
_ => { ::capnp::capability::Promise::err(::capnp::Error::unimplemented("Method not implemented.".to_string())) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub mod _private {
|
|
||||||
pub const TYPE_ID: u64 = 0xe657_e27e_b5ff_b1ad;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod step_params {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_data(self) -> ::capnp::Result<::capnp::data::Reader<'a>> {
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
pub fn has_data(&self) -> bool {
|
|
||||||
!self.reader.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_data(self) -> ::capnp::Result<::capnp::data::Builder<'a>> {
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_data(&mut self, value: ::capnp::data::Reader<'_>) {
|
|
||||||
self.builder.get_pointer_field(0).set_data(value);
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_data(self, size: u32) -> ::capnp::data::Builder<'a> {
|
|
||||||
self.builder.get_pointer_field(0).init_data(size)
|
|
||||||
}
|
|
||||||
pub fn has_data(&self) -> bool {
|
|
||||||
!self.builder.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 0, pointers: 1 };
|
|
||||||
pub const TYPE_ID: u64 = 0xcb98_01a0_63fb_684e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod abort_params {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 0, pointers: 0 };
|
|
||||||
pub const TYPE_ID: u64 = 0x9dbb_e5bb_dcce_a62c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod abort_results {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 0, pointers: 0 };
|
|
||||||
pub const TYPE_ID: u64 = 0xce2f_5a64_8658_0299;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,699 +0,0 @@
|
|||||||
// @generated by the capnpc-rust plugin to the Cap'n Proto schema compiler.
|
|
||||||
// DO NOT EDIT.
|
|
||||||
// source: general.capnp
|
|
||||||
|
|
||||||
|
|
||||||
pub mod u_u_i_d {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_uuid0(self) -> u64 {
|
|
||||||
self.reader.get_data_field::<u64>(0)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_uuid1(self) -> u64 {
|
|
||||||
self.reader.get_data_field::<u64>(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_uuid0(self) -> u64 {
|
|
||||||
self.builder.get_data_field::<u64>(0)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_uuid0(&mut self, value: u64) {
|
|
||||||
self.builder.set_data_field::<u64>(0, value);
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_uuid1(self) -> u64 {
|
|
||||||
self.builder.get_data_field::<u64>(1)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_uuid1(&mut self, value: u64) {
|
|
||||||
self.builder.set_data_field::<u64>(1, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 2, pointers: 0 };
|
|
||||||
pub const TYPE_ID: u64 = 0xb01b_03d4_f827_7597;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod key_value_pair {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_key(self) -> ::capnp::Result<::capnp::text::Reader<'a>> {
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
pub fn has_key(&self) -> bool {
|
|
||||||
!self.reader.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_value(self) -> ::capnp::Result<::capnp::text::Reader<'a>> {
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(1), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
pub fn has_value(&self) -> bool {
|
|
||||||
!self.reader.get_pointer_field(1).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_key(self) -> ::capnp::Result<::capnp::text::Builder<'a>> {
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_key(&mut self, value: ::capnp::text::Reader<'_>) {
|
|
||||||
self.builder.get_pointer_field(0).set_text(value);
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_key(self, size: u32) -> ::capnp::text::Builder<'a> {
|
|
||||||
self.builder.get_pointer_field(0).init_text(size)
|
|
||||||
}
|
|
||||||
pub fn has_key(&self) -> bool {
|
|
||||||
!self.builder.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_value(self) -> ::capnp::Result<::capnp::text::Builder<'a>> {
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(1), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_value(&mut self, value: ::capnp::text::Reader<'_>) {
|
|
||||||
self.builder.get_pointer_field(1).set_text(value);
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_value(self, size: u32) -> ::capnp::text::Builder<'a> {
|
|
||||||
self.builder.get_pointer_field(1).init_text(size)
|
|
||||||
}
|
|
||||||
pub fn has_value(&self) -> bool {
|
|
||||||
!self.builder.get_pointer_field(1).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 0, pointers: 2 };
|
|
||||||
pub const TYPE_ID: u64 = 0xfb54_3b21_ce63_7bf1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod optional { /* T */
|
|
||||||
pub use self::Which::{Nothing,Just};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned<T> {
|
|
||||||
_phantom: ::core::marker::PhantomData<T>
|
|
||||||
}
|
|
||||||
impl <'a, T> ::capnp::traits::Owned<'a> for Owned <T> where T: for<'c> ::capnp::traits::Owned<'c> { type Reader = Reader<'a, T>; type Builder = Builder<'a, T>; }
|
|
||||||
impl <'a, T> ::capnp::traits::OwnedStruct<'a> for Owned <T> where T: for<'c> ::capnp::traits::Owned<'c> { type Reader = Reader<'a, T>; type Builder = Builder<'a, T>; }
|
|
||||||
impl <T> ::capnp::traits::Pipelined for Owned<T> where T: for<'c> ::capnp::traits::Owned<'c> { type Pipeline = Pipeline<T>; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a,T> where T: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
reader: ::capnp::private::layout::StructReader<'a>,
|
|
||||||
_phantom: ::core::marker::PhantomData<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T> ::capnp::traits::HasTypeId for Reader<'a,T> where T: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,T> ::capnp::traits::FromStructReader<'a> for Reader<'a,T> where T: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,T> {
|
|
||||||
Reader { reader, _phantom: ::core::marker::PhantomData, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T> ::capnp::traits::FromPointerReader<'a> for Reader<'a,T> where T: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,T>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,T> where T: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T> ::capnp::traits::Imbue<'a> for Reader<'a,T> where T: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T> Reader<'a,T> where T: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,T> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
pub fn has_just(&self) -> bool {
|
|
||||||
if self.reader.get_data_field::<u16>(0) != 1 { return false; }
|
|
||||||
!self.reader.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn which(self) -> ::core::result::Result<WhichReader<'a,T>, ::capnp::NotInSchema> {
|
|
||||||
match self.reader.get_data_field::<u16>(0) {
|
|
||||||
0 => {
|
|
||||||
::core::result::Result::Ok(Nothing(
|
|
||||||
()
|
|
||||||
))
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
::core::result::Result::Ok(Just(
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
x => ::core::result::Result::Err(::capnp::NotInSchema(x))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a,T> where T: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
builder: ::capnp::private::layout::StructBuilder<'a>,
|
|
||||||
_phantom: ::core::marker::PhantomData<T>
|
|
||||||
}
|
|
||||||
impl <'a,T> ::capnp::traits::HasStructSize for Builder<'a,T> where T: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,T> ::capnp::traits::HasTypeId for Builder<'a,T> where T: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,T> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,T> where T: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, T> {
|
|
||||||
Builder { builder, _phantom: ::core::marker::PhantomData, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T> ::capnp::traits::ImbueMut<'a> for Builder<'a,T> where T: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,T> where T: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,T> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,T>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T> ::capnp::traits::SetPointerBuilder for Reader<'a,T> where T: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,T>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T> Builder<'a,T> where T: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,T> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,T> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,T> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_nothing(&mut self, _value: ()) {
|
|
||||||
self.builder.set_data_field::<u16>(0, 0);
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn initn_just(self, length: u32) -> <T as ::capnp::traits::Owned<'a>>::Builder {
|
|
||||||
self.builder.set_data_field::<u16>(0, 1);
|
|
||||||
::capnp::any_pointer::Builder::new(self.builder.get_pointer_field(0)).initn_as(length)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_just(&mut self, value: <T as ::capnp::traits::Owned<'_>>::Reader) -> ::capnp::Result<()> {
|
|
||||||
self.builder.set_data_field::<u16>(0, 1);
|
|
||||||
::capnp::traits::SetPointerBuilder::set_pointer_builder(self.builder.get_pointer_field(0), value, false)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_just(self, ) -> <T as ::capnp::traits::Owned<'a>>::Builder {
|
|
||||||
self.builder.set_data_field::<u16>(0, 1);
|
|
||||||
::capnp::any_pointer::Builder::new(self.builder.get_pointer_field(0)).init_as()
|
|
||||||
}
|
|
||||||
pub fn has_just(&self) -> bool {
|
|
||||||
if self.builder.get_data_field::<u16>(0) != 1 { return false; }
|
|
||||||
!self.builder.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn which(self) -> ::core::result::Result<WhichBuilder<'a,T>, ::capnp::NotInSchema> {
|
|
||||||
match self.builder.get_data_field::<u16>(0) {
|
|
||||||
0 => {
|
|
||||||
::core::result::Result::Ok(Nothing(
|
|
||||||
()
|
|
||||||
))
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
::core::result::Result::Ok(Just(
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
x => ::core::result::Result::Err(::capnp::NotInSchema(x))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline<T> {
|
|
||||||
_typeless: ::capnp::any_pointer::Pipeline,
|
|
||||||
_phantom: ::core::marker::PhantomData<T>
|
|
||||||
}
|
|
||||||
impl<T> ::capnp::capability::FromTypelessPipeline for Pipeline<T> {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline<T> {
|
|
||||||
Pipeline { _typeless: typeless, _phantom: ::core::marker::PhantomData, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> Pipeline<T> where T: ::capnp::traits::Pipelined, <T as ::capnp::traits::Pipelined>::Pipeline: ::capnp::capability::FromTypelessPipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 1, pointers: 1 };
|
|
||||||
pub const TYPE_ID: u64 = 0xd77e_cca2_05b0_6927;
|
|
||||||
}
|
|
||||||
pub enum Which<A0> {
|
|
||||||
Nothing(()),
|
|
||||||
Just(A0),
|
|
||||||
}
|
|
||||||
pub type WhichReader<'a,T> = Which<::capnp::Result<<T as ::capnp::traits::Owned<'a>>::Reader>>;
|
|
||||||
pub type WhichBuilder<'a,T> = Which<::capnp::Result<<T as ::capnp::traits::Owned<'a>>::Builder>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod fallible { /* T,E */
|
|
||||||
pub use self::Which::{Failed,Successful};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned<T,E> {
|
|
||||||
_phantom: ::core::marker::PhantomData<(T,E)>
|
|
||||||
}
|
|
||||||
impl <'a, T,E> ::capnp::traits::Owned<'a> for Owned <T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> { type Reader = Reader<'a, T,E>; type Builder = Builder<'a, T,E>; }
|
|
||||||
impl <'a, T,E> ::capnp::traits::OwnedStruct<'a> for Owned <T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> { type Reader = Reader<'a, T,E>; type Builder = Builder<'a, T,E>; }
|
|
||||||
impl <T,E> ::capnp::traits::Pipelined for Owned<T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> { type Pipeline = Pipeline<T,E>; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a,T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
reader: ::capnp::private::layout::StructReader<'a>,
|
|
||||||
_phantom: ::core::marker::PhantomData<(T,E)>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T,E> ::capnp::traits::HasTypeId for Reader<'a,T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,T,E> ::capnp::traits::FromStructReader<'a> for Reader<'a,T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,T,E> {
|
|
||||||
Reader { reader, _phantom: ::core::marker::PhantomData, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T,E> ::capnp::traits::FromPointerReader<'a> for Reader<'a,T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,T,E>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T,E> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T,E> ::capnp::traits::Imbue<'a> for Reader<'a,T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T,E> Reader<'a,T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,T,E> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
pub fn has_failed(&self) -> bool {
|
|
||||||
if self.reader.get_data_field::<u16>(0) != 0 { return false; }
|
|
||||||
!self.reader.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
pub fn has_successful(&self) -> bool {
|
|
||||||
if self.reader.get_data_field::<u16>(0) != 1 { return false; }
|
|
||||||
!self.reader.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn which(self) -> ::core::result::Result<WhichReader<'a,T,E>, ::capnp::NotInSchema> {
|
|
||||||
match self.reader.get_data_field::<u16>(0) {
|
|
||||||
0 => {
|
|
||||||
::core::result::Result::Ok(Failed(
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
::core::result::Result::Ok(Successful(
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
x => ::core::result::Result::Err(::capnp::NotInSchema(x))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a,T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
builder: ::capnp::private::layout::StructBuilder<'a>,
|
|
||||||
_phantom: ::core::marker::PhantomData<(T,E)>
|
|
||||||
}
|
|
||||||
impl <'a,T,E> ::capnp::traits::HasStructSize for Builder<'a,T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,T,E> ::capnp::traits::HasTypeId for Builder<'a,T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,T,E> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, T,E> {
|
|
||||||
Builder { builder, _phantom: ::core::marker::PhantomData, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T,E> ::capnp::traits::ImbueMut<'a> for Builder<'a,T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T,E> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,T,E> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,T,E>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T,E> ::capnp::traits::SetPointerBuilder for Reader<'a,T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,T,E>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,T,E> Builder<'a,T,E> where T: for<'c> ::capnp::traits::Owned<'c>, E: for<'c> ::capnp::traits::Owned<'c> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,T,E> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,T,E> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,T,E> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn initn_failed(self, length: u32) -> <E as ::capnp::traits::Owned<'a>>::Builder {
|
|
||||||
self.builder.set_data_field::<u16>(0, 0);
|
|
||||||
::capnp::any_pointer::Builder::new(self.builder.get_pointer_field(0)).initn_as(length)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_failed(&mut self, value: <E as ::capnp::traits::Owned<'_>>::Reader) -> ::capnp::Result<()> {
|
|
||||||
self.builder.set_data_field::<u16>(0, 0);
|
|
||||||
::capnp::traits::SetPointerBuilder::set_pointer_builder(self.builder.get_pointer_field(0), value, false)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_failed(self, ) -> <E as ::capnp::traits::Owned<'a>>::Builder {
|
|
||||||
self.builder.set_data_field::<u16>(0, 0);
|
|
||||||
::capnp::any_pointer::Builder::new(self.builder.get_pointer_field(0)).init_as()
|
|
||||||
}
|
|
||||||
pub fn has_failed(&self) -> bool {
|
|
||||||
if self.builder.get_data_field::<u16>(0) != 0 { return false; }
|
|
||||||
!self.builder.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn initn_successful(self, length: u32) -> <T as ::capnp::traits::Owned<'a>>::Builder {
|
|
||||||
self.builder.set_data_field::<u16>(0, 1);
|
|
||||||
::capnp::any_pointer::Builder::new(self.builder.get_pointer_field(0)).initn_as(length)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_successful(&mut self, value: <T as ::capnp::traits::Owned<'_>>::Reader) -> ::capnp::Result<()> {
|
|
||||||
self.builder.set_data_field::<u16>(0, 1);
|
|
||||||
::capnp::traits::SetPointerBuilder::set_pointer_builder(self.builder.get_pointer_field(0), value, false)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_successful(self, ) -> <T as ::capnp::traits::Owned<'a>>::Builder {
|
|
||||||
self.builder.set_data_field::<u16>(0, 1);
|
|
||||||
::capnp::any_pointer::Builder::new(self.builder.get_pointer_field(0)).init_as()
|
|
||||||
}
|
|
||||||
pub fn has_successful(&self) -> bool {
|
|
||||||
if self.builder.get_data_field::<u16>(0) != 1 { return false; }
|
|
||||||
!self.builder.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn which(self) -> ::core::result::Result<WhichBuilder<'a,T,E>, ::capnp::NotInSchema> {
|
|
||||||
match self.builder.get_data_field::<u16>(0) {
|
|
||||||
0 => {
|
|
||||||
::core::result::Result::Ok(Failed(
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
::core::result::Result::Ok(Successful(
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
x => ::core::result::Result::Err(::capnp::NotInSchema(x))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline<T,E> {
|
|
||||||
_typeless: ::capnp::any_pointer::Pipeline,
|
|
||||||
_phantom: ::core::marker::PhantomData<(T,E)>
|
|
||||||
}
|
|
||||||
impl<T,E> ::capnp::capability::FromTypelessPipeline for Pipeline<T,E> {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline<T,E> {
|
|
||||||
Pipeline { _typeless: typeless, _phantom: ::core::marker::PhantomData, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T,E> Pipeline<T,E> where T: ::capnp::traits::Pipelined, <T as ::capnp::traits::Pipelined>::Pipeline: ::capnp::capability::FromTypelessPipeline, E: ::capnp::traits::Pipelined, <E as ::capnp::traits::Pipelined>::Pipeline: ::capnp::capability::FromTypelessPipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 1, pointers: 1 };
|
|
||||||
pub const TYPE_ID: u64 = 0xf477_aafa_9205_11aa;
|
|
||||||
}
|
|
||||||
pub enum Which<A0,A1> {
|
|
||||||
Failed(A0),
|
|
||||||
Successful(A1),
|
|
||||||
}
|
|
||||||
pub type WhichReader<'a,T,E> = Which<::capnp::Result<<E as ::capnp::traits::Owned<'a>>::Reader>,::capnp::Result<<T as ::capnp::traits::Owned<'a>>::Reader>>;
|
|
||||||
pub type WhichBuilder<'a,T,E> = Which<::capnp::Result<<E as ::capnp::traits::Owned<'a>>::Builder>,::capnp::Result<<T as ::capnp::traits::Owned<'a>>::Builder>>;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,756 +0,0 @@
|
|||||||
// @generated by the capnpc-rust plugin to the Cap'n Proto schema compiler.
|
|
||||||
// DO NOT EDIT.
|
|
||||||
// source: machinesystem.capnp
|
|
||||||
|
|
||||||
|
|
||||||
pub mod machine_system {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_info(self) -> ::capnp::Result<crate::schema::machinesystem_capnp::machine_system::info::Client> {
|
|
||||||
match self.reader.get_pointer_field(0).get_capability() { ::core::result::Result::Ok(c) => ::core::result::Result::Ok(::capnp::capability::FromClientHook::new(c)), ::core::result::Result::Err(e) => ::core::result::Result::Err(e)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_info(self) -> ::capnp::Result<crate::schema::machinesystem_capnp::machine_system::info::Client> {
|
|
||||||
match self.builder.get_pointer_field(0).get_capability() { ::core::result::Result::Ok(c) => ::core::result::Result::Ok(::capnp::capability::FromClientHook::new(c)), ::core::result::Result::Err(e) => ::core::result::Result::Err(e)}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_info(&mut self, value: crate::schema::machinesystem_capnp::machine_system::info::Client) {
|
|
||||||
self.builder.get_pointer_field(0).set_capability(value.client.hook);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
pub fn get_info(&self) -> crate::schema::machinesystem_capnp::machine_system::info::Client {
|
|
||||||
::capnp::capability::FromClientHook::new(self._typeless.get_pointer_field(0).as_cap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 0, pointers: 1 };
|
|
||||||
pub const TYPE_ID: u64 = 0x8771_90c7_e487_ddb6;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub mod info {
|
|
||||||
#![allow(unused_variables)]
|
|
||||||
pub type GetMachineListParams<> = ::capnp::capability::Params<crate::schema::machinesystem_capnp::machine_system::info::get_machine_list_params::Owned>;
|
|
||||||
pub type GetMachineListResults<> = ::capnp::capability::Results<crate::schema::machinesystem_capnp::machine_system::info::get_machine_list_results::Owned>;
|
|
||||||
pub type GetMachineParams<> = ::capnp::capability::Params<crate::schema::machinesystem_capnp::machine_system::info::get_machine_params::Owned>;
|
|
||||||
pub type GetMachineResults<> = ::capnp::capability::Results<crate::schema::general_capnp::optional::Owned<crate::schema::machine_capnp::machine::Owned>>;
|
|
||||||
pub type GetMachineURNParams<> = ::capnp::capability::Params<crate::schema::machinesystem_capnp::machine_system::info::get_machine_u_r_n_params::Owned>;
|
|
||||||
pub type GetMachineURNResults<> = ::capnp::capability::Results<crate::schema::general_capnp::optional::Owned<crate::schema::machine_capnp::machine::Owned>>;
|
|
||||||
|
|
||||||
pub struct Client {
|
|
||||||
pub client: ::capnp::capability::Client,
|
|
||||||
}
|
|
||||||
impl ::capnp::capability::FromClientHook for Client {
|
|
||||||
fn new(hook: Box<dyn (::capnp::private::capability::ClientHook)>) -> Client {
|
|
||||||
Client { client: ::capnp::capability::Client::new(hook), }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Client; type Builder = Client; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Client; }
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Client<> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, _default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Client<>> {
|
|
||||||
::core::result::Result::Ok(::capnp::capability::FromClientHook::new(reader.get_capability()?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Client<> {
|
|
||||||
fn init_pointer(_builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Client<> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Client<>> {
|
|
||||||
::core::result::Result::Ok(::capnp::capability::FromClientHook::new(builder.get_capability()?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <> ::capnp::traits::SetPointerBuilder for Client<> {
|
|
||||||
fn set_pointer_builder(pointer: ::capnp::private::layout::PointerBuilder<'_>, from: Client<>, _canonicalize: bool) -> ::capnp::Result<()> {
|
|
||||||
pointer.set_capability(from.client.hook);
|
|
||||||
::core::result::Result::Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::capnp::traits::HasTypeId for Client {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl Clone for Client {
|
|
||||||
fn clone(&self) -> Client {
|
|
||||||
Client { client: ::capnp::capability::Client::new(self.client.hook.add_ref()), }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Client {
|
|
||||||
pub fn get_machine_list_request(&self) -> ::capnp::capability::Request<crate::schema::machinesystem_capnp::machine_system::info::get_machine_list_params::Owned,crate::schema::machinesystem_capnp::machine_system::info::get_machine_list_results::Owned> {
|
|
||||||
self.client.new_call(_private::TYPE_ID, 0, None)
|
|
||||||
}
|
|
||||||
pub fn get_machine_request(&self) -> ::capnp::capability::Request<crate::schema::machinesystem_capnp::machine_system::info::get_machine_params::Owned,crate::schema::general_capnp::optional::Owned<crate::schema::machine_capnp::machine::Owned>> {
|
|
||||||
self.client.new_call(_private::TYPE_ID, 1, None)
|
|
||||||
}
|
|
||||||
pub fn get_machine_u_r_n_request(&self) -> ::capnp::capability::Request<crate::schema::machinesystem_capnp::machine_system::info::get_machine_u_r_n_params::Owned,crate::schema::general_capnp::optional::Owned<crate::schema::machine_capnp::machine::Owned>> {
|
|
||||||
self.client.new_call(_private::TYPE_ID, 2, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub trait Server<> {
|
|
||||||
fn get_machine_list(&mut self, _: GetMachineListParams<>, _: GetMachineListResults<>) -> ::capnp::capability::Promise<(), ::capnp::Error> { ::capnp::capability::Promise::err(::capnp::Error::unimplemented("method not implemented".to_string())) }
|
|
||||||
fn get_machine(&mut self, _: GetMachineParams<>, _: GetMachineResults<>) -> ::capnp::capability::Promise<(), ::capnp::Error> { ::capnp::capability::Promise::err(::capnp::Error::unimplemented("method not implemented".to_string())) }
|
|
||||||
fn get_machine_u_r_n(&mut self, _: GetMachineURNParams<>, _: GetMachineURNResults<>) -> ::capnp::capability::Promise<(), ::capnp::Error> { ::capnp::capability::Promise::err(::capnp::Error::unimplemented("method not implemented".to_string())) }
|
|
||||||
}
|
|
||||||
pub struct ServerDispatch<_T,> {
|
|
||||||
pub server: _T,
|
|
||||||
}
|
|
||||||
impl <_S: Server + 'static, > ::capnp::capability::FromServer<_S> for Client {
|
|
||||||
type Dispatch = ServerDispatch<_S, >;
|
|
||||||
fn from_server(s: _S) -> ServerDispatch<_S, > {
|
|
||||||
ServerDispatch { server: s, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl <_T: Server> ::core::ops::Deref for ServerDispatch<_T> {
|
|
||||||
type Target = _T;
|
|
||||||
fn deref(&self) -> &_T { &self.server}
|
|
||||||
}
|
|
||||||
impl <_T: Server> ::core::ops::DerefMut for ServerDispatch<_T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut _T { &mut self.server}
|
|
||||||
}
|
|
||||||
impl <_T: Server> ::capnp::capability::Server for ServerDispatch<_T> {
|
|
||||||
fn dispatch_call(&mut self, interface_id: u64, method_id: u16, params: ::capnp::capability::Params<::capnp::any_pointer::Owned>, results: ::capnp::capability::Results<::capnp::any_pointer::Owned>) -> ::capnp::capability::Promise<(), ::capnp::Error> {
|
|
||||||
match interface_id {
|
|
||||||
_private::TYPE_ID => ServerDispatch::<_T, >::dispatch_call_internal(&mut self.server, method_id, params, results),
|
|
||||||
_ => { ::capnp::capability::Promise::err(::capnp::Error::unimplemented("Method not implemented.".to_string())) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl <_T :Server> ServerDispatch<_T> {
|
|
||||||
pub fn dispatch_call_internal(server: &mut _T, method_id: u16, params: ::capnp::capability::Params<::capnp::any_pointer::Owned>, results: ::capnp::capability::Results<::capnp::any_pointer::Owned>) -> ::capnp::capability::Promise<(), ::capnp::Error> {
|
|
||||||
match method_id {
|
|
||||||
0 => server.get_machine_list(::capnp::private::capability::internal_get_typed_params(params), ::capnp::private::capability::internal_get_typed_results(results)),
|
|
||||||
1 => server.get_machine(::capnp::private::capability::internal_get_typed_params(params), ::capnp::private::capability::internal_get_typed_results(results)),
|
|
||||||
2 => server.get_machine_u_r_n(::capnp::private::capability::internal_get_typed_params(params), ::capnp::private::capability::internal_get_typed_results(results)),
|
|
||||||
_ => { ::capnp::capability::Promise::err(::capnp::Error::unimplemented("Method not implemented.".to_string())) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub mod _private {
|
|
||||||
pub const TYPE_ID: u64 = 0xf8a1_a0ce_79e3_a4ae;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod get_machine_list_params {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 0, pointers: 0 };
|
|
||||||
pub const TYPE_ID: u64 = 0xae72_5a44_381c_cb81;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod get_machine_list_results {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_machine_list(self) -> ::capnp::Result<::capnp::struct_list::Reader<'a,crate::schema::machine_capnp::machine::Owned>> {
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
pub fn has_machine_list(&self) -> bool {
|
|
||||||
!self.reader.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_machine_list(self) -> ::capnp::Result<::capnp::struct_list::Builder<'a,crate::schema::machine_capnp::machine::Owned>> {
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_machine_list(&mut self, value: ::capnp::struct_list::Reader<'a,crate::schema::machine_capnp::machine::Owned>) -> ::capnp::Result<()> {
|
|
||||||
::capnp::traits::SetPointerBuilder::set_pointer_builder(self.builder.get_pointer_field(0), value, false)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_machine_list(self, size: u32) -> ::capnp::struct_list::Builder<'a,crate::schema::machine_capnp::machine::Owned> {
|
|
||||||
::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), size)
|
|
||||||
}
|
|
||||||
pub fn has_machine_list(&self) -> bool {
|
|
||||||
!self.builder.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 0, pointers: 1 };
|
|
||||||
pub const TYPE_ID: u64 = 0x9964_3fc1_40c0_b2c1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod get_machine_params {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_id(self) -> ::capnp::Result<::capnp::text::Reader<'a>> {
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
pub fn has_id(&self) -> bool {
|
|
||||||
!self.reader.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_id(self) -> ::capnp::Result<::capnp::text::Builder<'a>> {
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_id(&mut self, value: ::capnp::text::Reader<'_>) {
|
|
||||||
self.builder.get_pointer_field(0).set_text(value);
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_id(self, size: u32) -> ::capnp::text::Builder<'a> {
|
|
||||||
self.builder.get_pointer_field(0).init_text(size)
|
|
||||||
}
|
|
||||||
pub fn has_id(&self) -> bool {
|
|
||||||
!self.builder.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 0, pointers: 1 };
|
|
||||||
pub const TYPE_ID: u64 = 0xe315_5a06_09f4_4bed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod get_machine_u_r_n_params {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_urn(self) -> ::capnp::Result<::capnp::text::Reader<'a>> {
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
pub fn has_urn(&self) -> bool {
|
|
||||||
!self.reader.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_urn(self) -> ::capnp::Result<::capnp::text::Builder<'a>> {
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_urn(&mut self, value: ::capnp::text::Reader<'_>) {
|
|
||||||
self.builder.get_pointer_field(0).set_text(value);
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_urn(self, size: u32) -> ::capnp::text::Builder<'a> {
|
|
||||||
self.builder.get_pointer_field(0).init_text(size)
|
|
||||||
}
|
|
||||||
pub fn has_urn(&self) -> bool {
|
|
||||||
!self.builder.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 0, pointers: 1 };
|
|
||||||
pub const TYPE_ID: u64 = 0xeb9d_c2e7_8963_51aa;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,572 +0,0 @@
|
|||||||
// @generated by the capnpc-rust plugin to the Cap'n Proto schema compiler.
|
|
||||||
// DO NOT EDIT.
|
|
||||||
// source: permissionsystem.capnp
|
|
||||||
|
|
||||||
|
|
||||||
pub mod permission_system {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_info(self) -> ::capnp::Result<crate::schema::permissionsystem_capnp::permission_system::info::Client> {
|
|
||||||
match self.reader.get_pointer_field(0).get_capability() { ::core::result::Result::Ok(c) => ::core::result::Result::Ok(::capnp::capability::FromClientHook::new(c)), ::core::result::Result::Err(e) => ::core::result::Result::Err(e)}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_manage(self) -> ::capnp::Result<crate::schema::permissionsystem_capnp::permission_system::manage::Client> {
|
|
||||||
match self.reader.get_pointer_field(1).get_capability() { ::core::result::Result::Ok(c) => ::core::result::Result::Ok(::capnp::capability::FromClientHook::new(c)), ::core::result::Result::Err(e) => ::core::result::Result::Err(e)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_info(self) -> ::capnp::Result<crate::schema::permissionsystem_capnp::permission_system::info::Client> {
|
|
||||||
match self.builder.get_pointer_field(0).get_capability() { ::core::result::Result::Ok(c) => ::core::result::Result::Ok(::capnp::capability::FromClientHook::new(c)), ::core::result::Result::Err(e) => ::core::result::Result::Err(e)}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_info(&mut self, value: crate::schema::permissionsystem_capnp::permission_system::info::Client) {
|
|
||||||
self.builder.get_pointer_field(0).set_capability(value.client.hook);
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_manage(self) -> ::capnp::Result<crate::schema::permissionsystem_capnp::permission_system::manage::Client> {
|
|
||||||
match self.builder.get_pointer_field(1).get_capability() { ::core::result::Result::Ok(c) => ::core::result::Result::Ok(::capnp::capability::FromClientHook::new(c)), ::core::result::Result::Err(e) => ::core::result::Result::Err(e)}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_manage(&mut self, value: crate::schema::permissionsystem_capnp::permission_system::manage::Client) {
|
|
||||||
self.builder.get_pointer_field(1).set_capability(value.client.hook);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
pub fn get_info(&self) -> crate::schema::permissionsystem_capnp::permission_system::info::Client {
|
|
||||||
::capnp::capability::FromClientHook::new(self._typeless.get_pointer_field(0).as_cap())
|
|
||||||
}
|
|
||||||
pub fn get_manage(&self) -> crate::schema::permissionsystem_capnp::permission_system::manage::Client {
|
|
||||||
::capnp::capability::FromClientHook::new(self._typeless.get_pointer_field(1).as_cap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 0, pointers: 2 };
|
|
||||||
pub const TYPE_ID: u64 = 0xcafb_f059_d7a0_0c3b;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub mod info {
|
|
||||||
#![allow(unused_variables)]
|
|
||||||
pub type GetRoleListParams<> = ::capnp::capability::Params<crate::schema::permissionsystem_capnp::permission_system::info::get_role_list_params::Owned>;
|
|
||||||
pub type GetRoleListResults<> = ::capnp::capability::Results<crate::schema::permissionsystem_capnp::permission_system::info::get_role_list_results::Owned>;
|
|
||||||
|
|
||||||
pub struct Client {
|
|
||||||
pub client: ::capnp::capability::Client,
|
|
||||||
}
|
|
||||||
impl ::capnp::capability::FromClientHook for Client {
|
|
||||||
fn new(hook: Box<dyn (::capnp::private::capability::ClientHook)>) -> Client {
|
|
||||||
Client { client: ::capnp::capability::Client::new(hook), }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Client; type Builder = Client; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Client; }
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Client<> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, _default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Client<>> {
|
|
||||||
::core::result::Result::Ok(::capnp::capability::FromClientHook::new(reader.get_capability()?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Client<> {
|
|
||||||
fn init_pointer(_builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Client<> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Client<>> {
|
|
||||||
::core::result::Result::Ok(::capnp::capability::FromClientHook::new(builder.get_capability()?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <> ::capnp::traits::SetPointerBuilder for Client<> {
|
|
||||||
fn set_pointer_builder(pointer: ::capnp::private::layout::PointerBuilder<'_>, from: Client<>, _canonicalize: bool) -> ::capnp::Result<()> {
|
|
||||||
pointer.set_capability(from.client.hook);
|
|
||||||
::core::result::Result::Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::capnp::traits::HasTypeId for Client {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl Clone for Client {
|
|
||||||
fn clone(&self) -> Client {
|
|
||||||
Client { client: ::capnp::capability::Client::new(self.client.hook.add_ref()), }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Client {
|
|
||||||
pub fn get_role_list_request(&self) -> ::capnp::capability::Request<crate::schema::permissionsystem_capnp::permission_system::info::get_role_list_params::Owned,crate::schema::permissionsystem_capnp::permission_system::info::get_role_list_results::Owned> {
|
|
||||||
self.client.new_call(_private::TYPE_ID, 0, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub trait Server<> {
|
|
||||||
fn get_role_list(&mut self, _: GetRoleListParams<>, _: GetRoleListResults<>) -> ::capnp::capability::Promise<(), ::capnp::Error> { ::capnp::capability::Promise::err(::capnp::Error::unimplemented("method not implemented".to_string())) }
|
|
||||||
}
|
|
||||||
pub struct ServerDispatch<_T,> {
|
|
||||||
pub server: _T,
|
|
||||||
}
|
|
||||||
impl <_S: Server + 'static, > ::capnp::capability::FromServer<_S> for Client {
|
|
||||||
type Dispatch = ServerDispatch<_S, >;
|
|
||||||
fn from_server(s: _S) -> ServerDispatch<_S, > {
|
|
||||||
ServerDispatch { server: s, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl <_T: Server> ::core::ops::Deref for ServerDispatch<_T> {
|
|
||||||
type Target = _T;
|
|
||||||
fn deref(&self) -> &_T { &self.server}
|
|
||||||
}
|
|
||||||
impl <_T: Server> ::core::ops::DerefMut for ServerDispatch<_T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut _T { &mut self.server}
|
|
||||||
}
|
|
||||||
impl <_T: Server> ::capnp::capability::Server for ServerDispatch<_T> {
|
|
||||||
fn dispatch_call(&mut self, interface_id: u64, method_id: u16, params: ::capnp::capability::Params<::capnp::any_pointer::Owned>, results: ::capnp::capability::Results<::capnp::any_pointer::Owned>) -> ::capnp::capability::Promise<(), ::capnp::Error> {
|
|
||||||
match interface_id {
|
|
||||||
_private::TYPE_ID => ServerDispatch::<_T, >::dispatch_call_internal(&mut self.server, method_id, params, results),
|
|
||||||
_ => { ::capnp::capability::Promise::err(::capnp::Error::unimplemented("Method not implemented.".to_string())) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl <_T :Server> ServerDispatch<_T> {
|
|
||||||
pub fn dispatch_call_internal(server: &mut _T, method_id: u16, params: ::capnp::capability::Params<::capnp::any_pointer::Owned>, results: ::capnp::capability::Results<::capnp::any_pointer::Owned>) -> ::capnp::capability::Promise<(), ::capnp::Error> {
|
|
||||||
match method_id {
|
|
||||||
0 => server.get_role_list(::capnp::private::capability::internal_get_typed_params(params), ::capnp::private::capability::internal_get_typed_results(results)),
|
|
||||||
_ => { ::capnp::capability::Promise::err(::capnp::Error::unimplemented("Method not implemented.".to_string())) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub mod _private {
|
|
||||||
pub const TYPE_ID: u64 = 0xdff7_7e8f_ec2d_a43b;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod get_role_list_params {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 0, pointers: 0 };
|
|
||||||
pub const TYPE_ID: u64 = 0xd5b5_fb0f_7259_c89b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod get_role_list_results {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_role_list(self) -> ::capnp::Result<::capnp::struct_list::Reader<'a,crate::schema::role_capnp::role::Owned>> {
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
pub fn has_role_list(&self) -> bool {
|
|
||||||
!self.reader.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_role_list(self) -> ::capnp::Result<::capnp::struct_list::Builder<'a,crate::schema::role_capnp::role::Owned>> {
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_role_list(&mut self, value: ::capnp::struct_list::Reader<'a,crate::schema::role_capnp::role::Owned>) -> ::capnp::Result<()> {
|
|
||||||
::capnp::traits::SetPointerBuilder::set_pointer_builder(self.builder.get_pointer_field(0), value, false)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_role_list(self, size: u32) -> ::capnp::struct_list::Builder<'a,crate::schema::role_capnp::role::Owned> {
|
|
||||||
::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), size)
|
|
||||||
}
|
|
||||||
pub fn has_role_list(&self) -> bool {
|
|
||||||
!self.builder.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 0, pointers: 1 };
|
|
||||||
pub const TYPE_ID: u64 = 0xc506_980d_e52d_5ab2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub mod manage {
|
|
||||||
#![allow(unused_variables)]
|
|
||||||
|
|
||||||
pub struct Client {
|
|
||||||
pub client: ::capnp::capability::Client,
|
|
||||||
}
|
|
||||||
impl ::capnp::capability::FromClientHook for Client {
|
|
||||||
fn new(hook: Box<dyn (::capnp::private::capability::ClientHook)>) -> Client {
|
|
||||||
Client { client: ::capnp::capability::Client::new(hook), }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Client; type Builder = Client; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Client; }
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Client<> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, _default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Client<>> {
|
|
||||||
::core::result::Result::Ok(::capnp::capability::FromClientHook::new(reader.get_capability()?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Client<> {
|
|
||||||
fn init_pointer(_builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Client<> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Client<>> {
|
|
||||||
::core::result::Result::Ok(::capnp::capability::FromClientHook::new(builder.get_capability()?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <> ::capnp::traits::SetPointerBuilder for Client<> {
|
|
||||||
fn set_pointer_builder(pointer: ::capnp::private::layout::PointerBuilder<'_>, from: Client<>, _canonicalize: bool) -> ::capnp::Result<()> {
|
|
||||||
pointer.set_capability(from.client.hook);
|
|
||||||
::core::result::Result::Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::capnp::traits::HasTypeId for Client {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl Clone for Client {
|
|
||||||
fn clone(&self) -> Client {
|
|
||||||
Client { client: ::capnp::capability::Client::new(self.client.hook.add_ref()), }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Client {
|
|
||||||
}
|
|
||||||
pub trait Server<> {
|
|
||||||
}
|
|
||||||
pub struct ServerDispatch<_T,> {
|
|
||||||
pub server: _T,
|
|
||||||
}
|
|
||||||
impl <_S: Server + 'static, > ::capnp::capability::FromServer<_S> for Client {
|
|
||||||
type Dispatch = ServerDispatch<_S, >;
|
|
||||||
fn from_server(s: _S) -> ServerDispatch<_S, > {
|
|
||||||
ServerDispatch { server: s, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl <_T: Server> ::core::ops::Deref for ServerDispatch<_T> {
|
|
||||||
type Target = _T;
|
|
||||||
fn deref(&self) -> &_T { &self.server}
|
|
||||||
}
|
|
||||||
impl <_T: Server> ::core::ops::DerefMut for ServerDispatch<_T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut _T { &mut self.server}
|
|
||||||
}
|
|
||||||
impl <_T: Server> ::capnp::capability::Server for ServerDispatch<_T> {
|
|
||||||
fn dispatch_call(&mut self, interface_id: u64, method_id: u16, params: ::capnp::capability::Params<::capnp::any_pointer::Owned>, results: ::capnp::capability::Results<::capnp::any_pointer::Owned>) -> ::capnp::capability::Promise<(), ::capnp::Error> {
|
|
||||||
match interface_id {
|
|
||||||
_private::TYPE_ID => ServerDispatch::<_T, >::dispatch_call_internal(&mut self.server, method_id, params, results),
|
|
||||||
_ => { ::capnp::capability::Promise::err(::capnp::Error::unimplemented("Method not implemented.".to_string())) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl <_T :Server> ServerDispatch<_T> {
|
|
||||||
pub fn dispatch_call_internal(server: &mut _T, method_id: u16, params: ::capnp::capability::Params<::capnp::any_pointer::Owned>, results: ::capnp::capability::Results<::capnp::any_pointer::Owned>) -> ::capnp::capability::Promise<(), ::capnp::Error> {
|
|
||||||
match method_id {
|
|
||||||
_ => { ::capnp::capability::Promise::err(::capnp::Error::unimplemented("Method not implemented.".to_string())) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub mod _private {
|
|
||||||
pub const TYPE_ID: u64 = 0xfd2c_0c85_d50a_57e6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
// @generated by the capnpc-rust plugin to the Cap'n Proto schema compiler.
|
|
||||||
// DO NOT EDIT.
|
|
||||||
// source: programming_language/csharp.capnp
|
|
||||||
|
|
||||||
|
|
||||||
#[repr(u16)]
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum TypeVisibility {
|
|
||||||
Public = 0,
|
|
||||||
Internal = 1,
|
|
||||||
}
|
|
||||||
impl ::capnp::traits::FromU16 for TypeVisibility {
|
|
||||||
#[inline]
|
|
||||||
fn from_u16(value: u16) -> ::core::result::Result<TypeVisibility, ::capnp::NotInSchema> {
|
|
||||||
match value {
|
|
||||||
0 => ::core::result::Result::Ok(TypeVisibility::Public),
|
|
||||||
1 => ::core::result::Result::Ok(TypeVisibility::Internal),
|
|
||||||
n => ::core::result::Result::Err(::capnp::NotInSchema(n)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::capnp::traits::ToU16 for TypeVisibility {
|
|
||||||
#[inline]
|
|
||||||
fn to_u16(self) -> u16 { self as u16 }
|
|
||||||
}
|
|
||||||
impl ::capnp::traits::HasTypeId for TypeVisibility {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { 0xeb0d_8316_68c6_eda5u64 }
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
// @generated by the capnpc-rust plugin to the Cap'n Proto schema compiler.
|
|
||||||
// DO NOT EDIT.
|
|
||||||
// source: programming_language/rust.capnp
|
|
||||||
|
|
@ -1,139 +0,0 @@
|
|||||||
// @generated by the capnpc-rust plugin to the Cap'n Proto schema compiler.
|
|
||||||
// DO NOT EDIT.
|
|
||||||
// source: role.capnp
|
|
||||||
|
|
||||||
|
|
||||||
pub mod role {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_name(self) -> ::capnp::Result<::capnp::text::Reader<'a>> {
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
pub fn has_name(&self) -> bool {
|
|
||||||
!self.reader.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_name(self) -> ::capnp::Result<::capnp::text::Builder<'a>> {
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_name(&mut self, value: ::capnp::text::Reader<'_>) {
|
|
||||||
self.builder.get_pointer_field(0).set_text(value);
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_name(self, size: u32) -> ::capnp::text::Builder<'a> {
|
|
||||||
self.builder.get_pointer_field(0).init_text(size)
|
|
||||||
}
|
|
||||||
pub fn has_name(&self) -> bool {
|
|
||||||
!self.builder.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 0, pointers: 1 };
|
|
||||||
pub const TYPE_ID: u64 = 0xd53a_c00e_04e0_4b2d;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,186 +0,0 @@
|
|||||||
// @generated by the capnpc-rust plugin to the Cap'n Proto schema compiler.
|
|
||||||
// DO NOT EDIT.
|
|
||||||
// source: space.capnp
|
|
||||||
|
|
||||||
|
|
||||||
pub mod space {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Owned(());
|
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
|
||||||
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,> {
|
|
||||||
fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
|
|
||||||
Reader { reader, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
|
|
||||||
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
|
|
||||||
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
|
|
||||||
self.reader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
|
|
||||||
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
|
|
||||||
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Reader<'a,> {
|
|
||||||
pub fn reborrow(&self) -> Reader<'_,> {
|
|
||||||
Reader { .. *self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.reader.total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_id(self) -> ::capnp::Result<crate::schema::general_capnp::u_u_i_d::Reader<'a>> {
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
pub fn has_id(&self) -> bool {
|
|
||||||
!self.reader.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_name(self) -> ::capnp::Result<::capnp::text::Reader<'a>> {
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(1), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
pub fn has_name(&self) -> bool {
|
|
||||||
!self.reader.get_pointer_field(1).is_null()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_info(self) -> ::capnp::Result<::capnp::text::Reader<'a>> {
|
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(2), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
pub fn has_info(&self) -> bool {
|
|
||||||
!self.reader.get_pointer_field(2).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
|
|
||||||
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn struct_size() -> ::capnp::private::layout::StructSize { _private::STRUCT_SIZE }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
|
|
||||||
#[inline]
|
|
||||||
fn type_id() -> u64 { _private::TYPE_ID }
|
|
||||||
}
|
|
||||||
impl <'a,> ::capnp::traits::FromStructBuilder<'a> for Builder<'a,> {
|
|
||||||
fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a, > {
|
|
||||||
Builder { builder, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
|
|
||||||
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
|
|
||||||
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
|
|
||||||
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Builder<'a,> {
|
|
||||||
::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE))
|
|
||||||
}
|
|
||||||
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [capnp::Word]>) -> ::capnp::Result<Builder<'a,>> {
|
|
||||||
::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new(builder.get_struct(_private::STRUCT_SIZE, default)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> ::capnp::traits::SetPointerBuilder for Reader<'a,> {
|
|
||||||
fn set_pointer_builder<'b>(pointer: ::capnp::private::layout::PointerBuilder<'b>, value: Reader<'a,>, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a,> Builder<'a,> {
|
|
||||||
pub fn into_reader(self) -> Reader<'a,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
pub fn reborrow(&mut self) -> Builder<'_,> {
|
|
||||||
Builder { .. *self }
|
|
||||||
}
|
|
||||||
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
|
|
||||||
::capnp::traits::FromStructReader::new(self.builder.into_reader())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
|
|
||||||
self.builder.into_reader().total_size()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_id(self) -> ::capnp::Result<crate::schema::general_capnp::u_u_i_d::Builder<'a>> {
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_id(&mut self, value: crate::schema::general_capnp::u_u_i_d::Reader<'_>) -> ::capnp::Result<()> {
|
|
||||||
::capnp::traits::SetPointerBuilder::set_pointer_builder(self.builder.get_pointer_field(0), value, false)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_id(self, ) -> crate::schema::general_capnp::u_u_i_d::Builder<'a> {
|
|
||||||
::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), 0)
|
|
||||||
}
|
|
||||||
pub fn has_id(&self) -> bool {
|
|
||||||
!self.builder.get_pointer_field(0).is_null()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_name(self) -> ::capnp::Result<::capnp::text::Builder<'a>> {
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(1), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_name(&mut self, value: ::capnp::text::Reader<'_>) {
|
|
||||||
self.builder.get_pointer_field(1).set_text(value);
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_name(self, size: u32) -> ::capnp::text::Builder<'a> {
|
|
||||||
self.builder.get_pointer_field(1).init_text(size)
|
|
||||||
}
|
|
||||||
pub fn has_name(&self) -> bool {
|
|
||||||
!self.builder.get_pointer_field(1).is_null()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn get_info(self) -> ::capnp::Result<::capnp::text::Builder<'a>> {
|
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(2), ::core::option::Option::None)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set_info(&mut self, value: ::capnp::text::Reader<'_>) {
|
|
||||||
self.builder.get_pointer_field(2).set_text(value);
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn init_info(self, size: u32) -> ::capnp::text::Builder<'a> {
|
|
||||||
self.builder.get_pointer_field(2).init_text(size)
|
|
||||||
}
|
|
||||||
pub fn has_info(&self) -> bool {
|
|
||||||
!self.builder.get_pointer_field(2).is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
|
|
||||||
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
|
|
||||||
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline {
|
|
||||||
Pipeline { _typeless: typeless, }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Pipeline {
|
|
||||||
pub fn get_id(&self) -> crate::schema::general_capnp::u_u_i_d::Pipeline {
|
|
||||||
::capnp::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field(0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mod _private {
|
|
||||||
use capnp::private::layout;
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 0, pointers: 3 };
|
|
||||||
pub const TYPE_ID: u64 = 0x9e5f_9a6a_0528_b772;
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use futures_util::future;
|
use futures_util::future;
|
||||||
use futures_util::future::BoxFuture;
|
use futures_util::future::BoxFuture;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::actors::Actor;
|
use crate::actors::Actor;
|
||||||
use crate::db::ArchivedValue;
|
use crate::db::ArchivedValue;
|
||||||
|
@ -3,7 +3,7 @@ use crate::resources::state::State;
|
|||||||
use crate::{Config, ResourcesHandle};
|
use crate::{Config, ResourcesHandle};
|
||||||
use async_compat::CompatExt;
|
use async_compat::CompatExt;
|
||||||
use executor::pool::Executor;
|
use executor::pool::Executor;
|
||||||
use futures_signals::signal::{Signal};
|
use futures_signals::signal::Signal;
|
||||||
use futures_util::future::BoxFuture;
|
use futures_util::future::BoxFuture;
|
||||||
use rumqttc::{AsyncClient, ConnectionError, Event, Incoming, MqttOptions};
|
use rumqttc::{AsyncClient, ConnectionError, Event, Incoming, MqttOptions};
|
||||||
|
|
||||||
@ -12,21 +12,23 @@ use std::future::Future;
|
|||||||
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use miette::Diagnostic;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use rumqttc::ConnectReturnCode::Success;
|
use rumqttc::ConnectReturnCode::Success;
|
||||||
|
|
||||||
use rustls::{RootCertStore};
|
|
||||||
use url::Url;
|
|
||||||
use crate::actors::dummy::Dummy;
|
use crate::actors::dummy::Dummy;
|
||||||
use crate::actors::process::Process;
|
use crate::actors::process::Process;
|
||||||
use crate::db::ArchivedValue;
|
use crate::db::ArchivedValue;
|
||||||
|
use rustls::RootCertStore;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
mod shelly;
|
|
||||||
mod process;
|
|
||||||
mod dummy;
|
mod dummy;
|
||||||
|
mod process;
|
||||||
|
mod shelly;
|
||||||
|
|
||||||
pub trait Actor {
|
pub trait Actor {
|
||||||
fn apply(&mut self, state: ArchivedValue<State>) -> BoxFuture<'static, ()>;
|
fn apply(&mut self, state: ArchivedValue<State>) -> BoxFuture<'static, ()>;
|
||||||
@ -102,7 +104,7 @@ static ROOT_CERTS: Lazy<RootCertStore> = Lazy::new(|| {
|
|||||||
} else {
|
} else {
|
||||||
tracing::info!(loaded, "certificates loaded");
|
tracing::info!(loaded, "certificates loaded");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
tracing::error!(%error, "failed to load system certificates");
|
tracing::error!(%error, "failed to load system certificates");
|
||||||
}
|
}
|
||||||
@ -110,7 +112,29 @@ static ROOT_CERTS: Lazy<RootCertStore> = Lazy::new(|| {
|
|||||||
store
|
store
|
||||||
});
|
});
|
||||||
|
|
||||||
pub fn load(executor: Executor, config: &Config, resources: ResourcesHandle) -> anyhow::Result<()> {
|
#[derive(Debug, Error, Diagnostic)]
|
||||||
|
pub enum ActorError {
|
||||||
|
#[error("failed to parse MQTT url")]
|
||||||
|
UrlParseError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
url::ParseError,
|
||||||
|
),
|
||||||
|
#[error("MQTT config is invalid")]
|
||||||
|
InvalidConfig,
|
||||||
|
#[error("MQTT connection failed")]
|
||||||
|
ConnectionError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
rumqttc::ConnectionError,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(
|
||||||
|
executor: Executor,
|
||||||
|
config: &Config,
|
||||||
|
resources: ResourcesHandle,
|
||||||
|
) -> Result<(), ActorError> {
|
||||||
let span = tracing::info_span!("loading actors");
|
let span = tracing::info_span!("loading actors");
|
||||||
let _guard = span;
|
let _guard = span;
|
||||||
|
|
||||||
@ -131,12 +155,12 @@ pub fn load(executor: Executor, config: &Config, resources: ResourcesHandle) ->
|
|||||||
|
|
||||||
scheme => {
|
scheme => {
|
||||||
tracing::error!(%scheme, "MQTT url uses invalid scheme");
|
tracing::error!(%scheme, "MQTT url uses invalid scheme");
|
||||||
anyhow::bail!("invalid config");
|
return Err(ActorError::InvalidConfig);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let host = mqtt_url.host_str().ok_or_else(|| {
|
let host = mqtt_url.host_str().ok_or_else(|| {
|
||||||
tracing::error!("MQTT url must contain a hostname");
|
tracing::error!("MQTT url must contain a hostname");
|
||||||
anyhow::anyhow!("invalid config")
|
ActorError::InvalidConfig
|
||||||
})?;
|
})?;
|
||||||
let port = mqtt_url.port().unwrap_or(default_port);
|
let port = mqtt_url.port().unwrap_or(default_port);
|
||||||
|
|
||||||
@ -167,7 +191,7 @@ pub fn load(executor: Executor, config: &Config, resources: ResourcesHandle) ->
|
|||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
tracing::error!(?error, "MQTT connection failed");
|
tracing::error!(?error, "MQTT connection failed");
|
||||||
anyhow::bail!("mqtt connection failed")
|
return Err(ActorError::ConnectionError(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,8 +243,10 @@ pub fn load(executor: Executor, config: &Config, resources: ResourcesHandle) ->
|
|||||||
.compat(),
|
.compat(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut actor_map: HashMap<String, _> = config.actor_connections.iter()
|
let mut actor_map: HashMap<String, _> = config
|
||||||
.filter_map(|(k,v)| {
|
.actor_connections
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(k, v)| {
|
||||||
if let Some(resource) = resources.get_by_id(v) {
|
if let Some(resource) = resources.get_by_id(v) {
|
||||||
Some((k.clone(), resource.get_signal()))
|
Some((k.clone(), resource.get_signal()))
|
||||||
} else {
|
} else {
|
||||||
@ -258,8 +284,6 @@ fn load_single(
|
|||||||
"Dummy" => Some(Box::new(Dummy::new(name.clone(), params.clone()))),
|
"Dummy" => Some(Box::new(Dummy::new(name.clone(), params.clone()))),
|
||||||
"Process" => Process::new(name.clone(), params).map(|a| a.into_boxed_actuator()),
|
"Process" => Process::new(name.clone(), params).map(|a| a.into_boxed_actuator()),
|
||||||
"Shelly" => Some(Box::new(Shelly::new(name.clone(), client, params))),
|
"Shelly" => Some(Box::new(Shelly::new(name.clone(), client, params))),
|
||||||
_ => {
|
_ => None,
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
use futures_util::future::BoxFuture;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use futures_util::future::BoxFuture;
|
|
||||||
|
|
||||||
use crate::actors::Actor;
|
use crate::actors::Actor;
|
||||||
use crate::db::ArchivedValue;
|
use crate::db::ArchivedValue;
|
||||||
@ -16,11 +16,10 @@ pub struct Process {
|
|||||||
impl Process {
|
impl Process {
|
||||||
pub fn new(name: String, params: &HashMap<String, String>) -> Option<Self> {
|
pub fn new(name: String, params: &HashMap<String, String>) -> Option<Self> {
|
||||||
let cmd = params.get("cmd").map(|s| s.to_string())?;
|
let cmd = params.get("cmd").map(|s| s.to_string())?;
|
||||||
let args = params.get("args").map(|argv|
|
let args = params
|
||||||
argv.split_whitespace()
|
.get("args")
|
||||||
.map(|s| s.to_string())
|
.map(|argv| argv.split_whitespace().map(|s| s.to_string()).collect())
|
||||||
.collect())
|
.unwrap_or_else(Vec::new);
|
||||||
.unwrap_or_else(Vec::new);
|
|
||||||
|
|
||||||
Some(Self { name, cmd, args })
|
Some(Self { name, cmd, args })
|
||||||
}
|
}
|
||||||
@ -48,41 +47,42 @@ impl Actor for Process {
|
|||||||
command.arg("inuse").arg(by.id.as_str());
|
command.arg("inuse").arg(by.id.as_str());
|
||||||
}
|
}
|
||||||
ArchivedStatus::ToCheck(by) => {
|
ArchivedStatus::ToCheck(by) => {
|
||||||
command.arg("tocheck")
|
command.arg("tocheck").arg(by.id.as_str());
|
||||||
.arg(by.id.as_str());
|
|
||||||
}
|
}
|
||||||
ArchivedStatus::Blocked(by) => {
|
ArchivedStatus::Blocked(by) => {
|
||||||
command.arg("blocked")
|
command.arg("blocked").arg(by.id.as_str());
|
||||||
.arg(by.id.as_str());
|
}
|
||||||
|
ArchivedStatus::Disabled => {
|
||||||
|
command.arg("disabled");
|
||||||
}
|
}
|
||||||
ArchivedStatus::Disabled => { command.arg("disabled"); },
|
|
||||||
ArchivedStatus::Reserved(by) => {
|
ArchivedStatus::Reserved(by) => {
|
||||||
command.arg("reserved")
|
command.arg("reserved").arg(by.id.as_str());
|
||||||
.arg(by.id.as_str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = self.name.clone();
|
let name = self.name.clone();
|
||||||
Box::pin(async move { match command.output() {
|
Box::pin(async move {
|
||||||
Ok(retv) if retv.status.success() => {
|
match command.output() {
|
||||||
tracing::trace!("Actor was successful");
|
Ok(retv) if retv.status.success() => {
|
||||||
let outstr = String::from_utf8_lossy(&retv.stdout);
|
tracing::trace!("Actor was successful");
|
||||||
for line in outstr.lines() {
|
let outstr = String::from_utf8_lossy(&retv.stdout);
|
||||||
tracing::debug!(%name, %line, "actor stdout");
|
for line in outstr.lines() {
|
||||||
}
|
tracing::debug!(%name, %line, "actor stdout");
|
||||||
}
|
|
||||||
Ok(retv) => {
|
|
||||||
tracing::warn!(%name, ?state, code=?retv.status,
|
|
||||||
"Actor returned nonzero exitcode"
|
|
||||||
);
|
|
||||||
if !retv.stderr.is_empty() {
|
|
||||||
let errstr = String::from_utf8_lossy(&retv.stderr);
|
|
||||||
for line in errstr.lines() {
|
|
||||||
tracing::warn!(%name, %line, "actor stderr");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(retv) => {
|
||||||
|
tracing::warn!(%name, ?state, code=?retv.status,
|
||||||
|
"Actor returned nonzero exitcode"
|
||||||
|
);
|
||||||
|
if !retv.stderr.is_empty() {
|
||||||
|
let errstr = String::from_utf8_lossy(&retv.stderr);
|
||||||
|
for line in errstr.lines() {
|
||||||
|
tracing::warn!(%name, %line, "actor stderr");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => tracing::warn!(%name, ?error, "process actor failed to run cmd"),
|
||||||
}
|
}
|
||||||
Err(error) => tracing::warn!(%name, ?error, "process actor failed to run cmd"),
|
})
|
||||||
}})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use futures_util::future::BoxFuture;
|
use futures_util::future::BoxFuture;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use rumqttc::{AsyncClient, QoS};
|
|
||||||
use crate::actors::Actor;
|
use crate::actors::Actor;
|
||||||
use crate::db::ArchivedValue;
|
use crate::db::ArchivedValue;
|
||||||
use crate::resources::modules::fabaccess::ArchivedStatus;
|
use crate::resources::modules::fabaccess::ArchivedStatus;
|
||||||
use crate::resources::state::State;
|
use crate::resources::state::State;
|
||||||
|
use rumqttc::{AsyncClient, QoS};
|
||||||
|
|
||||||
/// An actuator for a Shellie connected listening on one MQTT broker
|
/// An actuator for a Shellie connected listening on one MQTT broker
|
||||||
///
|
///
|
||||||
@ -28,7 +28,11 @@ impl Shelly {
|
|||||||
|
|
||||||
tracing::debug!(%name,%topic,"Starting shelly module");
|
tracing::debug!(%name,%topic,"Starting shelly module");
|
||||||
|
|
||||||
Shelly { name, client, topic, }
|
Shelly {
|
||||||
|
name,
|
||||||
|
client,
|
||||||
|
topic,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the name to a new one. This changes the shelly that will be activated
|
/// Set the name to a new one. This changes the shelly that will be activated
|
||||||
@ -38,7 +42,6 @@ impl Shelly {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Actor for Shelly {
|
impl Actor for Shelly {
|
||||||
fn apply(&mut self, state: ArchivedValue<State>) -> BoxFuture<'static, ()> {
|
fn apply(&mut self, state: ArchivedValue<State>) -> BoxFuture<'static, ()> {
|
||||||
tracing::debug!(?state, name=%self.name,
|
tracing::debug!(?state, name=%self.name,
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
|
use miette::Diagnostic;
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{LineWriter, Write};
|
use std::io::{LineWriter, Write};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use once_cell::sync::OnceCell;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::Config;
|
use crate::Config;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Serializer;
|
use serde_json::Serializer;
|
||||||
|
|
||||||
pub static AUDIT: OnceCell<AuditLog> = OnceCell::new();
|
pub static AUDIT: OnceCell<AuditLog> = OnceCell::new();
|
||||||
|
|
||||||
|
// TODO: Make the audit log a tracing layer
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AuditLog {
|
pub struct AuditLog {
|
||||||
writer: Mutex<LineWriter<File>>,
|
writer: Mutex<LineWriter<File>>,
|
||||||
@ -22,11 +25,19 @@ pub struct AuditLogLine<'a> {
|
|||||||
state: &'a str,
|
state: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error, Diagnostic)]
|
||||||
|
#[error(transparent)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Error(#[from] pub io::Error);
|
||||||
|
|
||||||
impl AuditLog {
|
impl AuditLog {
|
||||||
pub fn new(config: &Config) -> io::Result<&'static Self> {
|
pub fn new(config: &Config) -> Result<&'static Self, Error> {
|
||||||
AUDIT.get_or_try_init(|| {
|
AUDIT.get_or_try_init(|| {
|
||||||
tracing::debug!(path = %config.auditlog_path.display(), "Initializing audit log");
|
tracing::debug!(path = %config.auditlog_path.display(), "Initializing audit log");
|
||||||
let fd = OpenOptions::new().create(true).append(true).open(&config.auditlog_path)?;
|
let fd = OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(&config.auditlog_path)?;
|
||||||
let writer = Mutex::new(LineWriter::new(fd));
|
let writer = Mutex::new(LineWriter::new(fd));
|
||||||
Ok(Self { writer })
|
Ok(Self { writer })
|
||||||
})
|
})
|
||||||
@ -34,7 +45,11 @@ impl AuditLog {
|
|||||||
|
|
||||||
pub fn log(&self, machine: &str, state: &str) -> io::Result<()> {
|
pub fn log(&self, machine: &str, state: &str) -> io::Result<()> {
|
||||||
let timestamp = chrono::Utc::now().timestamp();
|
let timestamp = chrono::Utc::now().timestamp();
|
||||||
let line = AuditLogLine { timestamp, machine, state };
|
let line = AuditLogLine {
|
||||||
|
timestamp,
|
||||||
|
machine,
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
|
||||||
tracing::debug!(?line, "writing audit log line");
|
tracing::debug!(?line, "writing audit log line");
|
||||||
|
|
||||||
@ -42,8 +57,9 @@ impl AuditLog {
|
|||||||
let mut writer: &mut LineWriter<File> = &mut *guard;
|
let mut writer: &mut LineWriter<File> = &mut *guard;
|
||||||
|
|
||||||
let mut ser = Serializer::new(&mut writer);
|
let mut ser = Serializer::new(&mut writer);
|
||||||
line.serialize(&mut ser).expect("failed to serialize audit log line");
|
line.serialize(&mut ser)
|
||||||
writer.write("\n".as_bytes())?;
|
.expect("failed to serialize audit log line");
|
||||||
|
writer.write_all("\n".as_bytes())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,43 +2,37 @@ mod server;
|
|||||||
pub use server::FabFire;
|
pub use server::FabFire;
|
||||||
|
|
||||||
use rsasl::mechname::Mechname;
|
use rsasl::mechname::Mechname;
|
||||||
use rsasl::registry::{Mechanism, MECHANISMS};
|
use rsasl::registry::{Matches, Mechanism, Named, Side, MECHANISMS};
|
||||||
use rsasl::session::Side;
|
|
||||||
|
|
||||||
const MECHNAME: &'static Mechname = &Mechname::const_new_unchecked(b"X-FABFIRE");
|
const MECHNAME: &'static Mechname = &Mechname::const_new_unchecked(b"X-FABFIRE");
|
||||||
|
|
||||||
#[linkme::distributed_slice(MECHANISMS)]
|
#[linkme::distributed_slice(MECHANISMS)]
|
||||||
pub static FABFIRE: Mechanism = Mechanism {
|
pub static FABFIRE: Mechanism = Mechanism::build(
|
||||||
mechanism: MECHNAME,
|
MECHNAME,
|
||||||
priority: 300,
|
300,
|
||||||
// In this situation there's one struct for both sides, however you can just as well use
|
None,
|
||||||
// different types than then have different `impl Authentication` instead of checking a value
|
Some(FabFire::new_server),
|
||||||
// in self.
|
Side::Client,
|
||||||
client: None,
|
|_| Some(Matches::<Select>::name()),
|
||||||
server: Some(FabFire::new_server),
|
|_| true,
|
||||||
first: Side::Client,
|
);
|
||||||
};
|
|
||||||
|
|
||||||
|
struct Select;
|
||||||
|
impl Named for Select {
|
||||||
|
fn mech() -> &'static Mechanism {
|
||||||
|
&FABFIRE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use rsasl::property::SizedProperty;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use rsasl::property::{Property, PropertyQ, PropertyDefinition};
|
|
||||||
// All Property types must implement Debug.
|
// All Property types must implement Debug.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
// The `PhantomData` in the constructor is only used so external crates can't construct this type.
|
// The `PhantomData` in the constructor is only used so external crates can't construct this type.
|
||||||
pub struct FabFireCardKey(PhantomData<()>);
|
pub struct FabFireCardKey(PhantomData<()>);
|
||||||
impl PropertyQ for FabFireCardKey {
|
|
||||||
// This is the type stored for this property. This could also be the struct itself if you
|
impl SizedProperty<'_> for FabFireCardKey {
|
||||||
// so choose
|
type Value = [u8; 16];
|
||||||
type Item = [u8; 16];
|
const DESCRIPTION: &'static str = "A AES128 key for a FabFire card";
|
||||||
// You need to return the constant you define below here for things to work properly
|
|
||||||
fn property() -> Property {
|
|
||||||
FABFIRECARDKEY
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// This const is used by your mechanism to query and by your users to set your property. It
|
|
||||||
// thus needs to be exported from your crate
|
|
||||||
pub const FABFIRECARDKEY: Property = Property::new(&PropertyDefinition::new(
|
|
||||||
// Short name, used in `Debug` output
|
|
||||||
"FabFireCardKey",
|
|
||||||
// A longer user-facing name used in `Display` output
|
|
||||||
"A AES128 key for a FabFire card",
|
|
||||||
));
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
|
use desfire::desfire::desfire::MAX_BYTES_PER_TRANSACTION;
|
||||||
|
use desfire::desfire::Desfire;
|
||||||
|
use desfire::error::Error as DesfireError;
|
||||||
|
use desfire::iso7816_4::apduresponse::APDUResponse;
|
||||||
|
use rsasl::mechanism::{
|
||||||
|
Authentication, Demand, DemandReply, MechanismData, MechanismError, MechanismErrorKind,
|
||||||
|
Provider, State, ThisProvider,
|
||||||
|
};
|
||||||
|
use rsasl::prelude::{MessageSent, SASLConfig, SASLError, SessionError};
|
||||||
|
use rsasl::property::AuthId;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use rsasl::error::{MechanismError, MechanismErrorKind, SASLError, SessionError};
|
|
||||||
use rsasl::mechanism::Authentication;
|
|
||||||
use rsasl::SASL;
|
|
||||||
use rsasl::session::{SessionData, StepResult};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use desfire::desfire::Desfire;
|
|
||||||
use desfire::iso7816_4::apduresponse::APDUResponse;
|
|
||||||
use desfire::error::{Error as DesfireError};
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use desfire::desfire::desfire::MAX_BYTES_PER_TRANSACTION;
|
|
||||||
use rsasl::property::AuthId;
|
|
||||||
|
|
||||||
use crate::authentication::fabfire::FabFireCardKey;
|
use crate::authentication::fabfire::FabFireCardKey;
|
||||||
|
|
||||||
@ -37,7 +37,9 @@ impl Debug for FabFireError {
|
|||||||
FabFireError::InvalidMagic(magic) => write!(f, "InvalidMagic: {}", magic),
|
FabFireError::InvalidMagic(magic) => write!(f, "InvalidMagic: {}", magic),
|
||||||
FabFireError::InvalidToken(token) => write!(f, "InvalidToken: {}", token),
|
FabFireError::InvalidToken(token) => write!(f, "InvalidToken: {}", token),
|
||||||
FabFireError::InvalidURN(urn) => write!(f, "InvalidURN: {}", urn),
|
FabFireError::InvalidURN(urn) => write!(f, "InvalidURN: {}", urn),
|
||||||
FabFireError::InvalidCredentials(credentials) => write!(f, "InvalidCredentials: {}", credentials),
|
FabFireError::InvalidCredentials(credentials) => {
|
||||||
|
write!(f, "InvalidCredentials: {}", credentials)
|
||||||
|
}
|
||||||
FabFireError::Session(err) => write!(f, "Session: {}", err),
|
FabFireError::Session(err) => write!(f, "Session: {}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,12 +55,16 @@ impl Display for FabFireError {
|
|||||||
FabFireError::InvalidMagic(magic) => write!(f, "InvalidMagic: {}", magic),
|
FabFireError::InvalidMagic(magic) => write!(f, "InvalidMagic: {}", magic),
|
||||||
FabFireError::InvalidToken(token) => write!(f, "InvalidToken: {}", token),
|
FabFireError::InvalidToken(token) => write!(f, "InvalidToken: {}", token),
|
||||||
FabFireError::InvalidURN(urn) => write!(f, "InvalidURN: {}", urn),
|
FabFireError::InvalidURN(urn) => write!(f, "InvalidURN: {}", urn),
|
||||||
FabFireError::InvalidCredentials(credentials) => write!(f, "InvalidCredentials: {}", credentials),
|
FabFireError::InvalidCredentials(credentials) => {
|
||||||
|
write!(f, "InvalidCredentials: {}", credentials)
|
||||||
|
}
|
||||||
FabFireError::Session(err) => write!(f, "Session: {}", err),
|
FabFireError::Session(err) => write!(f, "Session: {}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for FabFireError {}
|
||||||
|
|
||||||
impl MechanismError for FabFireError {
|
impl MechanismError for FabFireError {
|
||||||
fn kind(&self) -> MechanismErrorKind {
|
fn kind(&self) -> MechanismErrorKind {
|
||||||
match self {
|
match self {
|
||||||
@ -85,6 +91,7 @@ struct CardInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct KeyInfo {
|
struct KeyInfo {
|
||||||
|
authid: String,
|
||||||
key_id: u8,
|
key_id: u8,
|
||||||
key: Box<[u8]>,
|
key: Box<[u8]>,
|
||||||
}
|
}
|
||||||
@ -95,6 +102,7 @@ struct AuthInfo {
|
|||||||
iv: Vec<u8>,
|
iv: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
#[serde(tag = "Cmd")]
|
#[serde(tag = "Cmd")]
|
||||||
enum CardCommand {
|
enum CardCommand {
|
||||||
@ -107,16 +115,22 @@ enum CardCommand {
|
|||||||
addn_txt: Option<String>,
|
addn_txt: Option<String>,
|
||||||
},
|
},
|
||||||
sendPICC {
|
sendPICC {
|
||||||
#[serde(deserialize_with = "hex::deserialize", serialize_with = "hex::serialize_upper")]
|
#[serde(
|
||||||
data: Vec<u8>
|
deserialize_with = "hex::deserialize",
|
||||||
|
serialize_with = "hex::serialize_upper"
|
||||||
|
)]
|
||||||
|
data: Vec<u8>,
|
||||||
},
|
},
|
||||||
readPICC {
|
readPICC {
|
||||||
#[serde(deserialize_with = "hex::deserialize", serialize_with = "hex::serialize_upper")]
|
#[serde(
|
||||||
data: Vec<u8>
|
deserialize_with = "hex::deserialize",
|
||||||
|
serialize_with = "hex::serialize_upper"
|
||||||
|
)]
|
||||||
|
data: Vec<u8>,
|
||||||
},
|
},
|
||||||
haltPICC,
|
haltPICC,
|
||||||
Key {
|
Key {
|
||||||
data: String
|
data: String,
|
||||||
},
|
},
|
||||||
ConfirmUser,
|
ConfirmUser,
|
||||||
}
|
}
|
||||||
@ -144,19 +158,36 @@ pub struct FabFire {
|
|||||||
const MAGIC: &'static str = "FABACCESS\0DESFIRE\01.0\0";
|
const MAGIC: &'static str = "FABACCESS\0DESFIRE\01.0\0";
|
||||||
|
|
||||||
impl FabFire {
|
impl FabFire {
|
||||||
pub fn new_server(_sasl: &SASL) -> Result<Box<dyn Authentication>, SASLError> {
|
pub fn new_server(_sasl: &SASLConfig) -> Result<Box<dyn Authentication>, SASLError> {
|
||||||
Ok(Box::new(Self { step: Step::New, card_info: None, key_info: None, auth_info: None, app_id: 1, local_urn: "urn:fabaccess:lab:innovisionlab".to_string(), desfire: Desfire { card: None, session_key: None, cbc_iv: None } }))
|
Ok(Box::new(Self {
|
||||||
|
step: Step::New,
|
||||||
|
card_info: None,
|
||||||
|
key_info: None,
|
||||||
|
auth_info: None,
|
||||||
|
app_id: 0x464142,
|
||||||
|
local_urn: "urn:fabaccess:lab:innovisionlab".to_string(),
|
||||||
|
desfire: Desfire {
|
||||||
|
card: None,
|
||||||
|
session_key: None,
|
||||||
|
cbc_iv: None,
|
||||||
|
},
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Authentication for FabFire {
|
impl Authentication for FabFire {
|
||||||
fn step(&mut self, session: &mut SessionData, input: Option<&[u8]>, writer: &mut dyn Write) -> StepResult {
|
fn step(
|
||||||
|
&mut self,
|
||||||
|
session: &mut MechanismData<'_, '_>,
|
||||||
|
input: Option<&[u8]>,
|
||||||
|
writer: &mut dyn Write,
|
||||||
|
) -> Result<State, SessionError> {
|
||||||
match self.step {
|
match self.step {
|
||||||
Step::New => {
|
Step::New => {
|
||||||
tracing::trace!("Step: New");
|
tracing::trace!("Step: New");
|
||||||
//receive card info (especially card UID) from reader
|
//receive card info (especially card UID) from reader
|
||||||
return match input {
|
return match input {
|
||||||
None => { Err(SessionError::InputDataRequired) }
|
None => Err(SessionError::InputDataRequired),
|
||||||
Some(cardinfo) => {
|
Some(cardinfo) => {
|
||||||
self.card_info = match serde_json::from_slice(cardinfo) {
|
self.card_info = match serde_json::from_slice(cardinfo) {
|
||||||
Ok(card_info) => Some(card_info),
|
Ok(card_info) => Some(card_info),
|
||||||
@ -170,7 +201,10 @@ impl Authentication for FabFire {
|
|||||||
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to convert APDUCommand to Vec<u8>: {:?}", e);
|
tracing::error!(
|
||||||
|
"Failed to convert APDUCommand to Vec<u8>: {:?}",
|
||||||
|
e
|
||||||
|
);
|
||||||
return Err(FabFireError::SerializationError.into());
|
return Err(FabFireError::SerializationError.into());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -183,8 +217,10 @@ impl Authentication for FabFire {
|
|||||||
return match serde_json::to_vec(&cmd) {
|
return match serde_json::to_vec(&cmd) {
|
||||||
Ok(send_buf) => {
|
Ok(send_buf) => {
|
||||||
self.step = Step::SelectApp;
|
self.step = Step::SelectApp;
|
||||||
writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?;
|
writer
|
||||||
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
.write_all(&send_buf)
|
||||||
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(State::Running)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
||||||
@ -198,30 +234,39 @@ impl Authentication for FabFire {
|
|||||||
tracing::trace!("Step: SelectApp");
|
tracing::trace!("Step: SelectApp");
|
||||||
// check that we successfully selected the application
|
// check that we successfully selected the application
|
||||||
let response: CardCommand = match input {
|
let response: CardCommand = match input {
|
||||||
None => { return Err(SessionError::InputDataRequired); }
|
None => {
|
||||||
Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) {
|
return Err(SessionError::InputDataRequired);
|
||||||
|
}
|
||||||
|
Some(buf) => match serde_json::from_slice(buf)
|
||||||
|
.map_err(|e| FabFireError::DeserializationError(e))
|
||||||
|
{
|
||||||
Ok(response) => response,
|
Ok(response) => response,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Deserializing data from card failed: {:?}", e);
|
tracing::error!("Deserializing data from card failed: {:?}", e);
|
||||||
return Err(e.into());
|
return Err(e.into());
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let apdu_response = match response {
|
let apdu_response = match response {
|
||||||
CardCommand::readPICC { data } => { APDUResponse::new(&*data) }
|
CardCommand::readPICC { data } => APDUResponse::new(&*data),
|
||||||
_ => {
|
_ => {
|
||||||
tracing::error!("Unexpected response: {:?}", response);
|
tracing::error!("Unexpected response: {:?}", response);
|
||||||
return Err(FabFireError::ParseError.into());
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
apdu_response.check().map_err(|e| FabFireError::CardError(e))?;
|
apdu_response
|
||||||
|
.check()
|
||||||
|
.map_err(|e| FabFireError::CardError(e))?;
|
||||||
|
|
||||||
// request the contents of the file containing the magic string
|
// request the contents of the file containing the magic string
|
||||||
const MAGIC_FILE_ID: u8 = 0x01;
|
const MAGIC_FILE_ID: u8 = 0x01;
|
||||||
|
|
||||||
let buf = match self.desfire.read_data_chunk_cmd(MAGIC_FILE_ID, 0, MAGIC.len()) {
|
let buf = match self
|
||||||
|
.desfire
|
||||||
|
.read_data_chunk_cmd(MAGIC_FILE_ID, 0, MAGIC.len())
|
||||||
|
{
|
||||||
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -238,8 +283,10 @@ impl Authentication for FabFire {
|
|||||||
return match serde_json::to_vec(&cmd) {
|
return match serde_json::to_vec(&cmd) {
|
||||||
Ok(send_buf) => {
|
Ok(send_buf) => {
|
||||||
self.step = Step::VerifyMagic;
|
self.step = Step::VerifyMagic;
|
||||||
writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?;
|
writer
|
||||||
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
.write_all(&send_buf)
|
||||||
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(State::Running)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
||||||
@ -251,25 +298,28 @@ impl Authentication for FabFire {
|
|||||||
tracing::trace!("Step: VerifyMagic");
|
tracing::trace!("Step: VerifyMagic");
|
||||||
// verify the magic string to determine that we have a valid fabfire card
|
// verify the magic string to determine that we have a valid fabfire card
|
||||||
let response: CardCommand = match input {
|
let response: CardCommand = match input {
|
||||||
None => { return Err(SessionError::InputDataRequired); }
|
None => {
|
||||||
Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) {
|
return Err(SessionError::InputDataRequired);
|
||||||
|
}
|
||||||
|
Some(buf) => match serde_json::from_slice(buf)
|
||||||
|
.map_err(|e| FabFireError::DeserializationError(e))
|
||||||
|
{
|
||||||
Ok(response) => response,
|
Ok(response) => response,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Deserializing data from card failed: {:?}", e);
|
tracing::error!("Deserializing data from card failed: {:?}", e);
|
||||||
return Err(e.into());
|
return Err(e.into());
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let apdu_response = match response {
|
let apdu_response = match response {
|
||||||
CardCommand::readPICC { data } => { APDUResponse::new(&*data) }
|
CardCommand::readPICC { data } => APDUResponse::new(&*data),
|
||||||
_ => {
|
_ => {
|
||||||
tracing::error!("Unexpected response: {:?}", response);
|
tracing::error!("Unexpected response: {:?}", response);
|
||||||
return Err(FabFireError::ParseError.into());
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
match apdu_response.check() {
|
match apdu_response.check() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
match apdu_response.body {
|
match apdu_response.body {
|
||||||
@ -291,11 +341,15 @@ impl Authentication for FabFire {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// request the contents of the file containing the URN
|
// request the contents of the file containing the URN
|
||||||
const URN_FILE_ID: u8 = 0x02;
|
const URN_FILE_ID: u8 = 0x02;
|
||||||
|
|
||||||
let buf = match self.desfire.read_data_chunk_cmd(URN_FILE_ID, 0, self.local_urn.as_bytes().len()) { // TODO: support urn longer than 47 Bytes
|
let buf = match self.desfire.read_data_chunk_cmd(
|
||||||
|
URN_FILE_ID,
|
||||||
|
0,
|
||||||
|
self.local_urn.as_bytes().len(),
|
||||||
|
) {
|
||||||
|
// TODO: support urn longer than 47 Bytes
|
||||||
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -312,8 +366,10 @@ impl Authentication for FabFire {
|
|||||||
return match serde_json::to_vec(&cmd) {
|
return match serde_json::to_vec(&cmd) {
|
||||||
Ok(send_buf) => {
|
Ok(send_buf) => {
|
||||||
self.step = Step::GetURN;
|
self.step = Step::GetURN;
|
||||||
writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?;
|
writer
|
||||||
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
.write_all(&send_buf)
|
||||||
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(State::Running)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
||||||
@ -325,32 +381,39 @@ impl Authentication for FabFire {
|
|||||||
tracing::trace!("Step: GetURN");
|
tracing::trace!("Step: GetURN");
|
||||||
// parse the urn and match it to our local urn
|
// parse the urn and match it to our local urn
|
||||||
let response: CardCommand = match input {
|
let response: CardCommand = match input {
|
||||||
None => { return Err(SessionError::InputDataRequired); }
|
None => {
|
||||||
Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) {
|
return Err(SessionError::InputDataRequired);
|
||||||
|
}
|
||||||
|
Some(buf) => match serde_json::from_slice(buf)
|
||||||
|
.map_err(|e| FabFireError::DeserializationError(e))
|
||||||
|
{
|
||||||
Ok(response) => response,
|
Ok(response) => response,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Deserializing data from card failed: {:?}", e);
|
tracing::error!("Deserializing data from card failed: {:?}", e);
|
||||||
return Err(e.into());
|
return Err(e.into());
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let apdu_response = match response {
|
let apdu_response = match response {
|
||||||
CardCommand::readPICC { data } => { APDUResponse::new(&*data) }
|
CardCommand::readPICC { data } => APDUResponse::new(&*data),
|
||||||
_ => {
|
_ => {
|
||||||
tracing::error!("Unexpected response: {:?}", response);
|
tracing::error!("Unexpected response: {:?}", response);
|
||||||
return Err(FabFireError::ParseError.into());
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
match apdu_response.check() {
|
match apdu_response.check() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
match apdu_response.body {
|
match apdu_response.body {
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
let received_urn = String::from_utf8(data).unwrap();
|
let received_urn = String::from_utf8(data).unwrap();
|
||||||
if received_urn != self.local_urn {
|
if received_urn != self.local_urn {
|
||||||
tracing::error!("URN mismatch: {:?} != {:?}", received_urn, self.local_urn);
|
tracing::error!(
|
||||||
|
"URN mismatch: {:?} != {:?}",
|
||||||
|
received_urn,
|
||||||
|
self.local_urn
|
||||||
|
);
|
||||||
return Err(FabFireError::ParseError.into());
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -361,14 +424,19 @@ impl Authentication for FabFire {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Got invalid APDUResponse: {:?}", e);
|
tracing::error!("Got invalid APDUResponse: {:?}", e);
|
||||||
return Err(FabFireError::ParseError.into());
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// request the contents of the file containing the URN
|
// request the contents of the file containing the URN
|
||||||
const TOKEN_FILE_ID: u8 = 0x03;
|
const TOKEN_FILE_ID: u8 = 0x03;
|
||||||
|
|
||||||
let buf = match self.desfire.read_data_chunk_cmd(TOKEN_FILE_ID, 0, MAX_BYTES_PER_TRANSACTION) { // TODO: support data longer than 47 Bytes
|
let buf = match self.desfire.read_data_chunk_cmd(
|
||||||
|
TOKEN_FILE_ID,
|
||||||
|
0,
|
||||||
|
MAX_BYTES_PER_TRANSACTION,
|
||||||
|
) {
|
||||||
|
// TODO: support data longer than 47 Bytes
|
||||||
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -385,8 +453,10 @@ impl Authentication for FabFire {
|
|||||||
return match serde_json::to_vec(&cmd) {
|
return match serde_json::to_vec(&cmd) {
|
||||||
Ok(send_buf) => {
|
Ok(send_buf) => {
|
||||||
self.step = Step::GetToken;
|
self.step = Step::GetToken;
|
||||||
writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?;
|
writer
|
||||||
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
.write_all(&send_buf)
|
||||||
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(State::Running)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
tracing::error!("Failed to serialize APDUCommand: {:?}", e);
|
||||||
@ -398,43 +468,46 @@ impl Authentication for FabFire {
|
|||||||
// println!("Step: GetToken");
|
// println!("Step: GetToken");
|
||||||
// parse the token and select the appropriate user
|
// parse the token and select the appropriate user
|
||||||
let response: CardCommand = match input {
|
let response: CardCommand = match input {
|
||||||
None => { return Err(SessionError::InputDataRequired); }
|
None => {
|
||||||
Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) {
|
return Err(SessionError::InputDataRequired);
|
||||||
|
}
|
||||||
|
Some(buf) => match serde_json::from_slice(buf)
|
||||||
|
.map_err(|e| FabFireError::DeserializationError(e))
|
||||||
|
{
|
||||||
Ok(response) => response,
|
Ok(response) => response,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Deserializing data from card failed: {:?}", e);
|
tracing::error!("Deserializing data from card failed: {:?}", e);
|
||||||
return Err(e.into());
|
return Err(e.into());
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let apdu_response = match response {
|
let apdu_response = match response {
|
||||||
CardCommand::readPICC { data } => { APDUResponse::new(&*data) }
|
CardCommand::readPICC { data } => APDUResponse::new(&*data),
|
||||||
_ => {
|
_ => {
|
||||||
tracing::error!("Unexpected response: {:?}", response);
|
tracing::error!("Unexpected response: {:?}", response);
|
||||||
return Err(FabFireError::ParseError.into());
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
match apdu_response.check() {
|
match apdu_response.check() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
match apdu_response.body {
|
match apdu_response.body {
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
let token = String::from_utf8(data).unwrap();
|
let authid = String::from_utf8(data)
|
||||||
session.set_property::<AuthId>(Arc::new(token.trim_matches(char::from(0)).to_string()));
|
.unwrap()
|
||||||
let key = match session.get_property_or_callback::<FabFireCardKey>() {
|
.trim_matches(char::from(0))
|
||||||
Ok(Some(key)) => Box::from(key.as_slice()),
|
.to_string();
|
||||||
Ok(None) => {
|
let prov = ThisProvider::<AuthId>::with(&authid);
|
||||||
tracing::error!("No keys on file for token");
|
let key = session
|
||||||
return Err(FabFireError::InvalidCredentials("No keys on file for token".to_string()).into());
|
.need_with::<FabFireCardKey, _, _>(&prov, |key| {
|
||||||
}
|
Ok(Box::from(key.as_slice()))
|
||||||
Err(e) => {
|
})?;
|
||||||
tracing::error!("Failed to get key: {:?}", e);
|
self.key_info = Some(KeyInfo {
|
||||||
return Err(FabFireError::Session(e).into());
|
authid,
|
||||||
}
|
key_id: 0x01,
|
||||||
};
|
key,
|
||||||
self.key_info = Some(KeyInfo{ key_id: 0x01, key });
|
});
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
tracing::error!("No data in response");
|
tracing::error!("No data in response");
|
||||||
@ -448,7 +521,10 @@ impl Authentication for FabFire {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let buf = match self.desfire.authenticate_iso_aes_challenge_cmd(self.key_info.as_ref().unwrap().key_id) {
|
let buf = match self
|
||||||
|
.desfire
|
||||||
|
.authenticate_iso_aes_challenge_cmd(self.key_info.as_ref().unwrap().key_id)
|
||||||
|
{
|
||||||
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -465,8 +541,10 @@ impl Authentication for FabFire {
|
|||||||
return match serde_json::to_vec(&cmd) {
|
return match serde_json::to_vec(&cmd) {
|
||||||
Ok(send_buf) => {
|
Ok(send_buf) => {
|
||||||
self.step = Step::Authenticate1;
|
self.step = Step::Authenticate1;
|
||||||
writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?;
|
writer
|
||||||
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
.write_all(&send_buf)
|
||||||
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(State::Running)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to serialize command: {:?}", e);
|
tracing::error!("Failed to serialize command: {:?}", e);
|
||||||
@ -477,25 +555,28 @@ impl Authentication for FabFire {
|
|||||||
Step::Authenticate1 => {
|
Step::Authenticate1 => {
|
||||||
tracing::trace!("Step: Authenticate1");
|
tracing::trace!("Step: Authenticate1");
|
||||||
let response: CardCommand = match input {
|
let response: CardCommand = match input {
|
||||||
None => { return Err(SessionError::InputDataRequired); }
|
None => {
|
||||||
Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) {
|
return Err(SessionError::InputDataRequired);
|
||||||
|
}
|
||||||
|
Some(buf) => match serde_json::from_slice(buf)
|
||||||
|
.map_err(|e| FabFireError::DeserializationError(e))
|
||||||
|
{
|
||||||
Ok(response) => response,
|
Ok(response) => response,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to deserialize response: {:?}", e);
|
tracing::error!("Failed to deserialize response: {:?}", e);
|
||||||
return Err(e.into());
|
return Err(e.into());
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let apdu_response = match response {
|
let apdu_response = match response {
|
||||||
CardCommand::readPICC { data } => { APDUResponse::new(&*data) }
|
CardCommand::readPICC { data } => APDUResponse::new(&*data),
|
||||||
_ => {
|
_ => {
|
||||||
tracing::error!("Unexpected response: {:?}", response);
|
tracing::error!("Unexpected response: {:?}", response);
|
||||||
return Err(FabFireError::ParseError.into());
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
match apdu_response.check() {
|
match apdu_response.check() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
match apdu_response.body {
|
match apdu_response.body {
|
||||||
@ -506,13 +587,19 @@ impl Authentication for FabFire {
|
|||||||
//TODO: Check if we need a CSPRNG here
|
//TODO: Check if we need a CSPRNG here
|
||||||
let rnd_a: [u8; 16] = rand::random();
|
let rnd_a: [u8; 16] = rand::random();
|
||||||
|
|
||||||
let (cmd_challenge_response,
|
let (cmd_challenge_response, rnd_b, iv) = self
|
||||||
|
.desfire
|
||||||
|
.authenticate_iso_aes_response_cmd(
|
||||||
|
rnd_b_enc,
|
||||||
|
&*(self.key_info.as_ref().unwrap().key),
|
||||||
|
&rnd_a,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
self.auth_info = Some(AuthInfo {
|
||||||
|
rnd_a: Vec::<u8>::from(rnd_a),
|
||||||
rnd_b,
|
rnd_b,
|
||||||
iv) = self.desfire.authenticate_iso_aes_response_cmd(
|
iv,
|
||||||
rnd_b_enc,
|
});
|
||||||
&*(self.key_info.as_ref().unwrap().key),
|
|
||||||
&rnd_a).unwrap();
|
|
||||||
self.auth_info = Some(AuthInfo { rnd_a: Vec::<u8>::from(rnd_a), rnd_b, iv });
|
|
||||||
let buf = match Vec::<u8>::try_from(cmd_challenge_response) {
|
let buf = match Vec::<u8>::try_from(cmd_challenge_response) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -524,8 +611,10 @@ impl Authentication for FabFire {
|
|||||||
return match serde_json::to_vec(&cmd) {
|
return match serde_json::to_vec(&cmd) {
|
||||||
Ok(send_buf) => {
|
Ok(send_buf) => {
|
||||||
self.step = Step::Authenticate2;
|
self.step = Step::Authenticate2;
|
||||||
writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?;
|
writer
|
||||||
Ok(rsasl::session::Step::NeedsMore(Some(send_buf.len())))
|
.write_all(&send_buf)
|
||||||
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(State::Running)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to serialize command: {:?}", e);
|
tracing::error!("Failed to serialize command: {:?}", e);
|
||||||
@ -548,58 +637,90 @@ impl Authentication for FabFire {
|
|||||||
Step::Authenticate2 => {
|
Step::Authenticate2 => {
|
||||||
// println!("Step: Authenticate2");
|
// println!("Step: Authenticate2");
|
||||||
let response: CardCommand = match input {
|
let response: CardCommand = match input {
|
||||||
None => { return Err(SessionError::InputDataRequired); }
|
None => {
|
||||||
Some(buf) => match serde_json::from_slice(buf).map_err(|e| FabFireError::DeserializationError(e)) {
|
return Err(SessionError::InputDataRequired);
|
||||||
|
}
|
||||||
|
Some(buf) => match serde_json::from_slice(buf)
|
||||||
|
.map_err(|e| FabFireError::DeserializationError(e))
|
||||||
|
{
|
||||||
Ok(response) => response,
|
Ok(response) => response,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to deserialize response: {:?}", e);
|
tracing::error!("Failed to deserialize response: {:?}", e);
|
||||||
return Err(e.into());
|
return Err(e.into());
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let apdu_response = match response {
|
let apdu_response = match response {
|
||||||
CardCommand::readPICC { data } => { APDUResponse::new(&*data) }
|
CardCommand::readPICC { data } => APDUResponse::new(&*data),
|
||||||
_ => {
|
_ => {
|
||||||
tracing::error!("Got invalid response: {:?}", response);
|
tracing::error!("Got invalid response: {:?}", response);
|
||||||
return Err(FabFireError::ParseError.into());
|
return Err(FabFireError::ParseError.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
match apdu_response.check() {
|
match apdu_response.check() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
match apdu_response.body {
|
match apdu_response.body {
|
||||||
Some(data) => {
|
Some(data) => match self.auth_info.as_ref() {
|
||||||
match self.auth_info.as_ref() {
|
None => {
|
||||||
None => { return Err(FabFireError::ParseError.into()); }
|
return Err(FabFireError::ParseError.into());
|
||||||
Some(auth_info) => {
|
}
|
||||||
if self.desfire.authenticate_iso_aes_verify(
|
Some(auth_info) => {
|
||||||
|
if self
|
||||||
|
.desfire
|
||||||
|
.authenticate_iso_aes_verify(
|
||||||
data.as_slice(),
|
data.as_slice(),
|
||||||
auth_info.rnd_a.as_slice(),
|
auth_info.rnd_a.as_slice(),
|
||||||
auth_info.rnd_b.as_slice(), &*(self.key_info.as_ref().unwrap().key),
|
auth_info.rnd_b.as_slice(),
|
||||||
auth_info.iv.as_slice()).is_ok() {
|
&*(self.key_info.as_ref().unwrap().key),
|
||||||
|
auth_info.iv.as_slice(),
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
let cmd = CardCommand::message {
|
||||||
|
msg_id: Some(4),
|
||||||
|
clr_txt: None,
|
||||||
|
addn_txt: Some("".to_string()),
|
||||||
|
};
|
||||||
|
return match serde_json::to_vec(&cmd) {
|
||||||
|
Ok(send_buf) => {
|
||||||
|
self.step = Step::Authenticate1;
|
||||||
|
writer
|
||||||
|
.write_all(&send_buf)
|
||||||
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
|
||||||
let cmd = CardCommand::message{
|
struct Prov<'a> {
|
||||||
msg_id: Some(4),
|
authid: &'a str,
|
||||||
clr_txt: None,
|
|
||||||
addn_txt: Some("".to_string()),
|
|
||||||
};
|
|
||||||
return match serde_json::to_vec(&cmd) {
|
|
||||||
Ok(send_buf) => {
|
|
||||||
self.step = Step::Authenticate1;
|
|
||||||
writer.write_all(&send_buf).map_err(|e| SessionError::Io { source: e })?;
|
|
||||||
return Ok(rsasl::session::Step::Done(Some(send_buf.len())))
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
impl<'a> Provider<'a> for Prov<'a> {
|
||||||
tracing::error!("Failed to serialize command: {:?}", e);
|
fn provide(
|
||||||
Err(FabFireError::SerializationError.into())
|
&self,
|
||||||
|
req: &mut Demand<'a>,
|
||||||
|
) -> DemandReply<()>
|
||||||
|
{
|
||||||
|
req.provide_ref::<AuthId>(self.authid)?
|
||||||
|
.done()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
let prov = Prov {
|
||||||
}
|
authid: &self.key_info.as_ref().unwrap().authid,
|
||||||
|
};
|
||||||
|
session.validate(&prov)?;
|
||||||
|
|
||||||
|
return Ok(State::Finished(MessageSent::Yes));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!(
|
||||||
|
"Failed to serialize command: {:?}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
Err(FabFireError::SerializationError.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
None => {
|
None => {
|
||||||
tracing::error!("got empty response");
|
tracing::error!("got empty response");
|
||||||
return Err(FabFireError::ParseError.into());
|
return Err(FabFireError::ParseError.into());
|
||||||
@ -608,12 +729,14 @@ impl Authentication for FabFire {
|
|||||||
}
|
}
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
tracing::error!("Got invalid response: {:?}", apdu_response);
|
tracing::error!("Got invalid response: {:?}", apdu_response);
|
||||||
return Err(FabFireError::InvalidCredentials(format!("{}", apdu_response)).into());
|
return Err(
|
||||||
|
FabFireError::InvalidCredentials(format!("{}", apdu_response)).into(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(rsasl::session::Step::Done(None));
|
return Ok(State::Finished(MessageSent::No));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
bffhd/authentication/fabfire_bin/mod.rs
Normal file
25
bffhd/authentication/fabfire_bin/mod.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
mod server;
|
||||||
|
pub use server::FabFire;
|
||||||
|
|
||||||
|
use rsasl::mechname::Mechname;
|
||||||
|
use rsasl::registry::{Matches, Mechanism, Named, Side, MECHANISMS};
|
||||||
|
|
||||||
|
const MECHNAME: &'static Mechname = &Mechname::const_new_unchecked(b"X-FABFIRE-BIN");
|
||||||
|
|
||||||
|
#[linkme::distributed_slice(MECHANISMS)]
|
||||||
|
pub static FABFIRE: Mechanism = Mechanism::build(
|
||||||
|
MECHNAME,
|
||||||
|
300,
|
||||||
|
None,
|
||||||
|
Some(FabFire::new_server),
|
||||||
|
Side::Client,
|
||||||
|
|_| Some(Matches::<Select>::name()),
|
||||||
|
|_| true,
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Select;
|
||||||
|
impl Named for Select {
|
||||||
|
fn mech() -> &'static Mechanism {
|
||||||
|
&FABFIRE
|
||||||
|
}
|
||||||
|
}
|
532
bffhd/authentication/fabfire_bin/server.rs
Normal file
532
bffhd/authentication/fabfire_bin/server.rs
Normal file
@ -0,0 +1,532 @@
|
|||||||
|
use desfire::desfire::desfire::MAX_BYTES_PER_TRANSACTION;
|
||||||
|
use desfire::desfire::Desfire;
|
||||||
|
use desfire::error::Error as DesfireError;
|
||||||
|
use desfire::iso7816_4::apduresponse::APDUResponse;
|
||||||
|
use rsasl::mechanism::{
|
||||||
|
Authentication, Demand, DemandReply, MechanismData, MechanismError, MechanismErrorKind,
|
||||||
|
Provider, State, ThisProvider,
|
||||||
|
};
|
||||||
|
use rsasl::prelude::{MessageSent, SASLConfig, SASLError, SessionError};
|
||||||
|
use rsasl::property::AuthId;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use crate::authentication::fabfire::FabFireCardKey;
|
||||||
|
use crate::CONFIG;
|
||||||
|
|
||||||
|
enum FabFireError {
|
||||||
|
ParseError,
|
||||||
|
SerializationError,
|
||||||
|
DeserializationError(serde_json::Error),
|
||||||
|
CardError(DesfireError),
|
||||||
|
InvalidMagic(String),
|
||||||
|
InvalidToken(String),
|
||||||
|
InvalidURN(String),
|
||||||
|
InvalidCredentials(String),
|
||||||
|
Session(SessionError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for FabFireError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
FabFireError::ParseError => write!(f, "ParseError"),
|
||||||
|
FabFireError::SerializationError => write!(f, "SerializationError"),
|
||||||
|
FabFireError::DeserializationError(e) => write!(f, "DeserializationError: {}", e),
|
||||||
|
FabFireError::CardError(err) => write!(f, "CardError: {}", err),
|
||||||
|
FabFireError::InvalidMagic(magic) => write!(f, "InvalidMagic: {}", magic),
|
||||||
|
FabFireError::InvalidToken(token) => write!(f, "InvalidToken: {}", token),
|
||||||
|
FabFireError::InvalidURN(urn) => write!(f, "InvalidURN: {}", urn),
|
||||||
|
FabFireError::InvalidCredentials(credentials) => {
|
||||||
|
write!(f, "InvalidCredentials: {}", credentials)
|
||||||
|
}
|
||||||
|
FabFireError::Session(err) => write!(f, "Session: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for FabFireError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
FabFireError::ParseError => write!(f, "ParseError"),
|
||||||
|
FabFireError::SerializationError => write!(f, "SerializationError"),
|
||||||
|
FabFireError::DeserializationError(e) => write!(f, "DeserializationError: {}", e),
|
||||||
|
FabFireError::CardError(err) => write!(f, "CardError: {}", err),
|
||||||
|
FabFireError::InvalidMagic(magic) => write!(f, "InvalidMagic: {}", magic),
|
||||||
|
FabFireError::InvalidToken(token) => write!(f, "InvalidToken: {}", token),
|
||||||
|
FabFireError::InvalidURN(urn) => write!(f, "InvalidURN: {}", urn),
|
||||||
|
FabFireError::InvalidCredentials(credentials) => {
|
||||||
|
write!(f, "InvalidCredentials: {}", credentials)
|
||||||
|
}
|
||||||
|
FabFireError::Session(err) => write!(f, "Session: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for FabFireError {}
|
||||||
|
|
||||||
|
impl MechanismError for FabFireError {
|
||||||
|
fn kind(&self) -> MechanismErrorKind {
|
||||||
|
match self {
|
||||||
|
FabFireError::ParseError => MechanismErrorKind::Parse,
|
||||||
|
FabFireError::SerializationError => MechanismErrorKind::Protocol,
|
||||||
|
FabFireError::DeserializationError(_) => MechanismErrorKind::Parse,
|
||||||
|
FabFireError::CardError(_) => MechanismErrorKind::Protocol,
|
||||||
|
FabFireError::InvalidMagic(_) => MechanismErrorKind::Protocol,
|
||||||
|
FabFireError::InvalidToken(_) => MechanismErrorKind::Protocol,
|
||||||
|
FabFireError::InvalidURN(_) => MechanismErrorKind::Protocol,
|
||||||
|
FabFireError::InvalidCredentials(_) => MechanismErrorKind::Protocol,
|
||||||
|
FabFireError::Session(_) => MechanismErrorKind::Protocol,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct CardInfo {
|
||||||
|
#[serde(rename = "UID", with = "hex")]
|
||||||
|
uid: [u8; 7],
|
||||||
|
key_old: Option<Box<[u8]>>,
|
||||||
|
key_new: Option<Box<[u8]>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct KeyInfo {
|
||||||
|
authid: String,
|
||||||
|
key_id: u8,
|
||||||
|
key: Box<[u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AuthInfo {
|
||||||
|
rnd_a: Vec<u8>,
|
||||||
|
rnd_b: Vec<u8>,
|
||||||
|
iv: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Step {
|
||||||
|
New,
|
||||||
|
SelectApp,
|
||||||
|
VerifyMagic,
|
||||||
|
GetURN,
|
||||||
|
GetToken,
|
||||||
|
Authenticate1,
|
||||||
|
Authenticate2,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FabFire {
|
||||||
|
step: Step,
|
||||||
|
card_info: Option<CardInfo>,
|
||||||
|
key_info: Option<KeyInfo>,
|
||||||
|
auth_info: Option<AuthInfo>,
|
||||||
|
app_id: u32,
|
||||||
|
local_urn: String,
|
||||||
|
desfire: Desfire,
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAGIC: &'static str = "FABACCESS\0DESFIRE\01.0\0";
|
||||||
|
|
||||||
|
impl FabFire {
|
||||||
|
pub fn new_server(_sasl: &SASLConfig) -> Result<Box<dyn Authentication>, SASLError> {
|
||||||
|
let space = if let Some(space) = CONFIG.get().map(|c| c.spacename.as_str()) {
|
||||||
|
space
|
||||||
|
} else {
|
||||||
|
tracing::error!("No space configured");
|
||||||
|
"generic"
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Box::new(Self {
|
||||||
|
step: Step::New,
|
||||||
|
card_info: None,
|
||||||
|
key_info: None,
|
||||||
|
auth_info: None,
|
||||||
|
app_id: 0x464142,
|
||||||
|
local_urn: format!("urn:fabaccess:lab:{space}"),
|
||||||
|
desfire: Desfire {
|
||||||
|
card: None,
|
||||||
|
session_key: None,
|
||||||
|
cbc_iv: None,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Authentication for FabFire {
|
||||||
|
fn step(
|
||||||
|
&mut self,
|
||||||
|
session: &mut MechanismData<'_, '_>,
|
||||||
|
input: Option<&[u8]>,
|
||||||
|
writer: &mut dyn Write,
|
||||||
|
) -> Result<State, SessionError> {
|
||||||
|
match self.step {
|
||||||
|
Step::New => {
|
||||||
|
tracing::trace!("Step: New");
|
||||||
|
//receive card info (especially card UID) from reader
|
||||||
|
return match input {
|
||||||
|
None => Err(SessionError::InputDataRequired),
|
||||||
|
Some(_) => {
|
||||||
|
//select application
|
||||||
|
return match self.desfire.select_application_cmd(self.app_id) {
|
||||||
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
|
Ok(data) => {
|
||||||
|
self.step = Step::SelectApp;
|
||||||
|
writer
|
||||||
|
.write_all(&data)
|
||||||
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(State::Running)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!(
|
||||||
|
"Failed to convert APDUCommand to Vec<u8>: {:?}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return Err(FabFireError::SerializationError.into());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to generate APDUCommand: {:?}", e);
|
||||||
|
return Err(FabFireError::SerializationError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Step::SelectApp => {
|
||||||
|
tracing::trace!("Step: SelectApp");
|
||||||
|
// check that we successfully selected the application
|
||||||
|
|
||||||
|
let apdu_response = match input {
|
||||||
|
Some(data) => APDUResponse::new(data),
|
||||||
|
None => return Err(SessionError::InputDataRequired),
|
||||||
|
};
|
||||||
|
|
||||||
|
apdu_response
|
||||||
|
.check()
|
||||||
|
.map_err(|e| FabFireError::CardError(e))?;
|
||||||
|
|
||||||
|
// request the contents of the file containing the magic string
|
||||||
|
const MAGIC_FILE_ID: u8 = 0x01;
|
||||||
|
|
||||||
|
return match self
|
||||||
|
.desfire
|
||||||
|
.read_data_chunk_cmd(MAGIC_FILE_ID, 0, MAGIC.len())
|
||||||
|
{
|
||||||
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
|
Ok(data) => {
|
||||||
|
self.step = Step::VerifyMagic;
|
||||||
|
writer
|
||||||
|
.write_all(&data)
|
||||||
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(State::Running)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to convert APDUCommand to Vec<u8>: {:?}", e);
|
||||||
|
return Err(FabFireError::SerializationError.into());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to generate APDUCommand: {:?}", e);
|
||||||
|
return Err(FabFireError::SerializationError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Step::VerifyMagic => {
|
||||||
|
tracing::trace!("Step: VerifyMagic");
|
||||||
|
// verify the magic string to determine that we have a valid fabfire card
|
||||||
|
let apdu_response = match input {
|
||||||
|
Some(data) => APDUResponse::new(data),
|
||||||
|
None => return Err(SessionError::InputDataRequired),
|
||||||
|
};
|
||||||
|
|
||||||
|
match apdu_response.check() {
|
||||||
|
Ok(_) => {
|
||||||
|
match apdu_response.body {
|
||||||
|
Some(data) => {
|
||||||
|
if std::str::from_utf8(data.as_slice()) != Ok(MAGIC) {
|
||||||
|
tracing::error!("Invalid magic string");
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
tracing::error!("No data returned from card");
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Got invalid APDUResponse: {:?}", e);
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// request the contents of the file containing the URN
|
||||||
|
const URN_FILE_ID: u8 = 0x02;
|
||||||
|
|
||||||
|
return match self.desfire.read_data_chunk_cmd(
|
||||||
|
URN_FILE_ID,
|
||||||
|
0,
|
||||||
|
self.local_urn.as_bytes().len(),
|
||||||
|
) {
|
||||||
|
// TODO: support urn longer than 47 Bytes
|
||||||
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
|
Ok(data) => {
|
||||||
|
self.step = Step::GetURN;
|
||||||
|
writer
|
||||||
|
.write_all(&data)
|
||||||
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(State::Running)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to convert APDUCommand to Vec<u8>: {:?}", e);
|
||||||
|
return Err(FabFireError::SerializationError.into());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to generate APDUCommand: {:?}", e);
|
||||||
|
return Err(FabFireError::SerializationError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Step::GetURN => {
|
||||||
|
tracing::trace!("Step: GetURN");
|
||||||
|
// parse the urn and match it to our local urn
|
||||||
|
let apdu_response = match input {
|
||||||
|
Some(data) => APDUResponse::new(data),
|
||||||
|
None => return Err(SessionError::InputDataRequired),
|
||||||
|
};
|
||||||
|
|
||||||
|
match apdu_response.check() {
|
||||||
|
Ok(_) => {
|
||||||
|
match apdu_response.body {
|
||||||
|
Some(data) => {
|
||||||
|
let received_urn = String::from_utf8(data).unwrap();
|
||||||
|
if received_urn != self.local_urn {
|
||||||
|
tracing::error!(
|
||||||
|
"URN mismatch: {:?} != {:?}",
|
||||||
|
received_urn,
|
||||||
|
self.local_urn
|
||||||
|
);
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
tracing::error!("No data returned from card");
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Got invalid APDUResponse: {:?}", e);
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// request the contents of the file containing the URN
|
||||||
|
const TOKEN_FILE_ID: u8 = 0x03;
|
||||||
|
|
||||||
|
return match self.desfire.read_data_chunk_cmd(
|
||||||
|
TOKEN_FILE_ID,
|
||||||
|
0,
|
||||||
|
MAX_BYTES_PER_TRANSACTION,
|
||||||
|
) {
|
||||||
|
// TODO: support data longer than 47 Bytes
|
||||||
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
|
Ok(data) => {
|
||||||
|
self.step = Step::GetToken;
|
||||||
|
writer
|
||||||
|
.write_all(&data)
|
||||||
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(State::Running)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to convert APDUCommand to Vec<u8>: {:?}", e);
|
||||||
|
return Err(FabFireError::SerializationError.into());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to generate APDUCommand: {:?}", e);
|
||||||
|
return Err(FabFireError::SerializationError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Step::GetToken => {
|
||||||
|
// println!("Step: GetToken");
|
||||||
|
// parse the token and select the appropriate user
|
||||||
|
let apdu_response = match input {
|
||||||
|
Some(data) => APDUResponse::new(data),
|
||||||
|
None => return Err(SessionError::InputDataRequired),
|
||||||
|
};
|
||||||
|
|
||||||
|
match apdu_response.check() {
|
||||||
|
Ok(_) => {
|
||||||
|
match apdu_response.body {
|
||||||
|
Some(data) => {
|
||||||
|
let authid = String::from_utf8(data)
|
||||||
|
.unwrap()
|
||||||
|
.trim_matches(char::from(0))
|
||||||
|
.to_string();
|
||||||
|
let prov = ThisProvider::<AuthId>::with(&authid);
|
||||||
|
let key = session
|
||||||
|
.need_with::<FabFireCardKey, _, _>(&prov, |key| {
|
||||||
|
Ok(Box::from(key.as_slice()))
|
||||||
|
})?;
|
||||||
|
self.key_info = Some(KeyInfo {
|
||||||
|
authid,
|
||||||
|
key_id: 0x01,
|
||||||
|
key,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
tracing::error!("No data in response");
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to check response: {:?}", e);
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return match self
|
||||||
|
.desfire
|
||||||
|
.authenticate_iso_aes_challenge_cmd(self.key_info.as_ref().unwrap().key_id)
|
||||||
|
{
|
||||||
|
Ok(buf) => match Vec::<u8>::try_from(buf) {
|
||||||
|
Ok(data) => {
|
||||||
|
self.step = Step::Authenticate1;
|
||||||
|
writer
|
||||||
|
.write_all(&data)
|
||||||
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(State::Running)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to convert to Vec<u8>: {:?}", e);
|
||||||
|
return Err(FabFireError::SerializationError.into());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to create authenticate command: {:?}", e);
|
||||||
|
return Err(FabFireError::SerializationError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Step::Authenticate1 => {
|
||||||
|
tracing::trace!("Step: Authenticate1");
|
||||||
|
let apdu_response = match input {
|
||||||
|
Some(data) => APDUResponse::new(data),
|
||||||
|
None => return Err(SessionError::InputDataRequired),
|
||||||
|
};
|
||||||
|
|
||||||
|
return match apdu_response.check() {
|
||||||
|
Ok(_) => {
|
||||||
|
match apdu_response.body {
|
||||||
|
Some(data) => {
|
||||||
|
let rnd_b_enc = data.as_slice();
|
||||||
|
|
||||||
|
//FIXME: This is ugly, we should find a better way to make the function testable
|
||||||
|
//TODO: Check if we need a CSPRNG here
|
||||||
|
let rnd_a: [u8; 16] = rand::random();
|
||||||
|
|
||||||
|
let (cmd_challenge_response, rnd_b, iv) = self
|
||||||
|
.desfire
|
||||||
|
.authenticate_iso_aes_response_cmd(
|
||||||
|
rnd_b_enc,
|
||||||
|
&*(self.key_info.as_ref().unwrap().key),
|
||||||
|
&rnd_a,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
self.auth_info = Some(AuthInfo {
|
||||||
|
rnd_a: Vec::<u8>::from(rnd_a),
|
||||||
|
rnd_b,
|
||||||
|
iv,
|
||||||
|
});
|
||||||
|
match Vec::<u8>::try_from(cmd_challenge_response) {
|
||||||
|
Ok(data) => {
|
||||||
|
self.step = Step::Authenticate2;
|
||||||
|
writer
|
||||||
|
.write_all(&data)
|
||||||
|
.map_err(|e| SessionError::Io { source: e })?;
|
||||||
|
Ok(State::Running)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to convert to Vec<u8>: {:?}", e);
|
||||||
|
return Err(FabFireError::SerializationError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
tracing::error!("Got invalid response: {:?}", apdu_response);
|
||||||
|
Err(FabFireError::ParseError.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to check response: {:?}", e);
|
||||||
|
Err(FabFireError::ParseError.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Step::Authenticate2 => {
|
||||||
|
// println!("Step: Authenticate2");
|
||||||
|
let apdu_response = match input {
|
||||||
|
Some(data) => APDUResponse::new(data),
|
||||||
|
None => return Err(SessionError::InputDataRequired),
|
||||||
|
};
|
||||||
|
|
||||||
|
match apdu_response.check() {
|
||||||
|
Ok(_) => {
|
||||||
|
match apdu_response.body {
|
||||||
|
Some(data) => match self.auth_info.as_ref() {
|
||||||
|
None => {
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
Some(auth_info) => {
|
||||||
|
if self
|
||||||
|
.desfire
|
||||||
|
.authenticate_iso_aes_verify(
|
||||||
|
data.as_slice(),
|
||||||
|
auth_info.rnd_a.as_slice(),
|
||||||
|
auth_info.rnd_b.as_slice(),
|
||||||
|
&*(self.key_info.as_ref().unwrap().key),
|
||||||
|
auth_info.iv.as_slice(),
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
struct Prov<'a> {
|
||||||
|
authid: &'a str,
|
||||||
|
}
|
||||||
|
impl<'a> Provider<'a> for Prov<'a> {
|
||||||
|
fn provide(
|
||||||
|
&self,
|
||||||
|
req: &mut Demand<'a>,
|
||||||
|
) -> DemandReply<()>
|
||||||
|
{
|
||||||
|
req.provide_ref::<AuthId>(self.authid)?.done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let prov = Prov {
|
||||||
|
authid: &self.key_info.as_ref().unwrap().authid,
|
||||||
|
};
|
||||||
|
session.validate(&prov)?;
|
||||||
|
return Ok(State::Finished(MessageSent::Yes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
tracing::error!("got empty response");
|
||||||
|
return Err(FabFireError::ParseError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(_e) => {
|
||||||
|
tracing::error!("Got invalid response: {:?}", apdu_response);
|
||||||
|
return Err(
|
||||||
|
FabFireError::InvalidCredentials(format!("{}", apdu_response)).into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(State::Finished(MessageSent::No));
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,17 @@
|
|||||||
|
|
||||||
use crate::users::Users;
|
use crate::users::Users;
|
||||||
use rsasl::error::{SessionError};
|
use miette::{IntoDiagnostic, WrapErr};
|
||||||
use rsasl::mechname::Mechname;
|
use rsasl::callback::{CallbackError, Context, Request, SessionCallback, SessionData};
|
||||||
use rsasl::property::{AuthId, Password};
|
use rsasl::mechanism::SessionError;
|
||||||
use rsasl::session::{Session, SessionData};
|
use rsasl::prelude::{Mechname, SASLConfig, SASLServer, Session, Validation};
|
||||||
use rsasl::validate::{validations, Validation};
|
use rsasl::property::{AuthId, AuthzId, Password};
|
||||||
use rsasl::{Property, SASL};
|
use rsasl::validate::{Validate, ValidationError};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::authentication::fabfire::FabFireCardKey;
|
use crate::authentication::fabfire::FabFireCardKey;
|
||||||
|
use crate::users::db::User;
|
||||||
|
|
||||||
mod fabfire;
|
mod fabfire;
|
||||||
|
mod fabfire_bin;
|
||||||
|
|
||||||
struct Callback {
|
struct Callback {
|
||||||
users: Users,
|
users: Users,
|
||||||
@ -22,85 +23,102 @@ impl Callback {
|
|||||||
Self { users, span }
|
Self { users, span }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl rsasl::callback::Callback for Callback {
|
impl SessionCallback for Callback {
|
||||||
fn provide_prop(
|
fn callback(
|
||||||
&self,
|
&self,
|
||||||
session: &mut rsasl::session::SessionData,
|
_session_data: &SessionData,
|
||||||
property: Property,
|
context: &Context,
|
||||||
|
request: &mut Request,
|
||||||
) -> Result<(), SessionError> {
|
) -> Result<(), SessionError> {
|
||||||
match property {
|
if let Some(authid) = context.get_ref::<AuthId>() {
|
||||||
fabfire::FABFIRECARDKEY => {
|
request.satisfy_with::<FabFireCardKey, _>(|| {
|
||||||
let authcid = session.get_property_or_callback::<AuthId>()?;
|
let user = self.users.get_user(authid).ok_or(CallbackError::NoValue)?;
|
||||||
let user = self.users.get_user(authcid.unwrap().as_ref())
|
let kv = user
|
||||||
.ok_or(SessionError::AuthenticationFailure)?;
|
.userdata
|
||||||
let kv = user.userdata.kv.get("cardkey")
|
.kv
|
||||||
.ok_or(SessionError::AuthenticationFailure)?;
|
.get("cardkey")
|
||||||
let card_key = <[u8; 16]>::try_from(hex::decode(kv)
|
.ok_or(CallbackError::NoValue)?;
|
||||||
.map_err(|_| SessionError::AuthenticationFailure)?)
|
let card_key =
|
||||||
.map_err(|_| SessionError::AuthenticationFailure)?;
|
<[u8; 16]>::try_from(hex::decode(kv).map_err(|_| CallbackError::NoValue)?)
|
||||||
session.set_property::<FabFireCardKey>(Arc::new(card_key));
|
.map_err(|_| CallbackError::NoValue)?;
|
||||||
Ok(())
|
Ok(card_key)
|
||||||
}
|
})?;
|
||||||
_ => Err(SessionError::NoProperty { property }),
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate(
|
fn validate(
|
||||||
&self,
|
&self,
|
||||||
session: &mut SessionData,
|
session_data: &SessionData,
|
||||||
validation: Validation,
|
context: &Context,
|
||||||
_mechanism: &Mechname,
|
validate: &mut Validate<'_>,
|
||||||
) -> Result<(), SessionError> {
|
) -> Result<(), ValidationError> {
|
||||||
let span = tracing::info_span!(parent: &self.span, "validate");
|
let span = tracing::info_span!(parent: &self.span, "validate");
|
||||||
let _guard = span.enter();
|
let _guard = span.enter();
|
||||||
match validation {
|
if validate.is::<V>() {
|
||||||
validations::SIMPLE => {
|
match session_data.mechanism().mechanism.as_str() {
|
||||||
let authnid = session
|
"PLAIN" => {
|
||||||
.get_property::<AuthId>()
|
let authcid = context
|
||||||
.ok_or(SessionError::no_property::<AuthId>())?;
|
.get_ref::<AuthId>()
|
||||||
tracing::debug!(authid=%authnid, "SIMPLE validation requested");
|
.ok_or(ValidationError::MissingRequiredProperty)?;
|
||||||
|
let authzid = context
|
||||||
|
.get_ref::<AuthzId>()
|
||||||
|
.ok_or(ValidationError::MissingRequiredProperty)?;
|
||||||
|
let password = context
|
||||||
|
.get_ref::<Password>()
|
||||||
|
.ok_or(ValidationError::MissingRequiredProperty)?;
|
||||||
|
|
||||||
if let Some(user) = self
|
if !authzid.is_empty() {
|
||||||
.users
|
|
||||||
.get_user(authnid.as_str()) {
|
|
||||||
let passwd = session
|
|
||||||
.get_property::<Password>()
|
|
||||||
.ok_or(SessionError::no_property::<Password>())?;
|
|
||||||
|
|
||||||
if user
|
|
||||||
.check_password(passwd.as_bytes())
|
|
||||||
.map_err(|_e| SessionError::AuthenticationFailure)?
|
|
||||||
{
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
|
||||||
tracing::warn!(authid=%authnid, "AUTH FAILED: bad password");
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
tracing::warn!(authid=%authnid, "AUTH FAILED: no such user '{}'", authnid);
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(SessionError::AuthenticationFailure)
|
if let Some(user) = self.users.get_user(authcid) {
|
||||||
|
match user.check_password(password) {
|
||||||
|
Ok(true) => validate.finalize::<V>(user),
|
||||||
|
Ok(false) => {
|
||||||
|
tracing::warn!(authid=%authcid, "AUTH FAILED: bad password");
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
tracing::warn!(authid=%authcid, "Bad DB entry: {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tracing::warn!(authid=%authcid, "AUTH FAILED: no such user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"X-FABFIRE" | "X-FABFIRE-BIN" => {
|
||||||
|
let authcid = context
|
||||||
|
.get_ref::<AuthId>()
|
||||||
|
.ok_or(ValidationError::MissingRequiredProperty)?;
|
||||||
|
if let Some(user) = self.users.get_user(authcid) {
|
||||||
|
validate.finalize::<V>(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
_ => {
|
|
||||||
tracing::error!(?validation, "Unimplemented validation requested");
|
|
||||||
Err(SessionError::no_validate(validation))
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct V;
|
||||||
|
impl Validation for V {
|
||||||
|
type Value = User;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct Inner {
|
struct Inner {
|
||||||
rsasl: SASL,
|
rsasl: Arc<SASLConfig>,
|
||||||
}
|
}
|
||||||
impl Inner {
|
impl Inner {
|
||||||
pub fn new(rsasl: SASL) -> Self {
|
pub fn new(rsasl: Arc<SASLConfig>) -> Self {
|
||||||
Self { rsasl }
|
Self { rsasl }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AuthenticationHandle {
|
pub struct AuthenticationHandle {
|
||||||
inner: Arc<Inner>,
|
inner: Inner,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthenticationHandle {
|
impl AuthenticationHandle {
|
||||||
@ -108,29 +126,32 @@ impl AuthenticationHandle {
|
|||||||
let span = tracing::debug_span!("authentication");
|
let span = tracing::debug_span!("authentication");
|
||||||
let _guard = span.enter();
|
let _guard = span.enter();
|
||||||
|
|
||||||
let mut rsasl = SASL::new();
|
let config = SASLConfig::builder()
|
||||||
rsasl.install_callback(Arc::new(Callback::new(userdb)));
|
.with_defaults()
|
||||||
|
.with_callback(Callback::new(userdb))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mechs: Vec<&'static str> = rsasl.server_mech_list().into_iter()
|
let mechs: Vec<&'static str> = SASLServer::<V>::new(config.clone())
|
||||||
|
.get_available()
|
||||||
|
.into_iter()
|
||||||
.map(|m| m.mechanism.as_str())
|
.map(|m| m.mechanism.as_str())
|
||||||
.collect();
|
.collect();
|
||||||
tracing::info!(available_mechs=mechs.len(), "initialized sasl backend");
|
tracing::info!(available_mechs = mechs.len(), "initialized sasl backend");
|
||||||
tracing::debug!(?mechs, "available mechs");
|
tracing::debug!(?mechs, "available mechs");
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
inner: Arc::new(Inner::new(rsasl)),
|
inner: Inner::new(config),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&self, mechanism: &Mechname) -> anyhow::Result<Session> {
|
pub fn start(&self, mechanism: &Mechname) -> miette::Result<Session<V>> {
|
||||||
Ok(self.inner.rsasl.server_start(mechanism)?)
|
Ok(SASLServer::new(self.inner.rsasl.clone())
|
||||||
|
.start_suggested(mechanism)
|
||||||
|
.into_diagnostic()
|
||||||
|
.wrap_err("Failed to start a SASL authentication with the given mechanism")?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_available_mechs(&self) -> impl IntoIterator<Item = &Mechname> {
|
pub fn sess(&self) -> SASLServer<V> {
|
||||||
self.inner
|
SASLServer::new(self.inner.rsasl.clone())
|
||||||
.rsasl
|
|
||||||
.server_mech_list()
|
|
||||||
.into_iter()
|
|
||||||
.map(|m| m.mechanism)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
|
use crate::authorization::roles::Roles;
|
||||||
|
|
||||||
use crate::authorization::roles::{Roles};
|
|
||||||
use crate::Users;
|
use crate::Users;
|
||||||
|
|
||||||
|
|
||||||
pub mod permissions;
|
pub mod permissions;
|
||||||
pub mod roles;
|
pub mod roles;
|
||||||
|
|
||||||
@ -22,4 +19,4 @@ impl AuthorizationHandle {
|
|||||||
let user = self.users.get_user(uid.as_ref())?;
|
let user = self.users.get_user(uid.as_ref())?;
|
||||||
Some(user.userdata.roles.clone())
|
Some(user.userdata.roles.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
//! Access control logic
|
//! Access control logic
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::convert::{TryFrom, Into};
|
use std::convert::{Into, TryFrom};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
fn is_sep_char(c: char) -> bool {
|
fn is_sep_char(c: char) -> bool {
|
||||||
c == '.'
|
c == '.'
|
||||||
@ -20,7 +19,7 @@ pub struct PrivilegesBuf {
|
|||||||
/// Which permission is required to write parts of this thing
|
/// Which permission is required to write parts of this thing
|
||||||
pub write: PermissionBuf,
|
pub write: PermissionBuf,
|
||||||
/// Which permission is required to manage all parts of this thing
|
/// Which permission is required to manage all parts of this thing
|
||||||
pub manage: PermissionBuf
|
pub manage: PermissionBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||||
@ -39,13 +38,17 @@ impl PermissionBuf {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
/// Allocate an empty `PermissionBuf`
|
/// Allocate an empty `PermissionBuf`
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
PermissionBuf { inner: String::new() }
|
PermissionBuf {
|
||||||
|
inner: String::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
/// Allocate a `PermissionBuf` with the given capacity given to the internal [`String`]
|
/// Allocate a `PermissionBuf` with the given capacity given to the internal [`String`]
|
||||||
pub fn with_capacity(cap: usize) -> Self {
|
pub fn with_capacity(cap: usize) -> Self {
|
||||||
PermissionBuf { inner: String::with_capacity(cap) }
|
PermissionBuf {
|
||||||
|
inner: String::with_capacity(cap),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -59,7 +62,13 @@ impl PermissionBuf {
|
|||||||
|
|
||||||
pub fn _push(&mut self, perm: &Permission) {
|
pub fn _push(&mut self, perm: &Permission) {
|
||||||
// in general we always need a separator unless the last byte is one or the string is empty
|
// in general we always need a separator unless the last byte is one or the string is empty
|
||||||
let need_sep = self.inner.chars().rev().next().map(|c| !is_sep_char(c)).unwrap_or(false);
|
let need_sep = self
|
||||||
|
.inner
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.next()
|
||||||
|
.map(|c| !is_sep_char(c))
|
||||||
|
.unwrap_or(false);
|
||||||
if need_sep {
|
if need_sep {
|
||||||
self.inner.push('.')
|
self.inner.push('.')
|
||||||
}
|
}
|
||||||
@ -73,7 +82,9 @@ impl PermissionBuf {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_perm(perm: &Permission) -> Self {
|
pub fn from_perm(perm: &Permission) -> Self {
|
||||||
Self { inner: perm.as_str().to_string() }
|
Self {
|
||||||
|
inner: perm.as_str().to_string(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -119,7 +130,7 @@ impl fmt::Display for PermissionBuf {
|
|||||||
#[derive(PartialEq, Eq, Hash, Debug)]
|
#[derive(PartialEq, Eq, Hash, Debug)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
/// A borrowed permission string
|
/// A borrowed permission string
|
||||||
///
|
///
|
||||||
/// Permissions have total equality and partial ordering.
|
/// Permissions have total equality and partial ordering.
|
||||||
/// Specifically permissions on the same path in a tree can be compared for specificity.
|
/// Specifically permissions on the same path in a tree can be compared for specificity.
|
||||||
/// This means that ```(bffh.perm) > (bffh.perm.sub) == true```
|
/// This means that ```(bffh.perm) > (bffh.perm.sub) == true```
|
||||||
@ -141,7 +152,7 @@ impl Permission {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn iter(&self) -> std::str::Split<char> {
|
pub fn iter(&self) -> std::str::Split<char> {
|
||||||
self.0.split('.')
|
self.0.split('.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,12 +173,14 @@ impl PartialOrd for Permission {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match (l,r) {
|
match (l, r) {
|
||||||
(None, None) => Some(Ordering::Equal),
|
(None, None) => Some(Ordering::Equal),
|
||||||
(Some(_), None) => Some(Ordering::Less),
|
(Some(_), None) => Some(Ordering::Less),
|
||||||
(None, Some(_)) => Some(Ordering::Greater),
|
(None, Some(_)) => Some(Ordering::Greater),
|
||||||
(Some(_), Some(_)) => unreachable!("Broken contract in Permission::partial_cmp: sides \
|
(Some(_), Some(_)) => unreachable!(
|
||||||
should never be both Some!"),
|
"Broken contract in Permission::partial_cmp: sides \
|
||||||
|
should never be both Some!"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -183,7 +196,7 @@ impl AsRef<Permission> for Permission {
|
|||||||
#[serde(try_from = "String")]
|
#[serde(try_from = "String")]
|
||||||
#[serde(into = "String")]
|
#[serde(into = "String")]
|
||||||
pub enum PermRule {
|
pub enum PermRule {
|
||||||
/// The permission is precise,
|
/// The permission is precise,
|
||||||
///
|
///
|
||||||
/// i.e. `Base("bffh.perm")` grants bffh.perm but does not grant permission for bffh.perm.sub
|
/// i.e. `Base("bffh.perm")` grants bffh.perm but does not grant permission for bffh.perm.sub
|
||||||
Base(PermissionBuf),
|
Base(PermissionBuf),
|
||||||
@ -208,7 +221,7 @@ impl PermRule {
|
|||||||
pub fn match_perm<P: AsRef<Permission> + ?Sized>(&self, perm: &P) -> bool {
|
pub fn match_perm<P: AsRef<Permission> + ?Sized>(&self, perm: &P) -> bool {
|
||||||
match self {
|
match self {
|
||||||
PermRule::Base(ref base) => base.as_permission() == perm.as_ref(),
|
PermRule::Base(ref base) => base.as_permission() == perm.as_ref(),
|
||||||
PermRule::Children(ref parent) => parent.as_permission() > perm.as_ref() ,
|
PermRule::Children(ref parent) => parent.as_permission() > perm.as_ref(),
|
||||||
PermRule::Subtree(ref parent) => parent.as_permission() >= perm.as_ref(),
|
PermRule::Subtree(ref parent) => parent.as_permission() >= perm.as_ref(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,12 +230,9 @@ impl PermRule {
|
|||||||
impl fmt::Display for PermRule {
|
impl fmt::Display for PermRule {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
PermRule::Base(perm)
|
PermRule::Base(perm) => write!(f, "{}", perm),
|
||||||
=> write!(f, "{}", perm),
|
PermRule::Children(parent) => write!(f, "{}.+", parent),
|
||||||
PermRule::Children(parent)
|
PermRule::Subtree(parent) => write!(f, "{}.*", parent),
|
||||||
=> write!(f,"{}.+", parent),
|
|
||||||
PermRule::Subtree(parent)
|
|
||||||
=> write!(f,"{}.*", parent),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,7 +244,7 @@ impl Into<String> for PermRule {
|
|||||||
PermRule::Children(mut perm) => {
|
PermRule::Children(mut perm) => {
|
||||||
perm.push(Permission::new("+"));
|
perm.push(Permission::new("+"));
|
||||||
perm.into_string()
|
perm.into_string()
|
||||||
},
|
}
|
||||||
PermRule::Subtree(mut perm) => {
|
PermRule::Subtree(mut perm) => {
|
||||||
perm.push(Permission::new("+"));
|
perm.push(Permission::new("+"));
|
||||||
perm.into_string()
|
perm.into_string()
|
||||||
@ -252,15 +262,19 @@ impl TryFrom<String> for PermRule {
|
|||||||
if len <= 2 {
|
if len <= 2 {
|
||||||
Err("Input string for PermRule is too short")
|
Err("Input string for PermRule is too short")
|
||||||
} else {
|
} else {
|
||||||
match &input[len-2..len] {
|
match &input[len - 2..len] {
|
||||||
".+" => {
|
".+" => {
|
||||||
input.truncate(len-2);
|
input.truncate(len - 2);
|
||||||
Ok(PermRule::Children(PermissionBuf::from_string_unchecked(input)))
|
Ok(PermRule::Children(PermissionBuf::from_string_unchecked(
|
||||||
},
|
input,
|
||||||
|
)))
|
||||||
|
}
|
||||||
".*" => {
|
".*" => {
|
||||||
input.truncate(len-2);
|
input.truncate(len - 2);
|
||||||
Ok(PermRule::Subtree(PermissionBuf::from_string_unchecked(input)))
|
Ok(PermRule::Subtree(PermissionBuf::from_string_unchecked(
|
||||||
},
|
input,
|
||||||
|
)))
|
||||||
|
}
|
||||||
_ => Ok(PermRule::Base(PermissionBuf::from_string_unchecked(input))),
|
_ => Ok(PermRule::Base(PermissionBuf::from_string_unchecked(input))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -273,8 +287,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn permission_ord_test() {
|
fn permission_ord_test() {
|
||||||
assert!(PermissionBuf::from_string_unchecked("bffh.perm".to_string())
|
assert!(
|
||||||
> PermissionBuf::from_string_unchecked("bffh.perm.sub".to_string()));
|
PermissionBuf::from_string_unchecked("bffh.perm".to_string())
|
||||||
|
> PermissionBuf::from_string_unchecked("bffh.perm.sub".to_string())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -312,44 +328,24 @@ mod tests {
|
|||||||
assert!(rule.match_perm(&perm3));
|
assert!(rule.match_perm(&perm3));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn format_and_read_compatible() {
|
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
let testdata = vec![
|
|
||||||
("testrole", "testsource"),
|
|
||||||
("", "norole"),
|
|
||||||
("nosource", "")
|
|
||||||
].into_iter().map(|(n,s)| (n.to_string(), s.to_string()));
|
|
||||||
|
|
||||||
for (name, source) in testdata {
|
|
||||||
let role = RoleIdentifier { name, source };
|
|
||||||
|
|
||||||
let fmt_string = format!("{}", &role);
|
|
||||||
|
|
||||||
println!("{:?} is formatted: {}", &role, &fmt_string);
|
|
||||||
|
|
||||||
let parsed: RoleIdentifier = fmt_string.try_into().unwrap();
|
|
||||||
|
|
||||||
println!("Which parses into {:?}", &parsed);
|
|
||||||
|
|
||||||
assert_eq!(role, parsed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rules_from_string_test() {
|
fn rules_from_string_test() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PermRule::Base(PermissionBuf::from_string_unchecked("bffh.perm".to_string())),
|
PermRule::Base(PermissionBuf::from_string_unchecked(
|
||||||
|
"bffh.perm".to_string()
|
||||||
|
)),
|
||||||
PermRule::try_from("bffh.perm".to_string()).unwrap()
|
PermRule::try_from("bffh.perm".to_string()).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PermRule::Children(PermissionBuf::from_string_unchecked("bffh.perm".to_string())),
|
PermRule::Children(PermissionBuf::from_string_unchecked(
|
||||||
|
"bffh.perm".to_string()
|
||||||
|
)),
|
||||||
PermRule::try_from("bffh.perm.+".to_string()).unwrap()
|
PermRule::try_from("bffh.perm.+".to_string()).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PermRule::Subtree(PermissionBuf::from_string_unchecked("bffh.perm".to_string())),
|
PermRule::Subtree(PermissionBuf::from_string_unchecked(
|
||||||
|
"bffh.perm".to_string()
|
||||||
|
)),
|
||||||
PermRule::try_from("bffh.perm.*".to_string()).unwrap()
|
PermRule::try_from("bffh.perm.*".to_string()).unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
use crate::authorization::permissions::{PermRule, Permission};
|
||||||
|
use crate::users::db::UserData;
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use crate::authorization::permissions::{Permission, PermRule};
|
|
||||||
use crate::users::db::UserData;
|
|
||||||
|
|
||||||
static ROLES: OnceCell<HashMap<String, Role>> = OnceCell::new();
|
static ROLES: OnceCell<HashMap<String, Role>> = OnceCell::new();
|
||||||
|
|
||||||
@ -27,6 +27,9 @@ impl Roles {
|
|||||||
self.roles.get(roleid)
|
self.roles.get(roleid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn list(&self) -> impl Iterator<Item = &String> {
|
||||||
|
self.roles.keys()
|
||||||
|
}
|
||||||
|
|
||||||
/// Tally a role dependency tree into a set
|
/// Tally a role dependency tree into a set
|
||||||
///
|
///
|
||||||
@ -62,22 +65,26 @@ impl Roles {
|
|||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
fn permitted_tally(&self,
|
fn permitted_tally(
|
||||||
roles: &mut HashSet<String>,
|
&self,
|
||||||
role_id: &String,
|
roles: &mut HashSet<String>,
|
||||||
perm: &Permission
|
role_id: &String,
|
||||||
|
perm: &Permission,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
let _guard = tracing::debug_span!("tally", %role_id, perm=perm.as_str());
|
||||||
if let Some(role) = self.get(role_id) {
|
if let Some(role) = self.get(role_id) {
|
||||||
// Only check and tally parents of a role at the role itself if it's the first time we
|
// Only check and tally parents of a role at the role itself if it's the first time we
|
||||||
// see it
|
// see it
|
||||||
if !roles.contains(role_id) {
|
if !roles.contains(role_id) {
|
||||||
for perm_rule in role.permissions.iter() {
|
for perm_rule in role.permissions.iter() {
|
||||||
if perm_rule.match_perm(perm) {
|
if perm_rule.match_perm(perm) {
|
||||||
|
tracing::debug!("Permission granted by direct role");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for parent in role.parents.iter() {
|
for parent in role.parents.iter() {
|
||||||
if self.permitted_tally(roles, parent, perm) {
|
if self.permitted_tally(roles, parent, perm) {
|
||||||
|
tracing::debug!(%parent, "Permission granted by parent role");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,10 +93,13 @@ impl Roles {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing::trace!(%role_id, "Permission not granted by role");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_permitted(&self, user: &UserData, perm: impl AsRef<Permission>) -> bool {
|
pub fn is_permitted(&self, user: &UserData, perm: impl AsRef<Permission>) -> bool {
|
||||||
|
let perm = perm.as_ref();
|
||||||
|
tracing::debug!(perm = perm.as_str(), "Checking permission");
|
||||||
let mut seen = HashSet::new();
|
let mut seen = HashSet::new();
|
||||||
for role_id in user.roles.iter() {
|
for role_id in user.roles.iter() {
|
||||||
if self.permitted_tally(&mut seen, role_id, perm.as_ref()) {
|
if self.permitted_tally(&mut seen, role_id, perm.as_ref()) {
|
||||||
@ -130,7 +140,10 @@ pub struct Role {
|
|||||||
|
|
||||||
impl Role {
|
impl Role {
|
||||||
pub fn new(parents: Vec<String>, permissions: Vec<PermRule>) -> Self {
|
pub fn new(parents: Vec<String>, permissions: Vec<PermRule>) -> Self {
|
||||||
Self { parents, permissions }
|
Self {
|
||||||
|
parents,
|
||||||
|
permissions,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,4 +170,4 @@ impl fmt::Display for Role {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
use capnp::capability::Promise;
|
use capnp::capability::Promise;
|
||||||
use capnp::Error;
|
use capnp::Error;
|
||||||
use capnp_rpc::pry;
|
use capnp_rpc::pry;
|
||||||
use rsasl::property::AuthId;
|
use rsasl::mechname::Mechname;
|
||||||
use rsasl::session::{Session, Step};
|
use rsasl::prelude::State as SaslState;
|
||||||
use std::io::Cursor;
|
use rsasl::prelude::{MessageSent, Session};
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::{Formatter, Write};
|
||||||
|
use tracing::Span;
|
||||||
|
|
||||||
|
use crate::authentication::V;
|
||||||
use crate::capnp::session::APISession;
|
use crate::capnp::session::APISession;
|
||||||
use crate::session::SessionManager;
|
use crate::session::SessionManager;
|
||||||
use api::authenticationsystem_capnp::authentication::{
|
use api::authenticationsystem_capnp::authentication::{
|
||||||
@ -13,19 +16,46 @@ use api::authenticationsystem_capnp::authentication::{
|
|||||||
};
|
};
|
||||||
use api::authenticationsystem_capnp::{response, response::Error as ErrorCode};
|
use api::authenticationsystem_capnp::{response, response::Error as ErrorCode};
|
||||||
|
|
||||||
|
const TARGET: &str = "bffh::api::authenticationsystem";
|
||||||
|
|
||||||
pub struct Authentication {
|
pub struct Authentication {
|
||||||
|
span: Span,
|
||||||
state: State,
|
state: State,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Authentication {
|
impl Authentication {
|
||||||
pub fn new(session: Session, sessionmanager: SessionManager) -> Self {
|
pub fn new(
|
||||||
|
parent: &Span,
|
||||||
|
mechanism: &Mechname, /* TODO: this is stored in session as well, get it out of there. */
|
||||||
|
session: Session<V>,
|
||||||
|
sessionmanager: SessionManager,
|
||||||
|
) -> Self {
|
||||||
|
let span = tracing::info_span!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: parent,
|
||||||
|
"Authentication",
|
||||||
|
mechanism = mechanism.as_str()
|
||||||
|
);
|
||||||
|
tracing::trace!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: &span,
|
||||||
|
"constructing valid authentication system"
|
||||||
|
);
|
||||||
Self {
|
Self {
|
||||||
|
span,
|
||||||
state: State::Running(session, sessionmanager),
|
state: State::Running(session, sessionmanager),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invalid_mechanism() -> Self {
|
pub fn invalid_mechanism() -> Self {
|
||||||
|
let span = tracing::info_span!(target: TARGET, "Authentication",);
|
||||||
|
tracing::trace!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: &span,
|
||||||
|
"constructing invalid mechanism authentication system"
|
||||||
|
);
|
||||||
Self {
|
Self {
|
||||||
|
span,
|
||||||
state: State::InvalidMechanism,
|
state: State::InvalidMechanism,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,59 +75,124 @@ impl Authentication {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Authentication {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("Authentication(")?;
|
||||||
|
match &self.state {
|
||||||
|
State::InvalidMechanism => f.write_str("invalid mechanism")?,
|
||||||
|
State::Finished => f.write_str("finished")?,
|
||||||
|
State::Aborted => f.write_str("aborted")?,
|
||||||
|
State::Running(_, _) => f.write_str("running")?,
|
||||||
|
}
|
||||||
|
f.write_char(')')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
InvalidMechanism,
|
InvalidMechanism,
|
||||||
Finished,
|
Finished,
|
||||||
Aborted,
|
Aborted,
|
||||||
Running(Session, SessionManager),
|
Running(Session<V>, SessionManager),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthenticationSystem for Authentication {
|
impl AuthenticationSystem for Authentication {
|
||||||
fn step(&mut self, params: StepParams, mut results: StepResults) -> Promise<(), Error> {
|
fn step(&mut self, params: StepParams, mut results: StepResults) -> Promise<(), Error> {
|
||||||
let span = tracing::trace_span!("step");
|
let _guard = self.span.enter();
|
||||||
let _guard = span.enter();
|
let _span = tracing::trace_span!(target: TARGET, "step",).entered();
|
||||||
|
|
||||||
|
tracing::trace!(params.data = "<authentication data>", "method call");
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct Response {
|
||||||
|
union_field: &'static str,
|
||||||
|
}
|
||||||
|
impl fmt::Display for Response {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("Response(")?;
|
||||||
|
f.write_str(self.union_field)?;
|
||||||
|
f.write_char(')')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let response;
|
||||||
|
|
||||||
let mut builder = results.get();
|
let mut builder = results.get();
|
||||||
if let State::Running(mut session, manager) =
|
if let State::Running(mut session, manager) =
|
||||||
std::mem::replace(&mut self.state, State::Aborted)
|
std::mem::replace(&mut self.state, State::Aborted)
|
||||||
{
|
{
|
||||||
let data: &[u8] = pry!(pry!(params.get()).get_data());
|
let data: &[u8] = pry!(pry!(params.get()).get_data());
|
||||||
let mut out = Cursor::new(Vec::new());
|
|
||||||
|
let mut out = Vec::new();
|
||||||
match session.step(Some(data), &mut out) {
|
match session.step(Some(data), &mut out) {
|
||||||
Ok(Step::Done(data)) => {
|
Ok(SaslState::Finished(sent)) => {
|
||||||
self.state = State::Finished;
|
self.state = State::Finished;
|
||||||
|
|
||||||
let uid = pry!(session.get_property::<AuthId>().ok_or(capnp::Error::failed(
|
if let Some(user) = session.validation() {
|
||||||
"Authentication didn't provide an authid as required".to_string()
|
let session = manager.open(&self.span, user);
|
||||||
)));
|
response = Response {
|
||||||
let session = pry!(manager.open(uid.as_ref()).ok_or(capnp::Error::failed(
|
union_field: "successful",
|
||||||
"Failed to lookup the given user".to_string()
|
};
|
||||||
)));
|
|
||||||
|
|
||||||
let mut builder = builder.init_successful();
|
let mut builder = builder.init_successful();
|
||||||
if data.is_some() {
|
if sent == MessageSent::Yes {
|
||||||
builder.set_additional_data(out.into_inner().as_slice());
|
builder.set_additional_data(out.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
APISession::build(session, builder)
|
||||||
|
} else {
|
||||||
|
let mut builder = builder.init_failed();
|
||||||
|
builder.set_code(ErrorCode::InvalidCredentials);
|
||||||
|
|
||||||
|
response = Response {
|
||||||
|
union_field: "error",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
APISession::build(session, builder)
|
|
||||||
}
|
}
|
||||||
Ok(Step::NeedsMore(_)) => {
|
Ok(SaslState::Running) => {
|
||||||
self.state = State::Running(session, manager);
|
self.state = State::Running(session, manager);
|
||||||
builder.set_challenge(out.into_inner().as_slice());
|
builder.set_challenge(out.as_slice());
|
||||||
|
|
||||||
|
response = Response {
|
||||||
|
union_field: "challenge",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
self.state = State::Aborted;
|
self.state = State::Aborted;
|
||||||
self.build_error(builder);
|
self.build_error(builder);
|
||||||
|
|
||||||
|
response = Response {
|
||||||
|
union_field: "error",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.build_error(builder);
|
self.build_error(builder);
|
||||||
|
response = Response {
|
||||||
|
union_field: "error",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing::trace!(
|
||||||
|
results = %response,
|
||||||
|
"method return"
|
||||||
|
);
|
||||||
|
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn abort(&mut self, _: AbortParams, _: AbortResults) -> Promise<(), Error> {
|
fn abort(&mut self, _: AbortParams, _: AbortResults) -> Promise<(), Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: &self.span,
|
||||||
|
"abort",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
|
||||||
|
tracing::trace!("method call");
|
||||||
|
|
||||||
self.state = State::Aborted;
|
self.state = State::Aborted;
|
||||||
|
|
||||||
|
tracing::trace!("method return");
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use std::fmt::Formatter;
|
|||||||
use std::net::ToSocketAddrs;
|
use std::net::ToSocketAddrs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::config::deser_option;
|
use crate::config::deser_option;
|
||||||
|
|
||||||
@ -14,7 +14,11 @@ use crate::config::deser_option;
|
|||||||
pub struct Listen {
|
pub struct Listen {
|
||||||
pub address: String,
|
pub address: String,
|
||||||
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "deser_option")]
|
#[serde(
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Option::is_none",
|
||||||
|
deserialize_with = "deser_option"
|
||||||
|
)]
|
||||||
pub port: Option<u16>,
|
pub port: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,4 +60,4 @@ pub struct TlsListen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The default port in the non-assignable i.e. free-use area
|
// The default port in the non-assignable i.e. free-use area
|
||||||
pub const DEFAULT_PORT: u16 = 59661;
|
pub const DEFAULT_PORT: u16 = 59661;
|
||||||
|
@ -1,28 +1,37 @@
|
|||||||
use std::net::SocketAddr;
|
|
||||||
pub use api::connection_capnp::bootstrap::Client;
|
|
||||||
use api::connection_capnp::bootstrap;
|
use api::connection_capnp::bootstrap;
|
||||||
|
pub use api::connection_capnp::bootstrap::Client;
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::{Formatter, Write};
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use capnp::capability::Promise;
|
|
||||||
use capnp_rpc::pry;
|
|
||||||
use rsasl::mechname::Mechname;
|
|
||||||
use crate::authentication::AuthenticationHandle;
|
use crate::authentication::AuthenticationHandle;
|
||||||
use crate::capnp::authenticationsystem::Authentication;
|
use crate::capnp::authenticationsystem::Authentication;
|
||||||
use crate::session::SessionManager;
|
use crate::session::SessionManager;
|
||||||
|
use capnp::capability::Promise;
|
||||||
|
use capnp_rpc::pry;
|
||||||
|
use rsasl::mechname::Mechname;
|
||||||
|
use tracing::Span;
|
||||||
|
|
||||||
/// Cap'n Proto API Handler
|
/// Cap'n Proto API Handler
|
||||||
pub struct BootCap {
|
pub struct BootCap {
|
||||||
peer_addr: SocketAddr,
|
peer_addr: SocketAddr,
|
||||||
authentication: AuthenticationHandle,
|
authentication: AuthenticationHandle,
|
||||||
sessionmanager: SessionManager,
|
sessionmanager: SessionManager,
|
||||||
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BootCap {
|
impl BootCap {
|
||||||
pub fn new(peer_addr: SocketAddr, authentication: AuthenticationHandle, sessionmanager: SessionManager) -> Self {
|
pub fn new(
|
||||||
tracing::trace!(%peer_addr, "bootstrapping RPC");
|
peer_addr: SocketAddr,
|
||||||
|
authentication: AuthenticationHandle,
|
||||||
|
sessionmanager: SessionManager,
|
||||||
|
span: Span,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
peer_addr,
|
peer_addr,
|
||||||
authentication,
|
authentication,
|
||||||
sessionmanager,
|
sessionmanager,
|
||||||
|
span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,8 +42,14 @@ impl bootstrap::Server for BootCap {
|
|||||||
_: bootstrap::GetAPIVersionParams,
|
_: bootstrap::GetAPIVersionParams,
|
||||||
_: bootstrap::GetAPIVersionResults,
|
_: bootstrap::GetAPIVersionResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
let span = tracing::trace_span!("get_api_version", peer_addr=%self.peer_addr);
|
let _guard = self.span.enter();
|
||||||
let _guard = span.enter();
|
let _span = tracing::trace_span!(
|
||||||
|
target: "bffh::api",
|
||||||
|
"Bootstrap",
|
||||||
|
method = "getAPIVersion",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
tracing::trace!("method call");
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,11 +58,24 @@ impl bootstrap::Server for BootCap {
|
|||||||
_: bootstrap::GetServerReleaseParams,
|
_: bootstrap::GetServerReleaseParams,
|
||||||
mut result: bootstrap::GetServerReleaseResults,
|
mut result: bootstrap::GetServerReleaseResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
let span = tracing::trace_span!("get_server_release", peer_addr=%self.peer_addr);
|
let _guard = self.span.enter();
|
||||||
let _guard = span.enter();
|
let _span = tracing::trace_span!(
|
||||||
|
target: "bffh::api",
|
||||||
|
"Bootstrap",
|
||||||
|
method = "getServerRelease",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
tracing::trace!("method call");
|
||||||
|
|
||||||
let mut builder = result.get();
|
let mut builder = result.get();
|
||||||
builder.set_name("bffhd");
|
builder.set_name("bffhd");
|
||||||
builder.set_release(crate::RELEASE_STRING);
|
builder.set_release(crate::env::VERSION);
|
||||||
|
|
||||||
|
tracing::trace!(
|
||||||
|
results.name = "bffhd",
|
||||||
|
results.release = crate::env::VERSION,
|
||||||
|
"method return"
|
||||||
|
);
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,21 +84,49 @@ impl bootstrap::Server for BootCap {
|
|||||||
_params: bootstrap::MechanismsParams,
|
_params: bootstrap::MechanismsParams,
|
||||||
mut result: bootstrap::MechanismsResults,
|
mut result: bootstrap::MechanismsResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
let span = tracing::trace_span!("mechanisms", peer_addr=%self.peer_addr);
|
let _guard = self.span.enter();
|
||||||
let _guard = span.enter();
|
let _span = tracing::trace_span!(
|
||||||
|
target: "bffh::api",
|
||||||
tracing::trace!("mechanisms");
|
"mechanisms",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
tracing::trace!(target: "bffh::api", "method call");
|
||||||
|
|
||||||
let builder = result.get();
|
let builder = result.get();
|
||||||
let mechs: Vec<_> = self.authentication.list_available_mechs()
|
let mechs: Vec<_> = self
|
||||||
|
.authentication
|
||||||
|
.sess()
|
||||||
|
.get_available()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|m| m.as_str())
|
.map(|m| m.mechanism.as_str())
|
||||||
.collect();
|
.collect();
|
||||||
let mut mechbuilder = builder.init_mechs(mechs.len() as u32);
|
let mut mechbuilder = builder.init_mechs(mechs.len() as u32);
|
||||||
for (i,m) in mechs.iter().enumerate() {
|
for (i, m) in mechs.iter().enumerate() {
|
||||||
mechbuilder.set(i as u32, m);
|
mechbuilder.set(i as u32, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DisMechs<'a>(Vec<&'a str>);
|
||||||
|
impl fmt::Display for DisMechs<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_char('[')?;
|
||||||
|
let mut first = true;
|
||||||
|
for mechanism in self.0.iter() {
|
||||||
|
if first {
|
||||||
|
first = false;
|
||||||
|
f.write_str(mechanism)?;
|
||||||
|
} else {
|
||||||
|
f.write_str(" ,")?;
|
||||||
|
f.write_str(mechanism)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.write_char(']')?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tracing::trace!(
|
||||||
|
results.mechs = %DisMechs(mechs),
|
||||||
|
"method return"
|
||||||
|
);
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,18 +135,22 @@ impl bootstrap::Server for BootCap {
|
|||||||
params: bootstrap::CreateSessionParams,
|
params: bootstrap::CreateSessionParams,
|
||||||
mut result: bootstrap::CreateSessionResults,
|
mut result: bootstrap::CreateSessionResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
let span = tracing::trace_span!("create_session", peer_addr=%self.peer_addr);
|
let _guard = self.span.enter();
|
||||||
let _guard = span.enter();
|
let _span = tracing::trace_span!(
|
||||||
|
target: "bffh::api",
|
||||||
|
"createSession",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
|
||||||
let params = pry!(params.get());
|
let params = pry!(params.get());
|
||||||
let mechanism: &str = pry!(params.get_mechanism());
|
let mechanism: &str = pry!(params.get_mechanism());
|
||||||
|
|
||||||
tracing::trace!(mechanism);
|
tracing::trace!(params.mechanism = mechanism, "method call");
|
||||||
|
|
||||||
let mechname = Mechname::new(mechanism.as_bytes());
|
let mechname = Mechname::parse(mechanism.as_bytes());
|
||||||
let auth = if let Ok(mechname) = mechname {
|
let auth = if let Ok(mechname) = mechname {
|
||||||
if let Ok(session) = self.authentication.start(mechname) {
|
if let Ok(session) = self.authentication.start(mechname) {
|
||||||
Authentication::new(session, self.sessionmanager.clone())
|
Authentication::new(&self.span, mechname, session, self.sessionmanager.clone())
|
||||||
} else {
|
} else {
|
||||||
Authentication::invalid_mechanism()
|
Authentication::invalid_mechanism()
|
||||||
}
|
}
|
||||||
@ -98,6 +158,11 @@ impl bootstrap::Server for BootCap {
|
|||||||
Authentication::invalid_mechanism()
|
Authentication::invalid_mechanism()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
tracing::trace!(
|
||||||
|
results.authentication = %auth,
|
||||||
|
"method return"
|
||||||
|
);
|
||||||
|
|
||||||
let mut builder = result.get();
|
let mut builder = result.get();
|
||||||
builder.set_authentication(capnp_rpc::new_client(auth));
|
builder.set_authentication(capnp_rpc::new_client(auth));
|
||||||
|
|
||||||
|
@ -211,7 +211,6 @@ impl ManageServer for Machine {
|
|||||||
mut result: manage::GetMachineInfoExtendedResults,
|
mut result: manage::GetMachineInfoExtendedResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
let mut builder = result.get();
|
let mut builder = result.get();
|
||||||
let user = User::new_self(self.session.clone());
|
|
||||||
User::build_optional(
|
User::build_optional(
|
||||||
&self.session,
|
&self.session,
|
||||||
self.resource.get_current_user(),
|
self.resource.get_current_user(),
|
||||||
|
@ -1,25 +1,35 @@
|
|||||||
|
use crate::capnp::machine::Machine;
|
||||||
|
use crate::resources::search::ResourcesHandle;
|
||||||
|
use crate::resources::Resource;
|
||||||
use crate::session::SessionHandle;
|
use crate::session::SessionHandle;
|
||||||
use api::machinesystem_capnp::machine_system::{
|
use crate::RESOURCES;
|
||||||
info,
|
use api::machinesystem_capnp::machine_system::info;
|
||||||
};
|
|
||||||
use capnp::capability::Promise;
|
use capnp::capability::Promise;
|
||||||
use capnp_rpc::pry;
|
use capnp_rpc::pry;
|
||||||
use crate::capnp::machine::Machine;
|
use tracing::Span;
|
||||||
use crate::RESOURCES;
|
|
||||||
use crate::resources::Resource;
|
const TARGET: &str = "bffh::api::machinesystem";
|
||||||
use crate::resources::search::ResourcesHandle;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Machines {
|
pub struct Machines {
|
||||||
|
span: Span,
|
||||||
session: SessionHandle,
|
session: SessionHandle,
|
||||||
resources: ResourcesHandle,
|
resources: ResourcesHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Machines {
|
impl Machines {
|
||||||
pub fn new(session: SessionHandle) -> Self {
|
pub fn new(session: SessionHandle) -> Self {
|
||||||
|
let span = tracing::info_span!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: &session.span,
|
||||||
|
"MachineSystem",
|
||||||
|
);
|
||||||
// FIXME no unwrap bad
|
// FIXME no unwrap bad
|
||||||
Self { session, resources: RESOURCES.get().unwrap().clone() }
|
Self {
|
||||||
|
span,
|
||||||
|
session,
|
||||||
|
resources: RESOURCES.get().unwrap().clone(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +39,19 @@ impl info::Server for Machines {
|
|||||||
_: info::GetMachineListParams,
|
_: info::GetMachineListParams,
|
||||||
mut result: info::GetMachineListResults,
|
mut result: info::GetMachineListResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
let machine_list: Vec<(usize, &Resource)> = self.resources.list_all()
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: &self.span,
|
||||||
|
"getMachineList",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
|
||||||
|
tracing::trace!("method call");
|
||||||
|
|
||||||
|
let machine_list: Vec<(usize, &Resource)> = self
|
||||||
|
.resources
|
||||||
|
.list_all()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|resource| resource.visible(&self.session))
|
.filter(|resource| resource.visible(&self.session))
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@ -41,6 +63,9 @@ impl info::Server for Machines {
|
|||||||
Machine::build(self.session.clone(), resource, mbuilder);
|
Machine::build(self.session.clone(), resource, mbuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: indicate result?
|
||||||
|
tracing::trace!("method return");
|
||||||
|
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,12 +74,25 @@ impl info::Server for Machines {
|
|||||||
params: info::GetMachineParams,
|
params: info::GetMachineParams,
|
||||||
mut result: info::GetMachineResults,
|
mut result: info::GetMachineResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: &self.span,
|
||||||
|
"getMachine",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
|
||||||
let params = pry!(params.get());
|
let params = pry!(params.get());
|
||||||
let id = pry!(params.get_id());
|
let id = pry!(params.get_id());
|
||||||
|
|
||||||
|
tracing::trace!(params.id = id, "method call");
|
||||||
|
|
||||||
if let Some(resource) = self.resources.get_by_id(id) {
|
if let Some(resource) = self.resources.get_by_id(id) {
|
||||||
|
tracing::trace!(results = "Just", results.inner = id, "method return");
|
||||||
let builder = result.get();
|
let builder = result.get();
|
||||||
Machine::optional_build(self.session.clone(), resource.clone(), builder);
|
Machine::optional_build(self.session.clone(), resource.clone(), builder);
|
||||||
|
} else {
|
||||||
|
tracing::trace!(results = "Nothing", "method return");
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
@ -65,12 +103,29 @@ impl info::Server for Machines {
|
|||||||
params: info::GetMachineURNParams,
|
params: info::GetMachineURNParams,
|
||||||
mut result: info::GetMachineURNResults,
|
mut result: info::GetMachineURNResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(
|
||||||
|
target: TARGET,
|
||||||
|
parent: &self.span,
|
||||||
|
"getMachineURN",
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
|
||||||
let params = pry!(params.get());
|
let params = pry!(params.get());
|
||||||
let urn = pry!(params.get_urn());
|
let urn = pry!(params.get_urn());
|
||||||
|
|
||||||
|
tracing::trace!(params.urn = urn, "method call");
|
||||||
|
|
||||||
if let Some(resource) = self.resources.get_by_urn(urn) {
|
if let Some(resource) = self.resources.get_by_urn(urn) {
|
||||||
|
tracing::trace!(
|
||||||
|
results = "Just",
|
||||||
|
results.inner = resource.get_id(),
|
||||||
|
"method return"
|
||||||
|
);
|
||||||
let builder = result.get();
|
let builder = result.get();
|
||||||
Machine::optional_build(self.session.clone(), resource.clone(), builder);
|
Machine::optional_build(self.session.clone(), resource.clone(), builder);
|
||||||
|
} else {
|
||||||
|
tracing::trace!(results = "Nothing", "method return");
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
|
use miette::Diagnostic;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use async_net::TcpListener;
|
use async_net::TcpListener;
|
||||||
|
|
||||||
|
|
||||||
use capnp_rpc::rpc_twoparty_capnp::Side;
|
use capnp_rpc::rpc_twoparty_capnp::Side;
|
||||||
use capnp_rpc::twoparty::VatNetwork;
|
use capnp_rpc::twoparty::VatNetwork;
|
||||||
use capnp_rpc::RpcSystem;
|
use capnp_rpc::RpcSystem;
|
||||||
use executor::prelude::Executor;
|
use executor::prelude::{Executor, SupervisionRegistry};
|
||||||
use futures_rustls::server::TlsStream;
|
use futures_rustls::server::TlsStream;
|
||||||
use futures_rustls::TlsAcceptor;
|
use futures_rustls::TlsAcceptor;
|
||||||
use futures_util::stream::FuturesUnordered;
|
use futures_util::stream::FuturesUnordered;
|
||||||
use futures_util::{stream, AsyncRead, AsyncWrite, FutureExt, StreamExt};
|
use futures_util::{stream, AsyncRead, AsyncWrite, StreamExt};
|
||||||
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use std::net::SocketAddr;
|
use std::net::{IpAddr, SocketAddr};
|
||||||
|
|
||||||
use crate::authentication::AuthenticationHandle;
|
use crate::authentication::AuthenticationHandle;
|
||||||
use crate::session::SessionManager;
|
use crate::session::SessionManager;
|
||||||
@ -38,6 +39,10 @@ pub struct APIServer {
|
|||||||
authentication: AuthenticationHandle,
|
authentication: AuthenticationHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error, Diagnostic)]
|
||||||
|
#[error("Reached Void error, this should not be possible")]
|
||||||
|
pub enum Error {}
|
||||||
|
|
||||||
impl APIServer {
|
impl APIServer {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
executor: Executor<'static>,
|
executor: Executor<'static>,
|
||||||
@ -61,7 +66,7 @@ impl APIServer {
|
|||||||
acceptor: TlsAcceptor,
|
acceptor: TlsAcceptor,
|
||||||
sessionmanager: SessionManager,
|
sessionmanager: SessionManager,
|
||||||
authentication: AuthenticationHandle,
|
authentication: AuthenticationHandle,
|
||||||
) -> anyhow::Result<Self> {
|
) -> Result<Self, Error> {
|
||||||
let span = tracing::info_span!("binding API listen sockets");
|
let span = tracing::info_span!("binding API listen sockets");
|
||||||
let _guard = span.enter();
|
let _guard = span.enter();
|
||||||
|
|
||||||
@ -69,9 +74,7 @@ impl APIServer {
|
|||||||
|
|
||||||
listens
|
listens
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| async move {
|
.map(|a| async move { (async_net::resolve(a.to_tuple()).await, a) })
|
||||||
(async_net::resolve(a.to_tuple()).await, a)
|
|
||||||
})
|
|
||||||
.collect::<FuturesUnordered<_>>()
|
.collect::<FuturesUnordered<_>>()
|
||||||
.filter_map(|(res, addr)| async move {
|
.filter_map(|(res, addr)| async move {
|
||||||
match res {
|
match res {
|
||||||
@ -111,7 +114,13 @@ impl APIServer {
|
|||||||
tracing::warn!("No usable listen addresses configured for the API server!");
|
tracing::warn!("No usable listen addresses configured for the API server!");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self::new(executor, sockets, acceptor, sessionmanager, authentication))
|
Ok(Self::new(
|
||||||
|
executor,
|
||||||
|
sockets,
|
||||||
|
acceptor,
|
||||||
|
sessionmanager,
|
||||||
|
authentication,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_until(self, stop: impl Future) {
|
pub async fn handle_until(self, stop: impl Future) {
|
||||||
@ -129,10 +138,11 @@ impl APIServer {
|
|||||||
} else {
|
} else {
|
||||||
tracing::error!(?stream, "failing a TCP connection with no peer addr");
|
tracing::error!(?stream, "failing a TCP connection with no peer addr");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => tracing::warn!("Failed to accept stream: {}", e),
|
Err(e) => tracing::warn!("Failed to accept stream: {}", e),
|
||||||
}
|
}
|
||||||
}).await;
|
})
|
||||||
|
.await;
|
||||||
tracing::info!("closing down API handler");
|
tracing::info!("closing down API handler");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,24 +151,58 @@ impl APIServer {
|
|||||||
peer_addr: SocketAddr,
|
peer_addr: SocketAddr,
|
||||||
stream: impl Future<Output = io::Result<TlsStream<IO>>>,
|
stream: impl Future<Output = io::Result<TlsStream<IO>>>,
|
||||||
) {
|
) {
|
||||||
tracing::debug!("handling new API connection");
|
let span = tracing::trace_span!("api.handle");
|
||||||
|
let _guard = span.enter();
|
||||||
|
|
||||||
|
struct Peer {
|
||||||
|
ip: IpAddr,
|
||||||
|
port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
let peer = Peer {
|
||||||
|
ip: peer_addr.ip(),
|
||||||
|
port: peer_addr.port(),
|
||||||
|
};
|
||||||
|
tracing::debug!(
|
||||||
|
%peer.ip,
|
||||||
|
peer.port,
|
||||||
|
"spawning api handler"
|
||||||
|
);
|
||||||
|
|
||||||
|
let connection_span = tracing::info_span!(
|
||||||
|
target: "bffh::api",
|
||||||
|
"connection",
|
||||||
|
%peer.ip,
|
||||||
|
peer.port,
|
||||||
|
);
|
||||||
let f = async move {
|
let f = async move {
|
||||||
|
tracing::trace!(parent: &connection_span, "starting tls exchange");
|
||||||
let stream = match stream.await {
|
let stream = match stream.await {
|
||||||
Ok(stream) => stream,
|
Ok(stream) => stream,
|
||||||
Err(e) => {
|
Err(error) => {
|
||||||
tracing::error!("TLS handshake failed: {}", e);
|
tracing::error!(parent: &connection_span, %error, "TLS handshake failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let (rx, tx) = futures_lite::io::split(stream);
|
let (rx, tx) = futures_lite::io::split(stream);
|
||||||
let vat = VatNetwork::new(rx, tx, Side::Server, Default::default());
|
let vat = VatNetwork::new(rx, tx, Side::Server, Default::default());
|
||||||
|
|
||||||
let bootstrap: connection::Client = capnp_rpc::new_client(connection::BootCap::new(peer_addr, self.authentication.clone(), self.sessionmanager.clone()));
|
let bootstrap: connection::Client = capnp_rpc::new_client(connection::BootCap::new(
|
||||||
|
peer_addr,
|
||||||
|
self.authentication.clone(),
|
||||||
|
self.sessionmanager.clone(),
|
||||||
|
connection_span.clone(),
|
||||||
|
));
|
||||||
|
|
||||||
if let Err(e) = RpcSystem::new(Box::new(vat), Some(bootstrap.client)).await {
|
if let Err(error) = RpcSystem::new(Box::new(vat), Some(bootstrap.client)).await {
|
||||||
tracing::error!("Error during RPC handling: {}", e);
|
tracing::error!(
|
||||||
|
parent: &connection_span,
|
||||||
|
%error,
|
||||||
|
"error occured during rpc handling",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.executor.spawn_local(f);
|
let cgroup = SupervisionRegistry::with(SupervisionRegistry::new_group);
|
||||||
|
self.executor.spawn_local_cgroup(f, cgroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,48 @@
|
|||||||
use api::permissionsystem_capnp::permission_system::info::Server as PermissionSystem;
|
use crate::Roles;
|
||||||
|
use api::permissionsystem_capnp::permission_system::info::{
|
||||||
|
GetRoleListParams, GetRoleListResults, Server as PermissionSystem,
|
||||||
|
};
|
||||||
|
use capnp::capability::Promise;
|
||||||
|
use capnp::Error;
|
||||||
|
use tracing::Span;
|
||||||
|
|
||||||
use crate::session::SessionHandle;
|
use crate::session::SessionHandle;
|
||||||
|
|
||||||
pub struct Permissions;
|
const TARGET: &str = "bffh::api::permissionsystem";
|
||||||
|
|
||||||
|
pub struct Permissions {
|
||||||
|
span: Span,
|
||||||
|
roles: Roles,
|
||||||
|
}
|
||||||
|
|
||||||
impl Permissions {
|
impl Permissions {
|
||||||
pub fn new(_session: SessionHandle) -> Self {
|
pub fn new(session: SessionHandle) -> Self {
|
||||||
Self
|
let span = tracing::info_span!(target: TARGET, "PermissionSystem",);
|
||||||
|
Self {
|
||||||
|
span,
|
||||||
|
roles: session.roles,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PermissionSystem for Permissions {
|
impl PermissionSystem for Permissions {
|
||||||
|
fn get_role_list(
|
||||||
|
&mut self,
|
||||||
|
_: GetRoleListParams,
|
||||||
|
mut results: GetRoleListResults,
|
||||||
|
) -> Promise<(), Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "getRoleList",).entered();
|
||||||
|
|
||||||
}
|
tracing::trace!("method call");
|
||||||
|
let roles = self.roles.list().collect::<Vec<&String>>();
|
||||||
|
let builder = results.get();
|
||||||
|
let mut b = builder.init_role_list(roles.len() as u32);
|
||||||
|
for (i, role) in roles.into_iter().enumerate() {
|
||||||
|
let mut role_builder = b.reborrow().get(i as u32);
|
||||||
|
role_builder.set_name(role);
|
||||||
|
}
|
||||||
|
tracing::trace!("method return");
|
||||||
|
Promise::ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
use api::authenticationsystem_capnp::response::successful::Builder;
|
|
||||||
use crate::authorization::permissions::Permission;
|
use crate::authorization::permissions::Permission;
|
||||||
|
use api::authenticationsystem_capnp::response::successful::Builder;
|
||||||
|
|
||||||
use crate::capnp::machinesystem::Machines;
|
use crate::capnp::machinesystem::Machines;
|
||||||
use crate::capnp::permissionsystem::Permissions;
|
use crate::capnp::permissionsystem::Permissions;
|
||||||
use crate::capnp::user_system::Users;
|
use crate::capnp::user_system::Users;
|
||||||
use crate::session::{SessionHandle};
|
use crate::session::SessionHandle;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct APISession;
|
pub struct APISession;
|
||||||
@ -39,4 +37,4 @@ impl APISession {
|
|||||||
b.set_info(capnp_rpc::new_client(Permissions::new(session)));
|
b.set_info(capnp_rpc::new_client(Permissions::new(session)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,38 @@
|
|||||||
use capnp::capability::Promise;
|
|
||||||
use capnp_rpc::pry;
|
|
||||||
use crate::session::SessionHandle;
|
|
||||||
use api::user_capnp::user::{admin, info, manage, self};
|
|
||||||
use api::general_capnp::optional;
|
|
||||||
use crate::authorization::permissions::Permission;
|
use crate::authorization::permissions::Permission;
|
||||||
|
use crate::session::SessionHandle;
|
||||||
use crate::users::{db, UserRef};
|
use crate::users::{db, UserRef};
|
||||||
|
use crate::CONFIG;
|
||||||
|
use api::general_capnp::optional;
|
||||||
|
use api::user_capnp::user::card_d_e_s_fire_e_v2::{
|
||||||
|
BindParams, BindResults, GenCardTokenParams, GenCardTokenResults, GetMetaInfoParams,
|
||||||
|
GetMetaInfoResults, GetSpaceInfoParams, GetSpaceInfoResults, GetTokenListParams,
|
||||||
|
GetTokenListResults, UnbindParams, UnbindResults,
|
||||||
|
};
|
||||||
|
use api::user_capnp::user::{self, admin, card_d_e_s_fire_e_v2, info, manage};
|
||||||
|
use capnp::capability::Promise;
|
||||||
|
use capnp::Error;
|
||||||
|
use capnp_rpc::pry;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::io::Write;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
const TARGET: &str = "bffh::api::user";
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
|
span: tracing::Span,
|
||||||
session: SessionHandle,
|
session: SessionHandle,
|
||||||
user: UserRef,
|
user: UserRef,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
pub fn new(session: SessionHandle, user: UserRef) -> Self {
|
pub fn new(session: SessionHandle, user: UserRef) -> Self {
|
||||||
Self { session, user }
|
let span = tracing::info_span!(target: TARGET, "User");
|
||||||
|
Self {
|
||||||
|
span,
|
||||||
|
session,
|
||||||
|
user,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_self(session: SessionHandle) -> Self {
|
pub fn new_self(session: SessionHandle) -> Self {
|
||||||
@ -22,7 +40,11 @@ impl User {
|
|||||||
Self::new(session, user)
|
Self::new(session, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_optional(session: &SessionHandle, user: Option<UserRef>, builder: optional::Builder<user::Owned>) {
|
pub fn build_optional(
|
||||||
|
session: &SessionHandle,
|
||||||
|
user: Option<UserRef>,
|
||||||
|
builder: optional::Builder<user::Owned>,
|
||||||
|
) {
|
||||||
if let Some(user) = user.and_then(|u| session.users.get_user(u.get_username())) {
|
if let Some(user) = user.and_then(|u| session.users.get_user(u.get_username())) {
|
||||||
let builder = builder.init_just();
|
let builder = builder.init_just();
|
||||||
Self::fill(&session, user, builder);
|
Self::fill(&session, user, builder);
|
||||||
@ -51,6 +73,7 @@ impl User {
|
|||||||
}
|
}
|
||||||
if session.has_perm(Permission::new("bffh.users.admin")) {
|
if session.has_perm(Permission::new("bffh.users.admin")) {
|
||||||
builder.set_admin(capnp_rpc::new_client(client.clone()));
|
builder.set_admin(capnp_rpc::new_client(client.clone()));
|
||||||
|
builder.set_card_d_e_s_fire_e_v2(capnp_rpc::new_client(client));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,12 +98,21 @@ impl info::Server for User {
|
|||||||
impl manage::Server for User {
|
impl manage::Server for User {
|
||||||
fn pwd(
|
fn pwd(
|
||||||
&mut self,
|
&mut self,
|
||||||
_params: manage::PwdParams,
|
params: manage::PwdParams,
|
||||||
_results: manage::PwdResults,
|
_results: manage::PwdResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
Promise::err(::capnp::Error::unimplemented(
|
let params = pry!(params.get());
|
||||||
"method not implemented".to_string(),
|
let old_pw = pry!(params.get_old_pwd());
|
||||||
))
|
let new_pw = pry!(params.get_new_pwd());
|
||||||
|
|
||||||
|
let uid = self.user.get_username();
|
||||||
|
if let Some(mut user) = self.session.users.get_user(uid) {
|
||||||
|
if let Ok(true) = user.check_password(old_pw.as_bytes()) {
|
||||||
|
user.set_pw(new_pw.as_bytes());
|
||||||
|
pry!(self.session.users.put_user(uid, &user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,12 +134,18 @@ impl admin::Server for User {
|
|||||||
let rolename = pry!(pry!(pry!(param.get()).get_role()).get_name());
|
let rolename = pry!(pry!(pry!(param.get()).get_role()).get_name());
|
||||||
|
|
||||||
if let Some(_role) = self.session.roles.get(rolename) {
|
if let Some(_role) = self.session.roles.get(rolename) {
|
||||||
let mut target = self.session.users.get_user(self.user.get_username()).unwrap();
|
let mut target = self
|
||||||
|
.session
|
||||||
|
.users
|
||||||
|
.get_user(self.user.get_username())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Only update if needed
|
// Only update if needed
|
||||||
if !target.userdata.roles.iter().any(|r| r.as_str() == rolename) {
|
if !target.userdata.roles.iter().any(|r| r.as_str() == rolename) {
|
||||||
target.userdata.roles.push(rolename.to_string());
|
target.userdata.roles.push(rolename.to_string());
|
||||||
self.session.users.put_user(self.user.get_username(), &target);
|
pry!(self.session
|
||||||
|
.users
|
||||||
|
.put_user(self.user.get_username(), &target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,12 +159,18 @@ impl admin::Server for User {
|
|||||||
let rolename = pry!(pry!(pry!(param.get()).get_role()).get_name());
|
let rolename = pry!(pry!(pry!(param.get()).get_role()).get_name());
|
||||||
|
|
||||||
if let Some(_role) = self.session.roles.get(rolename) {
|
if let Some(_role) = self.session.roles.get(rolename) {
|
||||||
let mut target = self.session.users.get_user(self.user.get_username()).unwrap();
|
let mut target = self
|
||||||
|
.session
|
||||||
|
.users
|
||||||
|
.get_user(self.user.get_username())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Only update if needed
|
// Only update if needed
|
||||||
if target.userdata.roles.iter().any(|r| r.as_str() == rolename) {
|
if target.userdata.roles.iter().any(|r| r.as_str() == rolename) {
|
||||||
target.userdata.roles.retain(|r| r.as_str() != rolename);
|
target.userdata.roles.retain(|r| r.as_str() != rolename);
|
||||||
self.session.users.put_user(self.user.get_username(), &target);
|
pry!(self.session
|
||||||
|
.users
|
||||||
|
.put_user(self.user.get_username(), &target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,11 +178,225 @@ impl admin::Server for User {
|
|||||||
}
|
}
|
||||||
fn pwd(
|
fn pwd(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: admin::PwdParams,
|
param: admin::PwdParams,
|
||||||
_: admin::PwdResults,
|
_: admin::PwdResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
Promise::err(::capnp::Error::unimplemented(
|
let new_pw = pry!(pry!(param.get()).get_new_pwd());
|
||||||
"method not implemented".to_string(),
|
let uid = self.user.get_username();
|
||||||
))
|
if let Some(mut user) = self.session.users.get_user(uid) {
|
||||||
|
user.set_pw(new_pw.as_bytes());
|
||||||
|
pry!(self.session.users.put_user(uid, &user));
|
||||||
|
}
|
||||||
|
Promise::ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl card_d_e_s_fire_e_v2::Server for User {
|
||||||
|
fn get_token_list(
|
||||||
|
&mut self,
|
||||||
|
_: GetTokenListParams,
|
||||||
|
mut results: GetTokenListResults,
|
||||||
|
) -> Promise<(), Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "get_token_list").entered();
|
||||||
|
tracing::trace!("method call");
|
||||||
|
|
||||||
|
// TODO: This only supports a single token per user
|
||||||
|
let user = pry!(self
|
||||||
|
.session
|
||||||
|
.users
|
||||||
|
.get_user(self.user.get_username())
|
||||||
|
.ok_or_else(|| Error::failed(format!(
|
||||||
|
"User API object with nonexisting user \"{}\"",
|
||||||
|
self.user.get_username()
|
||||||
|
))));
|
||||||
|
let tk = user
|
||||||
|
.userdata
|
||||||
|
.kv
|
||||||
|
.get("cardtoken")
|
||||||
|
.map(|ck| hex::decode(ck).ok())
|
||||||
|
.flatten()
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
tracing::debug!(user.id = &user.id, "no tokens stored");
|
||||||
|
Vec::new()
|
||||||
|
});
|
||||||
|
if !tk.is_empty() {
|
||||||
|
let b = results.get();
|
||||||
|
let mut lb = b.init_token_list(1);
|
||||||
|
lb.set(0, &tk[..]);
|
||||||
|
}
|
||||||
|
Promise::ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind(&mut self, params: BindParams, _: BindResults) -> Promise<(), Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "bind").entered();
|
||||||
|
let params = pry!(params.get());
|
||||||
|
let card_key = pry!(params.get_auth_key());
|
||||||
|
let token = pry!(params.get_token());
|
||||||
|
|
||||||
|
let token: Cow<'_, str> = if let Ok(url) = std::str::from_utf8(token) {
|
||||||
|
Cow::Borrowed(url)
|
||||||
|
} else {
|
||||||
|
Cow::Owned(hex::encode(token))
|
||||||
|
};
|
||||||
|
|
||||||
|
tracing::trace!(
|
||||||
|
params.token = token.as_ref(),
|
||||||
|
params.auth_key = "<censored>",
|
||||||
|
"method call"
|
||||||
|
);
|
||||||
|
|
||||||
|
let card_key = hex::encode(card_key);
|
||||||
|
|
||||||
|
let mut user = pry!(self
|
||||||
|
.session
|
||||||
|
.users
|
||||||
|
.get_user(self.user.get_username())
|
||||||
|
.ok_or_else(|| Error::failed(format!(
|
||||||
|
"User API object with nonexisting user \"{}\"",
|
||||||
|
self.user.get_username()
|
||||||
|
))));
|
||||||
|
|
||||||
|
let prev_token = user.userdata.kv.get("cardtoken");
|
||||||
|
let prev_cardk = user.userdata.kv.get("cardkey");
|
||||||
|
|
||||||
|
match (prev_token, prev_cardk) {
|
||||||
|
(Some(prev_token), Some(prev_cardk))
|
||||||
|
if prev_token.as_str() == &token && prev_cardk.as_str() == card_key.as_str() =>
|
||||||
|
{
|
||||||
|
tracing::info!(
|
||||||
|
user.id, token = token.as_ref(),
|
||||||
|
"new token and card key are identical, skipping no-op"
|
||||||
|
);
|
||||||
|
return Promise::ok(());
|
||||||
|
},
|
||||||
|
(Some(prev_token), Some(_))
|
||||||
|
if prev_token.as_str() == token /* above guard means prev_cardk != card_key */ =>
|
||||||
|
{
|
||||||
|
tracing::warn!(
|
||||||
|
token = token.as_ref(),
|
||||||
|
"trying to overwrite card key for existing token, ignoring!"
|
||||||
|
);
|
||||||
|
return Promise::ok(());
|
||||||
|
},
|
||||||
|
(Some(prev_token), None) => tracing::warn!(
|
||||||
|
user.id, prev_token,
|
||||||
|
"token already set for user but no card key, setting new pair unconditionally!"
|
||||||
|
),
|
||||||
|
(None, Some(_)) => tracing::warn!(
|
||||||
|
user.id,
|
||||||
|
"card key already set for user but no token, setting new pair unconditionally!"
|
||||||
|
),
|
||||||
|
(Some(_), Some(_)) | (None, None) => tracing::debug!(
|
||||||
|
user.id, token = token.as_ref(),
|
||||||
|
"Adding new card key/token pair"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
user.userdata
|
||||||
|
.kv
|
||||||
|
.insert("cardtoken".to_string(), token.to_string());
|
||||||
|
user.userdata.kv.insert("cardkey".to_string(), card_key);
|
||||||
|
|
||||||
|
pry!(self.session.users.put_user(self.user.get_username(), &user));
|
||||||
|
|
||||||
|
Promise::ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind(&mut self, params: UnbindParams, _: UnbindResults) -> Promise<(), Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "unbind").entered();
|
||||||
|
|
||||||
|
let params = pry!(params.get());
|
||||||
|
let token = pry!(params.get_token());
|
||||||
|
|
||||||
|
let token: Cow<'_, str> = if let Ok(url) = std::str::from_utf8(token) {
|
||||||
|
Cow::Borrowed(url)
|
||||||
|
} else {
|
||||||
|
Cow::Owned(hex::encode(token))
|
||||||
|
};
|
||||||
|
|
||||||
|
tracing::trace!(params.token = token.as_ref(), "method call");
|
||||||
|
|
||||||
|
let mut user = pry!(self
|
||||||
|
.session
|
||||||
|
.users
|
||||||
|
.get_user(self.user.get_username())
|
||||||
|
.ok_or_else(|| Error::failed(format!(
|
||||||
|
"User API object with nonexisting user \"{}\"",
|
||||||
|
self.user.get_username()
|
||||||
|
))));
|
||||||
|
if let Some(prev_token) = user.userdata.kv.get("cardtoken") {
|
||||||
|
if token.as_ref() == prev_token.as_str() {
|
||||||
|
tracing::debug!(
|
||||||
|
user.id,
|
||||||
|
token = token.as_ref(),
|
||||||
|
"removing card key/token pair"
|
||||||
|
);
|
||||||
|
user.userdata.kv.remove("cardtoken");
|
||||||
|
user.userdata.kv.remove("cardkey");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pry!(self.session.users.put_user(self.user.get_username(), &user));
|
||||||
|
|
||||||
|
Promise::ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_card_token(
|
||||||
|
&mut self,
|
||||||
|
_: GenCardTokenParams,
|
||||||
|
mut results: GenCardTokenResults,
|
||||||
|
) -> Promise<(), Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "gen_card_token").entered();
|
||||||
|
tracing::trace!("method call");
|
||||||
|
|
||||||
|
results.get().set_token(Uuid::new_v4().as_bytes());
|
||||||
|
|
||||||
|
Promise::ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_meta_info(
|
||||||
|
&mut self,
|
||||||
|
_: GetMetaInfoParams,
|
||||||
|
mut results: GetMetaInfoResults,
|
||||||
|
) -> Promise<(), Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "get_meta_info").entered();
|
||||||
|
tracing::trace!("method call");
|
||||||
|
|
||||||
|
results.get().set_bytes(b"FABACCESS\x00DESFIRE\x001.0\x00");
|
||||||
|
|
||||||
|
Promise::ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_space_info(
|
||||||
|
&mut self,
|
||||||
|
_: GetSpaceInfoParams,
|
||||||
|
mut results: GetSpaceInfoResults,
|
||||||
|
) -> Promise<(), Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "get_space_info").entered();
|
||||||
|
tracing::trace!("method call");
|
||||||
|
|
||||||
|
let space = if let Some(space) = CONFIG.get().map(|c| c.spacename.as_str()) {
|
||||||
|
space
|
||||||
|
} else {
|
||||||
|
return Promise::err(Error::failed("No space name configured".to_string()));
|
||||||
|
};
|
||||||
|
|
||||||
|
let url = if let Some(url) = CONFIG.get().map(|c| c.instanceurl.as_str()) {
|
||||||
|
url
|
||||||
|
} else {
|
||||||
|
return Promise::err(Error::failed("No instance url configured".to_string()));
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut data = Vec::new();
|
||||||
|
write!(&mut data, "urn:fabaccess:lab:{space}\x00{url}").unwrap();
|
||||||
|
results.get().set_bytes(&data);
|
||||||
|
|
||||||
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,25 @@
|
|||||||
|
use api::usersystem_capnp::user_system::{info, manage, search};
|
||||||
use capnp::capability::Promise;
|
use capnp::capability::Promise;
|
||||||
use capnp_rpc::pry;
|
use capnp_rpc::pry;
|
||||||
use api::usersystem_capnp::user_system::{
|
use tracing::Span;
|
||||||
info, manage, search
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::capnp::user::User;
|
use crate::capnp::user::User;
|
||||||
|
|
||||||
use crate::session::SessionHandle;
|
use crate::session::SessionHandle;
|
||||||
use crate::users::{db, UserRef};
|
use crate::users::{db, UserRef};
|
||||||
|
|
||||||
|
const TARGET: &str = "bffh::api::usersystem";
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Users {
|
pub struct Users {
|
||||||
|
span: Span,
|
||||||
session: SessionHandle,
|
session: SessionHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Users {
|
impl Users {
|
||||||
pub fn new(session: SessionHandle) -> Self {
|
pub fn new(session: SessionHandle) -> Self {
|
||||||
Self { session }
|
let span = tracing::info_span!(target: TARGET, "UserSystem",);
|
||||||
|
Self { span, session }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,8 +29,14 @@ impl info::Server for Users {
|
|||||||
_: info::GetUserSelfParams,
|
_: info::GetUserSelfParams,
|
||||||
mut result: info::GetUserSelfResults,
|
mut result: info::GetUserSelfResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "getUserSelf").entered();
|
||||||
|
tracing::trace!("method call");
|
||||||
|
|
||||||
let builder = result.get();
|
let builder = result.get();
|
||||||
User::build(self.session.clone(), builder);
|
User::build(self.session.clone(), builder);
|
||||||
|
|
||||||
|
tracing::trace!("method return");
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -39,32 +47,50 @@ impl manage::Server for Users {
|
|||||||
_: manage::GetUserListParams,
|
_: manage::GetUserListParams,
|
||||||
mut result: manage::GetUserListResults,
|
mut result: manage::GetUserListResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "getUserList",).entered();
|
||||||
|
tracing::trace!("method call");
|
||||||
|
|
||||||
let userdb = self.session.users.into_inner();
|
let userdb = self.session.users.into_inner();
|
||||||
let users = pry!(userdb.get_all()
|
let users = pry!(userdb
|
||||||
|
.get_all()
|
||||||
.map_err(|e| capnp::Error::failed(format!("UserDB error: {:?}", e))));
|
.map_err(|e| capnp::Error::failed(format!("UserDB error: {:?}", e))));
|
||||||
let mut builder = result.get().init_user_list(users.len() as u32);
|
let mut builder = result.get().init_user_list(users.len() as u32);
|
||||||
for (i, (_, user)) in users.into_iter().enumerate() {
|
for (i, (id, userdata)) in users.into_iter().enumerate() {
|
||||||
|
let user = db::User { id, userdata };
|
||||||
User::fill(&self.session, user, builder.reborrow().get(i as u32));
|
User::fill(&self.session, user, builder.reborrow().get(i as u32));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing::trace!("method return");
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_user_fallible(
|
fn add_user_fallible(
|
||||||
&mut self,
|
&mut self,
|
||||||
params: manage::AddUserFallibleParams,
|
params: manage::AddUserFallibleParams,
|
||||||
mut result: manage::AddUserFallibleResults,
|
mut result: manage::AddUserFallibleResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "addUserFallible").entered();
|
||||||
|
|
||||||
let params = pry!(params.get());
|
let params = pry!(params.get());
|
||||||
let username = pry!(params.get_username());
|
let username = pry!(params.get_username());
|
||||||
let password = pry!(params.get_password());
|
let password = pry!(params.get_password());
|
||||||
// FIXME: saslprep passwords & usernames before storing them
|
// FIXME: saslprep passwords & usernames before storing them
|
||||||
|
|
||||||
let mut builder = result.get();
|
tracing::trace!(
|
||||||
|
params.username = username,
|
||||||
|
params.password = "<redacted>",
|
||||||
|
"method call"
|
||||||
|
);
|
||||||
|
|
||||||
|
let builder = result.get();
|
||||||
|
|
||||||
if !username.is_empty() && !password.is_empty() {
|
if !username.is_empty() && !password.is_empty() {
|
||||||
if self.session.users.get_user(username).is_none() {
|
if self.session.users.get_user(username).is_none() {
|
||||||
let user = db::User::new_with_plain_pw(username, password);
|
let user = db::User::new_with_plain_pw(username, password);
|
||||||
self.session.users.put_user(username, &user);
|
pry!(self.session.users.put_user(username, &user));
|
||||||
let mut builder = builder.init_successful();
|
let builder = builder.init_successful();
|
||||||
User::fill(&self.session, user, builder);
|
User::fill(&self.session, user, builder);
|
||||||
} else {
|
} else {
|
||||||
let mut builder = builder.init_failed();
|
let mut builder = builder.init_failed();
|
||||||
@ -83,21 +109,29 @@ impl manage::Server for Users {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing::trace!("method return");
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_user(
|
fn remove_user(
|
||||||
&mut self,
|
&mut self,
|
||||||
params: manage::RemoveUserParams,
|
params: manage::RemoveUserParams,
|
||||||
_: manage::RemoveUserResults,
|
_: manage::RemoveUserResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "removeUser",).entered();
|
||||||
|
|
||||||
let who: &str = pry!(pry!(pry!(params.get()).get_user()).get_username());
|
let who: &str = pry!(pry!(pry!(params.get()).get_user()).get_username());
|
||||||
|
|
||||||
|
tracing::trace!(params.user = who, "method call");
|
||||||
|
|
||||||
if let Err(e) = self.session.users.del_user(who) {
|
if let Err(e) = self.session.users.del_user(who) {
|
||||||
tracing::warn!("Failed to delete user: {:?}", e);
|
tracing::warn!("Failed to delete user: {:?}", e);
|
||||||
} else {
|
} else {
|
||||||
tracing::info!("Deleted user {}", who);
|
tracing::info!("Deleted user {}", who);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing::trace!("method return");
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,9 +142,17 @@ impl search::Server for Users {
|
|||||||
params: search::GetUserByNameParams,
|
params: search::GetUserByNameParams,
|
||||||
mut result: search::GetUserByNameResults,
|
mut result: search::GetUserByNameResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
|
let _guard = self.span.enter();
|
||||||
|
let _span = tracing::trace_span!(target: TARGET, "getUserByName",).entered();
|
||||||
|
|
||||||
let username: &str = pry!(pry!(params.get()).get_username());
|
let username: &str = pry!(pry!(params.get()).get_username());
|
||||||
|
|
||||||
|
tracing::trace!(params.username = username, "method call");
|
||||||
|
|
||||||
let userref = UserRef::new(username.to_string());
|
let userref = UserRef::new(username.to_string());
|
||||||
User::build_optional(&self.session, Some(userref), result.get());
|
User::build_optional(&self.session, Some(userref), result.get());
|
||||||
|
|
||||||
|
tracing::trace!("method return");
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,172 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::default::Default;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::authorization::permissions::PrivilegesBuf;
|
||||||
|
use crate::authorization::roles::Role;
|
||||||
|
use crate::capnp::{Listen, TlsListen};
|
||||||
|
use crate::logging::LogConfig;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use crate::Config;
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct DhallConfig<'a> {
|
||||||
|
path: &'a Path,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_config_file(path: impl AsRef<Path>) -> Result<Config, serde_dhall::Error> {
|
pub fn read_config_file(path: impl AsRef<Path>) -> Result<Config, serde_dhall::Error> {
|
||||||
serde_dhall::from_file(path)
|
serde_dhall::from_file(path).parse().map_err(Into::into)
|
||||||
.parse()
|
}
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
/// A description of a machine
|
||||||
|
///
|
||||||
|
/// This is the struct that a machine is serialized to/from.
|
||||||
|
/// Combining this with the actual state of the system will return a machine
|
||||||
|
pub struct MachineDescription {
|
||||||
|
/// The name of the machine. Doesn't need to be unique but is what humans will be presented.
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// An optional description of the Machine.
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Option::is_none",
|
||||||
|
deserialize_with = "deser_option"
|
||||||
|
)]
|
||||||
|
pub description: Option<String>,
|
||||||
|
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Option::is_none",
|
||||||
|
deserialize_with = "deser_option"
|
||||||
|
)]
|
||||||
|
pub wiki: Option<String>,
|
||||||
|
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Option::is_none",
|
||||||
|
deserialize_with = "deser_option"
|
||||||
|
)]
|
||||||
|
pub category: Option<String>,
|
||||||
|
|
||||||
|
/// The permission required
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub privs: PrivilegesBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
/// A list of address/port pairs to listen on.
|
||||||
|
pub listens: Vec<Listen>,
|
||||||
|
|
||||||
|
/// Machine descriptions to load
|
||||||
|
pub machines: HashMap<String, MachineDescription>,
|
||||||
|
|
||||||
|
/// Actors to load and their configuration options
|
||||||
|
pub actors: HashMap<String, ModuleConfig>,
|
||||||
|
|
||||||
|
/// Initiators to load and their configuration options
|
||||||
|
pub initiators: HashMap<String, ModuleConfig>,
|
||||||
|
|
||||||
|
pub mqtt_url: String,
|
||||||
|
|
||||||
|
pub actor_connections: Vec<(String, String)>,
|
||||||
|
pub init_connections: Vec<(String, String)>,
|
||||||
|
|
||||||
|
pub db_path: PathBuf,
|
||||||
|
pub auditlog_path: PathBuf,
|
||||||
|
|
||||||
|
pub roles: HashMap<String, Role>,
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub tlsconfig: TlsListen,
|
||||||
|
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub tlskeylog: Option<PathBuf>,
|
||||||
|
|
||||||
|
#[serde(default, skip)]
|
||||||
|
pub verbosity: isize,
|
||||||
|
|
||||||
|
#[serde(default, skip)]
|
||||||
|
pub logging: LogConfig,
|
||||||
|
|
||||||
|
pub spacename: String,
|
||||||
|
|
||||||
|
pub instanceurl: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn is_quiet(&self) -> bool {
|
||||||
|
self.verbosity < 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ModuleConfig {
|
||||||
|
pub module: String,
|
||||||
|
pub params: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn deser_option<'de, D, T>(d: D) -> std::result::Result<Option<T>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
T: serde::Deserialize<'de>,
|
||||||
|
{
|
||||||
|
Ok(T::deserialize(d).ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
let mut actors: HashMap<String, ModuleConfig> = HashMap::new();
|
||||||
|
let mut initiators: HashMap<String, ModuleConfig> = HashMap::new();
|
||||||
|
let machines = HashMap::new();
|
||||||
|
|
||||||
|
actors.insert(
|
||||||
|
"Actor".to_string(),
|
||||||
|
ModuleConfig {
|
||||||
|
module: "Shelly".to_string(),
|
||||||
|
params: HashMap::new(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
initiators.insert(
|
||||||
|
"Initiator".to_string(),
|
||||||
|
ModuleConfig {
|
||||||
|
module: "TCP-Listen".to_string(),
|
||||||
|
params: HashMap::new(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Config {
|
||||||
|
listens: vec![Listen {
|
||||||
|
address: "127.0.0.1".to_string(),
|
||||||
|
port: None,
|
||||||
|
}],
|
||||||
|
actors,
|
||||||
|
initiators,
|
||||||
|
machines,
|
||||||
|
mqtt_url: "tcp://localhost:1883".to_string(),
|
||||||
|
actor_connections: vec![("Testmachine".to_string(), "Actor".to_string())],
|
||||||
|
init_connections: vec![("Initiator".to_string(), "Testmachine".to_string())],
|
||||||
|
|
||||||
|
db_path: PathBuf::from("/run/bffh/database"),
|
||||||
|
auditlog_path: PathBuf::from("/var/log/bffh/audit.log"),
|
||||||
|
roles: HashMap::new(),
|
||||||
|
|
||||||
|
tlsconfig: TlsListen {
|
||||||
|
certfile: PathBuf::from("./bffh.crt"),
|
||||||
|
keyfile: PathBuf::from("./bffh.key"),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
|
||||||
|
tlskeylog: None,
|
||||||
|
verbosity: 0,
|
||||||
|
logging: LogConfig::default(),
|
||||||
|
instanceurl: "".into(),
|
||||||
|
spacename: "".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,144 +1,52 @@
|
|||||||
use std::default::Default;
|
use std::path::Path;
|
||||||
use std::path::{PathBuf};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
use miette::Diagnostic;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub(crate) use dhall::deser_option;
|
||||||
|
pub use dhall::{Config, MachineDescription, ModuleConfig};
|
||||||
mod dhall;
|
mod dhall;
|
||||||
pub use dhall::read_config_file as read;
|
|
||||||
|
|
||||||
use crate::authorization::permissions::{PrivilegesBuf};
|
#[derive(Debug, Error, Diagnostic)]
|
||||||
use crate::authorization::roles::Role;
|
pub enum ConfigError {
|
||||||
use crate::capnp::{Listen, TlsListen};
|
#[error("The config file '{0}' does not exist or is not readable")]
|
||||||
use crate::logging::LogConfig;
|
#[diagnostic(
|
||||||
|
code(config::notfound),
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
help("Make sure the config file and the directory it's in are readable by the user running bffh")
|
||||||
#[serde(deny_unknown_fields)]
|
)]
|
||||||
/// A description of a machine
|
NotFound(String),
|
||||||
///
|
#[error("The path '{0}' does not point to a file")]
|
||||||
/// This is the struct that a machine is serialized to/from.
|
#[diagnostic(
|
||||||
/// Combining this with the actual state of the system will return a machine
|
code(config::notafile),
|
||||||
pub struct MachineDescription {
|
help("The config must be a file in the dhall format")
|
||||||
/// The name of the machine. Doesn't need to be unique but is what humans will be presented.
|
)]
|
||||||
pub name: String,
|
NotAFile(String),
|
||||||
|
#[error("failed to parse config: {0}")]
|
||||||
/// An optional description of the Machine.
|
#[diagnostic(code(config::parse))]
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "deser_option")]
|
Parse(
|
||||||
pub description: Option<String>,
|
#[from]
|
||||||
|
#[source]
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "deser_option")]
|
serde_dhall::Error,
|
||||||
pub wiki: Option<String>,
|
),
|
||||||
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "deser_option")]
|
|
||||||
pub category: Option<String>,
|
|
||||||
|
|
||||||
/// The permission required
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub privs: PrivilegesBuf,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
pub fn read(file: impl AsRef<Path>) -> Result<Config, ConfigError> {
|
||||||
pub struct Config {
|
let path = file.as_ref();
|
||||||
/// A list of address/port pairs to listen on.
|
if !path.exists() {
|
||||||
pub listens: Vec<Listen>,
|
return Err(ConfigError::NotFound(path.to_string_lossy().to_string()));
|
||||||
|
|
||||||
/// Machine descriptions to load
|
|
||||||
pub machines: HashMap<String, MachineDescription>,
|
|
||||||
|
|
||||||
/// Actors to load and their configuration options
|
|
||||||
pub actors: HashMap<String, ModuleConfig>,
|
|
||||||
|
|
||||||
/// Initiators to load and their configuration options
|
|
||||||
pub initiators: HashMap<String, ModuleConfig>,
|
|
||||||
|
|
||||||
pub mqtt_url: String,
|
|
||||||
|
|
||||||
pub actor_connections: Vec<(String, String)>,
|
|
||||||
pub init_connections: Vec<(String, String)>,
|
|
||||||
|
|
||||||
pub db_path: PathBuf,
|
|
||||||
pub auditlog_path: PathBuf,
|
|
||||||
|
|
||||||
pub roles: HashMap<String, Role>,
|
|
||||||
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub tlsconfig: TlsListen,
|
|
||||||
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub tlskeylog: Option<PathBuf>,
|
|
||||||
|
|
||||||
#[serde(default, skip)]
|
|
||||||
pub verbosity: isize,
|
|
||||||
|
|
||||||
#[serde(default, skip)]
|
|
||||||
pub logging: LogConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
|
||||||
pub fn is_quiet(&self) -> bool {
|
|
||||||
self.verbosity < 0
|
|
||||||
}
|
}
|
||||||
}
|
if !path.is_file() {
|
||||||
|
return Err(ConfigError::NotAFile(path.to_string_lossy().to_string()));
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct ModuleConfig {
|
|
||||||
pub module: String,
|
|
||||||
pub params: HashMap<String, String>
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn deser_option<'de, D, T>(d: D) -> std::result::Result<Option<T>, D::Error>
|
|
||||||
where D: serde::Deserializer<'de>, T: serde::Deserialize<'de>,
|
|
||||||
{
|
|
||||||
Ok(T::deserialize(d).ok())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut actors: HashMap::<String, ModuleConfig> = HashMap::new();
|
|
||||||
let mut initiators: HashMap::<String, ModuleConfig> = HashMap::new();
|
|
||||||
let machines = HashMap::new();
|
|
||||||
|
|
||||||
actors.insert("Actor".to_string(), ModuleConfig {
|
|
||||||
module: "Shelly".to_string(),
|
|
||||||
params: HashMap::new(),
|
|
||||||
});
|
|
||||||
initiators.insert("Initiator".to_string(), ModuleConfig {
|
|
||||||
module: "TCP-Listen".to_string(),
|
|
||||||
params: HashMap::new(),
|
|
||||||
});
|
|
||||||
|
|
||||||
Config {
|
|
||||||
listens: vec![
|
|
||||||
Listen {
|
|
||||||
address: "127.0.0.1".to_string(),
|
|
||||||
port: None,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
actors,
|
|
||||||
initiators,
|
|
||||||
machines,
|
|
||||||
mqtt_url: "tcp://localhost:1883".to_string(),
|
|
||||||
actor_connections: vec![
|
|
||||||
("Testmachine".to_string(), "Actor".to_string()),
|
|
||||||
],
|
|
||||||
init_connections: vec![
|
|
||||||
("Initiator".to_string(), "Testmachine".to_string()),
|
|
||||||
],
|
|
||||||
|
|
||||||
db_path: PathBuf::from("/run/bffh/database"),
|
|
||||||
auditlog_path: PathBuf::from("/var/log/bffh/audit.log"),
|
|
||||||
roles: HashMap::new(),
|
|
||||||
|
|
||||||
tlsconfig: TlsListen {
|
|
||||||
certfile: PathBuf::from("./bffh.crt"),
|
|
||||||
keyfile: PathBuf::from("./bffh.key"),
|
|
||||||
.. Default::default()
|
|
||||||
},
|
|
||||||
|
|
||||||
tlskeylog: None,
|
|
||||||
verbosity: 0,
|
|
||||||
logging: LogConfig::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let config = dhall::read_config_file(file)?;
|
||||||
|
// TODO: configuration by environment variables?
|
||||||
|
// but rather in in a separate function
|
||||||
|
// for (envvar, value) in std::env::vars() {
|
||||||
|
// match envvar.as_str() {
|
||||||
|
// // Do things like this?
|
||||||
|
// // "BFFH_LOG" => config.logging.filter = Some(value),
|
||||||
|
// _ => {}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,90 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
// for converting a database error into a failed promise
|
||||||
|
use capnp;
|
||||||
|
|
||||||
mod raw;
|
mod raw;
|
||||||
|
|
||||||
|
use miette::{Diagnostic, Severity};
|
||||||
pub use raw::RawDB;
|
pub use raw::RawDB;
|
||||||
|
use std::fmt::{Debug, Display};
|
||||||
|
|
||||||
mod typed;
|
mod typed;
|
||||||
pub use typed::{DB, ArchivedValue, Adapter, AlignedAdapter};
|
pub use typed::{Adapter, AlignedAdapter, ArchivedValue, DB};
|
||||||
|
|
||||||
pub type Error = lmdb::Error;
|
pub type ErrorO = lmdb::Error;
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Error)]
|
||||||
|
#[error(transparent)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Error(#[from] lmdb::Error);
|
||||||
|
|
||||||
|
impl Diagnostic for Error {
|
||||||
|
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
||||||
|
Some(Box::new(match self.0 {
|
||||||
|
lmdb::Error::KeyExist => "bffh::db::raw::key_exists".to_string(),
|
||||||
|
lmdb::Error::NotFound => "bffh::db::raw::not_found".to_string(),
|
||||||
|
lmdb::Error::PageNotFound => "bffh::db::raw::page_not_found".to_string(),
|
||||||
|
lmdb::Error::Corrupted => "bffh::db::raw::corrupted".to_string(),
|
||||||
|
lmdb::Error::Panic => "bffh::db::raw::panic".to_string(),
|
||||||
|
lmdb::Error::VersionMismatch => "bffh::db::raw::version_mismatch".to_string(),
|
||||||
|
lmdb::Error::Invalid => "bffh::db::raw::invalid".to_string(),
|
||||||
|
lmdb::Error::MapFull => "bffh::db::raw::map_full".to_string(),
|
||||||
|
lmdb::Error::DbsFull => "bffh::db::raw::dbs_full".to_string(),
|
||||||
|
lmdb::Error::ReadersFull => "bffh::db::raw::readers_full".to_string(),
|
||||||
|
lmdb::Error::TlsFull => "bffh::db::raw::tls_full".to_string(),
|
||||||
|
lmdb::Error::TxnFull => "bffh::db::raw::txn_full".to_string(),
|
||||||
|
lmdb::Error::CursorFull => "bffh::db::raw::cursor_full".to_string(),
|
||||||
|
lmdb::Error::PageFull => "bffh::db::raw::page_full".to_string(),
|
||||||
|
lmdb::Error::MapResized => "bffh::db::raw::map_resized".to_string(),
|
||||||
|
lmdb::Error::Incompatible => "bffh::db::raw::incompatible".to_string(),
|
||||||
|
lmdb::Error::BadRslot => "bffh::db::raw::bad_rslot".to_string(),
|
||||||
|
lmdb::Error::BadTxn => "bffh::db::raw::bad_txn".to_string(),
|
||||||
|
lmdb::Error::BadValSize => "bffh::db::raw::bad_val_size".to_string(),
|
||||||
|
lmdb::Error::BadDbi => "bffh::db::raw::bad_dbi".to_string(),
|
||||||
|
lmdb::Error::Other(n) => format!("bffh::db::raw::e{}", n),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn severity(&self) -> Option<Severity> {
|
||||||
|
Some(Severity::Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
||||||
|
match self.0 {
|
||||||
|
lmdb::Error::KeyExist => Some(Box::new("The provided key already exists in the database")),
|
||||||
|
lmdb::Error::NotFound => Some(Box::new("The requested key was not found in the database")),
|
||||||
|
lmdb::Error::PageNotFound => Some(Box::new("The requested page was not found. This usually indicates corruption.")),
|
||||||
|
lmdb::Error::Corrupted => None,
|
||||||
|
lmdb::Error::Panic => None,
|
||||||
|
lmdb::Error::VersionMismatch => None,
|
||||||
|
lmdb::Error::Invalid => None,
|
||||||
|
lmdb::Error::MapFull => None,
|
||||||
|
lmdb::Error::DbsFull => None,
|
||||||
|
lmdb::Error::ReadersFull => None,
|
||||||
|
lmdb::Error::TlsFull => None,
|
||||||
|
lmdb::Error::TxnFull => None,
|
||||||
|
lmdb::Error::CursorFull => None,
|
||||||
|
lmdb::Error::PageFull => None,
|
||||||
|
lmdb::Error::MapResized => None,
|
||||||
|
lmdb::Error::Incompatible => None,
|
||||||
|
lmdb::Error::BadRslot => Some(Box::new("This usually indicates that the operation can't complete because an incompatible transaction is still open.")),
|
||||||
|
lmdb::Error::BadTxn => None,
|
||||||
|
lmdb::Error::BadValSize => None,
|
||||||
|
lmdb::Error::BadDbi => None,
|
||||||
|
lmdb::Error::Other(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Error> for capnp::Error {
|
||||||
|
fn from(dberr: Error) -> capnp::Error {
|
||||||
|
capnp::Error::failed(format!("database error: {}", dberr.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
use lmdb::{
|
use lmdb::{DatabaseFlags, Environment, RwTransaction, Transaction, WriteFlags};
|
||||||
Transaction,
|
|
||||||
RwTransaction,
|
|
||||||
Environment,
|
|
||||||
DatabaseFlags,
|
|
||||||
WriteFlags,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RawDB {
|
pub struct RawDB {
|
||||||
@ -15,13 +9,22 @@ impl RawDB {
|
|||||||
pub fn open(env: &Environment, name: Option<&str>) -> lmdb::Result<Self> {
|
pub fn open(env: &Environment, name: Option<&str>) -> lmdb::Result<Self> {
|
||||||
env.open_db(name).map(|db| Self { db })
|
env.open_db(name).map(|db| Self { db })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(env: &Environment, name: Option<&str>, flags: DatabaseFlags) -> lmdb::Result<Self> {
|
pub fn create(
|
||||||
|
env: &Environment,
|
||||||
|
name: Option<&str>,
|
||||||
|
flags: DatabaseFlags,
|
||||||
|
) -> lmdb::Result<Self> {
|
||||||
env.create_db(name, flags).map(|db| Self { db })
|
env.create_db(name, flags).map(|db| Self { db })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get<'txn, T: Transaction, K>(&self, txn: &'txn T, key: &K) -> lmdb::Result<Option<&'txn [u8]>>
|
pub fn get<'txn, T: Transaction, K>(
|
||||||
where K: AsRef<[u8]>
|
&self,
|
||||||
|
txn: &'txn T,
|
||||||
|
key: &K,
|
||||||
|
) -> lmdb::Result<Option<&'txn [u8]>>
|
||||||
|
where
|
||||||
|
K: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
match txn.get(self.db, key) {
|
match txn.get(self.db, key) {
|
||||||
Ok(buf) => Ok(Some(buf)),
|
Ok(buf) => Ok(Some(buf)),
|
||||||
@ -30,24 +33,37 @@ impl RawDB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put<K, V>(&self, txn: &mut RwTransaction, key: &K, value: &V, flags: WriteFlags)
|
pub fn put<K, V>(
|
||||||
-> lmdb::Result<()>
|
&self,
|
||||||
where K: AsRef<[u8]>,
|
txn: &mut RwTransaction,
|
||||||
V: AsRef<[u8]>,
|
key: &K,
|
||||||
|
value: &V,
|
||||||
|
flags: WriteFlags,
|
||||||
|
) -> lmdb::Result<()>
|
||||||
|
where
|
||||||
|
K: AsRef<[u8]>,
|
||||||
|
V: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
txn.put(self.db, key, value, flags)
|
txn.put(self.db, key, value, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reserve<'txn, K>(&self, txn: &'txn mut RwTransaction, key: &K, size: usize, flags: WriteFlags)
|
pub fn reserve<'txn, K>(
|
||||||
-> lmdb::Result<&'txn mut [u8]>
|
&self,
|
||||||
where K: AsRef<[u8]>
|
txn: &'txn mut RwTransaction,
|
||||||
|
key: &K,
|
||||||
|
size: usize,
|
||||||
|
flags: WriteFlags,
|
||||||
|
) -> lmdb::Result<&'txn mut [u8]>
|
||||||
|
where
|
||||||
|
K: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
txn.reserve(self.db, key, size, flags)
|
txn.reserve(self.db, key, size, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn del<K, V>(&self, txn: &mut RwTransaction, key: &K, value: Option<&V>) -> lmdb::Result<()>
|
pub fn del<K, V>(&self, txn: &mut RwTransaction, key: &K, value: Option<&V>) -> lmdb::Result<()>
|
||||||
where K: AsRef<[u8]>,
|
where
|
||||||
V: AsRef<[u8]>,
|
K: AsRef<[u8]>,
|
||||||
|
V: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
txn.del(self.db, key, value.map(AsRef::as_ref))
|
txn.del(self.db, key, value.map(AsRef::as_ref))
|
||||||
}
|
}
|
||||||
@ -60,7 +76,10 @@ impl RawDB {
|
|||||||
cursor.iter_start()
|
cursor.iter_start()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_ro_cursor<'txn, T: Transaction>(&self, txn: &'txn T) -> lmdb::Result<lmdb::RoCursor<'txn>> {
|
pub fn open_ro_cursor<'txn, T: Transaction>(
|
||||||
|
&self,
|
||||||
|
txn: &'txn T,
|
||||||
|
) -> lmdb::Result<lmdb::RoCursor<'txn>> {
|
||||||
txn.open_ro_cursor(self.db)
|
txn.open_ro_cursor(self.db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,11 @@ impl<A> DB<A> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Adapter> DB<A> {
|
impl<A: Adapter> DB<A> {
|
||||||
pub fn get<T: Transaction>(&self, txn: &T, key: &impl AsRef<[u8]>) -> Result<Option<A::Item>, db::Error> {
|
pub fn get<T: Transaction>(
|
||||||
|
&self,
|
||||||
|
txn: &T,
|
||||||
|
key: &impl AsRef<[u8]>,
|
||||||
|
) -> Result<Option<A::Item>, db::Error> {
|
||||||
Ok(self.db.get(txn, key)?.map(A::decode))
|
Ok(self.db.get(txn, key)?.map(A::decode))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,8 +133,7 @@ impl<A: Adapter> DB<A> {
|
|||||||
key: &impl AsRef<[u8]>,
|
key: &impl AsRef<[u8]>,
|
||||||
value: &A::Item,
|
value: &A::Item,
|
||||||
flags: WriteFlags,
|
flags: WriteFlags,
|
||||||
) -> Result<(), db::Error>
|
) -> Result<(), db::Error> {
|
||||||
{
|
|
||||||
let len = A::encoded_len(value);
|
let len = A::encoded_len(value);
|
||||||
let buf = self.db.reserve(txn, key, len, flags)?;
|
let buf = self.db.reserve(txn, key, len, flags)?;
|
||||||
assert_eq!(buf.len(), len, "Reserved buffer is not of requested size!");
|
assert_eq!(buf.len(), len, "Reserved buffer is not of requested size!");
|
||||||
@ -139,18 +142,19 @@ impl<A: Adapter> DB<A> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn del(&self, txn: &mut RwTransaction, key: &impl AsRef<[u8]>) -> Result<(), db::Error> {
|
pub fn del(&self, txn: &mut RwTransaction, key: &impl AsRef<[u8]>) -> Result<(), db::Error> {
|
||||||
self.db.del::<_, &[u8]>(txn, key, None)
|
Ok(self.db.del::<_, &[u8]>(txn, key, None)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&self, txn: &mut RwTransaction) -> Result<(), db::Error> {
|
pub fn clear(&self, txn: &mut RwTransaction) -> Result<(), db::Error> {
|
||||||
self.db.clear(txn)
|
Ok(self.db.clear(txn)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all<'txn, T: Transaction>(&self, txn: &'txn T) -> Result<impl IntoIterator<Item=(&'txn [u8], A::Item)>, db::Error> {
|
pub fn get_all<'txn, T: Transaction>(
|
||||||
|
&self,
|
||||||
|
txn: &'txn T,
|
||||||
|
) -> Result<impl IntoIterator<Item = (&'txn [u8], A::Item)>, db::Error> {
|
||||||
let mut cursor = self.db.open_ro_cursor(txn)?;
|
let mut cursor = self.db.open_ro_cursor(txn)?;
|
||||||
let it = cursor.iter_start();
|
let it = cursor.iter_start();
|
||||||
Ok(it.filter_map(|buf| buf.ok().map(|(kbuf,vbuf)| {
|
Ok(it.filter_map(|buf| buf.ok().map(|(kbuf, vbuf)| (kbuf, A::decode(vbuf)))))
|
||||||
(kbuf, A::decode(vbuf))
|
|
||||||
})))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
145
bffhd/error.rs
145
bffhd/error.rs
@ -1,74 +1,97 @@
|
|||||||
|
use miette::{Diagnostic, Severity};
|
||||||
|
use std::error;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::fmt;
|
use thiserror::Error;
|
||||||
use rsasl::error::SessionError;
|
|
||||||
use crate::db;
|
|
||||||
|
|
||||||
type DBError = db::Error;
|
pub trait Description {
|
||||||
|
const DESCRIPTION: Option<&'static str> = None;
|
||||||
#[derive(Debug)]
|
const CODE: &'static str;
|
||||||
/// Shared error type
|
const HELP: Option<&'static str> = None;
|
||||||
pub enum Error {
|
const URL: Option<&'static str> = None;
|
||||||
SASL(SessionError),
|
|
||||||
IO(io::Error),
|
|
||||||
Boxed(Box<dyn std::error::Error>),
|
|
||||||
Capnp(capnp::Error),
|
|
||||||
DB(DBError),
|
|
||||||
Denied,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
pub fn wrap<D: Description>(error: Source) -> Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
Error::new::<D>(error)
|
||||||
match self {
|
}
|
||||||
Error::SASL(e) => {
|
|
||||||
write!(f, "SASL Error: {}", e)
|
#[derive(Debug, Error, Diagnostic)]
|
||||||
},
|
pub enum Source {
|
||||||
Error::IO(e) => {
|
#[error("io error occured")]
|
||||||
write!(f, "IO Error: {}", e)
|
Io(
|
||||||
},
|
#[source]
|
||||||
Error::Boxed(e) => {
|
#[from]
|
||||||
write!(f, "{}", e)
|
io::Error,
|
||||||
},
|
),
|
||||||
Error::Capnp(e) => {
|
}
|
||||||
write!(f, "Cap'n Proto Error: {}", e)
|
|
||||||
},
|
#[derive(Debug)]
|
||||||
Error::DB(e) => {
|
pub struct Error {
|
||||||
write!(f, "DB Error: {:?}", e)
|
description: Option<&'static str>,
|
||||||
},
|
code: &'static str,
|
||||||
Error::Denied => {
|
severity: Option<Severity>,
|
||||||
write!(f, "You do not have the permission required to do that.")
|
help: Option<&'static str>,
|
||||||
}
|
url: Option<&'static str>,
|
||||||
|
source: Source,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Error {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Display::fmt(&self.source, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for Error {
|
||||||
|
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||||
|
Some(&self.source)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
if let Some(desc) = self.description {
|
||||||
|
desc
|
||||||
|
} else {
|
||||||
|
self.source.description()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SessionError> for Error {
|
impl Error {
|
||||||
fn from(e: SessionError) -> Error {
|
pub fn new<D: Description>(source: Source) -> Self {
|
||||||
Error::SASL(e)
|
Self {
|
||||||
|
description: D::DESCRIPTION,
|
||||||
|
code: D::CODE,
|
||||||
|
severity: source.severity(),
|
||||||
|
help: D::HELP,
|
||||||
|
url: D::URL,
|
||||||
|
source,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
impl miette::Diagnostic for Error {
|
||||||
fn from(e: io::Error) -> Error {
|
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
||||||
Error::IO(e)
|
Some(Box::new(self.code))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn severity(&self) -> Option<Severity> {
|
||||||
|
self.severity
|
||||||
|
}
|
||||||
|
|
||||||
|
fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
||||||
|
self.help.map(|r| {
|
||||||
|
let b: Box<dyn Display + 'a> = Box::new(r);
|
||||||
|
b
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
||||||
|
self.url.map(|r| {
|
||||||
|
let b: Box<dyn Display + 'a> = Box::new(r);
|
||||||
|
b
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
|
||||||
|
Some(&self.source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Box<dyn std::error::Error>> for Error {
|
|
||||||
fn from(e: Box<dyn std::error::Error>) -> Error {
|
|
||||||
Error::Boxed(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<capnp::Error> for Error {
|
|
||||||
fn from(e: capnp::Error) -> Error {
|
|
||||||
Error::Capnp(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DBError> for Error {
|
|
||||||
fn from(e: DBError) -> Error {
|
|
||||||
Error::DB(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
|
109
bffhd/initiators/dummy.rs
Normal file
109
bffhd/initiators/dummy.rs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
use miette::{miette, Diagnostic};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use super::Initiator;
|
||||||
|
use crate::initiators::InitiatorCallbacks;
|
||||||
|
use crate::resources::modules::fabaccess::Status;
|
||||||
|
use crate::session::SessionHandle;
|
||||||
|
use async_io::Timer;
|
||||||
|
use futures_util::future::BoxFuture;
|
||||||
|
use futures_util::ready;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
pub struct Dummy {
|
||||||
|
callbacks: InitiatorCallbacks,
|
||||||
|
session: SessionHandle,
|
||||||
|
state: DummyState,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DummyState {
|
||||||
|
Empty,
|
||||||
|
Sleeping(Timer, Option<Status>),
|
||||||
|
Updating(BoxFuture<'static, Status>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dummy {
|
||||||
|
fn timer() -> Timer {
|
||||||
|
Timer::after(Duration::from_secs(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flip(&self, status: Status) -> BoxFuture<'static, Status> {
|
||||||
|
let session = self.session.clone();
|
||||||
|
let mut callbacks = self.callbacks.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
let next = match &status {
|
||||||
|
Status::Free => Status::InUse(session.get_user_ref()),
|
||||||
|
Status::InUse(_) => Status::Free,
|
||||||
|
_ => Status::Free,
|
||||||
|
};
|
||||||
|
callbacks.try_update(session, status).await;
|
||||||
|
|
||||||
|
next
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error, Diagnostic)]
|
||||||
|
pub enum DummyError {}
|
||||||
|
|
||||||
|
impl Future for Dummy {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let span = tracing::debug_span!("Dummy initiator poll");
|
||||||
|
let _guard = span.enter();
|
||||||
|
tracing::trace!("polling Dummy initiator");
|
||||||
|
loop {
|
||||||
|
match &mut self.state {
|
||||||
|
DummyState::Empty => {
|
||||||
|
tracing::trace!("Dummy initiator is empty, initializing…");
|
||||||
|
self.state = DummyState::Sleeping(Self::timer(), Some(Status::Free));
|
||||||
|
}
|
||||||
|
DummyState::Sleeping(timer, next) => {
|
||||||
|
tracing::trace!("Sleep timer exists, polling it.");
|
||||||
|
|
||||||
|
let _: Instant = ready!(Pin::new(timer).poll(cx));
|
||||||
|
|
||||||
|
tracing::trace!("Timer has fired, poking out an update!");
|
||||||
|
|
||||||
|
let status = next.take().unwrap();
|
||||||
|
let f = self.flip(status);
|
||||||
|
self.state = DummyState::Updating(f);
|
||||||
|
}
|
||||||
|
DummyState::Updating(f) => {
|
||||||
|
tracing::trace!("Update future exists, polling it .");
|
||||||
|
|
||||||
|
let next = ready!(Pin::new(f).poll(cx));
|
||||||
|
|
||||||
|
tracing::trace!("Update future completed, sleeping!");
|
||||||
|
|
||||||
|
self.state = DummyState::Sleeping(Self::timer(), Some(next));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Initiator for Dummy {
|
||||||
|
fn new(params: &HashMap<String, String>, callbacks: InitiatorCallbacks) -> miette::Result<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let uid = params
|
||||||
|
.get("uid")
|
||||||
|
.ok_or_else(|| miette!("Dummy initiator configured without an UID"))?;
|
||||||
|
let session = callbacks
|
||||||
|
.open_session(uid)
|
||||||
|
.ok_or_else(|| miette!("The configured user for the dummy initiator does not exist"))?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
callbacks,
|
||||||
|
session,
|
||||||
|
state: DummyState::Empty,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,161 +1,173 @@
|
|||||||
|
use crate::initiators::dummy::Dummy;
|
||||||
|
use crate::initiators::process::Process;
|
||||||
|
use crate::resources::modules::fabaccess::Status;
|
||||||
|
use crate::session::SessionHandle;
|
||||||
|
use crate::{
|
||||||
|
AuthenticationHandle, Config, Resource, ResourcesHandle, SessionManager,
|
||||||
|
};
|
||||||
|
use executor::prelude::Executor;
|
||||||
|
use futures_util::ready;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use async_channel as channel;
|
use tracing::Span;
|
||||||
use async_oneshot as oneshot;
|
|
||||||
use futures_signals::signal::Signal;
|
|
||||||
use futures_util::future::BoxFuture;
|
|
||||||
use crate::resources::claim::{ResourceID, UserID};
|
|
||||||
use crate::resources::state::State;
|
|
||||||
|
|
||||||
pub enum UpdateError {
|
mod dummy;
|
||||||
/// We're not connected to anything anymore. You can't do anything about this error and the
|
mod process;
|
||||||
/// only reason why you even get it is because your future was called a last time before
|
|
||||||
/// being shelved so best way to handle this error is to just return from your loop entirely,
|
|
||||||
/// cleaning up any state that doesn't survive a freeze.
|
|
||||||
Closed,
|
|
||||||
|
|
||||||
Denied,
|
pub trait Initiator: Future<Output = ()> {
|
||||||
|
fn new(params: &HashMap<String, String>, callbacks: InitiatorCallbacks) -> miette::Result<Self>
|
||||||
Other(Box<dyn std::error::Error + Send>),
|
where
|
||||||
}
|
Self: Sized;
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
|
||||||
pub trait InitiatorError: std::error::Error + Send {
|
<Self as Future>::poll(self, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Initiator {
|
|
||||||
fn start_for(&mut self, machine: ResourceID)
|
|
||||||
-> BoxFuture<'static, Result<(), Box<dyn InitiatorError>>>;
|
|
||||||
|
|
||||||
fn run(&mut self, request: &mut UpdateSink)
|
|
||||||
-> BoxFuture<'static, Result<(), Box<dyn InitiatorError>>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct UpdateSink {
|
pub struct InitiatorCallbacks {
|
||||||
tx: channel::Sender<(Option<UserID>, State)>,
|
span: Span,
|
||||||
rx: channel::Receiver<Result<(), Error>>,
|
resource: Resource,
|
||||||
|
sessions: SessionManager,
|
||||||
}
|
}
|
||||||
|
impl InitiatorCallbacks {
|
||||||
impl UpdateSink {
|
pub fn new(span: Span, resource: Resource, sessions: SessionManager) -> Self {
|
||||||
fn new(tx: channel::Sender<(Option<UserID>, State)>,
|
|
||||||
rx: channel::Receiver<Result<(), Error>>)
|
|
||||||
-> Self
|
|
||||||
{
|
|
||||||
Self { tx, rx }
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send(&mut self, userid: Option<UserID>, state: State)
|
|
||||||
-> Result<(), UpdateError>
|
|
||||||
{
|
|
||||||
if let Err(_e) = self.tx.send((userid, state)).await {
|
|
||||||
return Err(UpdateError::Closed);
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.rx.recv().await {
|
|
||||||
Ok(Ok(())) => Ok(()),
|
|
||||||
Ok(Err(Error::Denied)) => Err(UpdateError::Denied),
|
|
||||||
Ok(Err(Error::Internal(e))) => Err(UpdateError::Other(e)),
|
|
||||||
// RecvError is send only when the channel is closed
|
|
||||||
Err(_) => Err(UpdateError::Closed),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Resource;
|
|
||||||
pub struct InitiatorDriver<S, I: Initiator> {
|
|
||||||
// TODO: make this a static reference to the resources because it's much easier and we don't
|
|
||||||
// need to replace resources at runtime at the moment.
|
|
||||||
resource_signal: S,
|
|
||||||
resource: Option<channel::Sender<Update>>,
|
|
||||||
|
|
||||||
// TODO: Initiators should instead
|
|
||||||
error_channel: Option<oneshot::Receiver<Error>>,
|
|
||||||
|
|
||||||
initiator: I,
|
|
||||||
initiator_future: Option<BoxFuture<'static, Result<(), Box<dyn InitiatorError>>>>,
|
|
||||||
update_sink: UpdateSink,
|
|
||||||
initiator_req_rx: channel::Receiver<(Option<UserID>, State)>,
|
|
||||||
initiator_reply_tx: channel::Sender<Result<(), Error>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ResourceSink {
|
|
||||||
pub id: ResourceID,
|
|
||||||
pub state_sink: channel::Sender<Update>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: Signal<Item=ResourceSink>, I: Initiator> InitiatorDriver<S, I> {
|
|
||||||
pub fn new(resource_signal: S, initiator: I) -> Self {
|
|
||||||
let (initiator_reply_tx, initiator_reply_rx) = channel::bounded(1);
|
|
||||||
let (initiator_req_tx, initiator_req_rx) = async_channel::bounded(1);
|
|
||||||
let update_sink = UpdateSink::new(initiator_req_tx, initiator_reply_rx);
|
|
||||||
Self {
|
Self {
|
||||||
resource: None,
|
span,
|
||||||
resource_signal,
|
resource,
|
||||||
error_channel: None,
|
sessions,
|
||||||
|
|
||||||
initiator,
|
|
||||||
initiator_future: None,
|
|
||||||
update_sink,
|
|
||||||
initiator_req_rx,
|
|
||||||
initiator_reply_tx,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn try_update(&mut self, session: SessionHandle, status: Status) {
|
||||||
|
self.resource.try_update(session, status).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_status(&mut self, status: Status) {
|
||||||
|
self.resource.set_status(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_session(&self, uid: &str) -> Option<SessionHandle> {
|
||||||
|
self.sessions.try_open(&self.span, uid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Signal<Item=ResourceSink> + Unpin, I: Initiator + Unpin> Future for InitiatorDriver<S, I> {
|
pub struct InitiatorDriver {
|
||||||
|
span: Span,
|
||||||
|
name: String,
|
||||||
|
initiator: Box<dyn Initiator + Unpin + Send>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InitiatorDriver {
|
||||||
|
pub fn new<I>(
|
||||||
|
span: Span,
|
||||||
|
name: String,
|
||||||
|
params: &HashMap<String, String>,
|
||||||
|
resource: Resource,
|
||||||
|
sessions: SessionManager,
|
||||||
|
) -> miette::Result<Self>
|
||||||
|
where
|
||||||
|
I: 'static + Initiator + Unpin + Send,
|
||||||
|
{
|
||||||
|
let callbacks = InitiatorCallbacks::new(span.clone(), resource, sessions);
|
||||||
|
let initiator = Box::new(I::new(params, callbacks)?);
|
||||||
|
Ok(Self {
|
||||||
|
span,
|
||||||
|
name,
|
||||||
|
initiator,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for InitiatorDriver {
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
match Pin::new(&mut self.resource_signal).poll_change(cx) {
|
let _guard = tracing::info_span!("initiator poll", initiator=%self.name);
|
||||||
Poll::Ready(Some(resource)) => {
|
tracing::trace!(initiator=%self.name, "polling initiator");
|
||||||
self.resource = Some(resource.state_sink);
|
|
||||||
self.error_channel = None;
|
|
||||||
let f = Box::pin(self.initiator.start_for(resource.id));
|
|
||||||
self.initiator_future.replace(f);
|
|
||||||
},
|
|
||||||
Poll::Ready(None) => self.resource = None,
|
|
||||||
Poll::Pending => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// do while there is work to do
|
ready!(Pin::new(&mut self.initiator).poll(cx));
|
||||||
while {
|
|
||||||
// First things first:
|
|
||||||
// If we've send an update to the resources in question we have error channel set, so
|
|
||||||
// we poll that first to determine if the resources has acted on it yet.
|
|
||||||
if let Some(ref mut errchan) = self.error_channel {
|
|
||||||
match Pin::new(errchan).poll(cx) {
|
|
||||||
// In case there's an ongoing
|
|
||||||
Poll::Pending => return Poll::Pending,
|
|
||||||
Poll::Ready(Ok(error)) => {
|
|
||||||
self.error_channel = None;
|
|
||||||
self.initiator_reply_tx.send(Err(error));
|
|
||||||
}
|
|
||||||
Poll::Ready(Err(_closed)) => {
|
|
||||||
// Error channel was dropped which means there was no error
|
|
||||||
self.error_channel = None;
|
|
||||||
self.initiator_reply_tx.send(Ok(()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ref mut init_fut) = self.initiator_future {
|
tracing::warn!(initiator=%self.name, "initiator module ran to completion!");
|
||||||
match init_fut.as_mut().poll(cx) {
|
|
||||||
Poll::Pending => return Poll::Pending,
|
|
||||||
Poll::Ready(Ok(())) => {},
|
|
||||||
Poll::Ready(Err(_e)) => {
|
|
||||||
// TODO: Log initiator error here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let Some(ref mut _resource) = self.resource {
|
|
||||||
let mut s = self.update_sink.clone();
|
|
||||||
let f = self.initiator.run(&mut s);
|
|
||||||
self.initiator_future.replace(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.error_channel.is_some()
|
|
||||||
} {}
|
|
||||||
|
|
||||||
Poll::Ready(())
|
Poll::Ready(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load(
|
||||||
|
executor: Executor,
|
||||||
|
config: &Config,
|
||||||
|
resources: ResourcesHandle,
|
||||||
|
sessions: SessionManager,
|
||||||
|
_authentication: AuthenticationHandle,
|
||||||
|
) -> miette::Result<()> {
|
||||||
|
let span = tracing::info_span!("loading initiators");
|
||||||
|
let _guard = span.enter();
|
||||||
|
|
||||||
|
let mut initiator_map: HashMap<String, Resource> = config
|
||||||
|
.init_connections
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(k, v)| {
|
||||||
|
if let Some(resource) = resources.get_by_id(v) {
|
||||||
|
Some((k.clone(), resource.clone()))
|
||||||
|
} else {
|
||||||
|
tracing::error!(initiator=%k, machine=%v,
|
||||||
|
"Machine configured for initiator not found!");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for (name, cfg) in config.initiators.iter() {
|
||||||
|
if let Some(resource) = initiator_map.remove(name) {
|
||||||
|
if let Some(driver) = load_single(name, &cfg.module, &cfg.params, resource, &sessions) {
|
||||||
|
tracing::debug!(module_name=%cfg.module, %name, "starting initiator task");
|
||||||
|
executor.spawn(driver);
|
||||||
|
} else {
|
||||||
|
tracing::error!(module_name=%cfg.module, %name, "Initiator module could not be configured");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tracing::warn!(actor=%name, ?config, "Initiator has no machine configured. Skipping!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_single(
|
||||||
|
name: &String,
|
||||||
|
module_name: &String,
|
||||||
|
params: &HashMap<String, String>,
|
||||||
|
resource: Resource,
|
||||||
|
sessions: &SessionManager,
|
||||||
|
) -> Option<InitiatorDriver> {
|
||||||
|
let span = tracing::info_span!(
|
||||||
|
"initiator",
|
||||||
|
name = %name,
|
||||||
|
module = %module_name,
|
||||||
|
);
|
||||||
|
tracing::info!(%name, %module_name, ?params, "Loading initiator");
|
||||||
|
let o = match module_name.as_ref() {
|
||||||
|
"Dummy" => Some(InitiatorDriver::new::<Dummy>(
|
||||||
|
span,
|
||||||
|
name.clone(),
|
||||||
|
params,
|
||||||
|
resource,
|
||||||
|
sessions.clone(),
|
||||||
|
)),
|
||||||
|
"Process" => Some(InitiatorDriver::new::<Process>(
|
||||||
|
span,
|
||||||
|
name.clone(),
|
||||||
|
params,
|
||||||
|
resource,
|
||||||
|
sessions.clone(),
|
||||||
|
)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
o.transpose().unwrap_or_else(|error| {
|
||||||
|
tracing::error!(%error, "failed to configure initiator");
|
||||||
|
None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
233
bffhd/initiators/process.rs
Normal file
233
bffhd/initiators/process.rs
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
use super::Initiator;
|
||||||
|
use super::InitiatorCallbacks;
|
||||||
|
use crate::resources::modules::fabaccess::Status;
|
||||||
|
use crate::utils::linebuffer::LineBuffer;
|
||||||
|
use async_process::{Child, ChildStderr, ChildStdout, Command, Stdio};
|
||||||
|
use futures_lite::AsyncRead;
|
||||||
|
use miette::{miette, IntoDiagnostic};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::io;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum InputMessage {
|
||||||
|
#[serde(rename = "state")]
|
||||||
|
SetState(Status),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct OutputLine {}
|
||||||
|
|
||||||
|
pub struct Process {
|
||||||
|
pub cmd: String,
|
||||||
|
pub args: Vec<String>,
|
||||||
|
state: Option<ProcessState>,
|
||||||
|
buffer: LineBuffer,
|
||||||
|
err_buffer: LineBuffer,
|
||||||
|
callbacks: InitiatorCallbacks,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Process {
|
||||||
|
fn spawn(&mut self) -> io::Result<()> {
|
||||||
|
let mut child = Command::new(&self.cmd)
|
||||||
|
.args(&self.args)
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
self.state = Some(ProcessState::new(
|
||||||
|
child
|
||||||
|
.stdout
|
||||||
|
.take()
|
||||||
|
.expect("Child just spawned with piped stdout has no stdout"),
|
||||||
|
child
|
||||||
|
.stderr
|
||||||
|
.take()
|
||||||
|
.expect("Child just spawned with piped stderr has no stderr"),
|
||||||
|
child,
|
||||||
|
));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ProcessState {
|
||||||
|
pub stdout: ChildStdout,
|
||||||
|
pub stderr: ChildStderr,
|
||||||
|
pub stderr_closed: bool,
|
||||||
|
pub child: Child,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcessState {
|
||||||
|
pub fn new(stdout: ChildStdout, stderr: ChildStderr, child: Child) -> Self {
|
||||||
|
Self {
|
||||||
|
stdout,
|
||||||
|
stderr,
|
||||||
|
stderr_closed: false,
|
||||||
|
child,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_process(&mut self, buffer: &[u8], callbacks: &mut InitiatorCallbacks) -> usize {
|
||||||
|
tracing::trace!("trying to process current buffer");
|
||||||
|
|
||||||
|
let mut end = 0;
|
||||||
|
|
||||||
|
while let Some(idx) = buffer[end..].iter().position(|b| *b == b'\n') {
|
||||||
|
if idx == 0 {
|
||||||
|
end += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let line = &buffer[end..(end + idx)];
|
||||||
|
self.process_line(line, callbacks);
|
||||||
|
end = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_line(&mut self, line: &[u8], callbacks: &mut InitiatorCallbacks) {
|
||||||
|
if !line.is_empty() {
|
||||||
|
let res = std::str::from_utf8(line);
|
||||||
|
if let Err(error) = &res {
|
||||||
|
tracing::warn!(%error, "Initiator sent line with invalid UTF-8");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let string = res.unwrap().trim();
|
||||||
|
// Ignore whitespace-only lines
|
||||||
|
if !string.is_empty() {
|
||||||
|
match serde_json::from_str::<InputMessage>(res.unwrap()) {
|
||||||
|
Ok(state) => {
|
||||||
|
tracing::trace!(?state, "got new state for process initiator");
|
||||||
|
let InputMessage::SetState(status) = state;
|
||||||
|
callbacks.set_status(status);
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
tracing::warn!(%error, "process initiator did not send a valid line")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for Process {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
if let Process {
|
||||||
|
state: Some(state),
|
||||||
|
buffer,
|
||||||
|
err_buffer,
|
||||||
|
callbacks,
|
||||||
|
..
|
||||||
|
} = self.get_mut()
|
||||||
|
{
|
||||||
|
match state.child.try_status() {
|
||||||
|
Err(error) => {
|
||||||
|
tracing::error!(%error, "checking child exit code returned an error");
|
||||||
|
return Poll::Ready(());
|
||||||
|
}
|
||||||
|
Ok(Some(exitcode)) => {
|
||||||
|
tracing::warn!(%exitcode, "child process exited");
|
||||||
|
return Poll::Ready(());
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
tracing::trace!("process initiator checking on process");
|
||||||
|
|
||||||
|
let stdout = &mut state.stdout;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let buf = buffer.get_mut_write(512);
|
||||||
|
match AsyncRead::poll_read(Pin::new(stdout), cx, buf) {
|
||||||
|
Poll::Pending => break,
|
||||||
|
Poll::Ready(Ok(read)) => {
|
||||||
|
buffer.advance_valid(read);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Poll::Ready(Err(error)) => {
|
||||||
|
tracing::warn!(%error, "reading from child stdout errored");
|
||||||
|
return Poll::Ready(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let processed = state.try_process(buffer, callbacks);
|
||||||
|
buffer.consume(processed);
|
||||||
|
|
||||||
|
if !state.stderr_closed {
|
||||||
|
let stderr = &mut state.stderr;
|
||||||
|
loop {
|
||||||
|
let buf = err_buffer.get_mut_write(512);
|
||||||
|
match AsyncRead::poll_read(Pin::new(stderr), cx, buf) {
|
||||||
|
Poll::Pending => break,
|
||||||
|
Poll::Ready(Ok(read)) => {
|
||||||
|
err_buffer.advance_valid(read);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Poll::Ready(Err(error)) => {
|
||||||
|
tracing::warn!(%error, "reading from child stderr errored");
|
||||||
|
state.stderr_closed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut consumed = 0;
|
||||||
|
|
||||||
|
while let Some(idx) = buffer[consumed..].iter().position(|b| *b == b'\n') {
|
||||||
|
if idx == 0 {
|
||||||
|
consumed += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let line = &buffer[consumed..(consumed + idx)];
|
||||||
|
match std::str::from_utf8(line) {
|
||||||
|
Ok(line) => tracing::debug!(line, "initiator STDERR"),
|
||||||
|
Err(error) => tracing::debug!(%error,
|
||||||
|
"invalid UTF-8 on initiator STDERR"),
|
||||||
|
}
|
||||||
|
consumed = idx;
|
||||||
|
}
|
||||||
|
err_buffer.consume(consumed);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Poll::Pending;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tracing::warn!("process initiator has no process attached!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Ready(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Initiator for Process {
|
||||||
|
fn new(params: &HashMap<String, String>, callbacks: InitiatorCallbacks) -> miette::Result<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let cmd = params
|
||||||
|
.get("cmd")
|
||||||
|
.ok_or(miette!("Process initiator requires a `cmd` parameter."))?
|
||||||
|
.clone();
|
||||||
|
let args = params
|
||||||
|
.get("args")
|
||||||
|
.map(|argv| argv.split_whitespace().map(|s| s.to_string()).collect())
|
||||||
|
.unwrap_or_else(Vec::new);
|
||||||
|
let mut this = Self {
|
||||||
|
cmd,
|
||||||
|
args,
|
||||||
|
state: None,
|
||||||
|
buffer: LineBuffer::new(),
|
||||||
|
err_buffer: LineBuffer::new(),
|
||||||
|
callbacks,
|
||||||
|
};
|
||||||
|
this.spawn().into_diagnostic()?;
|
||||||
|
Ok(this)
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
use std::fs::{File, OpenOptions};
|
|
||||||
use std::{fmt, io};
|
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use std::{fmt, io};
|
||||||
|
|
||||||
// Internal mutable state for KeyLogFile
|
// Internal mutable state for KeyLogFile
|
||||||
struct KeyLogFileInner {
|
struct KeyLogFileInner {
|
||||||
@ -18,10 +18,7 @@ impl fmt::Debug for KeyLogFileInner {
|
|||||||
|
|
||||||
impl KeyLogFileInner {
|
impl KeyLogFileInner {
|
||||||
fn new(path: impl AsRef<Path>) -> io::Result<Self> {
|
fn new(path: impl AsRef<Path>) -> io::Result<Self> {
|
||||||
let file = OpenOptions::new()
|
let file = OpenOptions::new().append(true).create(true).open(path)?;
|
||||||
.append(true)
|
|
||||||
.create(true)
|
|
||||||
.open(path)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
file,
|
file,
|
||||||
@ -70,4 +67,4 @@ impl rustls::KeyLog for KeyLogFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
236
bffhd/lib.rs
236
bffhd/lib.rs
@ -3,11 +3,14 @@
|
|||||||
//#![warn(missing_docs)]
|
//#![warn(missing_docs)]
|
||||||
//#![warn(missing_crate_level_docs)]
|
//#![warn(missing_crate_level_docs)]
|
||||||
|
|
||||||
//! Diflouroborane
|
//! Difluoroborane
|
||||||
//!
|
//!
|
||||||
//! This is the capnp component of the FabAccess project.
|
//! This is the capnp component of the FabAccess project.
|
||||||
//! The entry point of bffhd can be found in [bin/bffhd/main.rs](../bin/bffhd/main.rs)
|
//! The entry point of bffhd can be found in [bin/bffhd/main.rs](../bin/bffhd/main.rs)
|
||||||
|
|
||||||
|
use miette::{Diagnostic, IntoDiagnostic};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
|
||||||
/// Internal Databases build on top of LMDB, a mmap()'ed B-tree DB optimized for reads
|
/// Internal Databases build on top of LMDB, a mmap()'ed B-tree DB optimized for reads
|
||||||
@ -16,14 +19,15 @@ pub mod db;
|
|||||||
/// Shared error type
|
/// Shared error type
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
pub mod users;
|
|
||||||
pub mod authentication;
|
pub mod authentication;
|
||||||
pub mod authorization;
|
pub mod authorization;
|
||||||
|
pub mod users;
|
||||||
|
|
||||||
/// Resources
|
/// Resources
|
||||||
pub mod resources;
|
pub mod resources;
|
||||||
|
|
||||||
pub mod actors;
|
pub mod actors;
|
||||||
|
pub mod initiators;
|
||||||
|
|
||||||
pub mod sensors;
|
pub mod sensors;
|
||||||
|
|
||||||
@ -31,111 +35,235 @@ pub mod capnp;
|
|||||||
|
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
mod tls;
|
// Store build information in the `env` module.
|
||||||
|
shadow_rs::shadow!(env);
|
||||||
|
|
||||||
|
mod audit;
|
||||||
mod keylog;
|
mod keylog;
|
||||||
mod logging;
|
mod logging;
|
||||||
mod audit;
|
|
||||||
mod session;
|
mod session;
|
||||||
|
mod tls;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use futures_util::{FutureExt, StreamExt};
|
||||||
|
|
||||||
use std::sync::{Arc};
|
|
||||||
|
|
||||||
use anyhow::Context;
|
|
||||||
|
|
||||||
use futures_util::StreamExt;
|
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
|
|
||||||
use signal_hook::consts::signal::*;
|
|
||||||
use executor::pool::Executor;
|
|
||||||
use crate::audit::AuditLog;
|
use crate::audit::AuditLog;
|
||||||
use crate::authentication::AuthenticationHandle;
|
use crate::authentication::AuthenticationHandle;
|
||||||
use crate::authorization::roles::Roles;
|
use crate::authorization::roles::Roles;
|
||||||
use crate::capnp::APIServer;
|
use crate::capnp::APIServer;
|
||||||
use crate::config::{Config};
|
use crate::config::Config;
|
||||||
use crate::resources::modules::fabaccess::MachineState;
|
use crate::resources::modules::fabaccess::MachineState;
|
||||||
use crate::resources::Resource;
|
|
||||||
use crate::resources::search::ResourcesHandle;
|
use crate::resources::search::ResourcesHandle;
|
||||||
use crate::resources::state::db::StateDB;
|
use crate::resources::state::db::StateDB;
|
||||||
|
use crate::resources::Resource;
|
||||||
use crate::session::SessionManager;
|
use crate::session::SessionManager;
|
||||||
use crate::tls::TlsConfig;
|
use crate::tls::TlsConfig;
|
||||||
use crate::users::db::UserDB;
|
use crate::users::db::UserDB;
|
||||||
use crate::users::Users;
|
use crate::users::Users;
|
||||||
|
use executor::pool::Executor;
|
||||||
|
use lightproc::recoverable_handle::RecoverableHandle;
|
||||||
|
use signal_hook::consts::signal::*;
|
||||||
|
use tracing::Span;
|
||||||
|
|
||||||
pub const VERSION_STRING: &'static str = env!("BFFHD_VERSION_STRING");
|
use std::collections::HashMap;
|
||||||
pub const RELEASE_STRING: &'static str = env!("BFFHD_RELEASE_STRING");
|
|
||||||
|
|
||||||
pub struct Diflouroborane {
|
pub struct Difluoroborane {
|
||||||
config: Config,
|
config: Config,
|
||||||
executor: Executor<'static>,
|
executor: Executor<'static>,
|
||||||
pub statedb: StateDB,
|
pub statedb: StateDB,
|
||||||
pub users: Users,
|
pub users: Users,
|
||||||
pub roles: Roles,
|
pub roles: Roles,
|
||||||
pub resources: ResourcesHandle,
|
pub resources: ResourcesHandle,
|
||||||
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static RESOURCES: OnceCell<ResourcesHandle> = OnceCell::new();
|
pub static RESOURCES: OnceCell<ResourcesHandle> = OnceCell::new();
|
||||||
|
|
||||||
impl Diflouroborane {
|
pub static CONFIG: OnceCell<Config> = OnceCell::new();
|
||||||
pub fn new(config: Config) -> anyhow::Result<Self> {
|
|
||||||
logging::init(&config.logging);
|
|
||||||
tracing::info!(version=VERSION_STRING, "Starting BFFH");
|
|
||||||
|
|
||||||
let span = tracing::info_span!("setup");
|
struct SignalHandlerErr;
|
||||||
let _guard = span.enter();
|
impl error::Description for SignalHandlerErr {
|
||||||
|
const CODE: &'static str = "signals::new";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error, Diagnostic)]
|
||||||
|
// TODO 0.5: #[non_exhaustive]
|
||||||
|
pub enum BFFHError {
|
||||||
|
#[error("DB operation failed")]
|
||||||
|
DBError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
db::Error,
|
||||||
|
),
|
||||||
|
#[error("failed to initialize global user store")]
|
||||||
|
UsersError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
users::Error,
|
||||||
|
),
|
||||||
|
#[error("failed to initialize state database")]
|
||||||
|
StateDBError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
resources::state::db::StateDBError,
|
||||||
|
),
|
||||||
|
#[error("audit log failed")]
|
||||||
|
AuditLogError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
audit::Error,
|
||||||
|
),
|
||||||
|
#[error("Failed to initialize signal handler")]
|
||||||
|
SignalsError(#[source] std::io::Error),
|
||||||
|
#[error("error in actor subsystem")]
|
||||||
|
ActorError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
actors::ActorError,
|
||||||
|
),
|
||||||
|
#[error("failed to initialize TLS config")]
|
||||||
|
TlsSetup(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
tls::Error,
|
||||||
|
),
|
||||||
|
#[error("API handler failed")]
|
||||||
|
ApiError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
capnp::Error,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
|
struct DatabaseDump {
|
||||||
|
users: HashMap<String, users::db::UserData>,
|
||||||
|
state: HashMap<String, resources::state::State>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Difluoroborane {
|
||||||
|
pub fn setup() {}
|
||||||
|
|
||||||
|
pub fn new(config: Config) -> Result<Self, BFFHError> {
|
||||||
|
let mut server = logging::init(&config.logging);
|
||||||
|
let span = tracing::info_span!(
|
||||||
|
target: "bffh",
|
||||||
|
"bffh"
|
||||||
|
);
|
||||||
|
let span2 = span.clone();
|
||||||
|
let _guard = span2.enter();
|
||||||
|
tracing::info!(version = env::VERSION, "Starting BFFH");
|
||||||
|
|
||||||
let executor = Executor::new();
|
let executor = Executor::new();
|
||||||
|
|
||||||
let env = StateDB::open_env(&config.db_path)?;
|
if let Some(aggregator) = server.aggregator.take() {
|
||||||
let statedb = StateDB::create_with_env(env.clone())
|
executor.spawn(aggregator.run());
|
||||||
.context("Failed to open state DB file")?;
|
}
|
||||||
|
tracing::info!("Server is being spawned");
|
||||||
|
let handle = executor.spawn(server.serve());
|
||||||
|
executor.spawn(handle.map(|result| match result {
|
||||||
|
Some(Ok(())) => {
|
||||||
|
tracing::info!("console server finished without error");
|
||||||
|
}
|
||||||
|
Some(Err(error)) => {
|
||||||
|
tracing::info!(%error, "console server finished with error");
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
tracing::info!("console server finished with panic");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
let users = Users::new(env.clone()).context("Failed to open users DB file")?;
|
let env = StateDB::open_env(&config.db_path)?;
|
||||||
|
|
||||||
|
let statedb = StateDB::create_with_env(env.clone())?;
|
||||||
|
|
||||||
|
let users = Users::new(env.clone())?;
|
||||||
let roles = Roles::new(config.roles.clone());
|
let roles = Roles::new(config.roles.clone());
|
||||||
|
|
||||||
let _audit_log = AuditLog::new(&config).context("Failed to initialize audit log")?;
|
let _audit_log = AuditLog::new(&config)?;
|
||||||
|
|
||||||
let resources = ResourcesHandle::new(config.machines.iter().map(|(id, desc)| {
|
let resources = ResourcesHandle::new(config.machines.iter().map(|(id, desc)| {
|
||||||
Resource::new(Arc::new(resources::Inner::new(id.to_string(), statedb.clone(), desc.clone())))
|
Resource::new(Arc::new(resources::Inner::new(
|
||||||
|
id.to_string(),
|
||||||
|
statedb.clone(),
|
||||||
|
desc.clone(),
|
||||||
|
)))
|
||||||
}));
|
}));
|
||||||
RESOURCES.set(resources.clone());
|
RESOURCES.set(resources.clone()).unwrap();
|
||||||
|
CONFIG.set(config.clone()).unwrap();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
Ok(Self { config, executor, statedb, users, roles, resources })
|
config,
|
||||||
|
executor,
|
||||||
|
statedb,
|
||||||
|
users,
|
||||||
|
roles,
|
||||||
|
resources,
|
||||||
|
span,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) -> anyhow::Result<()> {
|
pub fn dump_db(&mut self, file: &str) -> Result<(), miette::Error> {
|
||||||
let mut signals = signal_hook_async_std::Signals::new(&[
|
let users = self.users.dump_map()?;
|
||||||
SIGINT,
|
let state = self.statedb.dump_map()?;
|
||||||
SIGQUIT,
|
let dump = DatabaseDump{users, state};
|
||||||
SIGTERM,
|
let data = toml::ser::to_vec(&dump).map_err(|e| miette::Error::msg(format!("Serializing database dump failed: {}", e)))?;
|
||||||
]).context("Failed to construct signal handler")?;
|
std::fs::write(file, &data).map_err(|e| miette::Error::msg(format!("writing database dump failed: {}", e)))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
actors::load(self.executor.clone(), &self.config, self.resources.clone())?;
|
pub fn load_db(&mut self, file: &str) -> Result<(), miette::Error> {
|
||||||
|
let data = std::fs::read(file).into_diagnostic()?;
|
||||||
|
let dump: DatabaseDump = toml::de::from_slice(&data).into_diagnostic()?;
|
||||||
|
self.users.load_map(&dump.users)?;
|
||||||
|
self.statedb.load_map(&dump.state)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self) -> Result<(), BFFHError> {
|
||||||
let tlsconfig = TlsConfig::new(self.config.tlskeylog.as_ref(), !self.config.is_quiet())?;
|
let _guard = self.span.enter();
|
||||||
let acceptor = tlsconfig.make_tls_acceptor(&self.config.tlsconfig)?;
|
let mut signals = signal_hook_async_std::Signals::new(&[SIGINT, SIGQUIT, SIGTERM])
|
||||||
|
.map_err(BFFHError::SignalsError)?;
|
||||||
|
|
||||||
let sessionmanager = SessionManager::new(self.users.clone(), self.roles.clone());
|
let sessionmanager = SessionManager::new(self.users.clone(), self.roles.clone());
|
||||||
let authentication = AuthenticationHandle::new(self.users.clone());
|
let authentication = AuthenticationHandle::new(self.users.clone());
|
||||||
|
|
||||||
let apiserver = self.executor.run(APIServer::bind(self.executor.clone(), &self.config.listens, acceptor, sessionmanager, authentication))?;
|
initiators::load(
|
||||||
|
self.executor.clone(),
|
||||||
|
&self.config,
|
||||||
|
self.resources.clone(),
|
||||||
|
sessionmanager.clone(),
|
||||||
|
authentication.clone(),
|
||||||
|
).expect("initializing initiators failed");
|
||||||
|
// TODO 0.5: error handling. Add variant to BFFHError
|
||||||
|
|
||||||
|
actors::load(self.executor.clone(), &self.config, self.resources.clone())?;
|
||||||
|
|
||||||
|
let tlsconfig = TlsConfig::new(self.config.tlskeylog.as_ref(), !self.config.is_quiet())?;
|
||||||
|
let acceptor = tlsconfig.make_tls_acceptor(&self.config.tlsconfig)?;
|
||||||
|
|
||||||
|
let apiserver = self.executor.run(APIServer::bind(
|
||||||
|
self.executor.clone(),
|
||||||
|
&self.config.listens,
|
||||||
|
acceptor,
|
||||||
|
sessionmanager,
|
||||||
|
authentication,
|
||||||
|
))?;
|
||||||
|
|
||||||
let (mut tx, rx) = async_oneshot::oneshot();
|
let (mut tx, rx) = async_oneshot::oneshot();
|
||||||
|
|
||||||
self.executor.spawn(apiserver.handle_until(rx));
|
self.executor.spawn(apiserver.handle_until(rx));
|
||||||
|
|
||||||
let f = async {
|
let f = async {
|
||||||
let mut sig = None;
|
let mut sig;
|
||||||
while {
|
while {
|
||||||
sig = signals.next().await;
|
sig = signals.next().await;
|
||||||
sig.is_none()
|
sig.is_none()
|
||||||
} {}
|
} {}
|
||||||
tracing::info!(signal = %sig.unwrap(), "Received signal");
|
tracing::info!(signal = %sig.unwrap(), "Received signal");
|
||||||
tx.send(());
|
_ = tx.send(()); // ignore result, as an Err means that the executor we want to stop has already stopped
|
||||||
};
|
};
|
||||||
|
|
||||||
self.executor.run(f);
|
self.executor.run(f);
|
||||||
@ -143,3 +271,17 @@ impl Diflouroborane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ShutdownHandler {
|
||||||
|
tasks: Vec<RecoverableHandle<()>>,
|
||||||
|
}
|
||||||
|
impl ShutdownHandler {
|
||||||
|
pub fn new(tasks: Vec<RecoverableHandle<()>>) -> Self {
|
||||||
|
Self { tasks }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shutdown(&mut self) {
|
||||||
|
for handle in self.tasks.drain(..) {
|
||||||
|
handle.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
use tracing_subscriber::{EnvFilter};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::path::Path;
|
||||||
|
use tracing_subscriber::fmt::format::Format;
|
||||||
use serde::{Serialize, Deserialize};
|
use tracing_subscriber::prelude::*;
|
||||||
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct LogConfig {
|
pub struct LogConfig {
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
/// Log filter string in the tracing format `target[span{field=value}]=level`.
|
/// Log filter string in the tracing format `target[span{field=value}]=level`.
|
||||||
/// lvalue is optional and multiple filters can be combined with comma.
|
/// lvalue is optional and multiple filters can be combined with comma.
|
||||||
/// e.g. `warn,diflouroborane::actors=debug` will only print `WARN` and `ERROR` unless the
|
/// e.g. `warn,difluoroborane::actors=debug` will only print `WARN` and `ERROR` unless the
|
||||||
/// message is logged in a span below `diflouroborane::actors` (i.e. by an actor task) in
|
/// message is logged in a span below `difluoroborane::actors` (i.e. by an actor task) in
|
||||||
/// which case `DEBUG` and `INFO` will also be printed.
|
/// which case `DEBUG` and `INFO` will also be printed.
|
||||||
pub filter: Option<String>,
|
pub filter: Option<String>,
|
||||||
|
|
||||||
@ -25,22 +26,49 @@ impl Default for LogConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(config: &LogConfig) {
|
pub enum LogOutput<'a> {
|
||||||
|
Journald,
|
||||||
|
Stdout,
|
||||||
|
File(&'a Path),
|
||||||
|
}
|
||||||
|
pub struct LogConfig2<'a, F> {
|
||||||
|
output: LogOutput<'a>,
|
||||||
|
filter_str: Option<&'a str>,
|
||||||
|
format: Format<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(config: &LogConfig) -> console::Server {
|
||||||
|
let subscriber = tracing_subscriber::registry();
|
||||||
|
|
||||||
|
let (console_layer, server) = console::ConsoleLayer::new();
|
||||||
|
let subscriber = subscriber.with(console_layer);
|
||||||
|
|
||||||
let filter = if let Some(ref filter) = config.filter {
|
let filter = if let Some(ref filter) = config.filter {
|
||||||
EnvFilter::new(filter.as_str())
|
EnvFilter::new(filter.as_str())
|
||||||
} else {
|
} else {
|
||||||
EnvFilter::from_env("BFFH_LOG")
|
EnvFilter::from_env("BFFH_LOG")
|
||||||
};
|
};
|
||||||
|
|
||||||
let builder = tracing_subscriber::fmt()
|
|
||||||
.with_env_filter(filter);
|
|
||||||
|
|
||||||
let format = config.format.to_lowercase();
|
let format = config.format.to_lowercase();
|
||||||
match format.as_str() {
|
|
||||||
"compact" => builder.compact().init(),
|
let fmt_layer = tracing_subscriber::fmt::layer();
|
||||||
"pretty" => builder.pretty().init(),
|
|
||||||
"full" => builder.init(),
|
match format.as_ref() {
|
||||||
_ => builder.init(),
|
"pretty" => {
|
||||||
|
let fmt_layer = fmt_layer.pretty().with_filter(filter);
|
||||||
|
subscriber.with(fmt_layer).init();
|
||||||
|
}
|
||||||
|
"compact" => {
|
||||||
|
let fmt_layer = fmt_layer.compact().with_filter(filter);
|
||||||
|
subscriber.with(fmt_layer).init();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let fmt_layer = fmt_layer.with_filter(filter);
|
||||||
|
subscriber.with(fmt_layer).init();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tracing::info!(format = format.as_str(), "Logging initialized")
|
|
||||||
}
|
tracing::info!(format = format.as_str(), "Logging initialized");
|
||||||
|
|
||||||
|
server
|
||||||
|
}
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
use rkyv::{Archive, Serialize, Deserialize};
|
use rkyv::{Archive, Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Archive,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
serde::Serialize,
|
||||||
#[derive(Archive, Serialize, Deserialize)]
|
serde::Deserialize,
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
)]
|
||||||
pub struct Resource {
|
pub struct Resource {
|
||||||
uuid: u128,
|
uuid: u128,
|
||||||
id: String,
|
id: String,
|
||||||
name_idx: u64,
|
name_idx: u64,
|
||||||
description_idx: u64,
|
description_idx: u64,
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
|
use futures_signals::signal::{Mutable, Signal};
|
||||||
use rkyv::Infallible;
|
use rkyv::Infallible;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use futures_signals::signal::{Mutable, Signal};
|
|
||||||
|
|
||||||
use rkyv::{Archived, Deserialize};
|
|
||||||
use rkyv::option::ArchivedOption;
|
|
||||||
use rkyv::ser::Serializer;
|
|
||||||
use rkyv::ser::serializers::AllocSerializer;
|
|
||||||
use crate::audit::AUDIT;
|
use crate::audit::AUDIT;
|
||||||
use crate::authorization::permissions::PrivilegesBuf;
|
use crate::authorization::permissions::PrivilegesBuf;
|
||||||
use crate::config::MachineDescription;
|
use crate::config::MachineDescription;
|
||||||
use crate::db::ArchivedValue;
|
use crate::db::ArchivedValue;
|
||||||
use crate::resources::modules::fabaccess::{MachineState, Status, ArchivedStatus};
|
use crate::resources::modules::fabaccess::{ArchivedStatus, MachineState, Status};
|
||||||
use crate::resources::state::db::StateDB;
|
use crate::resources::state::db::StateDB;
|
||||||
use crate::resources::state::State;
|
use crate::resources::state::State;
|
||||||
use crate::session::SessionHandle;
|
use crate::session::SessionHandle;
|
||||||
use crate::users::UserRef;
|
use crate::users::UserRef;
|
||||||
|
use rkyv::option::ArchivedOption;
|
||||||
|
use rkyv::ser::serializers::AllocSerializer;
|
||||||
|
use rkyv::ser::Serializer;
|
||||||
|
use rkyv::{Archived, Deserialize};
|
||||||
|
|
||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
@ -25,6 +25,7 @@ pub mod modules;
|
|||||||
|
|
||||||
pub struct PermissionDenied;
|
pub struct PermissionDenied;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub(crate) struct Inner {
|
pub(crate) struct Inner {
|
||||||
id: String,
|
id: String,
|
||||||
db: StateDB,
|
db: StateDB,
|
||||||
@ -43,27 +44,35 @@ impl Inner {
|
|||||||
let update = state.to_state();
|
let update = state.to_state();
|
||||||
|
|
||||||
let mut serializer = AllocSerializer::<1024>::default();
|
let mut serializer = AllocSerializer::<1024>::default();
|
||||||
serializer.serialize_value(&update).expect("failed to serialize new default state");
|
serializer
|
||||||
|
.serialize_value(&update)
|
||||||
|
.expect("failed to serialize new default state");
|
||||||
let val = ArchivedValue::new(serializer.into_serializer().into_inner());
|
let val = ArchivedValue::new(serializer.into_serializer().into_inner());
|
||||||
db.put(&id.as_bytes(), &val).unwrap();
|
db.put(&id.as_bytes(), &val).unwrap();
|
||||||
val
|
val
|
||||||
};
|
};
|
||||||
let signal = Mutable::new(state);
|
let signal = Mutable::new(state);
|
||||||
|
|
||||||
Self { id, db, signal, desc }
|
Self {
|
||||||
|
id,
|
||||||
|
db,
|
||||||
|
signal,
|
||||||
|
desc,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn signal(&self) -> impl Signal<Item=ArchivedValue<State>> {
|
pub fn signal(&self) -> impl Signal<Item = ArchivedValue<State>> {
|
||||||
Box::pin(self.signal.signal_cloned())
|
Box::pin(self.signal.signal_cloned())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_state(&self) -> ArchivedValue<State> {
|
fn get_state(&self) -> ArchivedValue<State> {
|
||||||
self.db.get(self.id.as_bytes())
|
self.db
|
||||||
|
.get(self.id.as_bytes())
|
||||||
.expect("lmdb error")
|
.expect("lmdb error")
|
||||||
.expect("state should never be None")
|
.expect("state should never be None")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_state_ref(&self) -> impl Deref<Target=ArchivedValue<State>> + '_ {
|
fn get_state_ref(&self) -> impl Deref<Target = ArchivedValue<State>> + '_ {
|
||||||
self.signal.lock_ref()
|
self.signal.lock_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,16 +85,22 @@ impl Inner {
|
|||||||
self.db.put(&self.id.as_bytes(), &state).unwrap();
|
self.db.put(&self.id.as_bytes(), &state).unwrap();
|
||||||
tracing::trace!("Updated DB, sending update signal");
|
tracing::trace!("Updated DB, sending update signal");
|
||||||
|
|
||||||
AUDIT.get().unwrap().log(self.id.as_str(), &format!("{}", state));
|
let res = AUDIT
|
||||||
|
.get()
|
||||||
|
.unwrap()
|
||||||
|
.log(self.id.as_str(), &format!("{}", state));
|
||||||
|
if let Err(e) = res {
|
||||||
|
tracing::error!("Writing to the audit log failed for {} {}: {e}", self.id.as_str(), state);
|
||||||
|
}
|
||||||
|
|
||||||
self.signal.set(state);
|
self.signal.set(state);
|
||||||
tracing::trace!("Sent update signal");
|
tracing::trace!("Sent update signal");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Resource {
|
pub struct Resource {
|
||||||
inner: Arc<Inner>
|
inner: Arc<Inner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Resource {
|
impl Resource {
|
||||||
@ -97,7 +112,7 @@ impl Resource {
|
|||||||
self.inner.get_state()
|
self.inner.get_state()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_state_ref(&self) -> impl Deref<Target=ArchivedValue<State>> + '_ {
|
pub fn get_state_ref(&self) -> impl Deref<Target = ArchivedValue<State>> + '_ {
|
||||||
self.inner.get_state_ref()
|
self.inner.get_state_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +124,7 @@ impl Resource {
|
|||||||
self.inner.desc.name.as_str()
|
self.inner.desc.name.as_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_signal(&self) -> impl Signal<Item=ArchivedValue<State>> {
|
pub fn get_signal(&self) -> impl Signal<Item = ArchivedValue<State>> {
|
||||||
self.inner.signal()
|
self.inner.signal()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,13 +140,13 @@ impl Resource {
|
|||||||
let state = self.get_state_ref();
|
let state = self.get_state_ref();
|
||||||
let state: &Archived<State> = state.as_ref();
|
let state: &Archived<State> = state.as_ref();
|
||||||
match &state.inner.state {
|
match &state.inner.state {
|
||||||
ArchivedStatus::Blocked(user) |
|
ArchivedStatus::Blocked(user)
|
||||||
ArchivedStatus::InUse(user) |
|
| ArchivedStatus::InUse(user)
|
||||||
ArchivedStatus::Reserved(user) |
|
| ArchivedStatus::Reserved(user)
|
||||||
ArchivedStatus::ToCheck(user) => {
|
| ArchivedStatus::ToCheck(user) => {
|
||||||
let user = Deserialize::<UserRef, _>::deserialize(user, &mut Infallible).unwrap();
|
let user = Deserialize::<UserRef, _>::deserialize(user, &mut Infallible).unwrap();
|
||||||
Some(user)
|
Some(user)
|
||||||
},
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,17 +164,18 @@ impl Resource {
|
|||||||
|
|
||||||
fn set_state(&self, state: MachineState) {
|
fn set_state(&self, state: MachineState) {
|
||||||
let mut serializer = AllocSerializer::<1024>::default();
|
let mut serializer = AllocSerializer::<1024>::default();
|
||||||
serializer.serialize_value(&state);
|
serializer.serialize_value(&state).expect("serializing a MachineState shoud be infallible");
|
||||||
let archived = ArchivedValue::new(serializer.into_serializer().into_inner());
|
let archived = ArchivedValue::new(serializer.into_serializer().into_inner());
|
||||||
self.inner.set_state(archived)
|
self.inner.set_state(archived)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_status(&self, state: Status) {
|
pub fn set_status(&self, state: Status) {
|
||||||
let old = self.inner.get_state();
|
let old = self.inner.get_state();
|
||||||
let oldref: &Archived<State> = old.as_ref();
|
let oldref: &Archived<State> = old.as_ref();
|
||||||
let previous: &Archived<Option<UserRef>> = &oldref.inner.previous;
|
let previous: &Archived<Option<UserRef>> = &oldref.inner.previous;
|
||||||
let previous = Deserialize::<Option<UserRef>, _>::deserialize(previous, &mut rkyv::Infallible)
|
let previous =
|
||||||
.expect("Infallible deserializer failed");
|
Deserialize::<Option<UserRef>, _>::deserialize(previous, &mut rkyv::Infallible)
|
||||||
|
.expect("Infallible deserializer failed");
|
||||||
let new = MachineState { state, previous };
|
let new = MachineState { state, previous };
|
||||||
self.set_state(new);
|
self.set_state(new);
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
use std::fmt;
|
use crate::config::deser_option;
|
||||||
use std::fmt::{Write};
|
|
||||||
use crate::utils::oid::ObjectIdentifier;
|
use crate::utils::oid::ObjectIdentifier;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use rkyv::{Archive, Archived, Deserialize, Infallible};
|
use rkyv::{Archive, Archived, Deserialize, Infallible};
|
||||||
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
//use crate::oidvalue;
|
//use crate::oidvalue;
|
||||||
use crate::resources::state::{State};
|
use crate::resources::state::State;
|
||||||
|
|
||||||
|
|
||||||
use crate::users::UserRef;
|
use crate::users::UserRef;
|
||||||
|
|
||||||
@ -55,6 +53,11 @@ pub enum Status {
|
|||||||
/// The status of the machine
|
/// The status of the machine
|
||||||
pub struct MachineState {
|
pub struct MachineState {
|
||||||
pub state: Status,
|
pub state: Status,
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Option::is_none",
|
||||||
|
deserialize_with = "deser_option"
|
||||||
|
)]
|
||||||
pub previous: Option<UserRef>,
|
pub previous: Option<UserRef>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,13 +83,12 @@ impl MachineState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn from(dbstate: &Archived<State>) -> Self {
|
pub fn from(dbstate: &Archived<State>) -> Self {
|
||||||
|
|
||||||
let state: &Archived<MachineState> = &dbstate.inner;
|
let state: &Archived<MachineState> = &dbstate.inner;
|
||||||
Deserialize::deserialize(state, &mut Infallible).unwrap()
|
Deserialize::deserialize(state, &mut Infallible).unwrap()
|
||||||
}
|
}
|
||||||
pub fn to_state(&self) -> State {
|
pub fn to_state(&self) -> State {
|
||||||
State {
|
State {
|
||||||
inner: self.clone()
|
inner: self.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
|
|
||||||
pub mod fabaccess;
|
pub mod fabaccess;
|
||||||
|
|
||||||
pub trait MachineModel {
|
pub trait MachineModel {}
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
|
use crate::resources::Resource;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use crate::resources::Resource;
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct Inner {
|
struct Inner {
|
||||||
id: HashMap<String, Resource>,
|
id: HashMap<String, Resource>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Inner {
|
impl Inner {
|
||||||
pub fn new(resources: impl IntoIterator<Item=Resource>) -> Self {
|
pub fn new(resources: impl IntoIterator<Item = Resource>) -> Self {
|
||||||
let mut id = HashMap::new();
|
let mut id = HashMap::new();
|
||||||
|
|
||||||
for resource in resources {
|
for resource in resources {
|
||||||
@ -19,19 +20,19 @@ impl Inner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ResourcesHandle {
|
pub struct ResourcesHandle {
|
||||||
inner: Arc<Inner>,
|
inner: Arc<Inner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResourcesHandle {
|
impl ResourcesHandle {
|
||||||
pub fn new(resources: impl IntoIterator<Item=Resource>) -> Self {
|
pub fn new(resources: impl IntoIterator<Item = Resource>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Arc::new(Inner::new(resources)),
|
inner: Arc::new(Inner::new(resources)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_all(&self) -> impl IntoIterator<Item=&Resource> {
|
pub fn list_all(&self) -> impl IntoIterator<Item = &Resource> {
|
||||||
self.inner.id.values()
|
self.inner.id.values()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
|
use rkyv::ser::Serializer;
|
||||||
|
use rkyv::ser::serializers::AllocSerializer;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::db::{ArchivedValue, RawDB, DB, AlignedAdapter};
|
use crate::db::{AlignedAdapter, ArchivedValue, RawDB, DB};
|
||||||
use lmdb::{
|
use lmdb::{DatabaseFlags, Environment, EnvironmentFlags, Transaction, WriteFlags};
|
||||||
DatabaseFlags, Environment, EnvironmentFlags, Transaction,
|
use miette::Diagnostic;
|
||||||
WriteFlags,
|
use std::fmt::Debug;
|
||||||
};
|
|
||||||
use std::{path::Path, sync::Arc};
|
use std::{path::Path, sync::Arc};
|
||||||
|
|
||||||
use crate::resources::state::State;
|
use crate::resources::state::State;
|
||||||
@ -14,8 +17,24 @@ pub struct StateDB {
|
|||||||
db: DB<AlignedAdapter<State>>,
|
db: DB<AlignedAdapter<State>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Error, Diagnostic)]
|
||||||
|
pub enum StateDBError {
|
||||||
|
#[error("opening the state db environment failed")]
|
||||||
|
#[diagnostic(
|
||||||
|
code(bffh::db::state::open_env),
|
||||||
|
help("does the parent directory for state_db exist?")
|
||||||
|
)]
|
||||||
|
OpenEnv(#[source] db::Error),
|
||||||
|
#[error("opening the state db failed")]
|
||||||
|
#[diagnostic(code(bffh::db::state::open))]
|
||||||
|
Open(#[source] db::Error),
|
||||||
|
#[error("creating the state db failed")]
|
||||||
|
#[diagnostic(code(bffh::db::state::create))]
|
||||||
|
Create(#[source] db::Error),
|
||||||
|
}
|
||||||
|
|
||||||
impl StateDB {
|
impl StateDB {
|
||||||
pub fn open_env<P: AsRef<Path>>(path: P) -> lmdb::Result<Arc<Environment>> {
|
pub fn open_env<P: AsRef<Path>>(path: P) -> Result<Arc<Environment>, StateDBError> {
|
||||||
Environment::new()
|
Environment::new()
|
||||||
.set_flags(
|
.set_flags(
|
||||||
EnvironmentFlags::WRITE_MAP
|
EnvironmentFlags::WRITE_MAP
|
||||||
@ -26,6 +45,7 @@ impl StateDB {
|
|||||||
.set_max_dbs(8)
|
.set_max_dbs(8)
|
||||||
.open(path.as_ref())
|
.open(path.as_ref())
|
||||||
.map(Arc::new)
|
.map(Arc::new)
|
||||||
|
.map_err(|e| StateDBError::OpenEnv(e.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(env: Arc<Environment>, db: RawDB) -> Self {
|
fn new(env: Arc<Environment>, db: RawDB) -> Self {
|
||||||
@ -33,30 +53,32 @@ impl StateDB {
|
|||||||
Self { env, db }
|
Self { env, db }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_with_env(env: Arc<Environment>) -> lmdb::Result<Self> {
|
pub fn open_with_env(env: Arc<Environment>) -> Result<Self, StateDBError> {
|
||||||
let db = unsafe { RawDB::open(&env, Some("state"))? };
|
let db = RawDB::open(&env, Some("state"))
|
||||||
|
.map_err(|e| StateDBError::Open(e.into()))?;
|
||||||
Ok(Self::new(env, db))
|
Ok(Self::new(env, db))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open<P: AsRef<Path>>(path: P) -> lmdb::Result<Self> {
|
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, StateDBError> {
|
||||||
let env = Self::open_env(path)?;
|
let env = Self::open_env(path)?;
|
||||||
Self::open_with_env(env)
|
Self::open_with_env(env)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_with_env(env: Arc<Environment>) -> lmdb::Result<Self> {
|
pub fn create_with_env(env: Arc<Environment>) -> Result<Self, StateDBError> {
|
||||||
let flags = DatabaseFlags::empty();
|
let flags = DatabaseFlags::empty();
|
||||||
let db = unsafe { RawDB::create(&env, Some("state"), flags)? };
|
let db = RawDB::create(&env, Some("state"), flags)
|
||||||
|
.map_err(|e| StateDBError::Create(e.into()))?;
|
||||||
|
|
||||||
Ok(Self::new(env, db))
|
Ok(Self::new(env, db))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create<P: AsRef<Path>>(path: P) -> lmdb::Result<Self> {
|
pub fn create<P: AsRef<Path>>(path: P) -> Result<Self, StateDBError> {
|
||||||
let env = Self::open_env(path)?;
|
let env = Self::open_env(path)?;
|
||||||
Self::create_with_env(env)
|
Self::create_with_env(env)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn begin_ro_txn(&self) -> Result<impl Transaction + '_, db::Error> {
|
pub fn begin_ro_txn(&self) -> Result<impl Transaction + '_, db::Error> {
|
||||||
self.env.begin_ro_txn()
|
self.env.begin_ro_txn().map_err(db::Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, key: impl AsRef<[u8]>) -> Result<Option<ArchivedValue<State>>, db::Error> {
|
pub fn get(&self, key: impl AsRef<[u8]>) -> Result<Option<ArchivedValue<State>>, db::Error> {
|
||||||
@ -67,7 +89,7 @@ impl StateDB {
|
|||||||
pub fn get_all<'txn, T: Transaction>(
|
pub fn get_all<'txn, T: Transaction>(
|
||||||
&self,
|
&self,
|
||||||
txn: &'txn T,
|
txn: &'txn T,
|
||||||
) -> Result<impl IntoIterator<Item = (&'txn [u8], ArchivedValue<State>)>, db::Error, > {
|
) -> Result<impl IntoIterator<Item = (&'txn [u8], ArchivedValue<State>)>, db::Error> {
|
||||||
self.db.get_all(txn)
|
self.db.get_all(txn)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +97,31 @@ impl StateDB {
|
|||||||
let mut txn = self.env.begin_rw_txn()?;
|
let mut txn = self.env.begin_rw_txn()?;
|
||||||
let flags = WriteFlags::empty();
|
let flags = WriteFlags::empty();
|
||||||
self.db.put(&mut txn, key, val, flags)?;
|
self.db.put(&mut txn, key, val, flags)?;
|
||||||
txn.commit()
|
Ok(txn.commit()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_map(&self, map: &std::collections::HashMap<String, State>) -> miette::Result<()> {
|
||||||
|
use miette::IntoDiagnostic;
|
||||||
|
let mut txn = self.env.begin_rw_txn().into_diagnostic()?;
|
||||||
|
let flags = WriteFlags::empty();
|
||||||
|
for (key, val) in map {
|
||||||
|
let mut serializer = AllocSerializer::<1024>::default();
|
||||||
|
serializer.serialize_value(val).into_diagnostic()?;
|
||||||
|
let serialized = ArchivedValue::new(serializer.into_serializer().into_inner());
|
||||||
|
self.db.put(&mut txn, &key.as_bytes(), &serialized, flags)?;
|
||||||
|
}
|
||||||
|
txn.commit().into_diagnostic()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dump_map(&self) -> miette::Result<std::collections::HashMap<String, State>> {
|
||||||
|
let mut map = std::collections::HashMap::new();
|
||||||
|
for (key, val) in self.get_all(&self.begin_ro_txn()?)? {
|
||||||
|
let key_str = core::str::from_utf8(&key).map_err(|_e| miette::Error::msg("state key not UTF8"))?.to_string();
|
||||||
|
let val_state: State = rkyv::Deserialize::deserialize(val.as_ref(), &mut rkyv::Infallible).unwrap();
|
||||||
|
map.insert(key_str, val_state);
|
||||||
|
}
|
||||||
|
Ok(map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,34 +129,5 @@ impl StateDB {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::resource::state::value::Vec3u8;
|
|
||||||
use crate::resource::state::value::{OID_COLOUR, OID_INTENSITY, OID_POWERED};
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn construct_state() {
|
|
||||||
let tmpdir = tempfile::tempdir().unwrap();
|
|
||||||
let mut tmppath = tmpdir.path().to_owned();
|
|
||||||
tmppath.push("db");
|
|
||||||
let db = StateDB::create(tmppath).unwrap();
|
|
||||||
let b = State::build()
|
|
||||||
.add(OID_COLOUR.clone(), Box::new(Vec3u8 { a: 1, b: 2, c: 3 }))
|
|
||||||
.add(OID_POWERED.clone(), Box::new(true))
|
|
||||||
.add(OID_INTENSITY.clone(), Box::new(1023))
|
|
||||||
.finish();
|
|
||||||
println!("({}) {:?}", b.hash(), b);
|
|
||||||
|
|
||||||
let c = State::build()
|
|
||||||
.add(OID_COLOUR.clone(), Box::new(Vec3u8 { a: 1, b: 2, c: 3 }))
|
|
||||||
.add(OID_POWERED.clone(), Box::new(true))
|
|
||||||
.add(OID_INTENSITY.clone(), Box::new(1023))
|
|
||||||
.finish();
|
|
||||||
|
|
||||||
let key = rand::random();
|
|
||||||
db.update(key, &b, &c).unwrap();
|
|
||||||
let d = db.get_input(key).unwrap().unwrap();
|
|
||||||
let e = db.get_output(key).unwrap().unwrap();
|
|
||||||
assert_eq!(&b, d.deref());
|
|
||||||
assert_eq!(&c, e.deref());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,27 @@
|
|||||||
use std::{
|
|
||||||
fmt,
|
|
||||||
hash::{
|
|
||||||
Hasher
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use rkyv::{out_field, Archive, Deserialize, Serialize};
|
||||||
use rkyv::{Archive, Deserialize, out_field, Serialize};
|
|
||||||
use serde::de::{Error, MapAccess, Unexpected};
|
use serde::de::{Error, MapAccess, Unexpected};
|
||||||
use serde::Deserializer;
|
|
||||||
use serde::ser::SerializeMap;
|
use serde::ser::SerializeMap;
|
||||||
|
use serde::Deserializer;
|
||||||
|
|
||||||
|
|
||||||
use crate::MachineState;
|
|
||||||
use crate::resources::modules::fabaccess::OID_VALUE;
|
use crate::resources::modules::fabaccess::OID_VALUE;
|
||||||
|
use crate::MachineState;
|
||||||
|
|
||||||
use crate::utils::oid::ObjectIdentifier;
|
use crate::utils::oid::ObjectIdentifier;
|
||||||
|
|
||||||
|
|
||||||
pub mod value;
|
|
||||||
pub mod db;
|
pub mod db;
|
||||||
|
pub mod value;
|
||||||
|
|
||||||
#[derive(Archive, Serialize, Deserialize)]
|
#[derive(Archive, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
|
||||||
#[archive_attr(derive(Debug))]
|
#[archive_attr(derive(Debug))]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub inner: MachineState,
|
pub inner: MachineState,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl fmt::Debug for State {
|
impl fmt::Debug for State {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let mut sf = f.debug_struct("State");
|
let mut sf = f.debug_struct("State");
|
||||||
@ -51,7 +41,8 @@ impl fmt::Display for ArchivedState {
|
|||||||
|
|
||||||
impl serde::Serialize for State {
|
impl serde::Serialize for State {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where S: serde::Serializer
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
{
|
{
|
||||||
let mut ser = serializer.serialize_map(Some(1))?;
|
let mut ser = serializer.serialize_map(Some(1))?;
|
||||||
ser.serialize_entry(OID_VALUE.deref(), &self.inner)?;
|
ser.serialize_entry(OID_VALUE.deref(), &self.inner)?;
|
||||||
@ -60,7 +51,8 @@ impl serde::Serialize for State {
|
|||||||
}
|
}
|
||||||
impl<'de> serde::Deserialize<'de> for State {
|
impl<'de> serde::Deserialize<'de> for State {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where D: Deserializer<'de>
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
deserializer.deserialize_map(StateVisitor)
|
deserializer.deserialize_map(StateVisitor)
|
||||||
}
|
}
|
||||||
@ -74,12 +66,13 @@ impl<'de> serde::de::Visitor<'de> for StateVisitor {
|
|||||||
write!(formatter, "a map from OIDs to value objects")
|
write!(formatter, "a map from OIDs to value objects")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error>
|
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
|
||||||
{
|
let oid: ObjectIdentifier = map.next_key()?.ok_or(A::Error::missing_field("oid"))?;
|
||||||
let oid: ObjectIdentifier = map.next_key()?
|
|
||||||
.ok_or(A::Error::missing_field("oid"))?;
|
|
||||||
if oid != *OID_VALUE.deref() {
|
if oid != *OID_VALUE.deref() {
|
||||||
return Err(A::Error::invalid_value(Unexpected::Other("Unknown OID"), &"OID of fabaccess state"))
|
return Err(A::Error::invalid_value(
|
||||||
|
Unexpected::Other("Unknown OID"),
|
||||||
|
&"OID of fabaccess state",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
let val: MachineState = map.next_value()?;
|
let val: MachineState = map.next_value()?;
|
||||||
Ok(State { inner: val })
|
Ok(State { inner: val })
|
||||||
@ -88,72 +81,6 @@ impl<'de> serde::de::Visitor<'de> for StateVisitor {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
|
||||||
use super::value::*;
|
use super::value::*;
|
||||||
|
use super::*;
|
||||||
pub(crate) fn gen_random() -> State {
|
}
|
||||||
let amt: u8 = rand::random::<u8>() % 20;
|
|
||||||
|
|
||||||
let mut sb = State::build();
|
|
||||||
for _ in 0..amt {
|
|
||||||
let oid = crate::utils::oid::tests::gen_random();
|
|
||||||
sb = match rand::random::<u32>()%12 {
|
|
||||||
0 => sb.add(oid, Box::new(rand::random::<bool>())),
|
|
||||||
1 => sb.add(oid, Box::new(rand::random::<u8>())),
|
|
||||||
2 => sb.add(oid, Box::new(rand::random::<u16>())),
|
|
||||||
3 => sb.add(oid, Box::new(rand::random::<u32>())),
|
|
||||||
4 => sb.add(oid, Box::new(rand::random::<u64>())),
|
|
||||||
5 => sb.add(oid, Box::new(rand::random::<u128>())),
|
|
||||||
6 => sb.add(oid, Box::new(rand::random::<i8>())),
|
|
||||||
7 => sb.add(oid, Box::new(rand::random::<i16>())),
|
|
||||||
8 => sb.add(oid, Box::new(rand::random::<i32>())),
|
|
||||||
9 => sb.add(oid, Box::new(rand::random::<i64>())),
|
|
||||||
10 => sb.add(oid, Box::new(rand::random::<i128>())),
|
|
||||||
11 => sb.add(oid, Box::new(rand::random::<Vec3u8>())),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_equal_state_is_eq() {
|
|
||||||
let stateA = State::build()
|
|
||||||
.add(OID_POWERED.clone(), Box::new(false))
|
|
||||||
.add(OID_INTENSITY.clone(), Box::new(1024))
|
|
||||||
.finish();
|
|
||||||
|
|
||||||
let stateB = State::build()
|
|
||||||
.add(OID_POWERED.clone(), Box::new(false))
|
|
||||||
.add(OID_INTENSITY.clone(), Box::new(1024))
|
|
||||||
.finish();
|
|
||||||
|
|
||||||
assert_eq!(stateA, stateB);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_unequal_state_is_ne() {
|
|
||||||
let stateA = State::build()
|
|
||||||
.add(OID_POWERED.clone(), Box::new(true))
|
|
||||||
.add(OID_INTENSITY.clone(), Box::new(512))
|
|
||||||
.finish();
|
|
||||||
|
|
||||||
let stateB = State::build()
|
|
||||||
.add(OID_POWERED.clone(), Box::new(false))
|
|
||||||
.add(OID_INTENSITY.clone(), Box::new(1024))
|
|
||||||
.finish();
|
|
||||||
|
|
||||||
assert_ne!(stateA, stateB);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_state_is_clone() {
|
|
||||||
let stateA = gen_random();
|
|
||||||
|
|
||||||
let stateB = stateA.clone();
|
|
||||||
let stateC = stateB.clone();
|
|
||||||
drop(stateA);
|
|
||||||
|
|
||||||
assert_eq!(stateC, stateB);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
use std::{hash::Hash};
|
use std::hash::Hash;
|
||||||
|
|
||||||
use ptr_meta::{DynMetadata, Pointee};
|
use ptr_meta::{DynMetadata, Pointee};
|
||||||
use rkyv::{out_field, Archive, ArchivePointee, ArchiveUnsized, Archived, ArchivedMetadata, Serialize, SerializeUnsized, RelPtr};
|
use rkyv::{
|
||||||
|
out_field, Archive, ArchivePointee, ArchiveUnsized, Archived, ArchivedMetadata, RelPtr,
|
||||||
|
Serialize, SerializeUnsized,
|
||||||
|
};
|
||||||
use rkyv_dyn::{DynError, DynSerializer};
|
use rkyv_dyn::{DynError, DynSerializer};
|
||||||
|
|
||||||
|
|
||||||
use crate::utils::oid::ObjectIdentifier;
|
use crate::utils::oid::ObjectIdentifier;
|
||||||
|
|
||||||
// Not using linkme because dynamically loaded modules
|
// Not using linkme because dynamically loaded modules
|
||||||
@ -12,11 +14,8 @@ use inventory;
|
|||||||
|
|
||||||
use rkyv::ser::{ScratchSpace, Serializer};
|
use rkyv::ser::{ScratchSpace, Serializer};
|
||||||
|
|
||||||
use serde::ser::SerializeMap;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
@ -61,7 +60,7 @@ struct NewStateBuilder {
|
|||||||
|
|
||||||
// turns into
|
// turns into
|
||||||
struct NewState {
|
struct NewState {
|
||||||
inner: ArchivedVec<ArchivedMetaBox<dyn ArchivedStateValue>>
|
inner: ArchivedVec<ArchivedMetaBox<dyn ArchivedStateValue>>,
|
||||||
}
|
}
|
||||||
impl NewState {
|
impl NewState {
|
||||||
pub fn get_value<T: TypeOid>(&self) -> Option<&T> {
|
pub fn get_value<T: TypeOid>(&self) -> Option<&T> {
|
||||||
@ -148,9 +147,9 @@ pub trait TypeOid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> SerializeDynOid for T
|
impl<T> SerializeDynOid for T
|
||||||
where
|
where
|
||||||
T: for<'a> Serialize<dyn DynSerializer + 'a>,
|
T: for<'a> Serialize<dyn DynSerializer + 'a>,
|
||||||
T::Archived: TypeOid,
|
T::Archived: TypeOid,
|
||||||
{
|
{
|
||||||
fn archived_type_oid(&self) -> &'static ObjectIdentifier {
|
fn archived_type_oid(&self) -> &'static ObjectIdentifier {
|
||||||
Archived::<T>::type_oid()
|
Archived::<T>::type_oid()
|
||||||
@ -274,10 +273,6 @@ pub struct ImplDebugInfo {
|
|||||||
/// [statevalue_register](macro@crate::statevalue_register) macro with your OID as first and type
|
/// [statevalue_register](macro@crate::statevalue_register) macro with your OID as first and type
|
||||||
/// as second parameter like so:
|
/// as second parameter like so:
|
||||||
///
|
///
|
||||||
/// ```no_run
|
|
||||||
/// struct MyStruct;
|
|
||||||
/// statevalue_register!(ObjectIdentifier::from_str("1.3.6.1.4.1.48398.612.1.14").unwrap(), MyStruct)
|
|
||||||
/// ```
|
|
||||||
pub struct ImplEntry<'a> {
|
pub struct ImplEntry<'a> {
|
||||||
id: ImplId<'a>,
|
id: ImplId<'a>,
|
||||||
data: ImplData<'a>,
|
data: ImplData<'a>,
|
||||||
@ -371,7 +366,7 @@ pub mod macros {
|
|||||||
stringify!($y)
|
stringify!($y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
@ -380,16 +375,15 @@ pub mod macros {
|
|||||||
unsafe impl $crate::resources::state::value::RegisteredImpl for $z {
|
unsafe impl $crate::resources::state::value::RegisteredImpl for $z {
|
||||||
fn vtable() -> usize {
|
fn vtable() -> usize {
|
||||||
unsafe {
|
unsafe {
|
||||||
::core::mem::transmute(ptr_meta::metadata(
|
::core::mem::transmute(ptr_meta::metadata(::core::ptr::null::<$z>()
|
||||||
::core::ptr::null::<$z>() as *const dyn $crate::resources::state::value::ArchivedStateValue
|
as *const dyn $crate::resources::state::value::ArchivedStateValue))
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn debug_info() -> $crate::resources::state::value::ImplDebugInfo {
|
fn debug_info() -> $crate::resources::state::value::ImplDebugInfo {
|
||||||
$crate::debug_info!()
|
$crate::debug_info!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
@ -801,4 +795,4 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
|
@ -1,20 +1,15 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use crate::authorization::permissions::Permission;
|
use crate::authorization::permissions::Permission;
|
||||||
use crate::authorization::roles::{Roles};
|
use crate::authorization::roles::Roles;
|
||||||
use crate::resources::Resource;
|
use crate::resources::Resource;
|
||||||
use crate::Users;
|
use crate::users::db::User;
|
||||||
use crate::users::{db, UserRef};
|
use crate::users::{db, UserRef};
|
||||||
|
use crate::Users;
|
||||||
|
use tracing::Span;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SessionManager {
|
pub struct SessionManager {
|
||||||
users: Users,
|
users: Users,
|
||||||
roles: Roles,
|
roles: Roles,
|
||||||
|
|
||||||
// cache: SessionCache // todo
|
// cache: SessionCache // todo
|
||||||
}
|
}
|
||||||
impl SessionManager {
|
impl SessionManager {
|
||||||
@ -22,24 +17,35 @@ impl SessionManager {
|
|||||||
Self { users, roles }
|
Self { users, roles }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_open(&self, parent: &Span, uid: impl AsRef<str>) -> Option<SessionHandle> {
|
||||||
|
self.users
|
||||||
|
.get_user(uid.as_ref())
|
||||||
|
.map(|user| self.open(parent, user))
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: make infallible
|
// TODO: make infallible
|
||||||
pub fn open(&self, uid: impl AsRef<str>) -> Option<SessionHandle> {
|
pub fn open(&self, parent: &Span, user: User) -> SessionHandle {
|
||||||
let uid = uid.as_ref();
|
let uid = user.id.as_str();
|
||||||
if let Some(user) = self.users.get_user(uid) {
|
let span = tracing::info_span!(
|
||||||
tracing::trace!(uid, ?user, "opening new session for user");
|
target: "bffh::api",
|
||||||
Some(SessionHandle {
|
parent: parent,
|
||||||
users: self.users.clone(),
|
"session",
|
||||||
roles: self.roles.clone(),
|
uid,
|
||||||
user: UserRef::new(user.id),
|
);
|
||||||
})
|
tracing::trace!(parent: &span, uid, ?user, "opening session");
|
||||||
} else {
|
SessionHandle {
|
||||||
None
|
span,
|
||||||
|
users: self.users.clone(),
|
||||||
|
roles: self.roles.clone(),
|
||||||
|
user: UserRef::new(user.id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SessionHandle {
|
pub struct SessionHandle {
|
||||||
|
pub span: Span,
|
||||||
|
|
||||||
pub users: Users,
|
pub users: Users,
|
||||||
pub roles: Roles,
|
pub roles: Roles,
|
||||||
|
|
||||||
@ -52,33 +58,39 @@ impl SessionHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_user(&self) -> db::User {
|
pub fn get_user(&self) -> db::User {
|
||||||
self.users.get_user(self.user.get_username()).expect("Failed to get user self")
|
self.users
|
||||||
|
.get_user(self.user.get_username())
|
||||||
|
.expect("Failed to get user self")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_disclose(&self, resource: &Resource) -> bool {
|
pub fn has_disclose(&self, resource: &Resource) -> bool {
|
||||||
if let Some(user) = self.users.get_user(self.user.get_username()) {
|
if let Some(user) = self.users.get_user(self.user.get_username()) {
|
||||||
self.roles.is_permitted(&user.userdata, &resource.get_required_privs().disclose)
|
self.roles
|
||||||
|
.is_permitted(&user.userdata, &resource.get_required_privs().disclose)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn has_read(&self, resource: &Resource) -> bool {
|
pub fn has_read(&self, resource: &Resource) -> bool {
|
||||||
if let Some(user) = self.users.get_user(self.user.get_username()) {
|
if let Some(user) = self.users.get_user(self.user.get_username()) {
|
||||||
self.roles.is_permitted(&user.userdata, &resource.get_required_privs().read)
|
self.roles
|
||||||
|
.is_permitted(&user.userdata, &resource.get_required_privs().read)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn has_write(&self, resource: &Resource) -> bool {
|
pub fn has_write(&self, resource: &Resource) -> bool {
|
||||||
if let Some(user) = self.users.get_user(self.user.get_username()) {
|
if let Some(user) = self.users.get_user(self.user.get_username()) {
|
||||||
self.roles.is_permitted(&user.userdata, &resource.get_required_privs().write)
|
self.roles
|
||||||
|
.is_permitted(&user.userdata, &resource.get_required_privs().write)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn has_manage(&self, resource: &Resource) -> bool {
|
pub fn has_manage(&self, resource: &Resource) -> bool {
|
||||||
if let Some(user) = self.users.get_user(self.user.get_username()) {
|
if let Some(user) = self.users.get_user(self.user.get_username()) {
|
||||||
self.roles.is_permitted(&user.userdata, &resource.get_required_privs().manage)
|
self.roles
|
||||||
|
.is_permitted(&user.userdata, &resource.get_required_privs().manage)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@ -90,4 +102,4 @@ impl SessionHandle {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
101
bffhd/tls.rs
101
bffhd/tls.rs
@ -1,29 +1,45 @@
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use futures_rustls::TlsAcceptor;
|
|
||||||
use rustls::{Certificate, PrivateKey, ServerConfig, SupportedCipherSuite};
|
|
||||||
use rustls::version::{TLS12, TLS13};
|
|
||||||
use tracing::{Level};
|
|
||||||
use crate::capnp::TlsListen;
|
use crate::capnp::TlsListen;
|
||||||
|
use futures_rustls::TlsAcceptor;
|
||||||
|
use miette::Diagnostic;
|
||||||
|
use rustls::version::{TLS12, TLS13};
|
||||||
|
use rustls::{Certificate, PrivateKey, ServerConfig, SupportedCipherSuite};
|
||||||
|
use thiserror::Error;
|
||||||
|
use tracing::Level;
|
||||||
|
|
||||||
use crate::keylog::KeyLogFile;
|
use crate::keylog::KeyLogFile;
|
||||||
|
use crate::tls::Error::KeyLogOpen;
|
||||||
|
|
||||||
fn lookup_cipher_suite(name: &str) -> Option<SupportedCipherSuite> {
|
fn lookup_cipher_suite(name: &str) -> Option<SupportedCipherSuite> {
|
||||||
match name {
|
match name {
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" => Some(rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" => {
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" => Some(rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
|
Some(rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
|
||||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" => Some(rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256),
|
}
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" => Some(rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256),
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" => {
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" => Some(rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
|
Some(rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384)
|
||||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" => Some(rustls::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256),
|
}
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" => {
|
||||||
|
Some(rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256)
|
||||||
|
}
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" => {
|
||||||
|
Some(rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
|
||||||
|
}
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" => {
|
||||||
|
Some(rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)
|
||||||
|
}
|
||||||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" => {
|
||||||
|
Some(rustls::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256)
|
||||||
|
}
|
||||||
"TLS13_AES_128_GCM_SHA256" => Some(rustls::cipher_suite::TLS13_AES_128_GCM_SHA256),
|
"TLS13_AES_128_GCM_SHA256" => Some(rustls::cipher_suite::TLS13_AES_128_GCM_SHA256),
|
||||||
"TLS13_AES_256_GCM_SHA384" => Some(rustls::cipher_suite::TLS13_AES_256_GCM_SHA384),
|
"TLS13_AES_256_GCM_SHA384" => Some(rustls::cipher_suite::TLS13_AES_256_GCM_SHA384),
|
||||||
"TLS13_CHACHA20_POLY1305_SHA256" => Some(rustls::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256),
|
"TLS13_CHACHA20_POLY1305_SHA256" => {
|
||||||
|
Some(rustls::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256)
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,8 +49,32 @@ pub struct TlsConfig {
|
|||||||
keylog: Option<Arc<KeyLogFile>>,
|
keylog: Option<Arc<KeyLogFile>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error, Diagnostic)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("failed to open certificate file at path {0}")]
|
||||||
|
OpenCertFile(PathBuf, #[source] io::Error),
|
||||||
|
#[error("failed to open private key file at path {0}")]
|
||||||
|
OpenKeyFile(PathBuf, #[source] io::Error),
|
||||||
|
#[error("failed to read system certs")]
|
||||||
|
SystemCertsFile(#[source] io::Error),
|
||||||
|
#[error("failed to read from key file")]
|
||||||
|
ReadKeyFile(#[source] io::Error),
|
||||||
|
#[error("private key file must contain a single PEM-encoded private key")]
|
||||||
|
KeyFileFormat,
|
||||||
|
#[error("invalid TLS version {0}")]
|
||||||
|
TlsVersion(String),
|
||||||
|
#[error("Initializing TLS context failed")]
|
||||||
|
Builder(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
rustls::Error,
|
||||||
|
),
|
||||||
|
#[error("failed to initialize key log")]
|
||||||
|
KeyLogOpen(#[source] io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
impl TlsConfig {
|
impl TlsConfig {
|
||||||
pub fn new(keylogfile: Option<impl AsRef<Path>>, warn: bool) -> io::Result<Self> {
|
pub fn new(keylogfile: Option<impl AsRef<Path>>, warn: bool) -> Result<Self, Error> {
|
||||||
let span = tracing::span!(Level::INFO, "tls");
|
let span = tracing::span!(Level::INFO, "tls");
|
||||||
let _guard = span.enter();
|
let _guard = span.enter();
|
||||||
|
|
||||||
@ -43,8 +83,11 @@ impl TlsConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(path) = keylogfile {
|
if let Some(path) = keylogfile {
|
||||||
|
let keylog = Some(
|
||||||
let keylog = Some(KeyLogFile::new(path).map(|ok| Arc::new(ok))?);
|
KeyLogFile::new(path)
|
||||||
|
.map(|ok| Arc::new(ok))
|
||||||
|
.map_err(KeyLogOpen)?,
|
||||||
|
);
|
||||||
Ok(Self { keylog })
|
Ok(Self { keylog })
|
||||||
} else {
|
} else {
|
||||||
Ok(Self { keylog: None })
|
Ok(Self { keylog: None })
|
||||||
@ -62,26 +105,31 @@ impl TlsConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_tls_acceptor(&self, config: &TlsListen) -> anyhow::Result<TlsAcceptor> {
|
pub fn make_tls_acceptor(&self, config: &TlsListen) -> Result<TlsAcceptor, Error> {
|
||||||
let span = tracing::debug_span!("tls");
|
let span = tracing::debug_span!("tls");
|
||||||
let _guard = span.enter();
|
let _guard = span.enter();
|
||||||
|
|
||||||
tracing::debug!(path = %config.certfile.as_path().display(), "reading certificates");
|
let path = config.certfile.as_path();
|
||||||
let mut certfp = BufReader::new(File::open(config.certfile.as_path())?);
|
tracing::debug!(path = %path.display(), "reading certificates");
|
||||||
let certs = rustls_pemfile::certs(&mut certfp)?
|
let mut certfp =
|
||||||
|
BufReader::new(File::open(path).map_err(|e| Error::OpenCertFile(path.into(), e))?);
|
||||||
|
let certs = rustls_pemfile::certs(&mut certfp)
|
||||||
|
.map_err(Error::SystemCertsFile)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(Certificate)
|
.map(Certificate)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
tracing::debug!(path = %config.keyfile.as_path().display(), "reading private key");
|
let path = config.keyfile.as_path();
|
||||||
let mut keyfp = BufReader::new(File::open(config.keyfile.as_path())?);
|
tracing::debug!(path = %path.display(), "reading private key");
|
||||||
let key = match rustls_pemfile::read_one(&mut keyfp)? {
|
let mut keyfp =
|
||||||
|
BufReader::new(File::open(path).map_err(|err| Error::OpenKeyFile(path.into(), err))?);
|
||||||
|
let key = match rustls_pemfile::read_one(&mut keyfp).map_err(Error::ReadKeyFile)? {
|
||||||
Some(rustls_pemfile::Item::PKCS8Key(key) | rustls_pemfile::Item::RSAKey(key)) => {
|
Some(rustls_pemfile::Item::PKCS8Key(key) | rustls_pemfile::Item::RSAKey(key)) => {
|
||||||
PrivateKey(key)
|
PrivateKey(key)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
tracing::error!("private key file invalid");
|
tracing::error!("private key file invalid");
|
||||||
anyhow::bail!("private key file must contain a PEM-encoded private key")
|
return Err(Error::KeyFileFormat);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -90,10 +138,11 @@ impl TlsConfig {
|
|||||||
.with_safe_default_kx_groups();
|
.with_safe_default_kx_groups();
|
||||||
|
|
||||||
let tls_builder = if let Some(ref min) = config.tls_min_version {
|
let tls_builder = if let Some(ref min) = config.tls_min_version {
|
||||||
match min.as_str() {
|
let v = min.to_lowercase();
|
||||||
|
match v.as_str() {
|
||||||
"tls12" => tls_builder.with_protocol_versions(&[&TLS12]),
|
"tls12" => tls_builder.with_protocol_versions(&[&TLS12]),
|
||||||
"tls13" => tls_builder.with_protocol_versions(&[&TLS13]),
|
"tls13" => tls_builder.with_protocol_versions(&[&TLS13]),
|
||||||
x => anyhow::bail!("TLS version {} is invalid", x),
|
_ => return Err(Error::TlsVersion(v)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tls_builder.with_safe_default_protocol_versions()
|
tls_builder.with_safe_default_protocol_versions()
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
use lmdb::{DatabaseFlags, Environment, RwTransaction, Transaction, WriteFlags};
|
use lmdb::{DatabaseFlags, Environment, RwTransaction, Transaction, WriteFlags};
|
||||||
use std::collections::{HashMap};
|
|
||||||
use rkyv::Infallible;
|
use rkyv::Infallible;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use anyhow::Context;
|
|
||||||
|
|
||||||
use rkyv::{Deserialize};
|
|
||||||
use rkyv::ser::Serializer;
|
|
||||||
use rkyv::ser::serializers::AllocSerializer;
|
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::db::{AlignedAdapter, ArchivedValue, DB, RawDB};
|
use crate::db::{AlignedAdapter, ArchivedValue, RawDB, DB};
|
||||||
|
use rkyv::ser::serializers::AllocSerializer;
|
||||||
|
use rkyv::ser::Serializer;
|
||||||
|
use rkyv::Deserialize;
|
||||||
|
|
||||||
|
pub use crate::db::Error;
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
@ -27,20 +28,23 @@ pub struct User {
|
|||||||
pub userdata: UserData,
|
pub userdata: UserData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hash_pw(pw: &[u8]) -> argon2::Result<String> {
|
||||||
|
let config = argon2::Config::default();
|
||||||
|
let salt: [u8; 16] = rand::random();
|
||||||
|
argon2::hash_encoded(pw, &salt, &config)
|
||||||
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
pub fn check_password(&self, pwd: &[u8]) -> anyhow::Result<bool> {
|
pub fn check_password(&self, pwd: &[u8]) -> Result<bool, argon2::Error> {
|
||||||
if let Some(ref encoded) = self.userdata.passwd {
|
if let Some(ref encoded) = self.userdata.passwd {
|
||||||
argon2::verify_encoded(encoded, pwd)
|
argon2::verify_encoded(encoded, pwd)
|
||||||
.context("Stored password is an invalid string")
|
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_plain_pw(username: &str, password: impl AsRef<[u8]>) -> Self {
|
pub fn new_with_plain_pw(username: &str, password: impl AsRef<[u8]>) -> Self {
|
||||||
let config = argon2::Config::default();
|
let hash = hash_pw(password.as_ref())
|
||||||
let salt: [u8; 16] = rand::random();
|
|
||||||
let hash = argon2::hash_encoded(password.as_ref(), &salt, &config)
|
|
||||||
.expect(&format!("Failed to hash password for {}: ", username));
|
.expect(&format!("Failed to hash password for {}: ", username));
|
||||||
tracing::debug!("Hashed pw for {} to {}", username, hash);
|
tracing::debug!("Hashed pw for {} to {}", username, hash);
|
||||||
|
|
||||||
@ -48,23 +52,30 @@ impl User {
|
|||||||
id: username.to_string(),
|
id: username.to_string(),
|
||||||
userdata: UserData {
|
userdata: UserData {
|
||||||
passwd: Some(hash),
|
passwd: Some(hash),
|
||||||
.. Default::default()
|
..Default::default()
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_pw(&mut self, password: impl AsRef<[u8]>) {
|
||||||
|
self.userdata.passwd = Some(hash_pw(password.as_ref()).expect(&format!(
|
||||||
|
"failed to update hashed password for {}",
|
||||||
|
&self.id
|
||||||
|
)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
Eq,
|
Eq,
|
||||||
Debug,
|
Debug,
|
||||||
Default,
|
Default,
|
||||||
rkyv::Archive,
|
rkyv::Archive,
|
||||||
rkyv::Serialize,
|
rkyv::Serialize,
|
||||||
rkyv::Deserialize,
|
rkyv::Deserialize,
|
||||||
serde::Serialize,
|
serde::Serialize,
|
||||||
serde::Deserialize,
|
serde::Deserialize,
|
||||||
)]
|
)]
|
||||||
/// Data on an user to base decisions on
|
/// Data on an user to base decisions on
|
||||||
///
|
///
|
||||||
@ -85,12 +96,19 @@ pub struct UserData {
|
|||||||
|
|
||||||
impl UserData {
|
impl UserData {
|
||||||
pub fn new(roles: Vec<String>) -> Self {
|
pub fn new(roles: Vec<String>) -> Self {
|
||||||
Self { roles, kv: HashMap::new(), passwd: None }
|
Self {
|
||||||
|
roles,
|
||||||
|
kv: HashMap::new(),
|
||||||
|
passwd: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn new_with_kv(roles: Vec<String>, kv: HashMap<String, String>) -> Self {
|
pub fn new_with_kv(roles: Vec<String>, kv: HashMap<String, String>) -> Self {
|
||||||
Self { roles, kv, passwd: None }
|
Self {
|
||||||
|
roles,
|
||||||
|
kv,
|
||||||
|
passwd: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -103,7 +121,7 @@ impl UserDB {
|
|||||||
// TODO: Make an userdb-specific Transaction newtype to make this safe
|
// TODO: Make an userdb-specific Transaction newtype to make this safe
|
||||||
pub unsafe fn get_rw_txn(&self) -> Result<RwTransaction, db::Error> {
|
pub unsafe fn get_rw_txn(&self) -> Result<RwTransaction, db::Error> {
|
||||||
// The returned transaction is only valid for *this* environment.
|
// The returned transaction is only valid for *this* environment.
|
||||||
self.env.begin_rw_txn()
|
Ok(self.env.begin_rw_txn()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn new(env: Arc<Environment>, db: RawDB) -> Self {
|
pub unsafe fn new(env: Arc<Environment>, db: RawDB) -> Self {
|
||||||
@ -140,7 +158,12 @@ impl UserDB {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_txn(&self, txn: &mut RwTransaction, uid: &str, user: &User) -> Result<(), db::Error> {
|
pub fn put_txn(
|
||||||
|
&self,
|
||||||
|
txn: &mut RwTransaction,
|
||||||
|
uid: &str,
|
||||||
|
user: &User,
|
||||||
|
) -> Result<(), db::Error> {
|
||||||
let mut serializer = AllocSerializer::<1024>::default();
|
let mut serializer = AllocSerializer::<1024>::default();
|
||||||
serializer.serialize_value(user).expect("rkyv error");
|
serializer.serialize_value(user).expect("rkyv error");
|
||||||
let v = serializer.into_serializer().into_inner();
|
let v = serializer.into_serializer().into_inner();
|
||||||
@ -159,20 +182,21 @@ impl UserDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_txn(&self, txn: &mut RwTransaction) -> Result<(), db::Error> {
|
pub fn clear_txn(&self, txn: &mut RwTransaction) -> Result<(), db::Error> {
|
||||||
self.db.clear(txn);
|
// TODO: why was the result ignored here?
|
||||||
Ok(())
|
self.db.clear(txn)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all(&self) -> Result<Vec<(String, User)>, db::Error> {
|
pub fn get_all(&self) -> Result<HashMap<String, UserData>, db::Error> {
|
||||||
let txn = self.env.begin_ro_txn()?;
|
let txn = self.env.begin_ro_txn()?;
|
||||||
let iter = self.db.get_all(&txn)?;
|
let iter = self.db.get_all(&txn)?;
|
||||||
let mut out = Vec::new();
|
let mut out = HashMap::new();
|
||||||
for (uid, user) in iter {
|
for (uid, user) in iter {
|
||||||
let uid = unsafe { std::str::from_utf8_unchecked(uid).to_string() };
|
let uid = unsafe { std::str::from_utf8_unchecked(uid).to_string() };
|
||||||
let user: User = Deserialize::<User, _>::deserialize(user.as_ref(), &mut Infallible).unwrap();
|
let user: User =
|
||||||
out.push((uid, user));
|
Deserialize::<User, _>::deserialize(user.as_ref(), &mut Infallible).unwrap();
|
||||||
|
out.insert(uid, user.userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
use anyhow::Context;
|
use std::fs;
|
||||||
|
|
||||||
use lmdb::{Environment, Transaction};
|
use lmdb::{Environment, Transaction};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use rkyv::{Archive, Deserialize, Infallible, Serialize};
|
use rkyv::{Archive, Deserialize, Infallible, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{Display, Formatter, Write};
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use miette::{Diagnostic, IntoDiagnostic, SourceSpan};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
pub mod db;
|
pub mod db;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use crate::users::db::UserData;
|
use crate::users::db::UserData;
|
||||||
use crate::UserDB;
|
use crate::UserDB;
|
||||||
|
|
||||||
@ -66,17 +69,20 @@ pub struct Users {
|
|||||||
userdb: &'static UserDB,
|
userdb: &'static UserDB,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Error, Diagnostic)]
|
||||||
|
#[error(transparent)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Error(#[from] pub db::Error);
|
||||||
|
|
||||||
impl Users {
|
impl Users {
|
||||||
pub fn new(env: Arc<Environment>) -> anyhow::Result<Self> {
|
pub fn new(env: Arc<Environment>) -> Result<Self, Error> {
|
||||||
let span = tracing::debug_span!("users", ?env, "Creating Users handle");
|
let span = tracing::debug_span!("users", ?env, "Creating Users handle");
|
||||||
let _guard = span.enter();
|
let _guard = span.enter();
|
||||||
|
|
||||||
let userdb = USERDB
|
let userdb = USERDB.get_or_try_init(|| {
|
||||||
.get_or_try_init(|| {
|
tracing::debug!("Global resource not yet initialized, initializing…");
|
||||||
tracing::debug!("Global resource not yet initialized, initializing…");
|
unsafe { UserDB::create(env) }
|
||||||
unsafe { UserDB::create(env) }
|
})?;
|
||||||
})
|
|
||||||
.context("Failed to open userdb")?;
|
|
||||||
|
|
||||||
Ok(Self { userdb })
|
Ok(Self { userdb })
|
||||||
}
|
}
|
||||||
@ -87,25 +93,53 @@ impl Users {
|
|||||||
|
|
||||||
pub fn get_user(&self, uid: &str) -> Option<db::User> {
|
pub fn get_user(&self, uid: &str) -> Option<db::User> {
|
||||||
tracing::trace!(uid, "Looking up user");
|
tracing::trace!(uid, "Looking up user");
|
||||||
self.userdb
|
self.userdb.get(uid).unwrap().map(|user| {
|
||||||
.get(uid)
|
Deserialize::<db::User, _>::deserialize(user.as_ref(), &mut Infallible).unwrap()
|
||||||
.unwrap()
|
})
|
||||||
.map(|user| Deserialize::<db::User, _>::deserialize(user.as_ref(), &mut Infallible).unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_user(&self, uid: &str, user: &db::User) -> Result<(), lmdb::Error> {
|
pub fn put_user(&self, uid: &str, user: &db::User) -> Result<(), crate::db::Error> {
|
||||||
tracing::trace!(uid, ?user, "Updating user");
|
tracing::trace!(uid, ?user, "Updating user");
|
||||||
self.userdb.put(uid, user)
|
self.userdb.put(uid, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn del_user(&self, uid: &str) -> Result<(), lmdb::Error> {
|
pub fn del_user(&self, uid: &str) -> Result<(), crate::db::Error> {
|
||||||
tracing::trace!(uid, "Deleting user");
|
tracing::trace!(uid, "Deleting user");
|
||||||
self.userdb.delete(uid)
|
self.userdb.delete(uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_file<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
|
pub fn load_file(&self, path_str: &str) -> miette::Result<()> {
|
||||||
let f = std::fs::read(path)?;
|
let path: &Path = Path::new(path_str);
|
||||||
let map: HashMap<String, UserData> = toml::from_slice(&f)?;
|
if path.is_dir() {
|
||||||
|
#[derive(Debug, Error, Diagnostic)]
|
||||||
|
#[error("load takes a file, not a directory")]
|
||||||
|
#[diagnostic(
|
||||||
|
code(load::file),
|
||||||
|
url("https://gitlab.com/fabinfra/fabaccess/bffh/-/issues/55")
|
||||||
|
)]
|
||||||
|
struct LoadIsDirError {
|
||||||
|
#[source_code]
|
||||||
|
src: String,
|
||||||
|
|
||||||
|
#[label("path provided")]
|
||||||
|
dir_path: SourceSpan,
|
||||||
|
|
||||||
|
#[help]
|
||||||
|
help: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(LoadIsDirError {
|
||||||
|
src: format!("--load {}", path_str),
|
||||||
|
dir_path: (7, path_str.as_bytes().len()).into(),
|
||||||
|
help: format!(
|
||||||
|
"Provide a path to a file instead, e.g. {}/users.toml",
|
||||||
|
path_str
|
||||||
|
),
|
||||||
|
})?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let f = std::fs::read(path).into_diagnostic()?;
|
||||||
|
let map: HashMap<String, UserData> = toml::from_slice(&f).into_diagnostic()?;
|
||||||
|
|
||||||
let mut txn = unsafe { self.userdb.get_rw_txn()? };
|
let mut txn = unsafe { self.userdb.get_rw_txn()? };
|
||||||
|
|
||||||
@ -135,7 +169,67 @@ impl Users {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
txn.commit()?;
|
txn.commit().map_err(crate::db::Error::from)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_map(&mut self, dump: &HashMap<String,UserData>) -> miette::Result<()> {
|
||||||
|
let mut txn = unsafe { self.userdb.get_rw_txn() }?;
|
||||||
|
|
||||||
|
self.userdb.clear_txn(&mut txn)?;
|
||||||
|
|
||||||
|
for (uid, data) in dump {
|
||||||
|
let user = db::User {
|
||||||
|
id: uid.clone(),
|
||||||
|
userdata: data.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
tracing::trace!(%uid, ?user, "Storing user object");
|
||||||
|
if let Err(e) = self.userdb.put_txn(&mut txn, uid.as_str(), &user) {
|
||||||
|
tracing::warn!(error=?e, "failed to add user")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
txn.commit().map_err(crate::db::Error::from)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dump_map(&self) -> miette::Result<HashMap<String, UserData>> {
|
||||||
|
return Ok(self.userdb.get_all()?)
|
||||||
|
}
|
||||||
|
pub fn dump_file(&self, path_str: &str, force: bool) -> miette::Result<usize> {
|
||||||
|
let path = Path::new(path_str);
|
||||||
|
let exists = path.exists();
|
||||||
|
if exists {
|
||||||
|
if !force {
|
||||||
|
#[derive(Debug, Error, Diagnostic)]
|
||||||
|
#[error("given file already exists, refusing to clobber")]
|
||||||
|
#[diagnostic(code(dump::clobber))]
|
||||||
|
struct DumpFileExists {
|
||||||
|
#[source_code]
|
||||||
|
src: String,
|
||||||
|
|
||||||
|
#[label("file provided")]
|
||||||
|
dir_path: SourceSpan,
|
||||||
|
|
||||||
|
#[help]
|
||||||
|
help: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(DumpFileExists {
|
||||||
|
src: format!("--load {}", path_str),
|
||||||
|
dir_path: (7, path_str.as_bytes().len()).into(),
|
||||||
|
help: "to force overwriting the file add `--force` as argument",
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
tracing::info!("output file already exists, overwriting due to `--force`");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut file = fs::File::create(path).into_diagnostic()?;
|
||||||
|
|
||||||
|
let users = self.dump_map()?;
|
||||||
|
let encoded = toml::ser::to_vec(&users).into_diagnostic()?;
|
||||||
|
file.write_all(&encoded[..]).into_diagnostic()?;
|
||||||
|
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
struct Locales {
|
struct Locales {
|
||||||
map: HashMap<&'static str, HashMap<&'static str, &'static str>>
|
map: HashMap<&'static str, HashMap<&'static str, &'static str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Locales {
|
impl Locales {
|
||||||
pub fn get(&self, lang: &str, msg: &str)
|
pub fn get(&self, lang: &str, msg: &str) -> Option<(&'static str, &'static str)> {
|
||||||
-> Option<(&'static str, &'static str)>
|
self.map
|
||||||
{
|
.get(msg)
|
||||||
self.map.get(msg).and_then(|map| {
|
.and_then(|map| map.get_key_value(lang).map(|(k, v)| (*k, *v)))
|
||||||
map.get_key_value(lang).map(|(k,v)| (*k, *v))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn available(&self, _msg: &str) -> &[&'static str] {
|
pub fn available(&self, _msg: &str) -> &[&'static str] {
|
||||||
@ -22,8 +18,8 @@ impl Locales {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static LANG: Lazy<Locales> = Lazy::new(|| {
|
static LANG: Lazy<Locales> = Lazy::new(|| Locales {
|
||||||
Locales { map: HashMap::new() }
|
map: HashMap::new(),
|
||||||
});
|
});
|
||||||
|
|
||||||
struct L10NString {
|
struct L10NString {
|
||||||
@ -59,4 +55,4 @@ impl l10n::Server for L10NString {
|
|||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
60
bffhd/utils/linebuffer.rs
Normal file
60
bffhd/utils/linebuffer.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
pub struct LineBuffer {
|
||||||
|
buffer: Vec<u8>,
|
||||||
|
valid: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LineBuffer {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
buffer: Vec::new(),
|
||||||
|
valid: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resize the internal Vec so that buffer.len() == buffer.capacity()
|
||||||
|
fn resize(&mut self) {
|
||||||
|
// SAFETY: Whatever is in memory is always valid as u8.
|
||||||
|
unsafe { self.buffer.set_len(self.buffer.capacity()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an (initialized but empty) writeable buffer of at least `atleast` bytes
|
||||||
|
pub fn get_mut_write(&mut self, atleast: usize) -> &mut [u8] {
|
||||||
|
let avail = self.buffer.len() - self.valid;
|
||||||
|
if avail < atleast {
|
||||||
|
self.buffer.reserve(atleast - avail);
|
||||||
|
self.resize()
|
||||||
|
}
|
||||||
|
&mut self.buffer[self.valid..]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn advance_valid(&mut self, amount: usize) {
|
||||||
|
self.valid += amount
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark `amount` bytes as 'consumed'
|
||||||
|
///
|
||||||
|
/// This will move any remaining data to the start of the buffer for future processing
|
||||||
|
pub fn consume(&mut self, amount: usize) {
|
||||||
|
assert!(amount <= self.valid);
|
||||||
|
|
||||||
|
if amount < self.valid {
|
||||||
|
self.buffer.copy_within(amount..self.valid, 0);
|
||||||
|
}
|
||||||
|
self.valid -= amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for LineBuffer {
|
||||||
|
type Target = [u8];
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.buffer[0..self.valid]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl DerefMut for LineBuffer {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.buffer[0..self.valid]
|
||||||
|
}
|
||||||
|
}
|
@ -7,4 +7,6 @@ pub mod varint;
|
|||||||
/// Localization strings
|
/// Localization strings
|
||||||
pub mod l10nstring;
|
pub mod l10nstring;
|
||||||
|
|
||||||
pub mod uuid;
|
pub mod uuid;
|
||||||
|
|
||||||
|
pub mod linebuffer;
|
||||||
|
@ -52,16 +52,16 @@
|
|||||||
//! [Object Identifiers]: https://en.wikipedia.org/wiki/Object_identifier
|
//! [Object Identifiers]: https://en.wikipedia.org/wiki/Object_identifier
|
||||||
//! [ITU]: https://en.wikipedia.org/wiki/International_Telecommunications_Union
|
//! [ITU]: https://en.wikipedia.org/wiki/International_Telecommunications_Union
|
||||||
|
|
||||||
use rkyv::{Archive, Serialize};
|
use crate::utils::varint::VarU128;
|
||||||
|
use rkyv::ser::Serializer;
|
||||||
use rkyv::vec::{ArchivedVec, VecResolver};
|
use rkyv::vec::{ArchivedVec, VecResolver};
|
||||||
|
use rkyv::{Archive, Serialize};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::ops::Deref;
|
use std::convert::TryInto;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use rkyv::ser::Serializer;
|
use std::ops::Deref;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use crate::utils::varint::VarU128;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
type Node = u128;
|
type Node = u128;
|
||||||
type VarNode = VarU128;
|
type VarNode = VarU128;
|
||||||
@ -69,8 +69,8 @@ type VarNode = VarU128;
|
|||||||
/// Convenience module for quickly importing the public interface (e.g., `use oid::prelude::*`)
|
/// Convenience module for quickly importing the public interface (e.g., `use oid::prelude::*`)
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use super::ObjectIdentifier;
|
pub use super::ObjectIdentifier;
|
||||||
pub use super::ObjectIdentifierRoot::*;
|
|
||||||
pub use super::ObjectIdentifierError;
|
pub use super::ObjectIdentifierError;
|
||||||
|
pub use super::ObjectIdentifierRoot::*;
|
||||||
pub use core::convert::{TryFrom, TryInto};
|
pub use core::convert::{TryFrom, TryInto};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +132,8 @@ impl ObjectIdentifier {
|
|||||||
let mut parsing_big_int = false;
|
let mut parsing_big_int = false;
|
||||||
let mut big_int: Node = 0;
|
let mut big_int: Node = 0;
|
||||||
for i in 1..nodes.len() {
|
for i in 1..nodes.len() {
|
||||||
if !parsing_big_int && nodes[i] < 128 {} else {
|
if !parsing_big_int && nodes[i] < 128 {
|
||||||
|
} else {
|
||||||
if big_int > 0 {
|
if big_int > 0 {
|
||||||
if big_int >= Node::MAX >> 7 {
|
if big_int >= Node::MAX >> 7 {
|
||||||
return Err(ObjectIdentifierError::IllegalChildNodeValue);
|
return Err(ObjectIdentifierError::IllegalChildNodeValue);
|
||||||
@ -149,9 +150,11 @@ impl ObjectIdentifier {
|
|||||||
Ok(Self { nodes })
|
Ok(Self { nodes })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build<B: AsRef<[Node]>>(root: ObjectIdentifierRoot, first: u8, children: B)
|
pub fn build<B: AsRef<[Node]>>(
|
||||||
-> Result<Self, ObjectIdentifierError>
|
root: ObjectIdentifierRoot,
|
||||||
{
|
first: u8,
|
||||||
|
children: B,
|
||||||
|
) -> Result<Self, ObjectIdentifierError> {
|
||||||
if first > 40 {
|
if first > 40 {
|
||||||
return Err(ObjectIdentifierError::IllegalFirstChildNode);
|
return Err(ObjectIdentifierError::IllegalFirstChildNode);
|
||||||
}
|
}
|
||||||
@ -163,7 +166,9 @@ impl ObjectIdentifier {
|
|||||||
let var: VarNode = child.into();
|
let var: VarNode = child.into();
|
||||||
vec.extend_from_slice(var.as_bytes())
|
vec.extend_from_slice(var.as_bytes())
|
||||||
}
|
}
|
||||||
Ok(Self { nodes: vec.into_boxed_slice() })
|
Ok(Self {
|
||||||
|
nodes: vec.into_boxed_slice(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -196,12 +201,14 @@ impl FromStr for ObjectIdentifier {
|
|||||||
|
|
||||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||||
let mut nodes = value.split(".");
|
let mut nodes = value.split(".");
|
||||||
let root = nodes.next()
|
let root = nodes
|
||||||
|
.next()
|
||||||
.and_then(|n| n.parse::<u8>().ok())
|
.and_then(|n| n.parse::<u8>().ok())
|
||||||
.and_then(|n| n.try_into().ok())
|
.and_then(|n| n.try_into().ok())
|
||||||
.ok_or(ObjectIdentifierError::IllegalRootNode)?;
|
.ok_or(ObjectIdentifierError::IllegalRootNode)?;
|
||||||
|
|
||||||
let first = nodes.next()
|
let first = nodes
|
||||||
|
.next()
|
||||||
.and_then(|n| parse_string_first_node(n).ok())
|
.and_then(|n| parse_string_first_node(n).ok())
|
||||||
.ok_or(ObjectIdentifierError::IllegalFirstChildNode)?;
|
.ok_or(ObjectIdentifierError::IllegalFirstChildNode)?;
|
||||||
|
|
||||||
@ -238,7 +245,7 @@ impl fmt::Debug for ObjectIdentifier {
|
|||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct ArchivedObjectIdentifier {
|
pub struct ArchivedObjectIdentifier {
|
||||||
archived: ArchivedVec<u8>
|
archived: ArchivedVec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for ArchivedObjectIdentifier {
|
impl Deref for ArchivedObjectIdentifier {
|
||||||
@ -250,8 +257,12 @@ impl Deref for ArchivedObjectIdentifier {
|
|||||||
|
|
||||||
impl fmt::Debug for ArchivedObjectIdentifier {
|
impl fmt::Debug for ArchivedObjectIdentifier {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}", &convert_to_string(self.archived.as_slice())
|
write!(
|
||||||
.unwrap_or_else(|e| format!("Invalid OID: {:?}", e)))
|
f,
|
||||||
|
"{}",
|
||||||
|
&convert_to_string(self.archived.as_slice())
|
||||||
|
.unwrap_or_else(|e| format!("Invalid OID: {:?}", e))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +286,8 @@ impl Archive for &'static ObjectIdentifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Serializer + ?Sized> Serialize<S> for ObjectIdentifier
|
impl<S: Serializer + ?Sized> Serialize<S> for ObjectIdentifier
|
||||||
where [u8]: rkyv::SerializeUnsized<S>
|
where
|
||||||
|
[u8]: rkyv::SerializeUnsized<S>,
|
||||||
{
|
{
|
||||||
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
|
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
|
||||||
ArchivedVec::serialize_from_slice(self.nodes.as_ref(), serializer)
|
ArchivedVec::serialize_from_slice(self.nodes.as_ref(), serializer)
|
||||||
@ -340,8 +352,7 @@ fn convert_to_string(nodes: &[u8]) -> Result<String, ObjectIdentifierError> {
|
|||||||
|
|
||||||
impl Into<String> for &ObjectIdentifier {
|
impl Into<String> for &ObjectIdentifier {
|
||||||
fn into(self) -> String {
|
fn into(self) -> String {
|
||||||
convert_to_string(&self.nodes)
|
convert_to_string(&self.nodes).expect("Valid OID object couldn't be serialized.")
|
||||||
.expect("Valid OID object couldn't be serialized.")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -468,16 +479,13 @@ mod serde_support {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl ser::Serialize for ArchivedObjectIdentifier {
|
impl ser::Serialize for ArchivedObjectIdentifier {
|
||||||
fn serialize<S>(
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
&self,
|
where
|
||||||
serializer: S,
|
S: ser::Serializer,
|
||||||
) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: ser::Serializer,
|
|
||||||
{
|
{
|
||||||
if serializer.is_human_readable() {
|
if serializer.is_human_readable() {
|
||||||
let encoded: String = convert_to_string(self.deref())
|
let encoded: String =
|
||||||
.expect("Failed to convert valid OID to String");
|
convert_to_string(self.deref()).expect("Failed to convert valid OID to String");
|
||||||
serializer.serialize_str(&encoded)
|
serializer.serialize_str(&encoded)
|
||||||
} else {
|
} else {
|
||||||
serializer.serialize_bytes(self.deref())
|
serializer.serialize_bytes(self.deref())
|
||||||
@ -498,30 +506,13 @@ pub(crate) mod tests {
|
|||||||
children.push(rand::random());
|
children.push(rand::random());
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectIdentifier::build(ObjectIdentifierRoot::JointIsoItuT, 25, children)
|
ObjectIdentifier::build(ObjectIdentifierRoot::JointIsoItuT, 25, children).unwrap()
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bincode_serde_roundtrip() {
|
|
||||||
let expected = ObjectIdentifier::build(
|
|
||||||
ObjectIdentifierRoot::ItuT,
|
|
||||||
0x01,
|
|
||||||
vec![1, 2, 3, 5, 8, 13, 21],
|
|
||||||
).unwrap();
|
|
||||||
let buffer: Vec<u8> = bincode::serialize(&expected).unwrap();
|
|
||||||
let actual = bincode::deserialize(&buffer).unwrap();
|
|
||||||
assert_eq!(expected, actual);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encode_binary_root_node_0() {
|
fn encode_binary_root_node_0() {
|
||||||
let expected: Vec<u8> = vec![0];
|
let expected: Vec<u8> = vec![0];
|
||||||
let oid = ObjectIdentifier::build(
|
let oid = ObjectIdentifier::build(ObjectIdentifierRoot::ItuT, 0x00, vec![]).unwrap();
|
||||||
ObjectIdentifierRoot::ItuT,
|
|
||||||
0x00,
|
|
||||||
vec![],
|
|
||||||
).unwrap();
|
|
||||||
let actual: Vec<u8> = oid.into();
|
let actual: Vec<u8> = oid.into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
@ -529,11 +520,7 @@ pub(crate) mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn encode_binary_root_node_1() {
|
fn encode_binary_root_node_1() {
|
||||||
let expected: Vec<u8> = vec![40];
|
let expected: Vec<u8> = vec![40];
|
||||||
let oid = ObjectIdentifier::build(
|
let oid = ObjectIdentifier::build(ObjectIdentifierRoot::Iso, 0x00, vec![]).unwrap();
|
||||||
ObjectIdentifierRoot::Iso,
|
|
||||||
0x00,
|
|
||||||
vec![],
|
|
||||||
).unwrap();
|
|
||||||
let actual: Vec<u8> = oid.into();
|
let actual: Vec<u8> = oid.into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
@ -541,11 +528,8 @@ pub(crate) mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn encode_binary_root_node_2() {
|
fn encode_binary_root_node_2() {
|
||||||
let expected: Vec<u8> = vec![80];
|
let expected: Vec<u8> = vec![80];
|
||||||
let oid = ObjectIdentifier::build(
|
let oid =
|
||||||
ObjectIdentifierRoot::JointIsoItuT,
|
ObjectIdentifier::build(ObjectIdentifierRoot::JointIsoItuT, 0x00, vec![]).unwrap();
|
||||||
0x00,
|
|
||||||
vec![],
|
|
||||||
).unwrap();
|
|
||||||
let actual: Vec<u8> = oid.into();
|
let actual: Vec<u8> = oid.into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
@ -557,7 +541,8 @@ pub(crate) mod tests {
|
|||||||
ObjectIdentifierRoot::ItuT,
|
ObjectIdentifierRoot::ItuT,
|
||||||
0x01,
|
0x01,
|
||||||
vec![1, 2, 3, 5, 8, 13, 21],
|
vec![1, 2, 3, 5, 8, 13, 21],
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
let actual: Vec<u8> = oid.into();
|
let actual: Vec<u8> = oid.into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
@ -572,7 +557,8 @@ pub(crate) mod tests {
|
|||||||
ObjectIdentifierRoot::JointIsoItuT,
|
ObjectIdentifierRoot::JointIsoItuT,
|
||||||
39,
|
39,
|
||||||
vec![42, 2501, 65535, 2147483647, 1235, 2352],
|
vec![42, 2501, 65535, 2147483647, 1235, 2352],
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
let actual: Vec<u8> = (oid).into();
|
let actual: Vec<u8> = (oid).into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
@ -580,11 +566,7 @@ pub(crate) mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn encode_string_root_node_0() {
|
fn encode_string_root_node_0() {
|
||||||
let expected = "0.0";
|
let expected = "0.0";
|
||||||
let oid = ObjectIdentifier::build(
|
let oid = ObjectIdentifier::build(ObjectIdentifierRoot::ItuT, 0x00, vec![]).unwrap();
|
||||||
ObjectIdentifierRoot::ItuT,
|
|
||||||
0x00,
|
|
||||||
vec![],
|
|
||||||
).unwrap();
|
|
||||||
let actual: String = (oid).into();
|
let actual: String = (oid).into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
@ -592,11 +574,7 @@ pub(crate) mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn encode_string_root_node_1() {
|
fn encode_string_root_node_1() {
|
||||||
let expected = "1.0";
|
let expected = "1.0";
|
||||||
let oid = ObjectIdentifier::build(
|
let oid = ObjectIdentifier::build(ObjectIdentifierRoot::Iso, 0x00, vec![]).unwrap();
|
||||||
ObjectIdentifierRoot::Iso,
|
|
||||||
0x00,
|
|
||||||
vec![],
|
|
||||||
).unwrap();
|
|
||||||
let actual: String = (&oid).into();
|
let actual: String = (&oid).into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
@ -604,11 +582,8 @@ pub(crate) mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn encode_string_root_node_2() {
|
fn encode_string_root_node_2() {
|
||||||
let expected = "2.0";
|
let expected = "2.0";
|
||||||
let oid = ObjectIdentifier::build(
|
let oid =
|
||||||
ObjectIdentifierRoot::JointIsoItuT,
|
ObjectIdentifier::build(ObjectIdentifierRoot::JointIsoItuT, 0x00, vec![]).unwrap();
|
||||||
0x00,
|
|
||||||
vec![],
|
|
||||||
).unwrap();
|
|
||||||
let actual: String = (&oid).into();
|
let actual: String = (&oid).into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
@ -620,7 +595,8 @@ pub(crate) mod tests {
|
|||||||
ObjectIdentifierRoot::ItuT,
|
ObjectIdentifierRoot::ItuT,
|
||||||
0x01,
|
0x01,
|
||||||
vec![1, 2, 3, 5, 8, 13, 21],
|
vec![1, 2, 3, 5, 8, 13, 21],
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
let actual: String = (&oid).into();
|
let actual: String = (&oid).into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
@ -632,40 +608,29 @@ pub(crate) mod tests {
|
|||||||
ObjectIdentifierRoot::JointIsoItuT,
|
ObjectIdentifierRoot::JointIsoItuT,
|
||||||
39,
|
39,
|
||||||
vec![42, 2501, 65535, 2147483647, 1235, 2352],
|
vec![42, 2501, 65535, 2147483647, 1235, 2352],
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
let actual: String = (&oid).into();
|
let actual: String = (&oid).into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_binary_root_node_0() {
|
fn parse_binary_root_node_0() {
|
||||||
let expected = ObjectIdentifier::build(
|
let expected = ObjectIdentifier::build(ObjectIdentifierRoot::ItuT, 0x00, vec![]);
|
||||||
ObjectIdentifierRoot::ItuT,
|
|
||||||
0x00,
|
|
||||||
vec![],
|
|
||||||
);
|
|
||||||
let actual = vec![0x00].try_into();
|
let actual = vec![0x00].try_into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_binary_root_node_1() {
|
fn parse_binary_root_node_1() {
|
||||||
let expected = ObjectIdentifier::build(
|
let expected = ObjectIdentifier::build(ObjectIdentifierRoot::Iso, 0x00, vec![]);
|
||||||
ObjectIdentifierRoot::Iso,
|
|
||||||
0x00,
|
|
||||||
vec![],
|
|
||||||
);
|
|
||||||
let actual = vec![40].try_into();
|
let actual = vec![40].try_into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_binary_root_node_2() {
|
fn parse_binary_root_node_2() {
|
||||||
let expected = ObjectIdentifier::build(
|
let expected = ObjectIdentifier::build(ObjectIdentifierRoot::JointIsoItuT, 0x00, vec![]);
|
||||||
ObjectIdentifierRoot::JointIsoItuT,
|
|
||||||
0x00,
|
|
||||||
vec![],
|
|
||||||
);
|
|
||||||
let actual = vec![80].try_into();
|
let actual = vec![80].try_into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
@ -698,33 +663,21 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_string_root_node_0() {
|
fn parse_string_root_node_0() {
|
||||||
let expected = ObjectIdentifier::build(
|
let expected = ObjectIdentifier::build(ObjectIdentifierRoot::ItuT, 0x00, vec![]);
|
||||||
ObjectIdentifierRoot::ItuT,
|
|
||||||
0x00,
|
|
||||||
vec![],
|
|
||||||
);
|
|
||||||
let actual = "0.0".try_into();
|
let actual = "0.0".try_into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_string_root_node_1() {
|
fn parse_string_root_node_1() {
|
||||||
let expected = ObjectIdentifier::build(
|
let expected = ObjectIdentifier::build(ObjectIdentifierRoot::Iso, 0x00, vec![]);
|
||||||
ObjectIdentifierRoot::Iso,
|
|
||||||
0x00,
|
|
||||||
vec![],
|
|
||||||
);
|
|
||||||
let actual = "1.0".try_into();
|
let actual = "1.0".try_into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_string_root_node_2() {
|
fn parse_string_root_node_2() {
|
||||||
let expected = ObjectIdentifier::build(
|
let expected = ObjectIdentifier::build(ObjectIdentifierRoot::JointIsoItuT, 0x00, vec![]);
|
||||||
ObjectIdentifierRoot::JointIsoItuT,
|
|
||||||
0x00,
|
|
||||||
vec![],
|
|
||||||
);
|
|
||||||
let actual = "2.0".try_into();
|
let actual = "2.0".try_into();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
@ -852,18 +805,21 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_string_large_children_ok() {
|
fn parse_string_large_children_ok() {
|
||||||
let expected =
|
let expected = ObjectIdentifier::build(
|
||||||
ObjectIdentifier::build(ObjectIdentifierRoot::JointIsoItuT,
|
ObjectIdentifierRoot::JointIsoItuT,
|
||||||
25,
|
25,
|
||||||
vec![190754093376743485973207716749546715206,
|
vec![
|
||||||
255822649272987943607843257596365752308,
|
190754093376743485973207716749546715206,
|
||||||
15843412533224453995377625663329542022,
|
255822649272987943607843257596365752308,
|
||||||
6457999595881951503805148772927347934,
|
15843412533224453995377625663329542022,
|
||||||
19545192863105095042881850060069531734,
|
6457999595881951503805148772927347934,
|
||||||
195548685662657784196186957311035194990,
|
19545192863105095042881850060069531734,
|
||||||
233020488258340943072303499291936117654,
|
195548685662657784196186957311035194990,
|
||||||
193307160423854019916786016773068715190,
|
233020488258340943072303499291936117654,
|
||||||
]).unwrap();
|
193307160423854019916786016773068715190,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let actual = "2.25.190754093376743485973207716749546715206.\
|
let actual = "2.25.190754093376743485973207716749546715206.\
|
||||||
255822649272987943607843257596365752308.\
|
255822649272987943607843257596365752308.\
|
||||||
15843412533224453995377625663329542022.\
|
15843412533224453995377625663329542022.\
|
||||||
@ -871,31 +827,27 @@ pub(crate) mod tests {
|
|||||||
19545192863105095042881850060069531734.\
|
19545192863105095042881850060069531734.\
|
||||||
195548685662657784196186957311035194990.\
|
195548685662657784196186957311035194990.\
|
||||||
233020488258340943072303499291936117654.\
|
233020488258340943072303499291936117654.\
|
||||||
193307160423854019916786016773068715190".try_into().unwrap();
|
193307160423854019916786016773068715190"
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encode_to_string() {
|
fn encode_to_string() {
|
||||||
let expected = String::from("1.2.3.4");
|
let expected = String::from("1.2.3.4");
|
||||||
let actual: String = ObjectIdentifier::build(
|
let actual: String = ObjectIdentifier::build(ObjectIdentifierRoot::Iso, 2, vec![3, 4])
|
||||||
ObjectIdentifierRoot::Iso,
|
.unwrap()
|
||||||
2,
|
.into();
|
||||||
vec![3, 4],
|
|
||||||
).unwrap()
|
|
||||||
.into();
|
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encode_to_bytes() {
|
fn encode_to_bytes() {
|
||||||
let expected = vec![0x2A, 0x03, 0x04];
|
let expected = vec![0x2A, 0x03, 0x04];
|
||||||
let actual: Vec<u8> = ObjectIdentifier::build(
|
let actual: Vec<u8> = ObjectIdentifier::build(ObjectIdentifierRoot::Iso, 2, vec![3, 4])
|
||||||
ObjectIdentifierRoot::Iso,
|
.unwrap()
|
||||||
2,
|
.into();
|
||||||
vec![3, 4],
|
|
||||||
).unwrap()
|
|
||||||
.into();
|
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
use uuid::Uuid;
|
|
||||||
use api::general_capnp::u_u_i_d::{Builder, Reader};
|
use api::general_capnp::u_u_i_d::{Builder, Reader};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub fn uuid_to_api(uuid: Uuid, mut builder: Builder) {
|
pub fn uuid_to_api(uuid: Uuid, mut builder: Builder) {
|
||||||
let [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p]
|
let [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p] = uuid.as_u128().to_ne_bytes();
|
||||||
= uuid.as_u128().to_ne_bytes();
|
let lower = u64::from_ne_bytes([a, b, c, d, e, f, g, h]);
|
||||||
let lower = u64::from_ne_bytes([a,b,c,d,e,f,g,h]);
|
let upper = u64::from_ne_bytes([i, j, k, l, m, n, o, p]);
|
||||||
let upper = u64::from_ne_bytes([i,j,k,l,m,n,o,p]);
|
|
||||||
builder.set_uuid0(lower);
|
builder.set_uuid0(lower);
|
||||||
builder.set_uuid1(upper);
|
builder.set_uuid1(upper);
|
||||||
}
|
}
|
||||||
@ -13,8 +12,8 @@ pub fn uuid_to_api(uuid: Uuid, mut builder: Builder) {
|
|||||||
pub fn api_to_uuid(reader: Reader) -> Uuid {
|
pub fn api_to_uuid(reader: Reader) -> Uuid {
|
||||||
let lower: u64 = reader.reborrow().get_uuid0();
|
let lower: u64 = reader.reborrow().get_uuid0();
|
||||||
let upper: u64 = reader.get_uuid1();
|
let upper: u64 = reader.get_uuid1();
|
||||||
let [a,b,c,d,e,f,g,h] = lower.to_ne_bytes();
|
let [a, b, c, d, e, f, g, h] = lower.to_ne_bytes();
|
||||||
let [i,j,k,l,m,n,o,p] = upper.to_ne_bytes();
|
let [i, j, k, l, m, n, o, p] = upper.to_ne_bytes();
|
||||||
let num = u128::from_ne_bytes([a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p]);
|
let num = u128::from_ne_bytes([a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p]);
|
||||||
Uuid::from_u128(num)
|
Uuid::from_u128(num)
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ impl<const N: usize> VarUInt<N> {
|
|||||||
pub const fn into_bytes(self) -> [u8; N] {
|
pub const fn into_bytes(self) -> [u8; N] {
|
||||||
self.bytes
|
self.bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> Default for VarUInt<N> {
|
impl<const N: usize> Default for VarUInt<N> {
|
||||||
@ -52,7 +51,7 @@ macro_rules! convert_from {
|
|||||||
let bytes = this.as_mut_bytes();
|
let bytes = this.as_mut_bytes();
|
||||||
|
|
||||||
let mut more = 0u8;
|
let mut more = 0u8;
|
||||||
let mut idx: usize = bytes.len()-1;
|
let mut idx: usize = bytes.len() - 1;
|
||||||
|
|
||||||
while num > 0x7f {
|
while num > 0x7f {
|
||||||
bytes[idx] = ((num & 0x7f) as u8 | more);
|
bytes[idx] = ((num & 0x7f) as u8 | more);
|
||||||
@ -65,7 +64,7 @@ macro_rules! convert_from {
|
|||||||
this.offset = idx;
|
this.offset = idx;
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! convert_into {
|
macro_rules! convert_into {
|
||||||
@ -84,7 +83,7 @@ macro_rules! convert_into {
|
|||||||
let mut shift = 0;
|
let mut shift = 0;
|
||||||
|
|
||||||
for neg in 1..=len {
|
for neg in 1..=len {
|
||||||
let idx = len-neg;
|
let idx = len - neg;
|
||||||
let val = (bytes[idx] & 0x7f) as $x;
|
let val = (bytes[idx] & 0x7f) as $x;
|
||||||
let shifted = val << shift;
|
let shifted = val << shift;
|
||||||
out |= shifted;
|
out |= shifted;
|
||||||
@ -93,7 +92,7 @@ macro_rules! convert_into {
|
|||||||
|
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_convert_from_to {
|
macro_rules! impl_convert_from_to {
|
||||||
@ -105,7 +104,7 @@ macro_rules! impl_convert_from_to {
|
|||||||
impl Into<$num> for VarUInt<$req> {
|
impl Into<$num> for VarUInt<$req> {
|
||||||
convert_into! { $num }
|
convert_into! { $num }
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_convert_from_to!(u8, 2, VarU8);
|
impl_convert_from_to!(u8, 2, VarU8);
|
||||||
@ -123,8 +122,9 @@ type VarUsize = VarU32;
|
|||||||
type VarUsize = VarU16;
|
type VarUsize = VarU16;
|
||||||
|
|
||||||
impl<T, const N: usize> From<&T> for VarUInt<N>
|
impl<T, const N: usize> From<&T> for VarUInt<N>
|
||||||
where T: Copy,
|
where
|
||||||
VarUInt<N>: From<T>
|
T: Copy,
|
||||||
|
VarUInt<N>: From<T>,
|
||||||
{
|
{
|
||||||
fn from(t: &T) -> Self {
|
fn from(t: &T) -> Self {
|
||||||
(*t).into()
|
(*t).into()
|
||||||
@ -162,4 +162,4 @@ mod tests {
|
|||||||
let expected: &[u8] = &[129, 0];
|
let expected: &[u8] = &[129, 0];
|
||||||
assert_eq!(vi.as_bytes(), expected)
|
assert_eq!(vi.as_bytes(), expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,43 @@
|
|||||||
use clap::{Arg, Command};
|
use clap::{Arg, Command, ValueHint};
|
||||||
use diflouroborane::{config, Diflouroborane};
|
use difluoroborane::{config, Difluoroborane};
|
||||||
|
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{env, io, io::Write, path::PathBuf};
|
use std::{env, io, io::Write, path::PathBuf};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use nix::NixPath;
|
use nix::NixPath;
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> miette::Result<()> {
|
||||||
// Argument parsing
|
// Argument parsing
|
||||||
// values for the name, description and version are pulled from `Cargo.toml`.
|
// values for the name, description and version are pulled from `Cargo.toml`.
|
||||||
let matches = Command::new(clap::crate_name!())
|
let matches = Command::new(clap::crate_name!())
|
||||||
.version(clap::crate_version!())
|
.version(clap::crate_version!())
|
||||||
.long_version(diflouroborane::VERSION_STRING)
|
.long_version(&*format!("{version}\n\
|
||||||
|
FabAccess {apiver}\n\
|
||||||
|
\t[{build_kind} build built on {build_time}]\n\
|
||||||
|
\t {rustc_version}\n\t {cargo_version}",
|
||||||
|
version=difluoroborane::env::PKG_VERSION,
|
||||||
|
apiver="0.3",
|
||||||
|
rustc_version=difluoroborane::env::RUST_VERSION,
|
||||||
|
cargo_version=difluoroborane::env::CARGO_VERSION,
|
||||||
|
build_time=difluoroborane::env::BUILD_TIME_3339,
|
||||||
|
build_kind=difluoroborane::env::BUILD_RUST_CHANNEL))
|
||||||
.about(clap::crate_description!())
|
.about(clap::crate_description!())
|
||||||
.arg(
|
.arg(Arg::new("config")
|
||||||
Arg::new("config")
|
|
||||||
.help("Path to the config file to use")
|
.help("Path to the config file to use")
|
||||||
.long("config")
|
.long("config")
|
||||||
.short('c')
|
.short('c')
|
||||||
.takes_value(true),
|
.takes_value(true))
|
||||||
)
|
|
||||||
.arg(Arg::new("verbosity")
|
.arg(Arg::new("verbosity")
|
||||||
.help("Increase logging verbosity")
|
.help("Increase logging verbosity")
|
||||||
.long("verbose")
|
.long("verbose")
|
||||||
.short('v')
|
.short('v')
|
||||||
.multiple_occurrences(true)
|
.multiple_occurrences(true)
|
||||||
.max_occurrences(3)
|
.max_occurrences(3)
|
||||||
.conflicts_with("quiet")
|
.conflicts_with("quiet"))
|
||||||
)
|
|
||||||
.arg(Arg::new("quiet")
|
.arg(Arg::new("quiet")
|
||||||
.help("Decrease logging verbosity")
|
.help("Decrease logging verbosity")
|
||||||
.long("quiet")
|
.long("quiet")
|
||||||
.conflicts_with("verbosity")
|
.conflicts_with("verbosity"))
|
||||||
)
|
|
||||||
.arg(Arg::new("log format")
|
.arg(Arg::new("log format")
|
||||||
.help("Use an alternative log formatter. Available: Full, Compact, Pretty")
|
.help("Use an alternative log formatter. Available: Full, Compact, Pretty")
|
||||||
.long("log-format")
|
.long("log-format")
|
||||||
@ -49,26 +51,59 @@ fn main() -> anyhow::Result<()> {
|
|||||||
.arg(
|
.arg(
|
||||||
Arg::new("print default")
|
Arg::new("print default")
|
||||||
.help("Print a default config to stdout instead of running")
|
.help("Print a default config to stdout instead of running")
|
||||||
.long("print-default"),
|
.long("print-default"))
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("check config")
|
Arg::new("check config")
|
||||||
.help("Check config for validity")
|
.help("Check config for validity")
|
||||||
.long("check"),
|
.long("check"))
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("dump")
|
Arg::new("dump-db")
|
||||||
.help("Dump all internal databases")
|
.help("Dump all internal databases")
|
||||||
.long("dump")
|
.long("dump-db")
|
||||||
.conflicts_with("load"),
|
.alias("dump")
|
||||||
|
.conflicts_with("dump-users")
|
||||||
|
.conflicts_with("load-users")
|
||||||
|
.conflicts_with("load-db")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("FILE")
|
||||||
|
.value_hint(ValueHint::AnyPath)
|
||||||
|
.default_missing_value("bffh-db.toml")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("dump-users")
|
||||||
|
.help("Dump the users db to the given file as TOML")
|
||||||
|
.long("dump-users")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("FILE")
|
||||||
|
.value_hint(ValueHint::AnyPath)
|
||||||
|
.default_missing_value("users.toml")
|
||||||
|
.conflicts_with("load-users")
|
||||||
|
.conflicts_with("load-db")
|
||||||
|
.conflicts_with("dump-db")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("force")
|
||||||
|
.help("force ops that may clobber")
|
||||||
|
.long("force")
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("load")
|
Arg::new("load-users")
|
||||||
.help("Load values into the internal databases")
|
.help("Load users into the internal databases")
|
||||||
.long("load")
|
.long("load-users")
|
||||||
|
.alias("load")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.conflicts_with("dump"),
|
.conflicts_with("dump-db")
|
||||||
)
|
.conflicts_with("load-db")
|
||||||
|
.conflicts_with("dump-users")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("load-db")
|
||||||
|
.help("Load values into the internal databases")
|
||||||
|
.long("load-db")
|
||||||
|
.takes_value(true)
|
||||||
|
.conflicts_with("dump-db")
|
||||||
|
.conflicts_with("load-users")
|
||||||
|
.conflicts_with("dump-users"))
|
||||||
.arg(Arg::new("keylog")
|
.arg(Arg::new("keylog")
|
||||||
.help("log TLS keys into PATH. If no path is specified the value of the envvar SSLKEYLOGFILE is used.")
|
.help("log TLS keys into PATH. If no path is specified the value of the envvar SSLKEYLOGFILE is used.")
|
||||||
.long("tls-key-log")
|
.long("tls-key-log")
|
||||||
@ -76,13 +111,17 @@ fn main() -> anyhow::Result<()> {
|
|||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.max_values(1)
|
.max_values(1)
|
||||||
.min_values(0)
|
.min_values(0)
|
||||||
.default_missing_value("")
|
.default_missing_value(""))
|
||||||
)
|
.try_get_matches();
|
||||||
.get_matches();
|
|
||||||
|
let matches = match matches {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(error) => error.exit(),
|
||||||
|
};
|
||||||
|
|
||||||
let configpath = matches
|
let configpath = matches
|
||||||
.value_of("config")
|
.value_of("config")
|
||||||
.unwrap_or("/etc/diflouroborane.dhall");
|
.unwrap_or("/etc/difluoroborane.dhall");
|
||||||
|
|
||||||
// Check for the --print-default option first because we don't need to do anything else in that
|
// Check for the --print-default option first because we don't need to do anything else in that
|
||||||
// case.
|
// case.
|
||||||
@ -119,18 +158,37 @@ fn main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut config = config::read(&PathBuf::from_str(configpath).unwrap()).unwrap();
|
let mut config = config::read(&PathBuf::from_str(configpath).unwrap())?;
|
||||||
|
|
||||||
if matches.is_present("dump") {
|
if matches.is_present("dump-db") {
|
||||||
unimplemented!()
|
let mut bffh = Difluoroborane::new(config)?;
|
||||||
} else if matches.is_present("load") {
|
let fname = matches.value_of("dump-db").unwrap();
|
||||||
let bffh = Diflouroborane::new(config)?;
|
bffh.dump_db(fname)?;
|
||||||
if bffh.users.load_file(matches.value_of("load").unwrap()).is_ok() {
|
return Ok(());
|
||||||
tracing::info!("loaded users from {}", matches.value_of("load").unwrap());
|
} else if matches.is_present("load-db") {
|
||||||
} else {
|
let mut bffh = Difluoroborane::new(config)?;
|
||||||
tracing::error!("failed to load users from {}", matches.value_of("load").unwrap());
|
let fname = matches.value_of("load-db").unwrap();
|
||||||
}
|
bffh.load_db(fname)?;
|
||||||
return Ok(())
|
return Ok(());
|
||||||
|
} else if matches.is_present("dump-users") {
|
||||||
|
let bffh = Difluoroborane::new(config)?;
|
||||||
|
|
||||||
|
let number = bffh.users.dump_file(
|
||||||
|
matches.value_of("dump-users").unwrap(),
|
||||||
|
matches.is_present("force"),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
tracing::info!("successfully dumped {} users", number);
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
} else if matches.is_present("load-users") {
|
||||||
|
let bffh = Difluoroborane::new(config)?;
|
||||||
|
|
||||||
|
bffh.users.load_file(matches.value_of("load-users").unwrap())?;
|
||||||
|
|
||||||
|
tracing::info!("loaded users from {}", matches.value_of("load-users").unwrap());
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
} else {
|
} else {
|
||||||
let keylog = matches.value_of("keylog");
|
let keylog = matches.value_of("keylog");
|
||||||
// When passed an empty string (i.e no value) take the value from the env
|
// When passed an empty string (i.e no value) take the value from the env
|
||||||
@ -152,7 +210,7 @@ fn main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
config.logging.format = matches.value_of("log format").unwrap_or("full").to_string();
|
config.logging.format = matches.value_of("log format").unwrap_or("full").to_string();
|
||||||
|
|
||||||
let mut bffh = Diflouroborane::new(config)?;
|
let mut bffh = Difluoroborane::new(config)?;
|
||||||
bffh.run()?;
|
bffh.run()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
51
build.rs
51
build.rs
@ -1,51 +1,4 @@
|
|||||||
use std::process::Command;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!(">>> Building version number...");
|
// Extract build-time information using the `shadow-rs` crate
|
||||||
|
shadow_rs::new().unwrap();
|
||||||
let rustc = std::env::var("RUSTC").unwrap();
|
|
||||||
let out = Command::new(rustc).arg("--version")
|
|
||||||
.output()
|
|
||||||
.expect("failed to run `rustc --version`");
|
|
||||||
let rustc_version = String::from_utf8(out.stdout)
|
|
||||||
.expect("rustc --version returned invalid UTF-8");
|
|
||||||
let rustc_version = rustc_version.trim();
|
|
||||||
println!("cargo:rustc-env=CARGO_RUSTC_VERSION={}", rustc_version);
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-env-changed=BFFHD_BUILD_TAGGED_RELEASE");
|
|
||||||
let tagged_release = option_env!("BFFHD_BUILD_TAGGED_RELEASE") == Some("1");
|
|
||||||
let version_string = if tagged_release {
|
|
||||||
format!("{version} [{rustc}]",
|
|
||||||
version = env!("CARGO_PKG_VERSION"),
|
|
||||||
rustc = rustc_version)
|
|
||||||
} else {
|
|
||||||
// Build version number using the current git commit id
|
|
||||||
let out = Command::new("git").arg("rev-list")
|
|
||||||
.args(["HEAD", "-1"])
|
|
||||||
.output()
|
|
||||||
.expect("failed to run `git rev-list HEAD -1`");
|
|
||||||
let owned_gitrev = String::from_utf8(out.stdout)
|
|
||||||
.expect("git rev-list output was not valid UTF8");
|
|
||||||
let gitrev = owned_gitrev.trim();
|
|
||||||
let abbrev = match gitrev.len(){
|
|
||||||
0 => "unknown",
|
|
||||||
_ => &gitrev[0..9],
|
|
||||||
};
|
|
||||||
|
|
||||||
let out = Command::new("git").arg("log")
|
|
||||||
.args(["-1", "--format=%as"])
|
|
||||||
.output()
|
|
||||||
.expect("failed to run `git log -1 --format=\"format:%as\"`");
|
|
||||||
let commit_date = String::from_utf8(out.stdout)
|
|
||||||
.expect("git log output was not valid UTF8");
|
|
||||||
let commit_date = commit_date.trim();
|
|
||||||
|
|
||||||
format!("{version} ({gitrev} {date}) [{rustc}]",
|
|
||||||
version=env!("CARGO_PKG_VERSION"),
|
|
||||||
gitrev=abbrev,
|
|
||||||
date=commit_date,
|
|
||||||
rustc=rustc_version)
|
|
||||||
};
|
|
||||||
println!("cargo:rustc-env=BFFHD_VERSION_STRING={}", version_string);
|
|
||||||
println!("cargo:rustc-env=BFFHD_RELEASE_STRING=\"BFFH {}\"", version_string);
|
|
||||||
}
|
}
|
||||||
|
8
cargo-cross-config
Normal file
8
cargo-cross-config
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[target.armv7-unknown-linux-gnueabihf]
|
||||||
|
linker = "arm-linux-gnueabihf-gcc"
|
||||||
|
|
||||||
|
[target.arm-unknown-linux-gnueabihf]
|
||||||
|
linker = "arm-linux-gnueabi-gcc"
|
||||||
|
|
||||||
|
[target.aarch64-unknown-linux-gnu]
|
||||||
|
linker = "aarch64-linux-gnu-gcc"
|
@ -1,39 +0,0 @@
|
|||||||
strict digraph connection {
|
|
||||||
Establish [label="TCP/SCTP connection established"];
|
|
||||||
Closed [label="TCP/SCTP connection closed"];
|
|
||||||
Open;
|
|
||||||
SASL;
|
|
||||||
Authenticated;
|
|
||||||
STARTTLS;
|
|
||||||
Encrypted;
|
|
||||||
|
|
||||||
Establish -> Open [label=open];
|
|
||||||
|
|
||||||
Open -> Closed [label=close];
|
|
||||||
|
|
||||||
Open -> SASL [label=auth];
|
|
||||||
SASL -> SASL [label=step];
|
|
||||||
// Authentication fails
|
|
||||||
SASL -> Closed [label=fails];
|
|
||||||
// Authentication succeeds
|
|
||||||
SASL -> Authenticated [label=successful];
|
|
||||||
|
|
||||||
Open -> STARTTLS [label=starttls];
|
|
||||||
// TLS wrapping succeeds
|
|
||||||
STARTTLS -> Encrypted [label=successful];
|
|
||||||
// TLS wrapping fails
|
|
||||||
STARTTLS -> Closed [label=fails];
|
|
||||||
|
|
||||||
Authenticated -> SASL_TLS [label=starttls];
|
|
||||||
SASL_TLS -> Closed [label=fails];
|
|
||||||
SASL_TLS -> AuthEnc [label=successful];
|
|
||||||
|
|
||||||
Encrypted -> TLS_SASL [label=auth];
|
|
||||||
TLS_SASL -> TLS_SASL [label=step];
|
|
||||||
TLS_SASL -> Closed [label=fails];
|
|
||||||
TLS_SASL -> AuthEnc [label=successful];
|
|
||||||
|
|
||||||
// Only authenticated connections may open RPC. For "unauth", use the `Anonymous` SASL method.
|
|
||||||
AuthEnc -> RPC [label=bootstrap];
|
|
||||||
Authenticated -> RPC [label=bootstrap];
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
# Stream initiation
|
|
||||||
|
|
||||||
In a session there are two parties: The initiating entity and the receiving
|
|
||||||
entity. This terminology does not refer to information flow but rather to the
|
|
||||||
side opening a connection respectively the one listening for connection
|
|
||||||
attempts.
|
|
||||||
In the currently envisioned use-case the initiating entity is a) a client
|
|
||||||
(i.e. interactive or batch/automated program) trying to interact in some way or
|
|
||||||
other with a server b) a server trying to exchange / request information
|
|
||||||
with/from another server (i.e. federating). The receiving entity however is
|
|
||||||
already a server.
|
|
||||||
|
|
||||||
Additionally the amount and type of clients is likely to be more diverse and
|
|
||||||
less up to date than the servers.
|
|
||||||
Conclusions I draw from this:
|
|
||||||
- Clients are more likely to implement an outdated version of the communication
|
|
||||||
protocol.
|
|
||||||
- The place for backwards-compatability should be the servers.
|
|
||||||
- Thus the client (initiating entity) should send the expected API version
|
|
||||||
first, the server then using that as a basis to decide with which API
|
|
||||||
version to answer.
|
|
||||||
|
|
||||||
# Stream negotiation
|
|
||||||
|
|
||||||
Since the receiving entity for a connection is responsible for the machines it
|
|
||||||
controls it imposes conditions for connecting either as client or as federating
|
|
||||||
server. At least every initiating entity is required to authenticate itself to
|
|
||||||
the receiving entity before attempting further actions or requesting
|
|
||||||
information. But a receiving entity can require other features, such as
|
|
||||||
transport layer encryption.
|
|
||||||
To this end a receiving entity informs the initiating entity about features that
|
|
||||||
it requires from the initiating entity before taking any further action and
|
|
||||||
features that are voluntary to negotiate but may improve qualities of the stream
|
|
||||||
(such as message compression)
|
|
||||||
|
|
||||||
A varying set of conditions implies negotiation needs to take place. Since
|
|
||||||
features potentially require a strict order (e.g. Encryption before
|
|
||||||
Authentication) negotiation has to be a multi-stage process. Further
|
|
||||||
restrictions are imposed because some features may only be offered after others
|
|
||||||
have been established (e.g. SASL authentication only becoming available after
|
|
||||||
encryption, EXTERNAL mechanism only being available to local sockets or
|
|
||||||
connections providing a certificate)
|
|
@ -1,93 +0,0 @@
|
|||||||
strict digraph state {
|
|
||||||
rank = 0
|
|
||||||
subgraph "cluster_internal_state" {
|
|
||||||
rank = 1
|
|
||||||
ctr = applied
|
|
||||||
start
|
|
||||||
[shape=doublecircle, label="BFFH"]
|
|
||||||
created
|
|
||||||
[label="Machine object created"];
|
|
||||||
start -> created;
|
|
||||||
|
|
||||||
created -> attach
|
|
||||||
[label="New state or loaded from disk"];
|
|
||||||
|
|
||||||
attach
|
|
||||||
[label="Attach actor", shape=box];
|
|
||||||
|
|
||||||
unapplied
|
|
||||||
[label="Unapplied"];
|
|
||||||
applied
|
|
||||||
[label="Applied"];
|
|
||||||
verified
|
|
||||||
[label="Verified"];
|
|
||||||
|
|
||||||
wait_apply
|
|
||||||
[label="Wait ∀ Actors", shape=box]
|
|
||||||
wait_verify
|
|
||||||
[label="Wait ∀ Actors", shape=box]
|
|
||||||
|
|
||||||
unapplied -> wait_apply -> applied;
|
|
||||||
applied -> wait_verify -> verified;
|
|
||||||
|
|
||||||
applied -> unapplied
|
|
||||||
[label="statechange received"];
|
|
||||||
verified -> unapplied
|
|
||||||
[label="statechange received"];
|
|
||||||
unapplied -> unapplied
|
|
||||||
[label="statechange received"];
|
|
||||||
|
|
||||||
unapplied -> attach -> unapplied;
|
|
||||||
applied -> attach -> unapplied;
|
|
||||||
verified -> attach -> unapplied;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
subgraph "cluster_actor" {
|
|
||||||
rank = 1
|
|
||||||
center = actor_applied
|
|
||||||
actor_start
|
|
||||||
[shape=doublecircle, label="Actor"];
|
|
||||||
actor_fresh
|
|
||||||
[label="Actor was just constructed"];
|
|
||||||
actor_start -> actor_fresh;
|
|
||||||
|
|
||||||
actor_attached
|
|
||||||
[label="Attached"];
|
|
||||||
actor_unapplied
|
|
||||||
[label="Unapplied"];
|
|
||||||
actor_applied
|
|
||||||
[label="Applied"];
|
|
||||||
actor_verified
|
|
||||||
[label="Verified"];
|
|
||||||
|
|
||||||
wait_initial
|
|
||||||
[label="Recv", shape=box];
|
|
||||||
wait_state
|
|
||||||
[label="Recv", shape=box];
|
|
||||||
|
|
||||||
actor_fresh -> wait_initial -> actor_attached;
|
|
||||||
|
|
||||||
actor_attached -> actor_applied
|
|
||||||
[label="initialize/apply"];
|
|
||||||
actor_unapplied -> actor_applied
|
|
||||||
[label="apply"];
|
|
||||||
actor_applied -> actor_verified
|
|
||||||
[label="verify"];
|
|
||||||
|
|
||||||
actor_unapplied -> wait_state;
|
|
||||||
actor_applied -> wait_state;
|
|
||||||
actor_verified -> wait_state;
|
|
||||||
|
|
||||||
wait_state -> actor_unapplied;
|
|
||||||
}
|
|
||||||
|
|
||||||
attach -> wait_initial
|
|
||||||
[label="Send initial state to that actor", style=dotted]
|
|
||||||
unapplied -> wait_state
|
|
||||||
[label="Send new state to all actors", style=dotted];
|
|
||||||
actor_applied -> wait_apply
|
|
||||||
[label="Confirm apply", style=dotted];
|
|
||||||
actor_verified -> wait_verify
|
|
||||||
[label="Confirm verify", style=dotted];
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
# API-Testsetup
|
|
||||||
|
|
||||||
wirklich nur um das API zu testen. ATM implementiert: machines::* & machine::read, authenticate
|
|
||||||
|
|
||||||
1. Ein mosquitto o.ä MQTT Server starten
|
|
||||||
1. Datenbanken füllen: `cargo run -- -c examples/bffh.dhall --load=examples`
|
|
||||||
1. Daemon starten: `cargo run -- -c examples/bffh.dhall`
|
|
||||||
1. ???
|
|
||||||
1. PROFIT!
|
|
||||||
|
|
||||||
A dockerized version of this example can be found in the docker subdirectory
|
|
@ -1,179 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
def on_free(args, actor_name):
|
|
||||||
"""
|
|
||||||
Function called when the state of the connected machine changes to Free
|
|
||||||
again
|
|
||||||
"""
|
|
||||||
if args.verbose > 2:
|
|
||||||
print("on_free called!")
|
|
||||||
|
|
||||||
if actor_name == "DoorControl1":
|
|
||||||
# Do whatever you want to do in case `DoorControl1` is returned back to free.
|
|
||||||
# Keep in mind that process actors should return quickly to not miss
|
|
||||||
# updates, so if you need to do things that take a while fork a new
|
|
||||||
# process e.g. with the `subprocess` Module
|
|
||||||
print("I'm locking door 1!")
|
|
||||||
pass
|
|
||||||
elif actor_name == "DoorControl2":
|
|
||||||
print("I'm locking door 2!")
|
|
||||||
pass # Close a different door
|
|
||||||
else:
|
|
||||||
if not args.quiet:
|
|
||||||
print("process called with unknown id %s for state `Free`" % actor_name)
|
|
||||||
# It's a good idea to exit with an error code in case something
|
|
||||||
# unexpected happens.
|
|
||||||
# The process module logs everything printed to stdout by actors into
|
|
||||||
# the server log, but marks them as `Error` in case the actor process
|
|
||||||
# exits with a code != 0, making debugging somewhat easier.
|
|
||||||
exit(-1)
|
|
||||||
|
|
||||||
def on_use(args, actor_name, user_id):
|
|
||||||
"""
|
|
||||||
Function called when an user takes control of the connected machine
|
|
||||||
|
|
||||||
user_id contains the UID of the user now using the machine
|
|
||||||
"""
|
|
||||||
if args.verbose > 2:
|
|
||||||
print("on_use called!")
|
|
||||||
if actor_name == "DoorControl1":
|
|
||||||
print("I'm opening door 1 for 10 seconds!")
|
|
||||||
pass # Open door one
|
|
||||||
elif actor_name == "DoorControl2":
|
|
||||||
print("I'm opening door 2 for 10 seconds!")
|
|
||||||
pass # Open a different door
|
|
||||||
else:
|
|
||||||
if not args.quiet:
|
|
||||||
print("process called with unknown id %s for state `InUse`" % actor_name)
|
|
||||||
# It's a good idea to exit with an error code in case something
|
|
||||||
# unexpected happens.
|
|
||||||
# The process module logs everything printed to stdout by actors into
|
|
||||||
# the server log, but marks them as `Error` in case the actor process
|
|
||||||
# exits with a code != 0, making debugging somewhat easier.
|
|
||||||
exit(-1)
|
|
||||||
|
|
||||||
def on_tocheck(args, actor_name, user_id):
|
|
||||||
"""
|
|
||||||
Function called when an user returns control and the connected machine is
|
|
||||||
configured to go to state `ToCheck` instead of `Free` in that case.
|
|
||||||
|
|
||||||
user_id contains the UID of the manager expected to check the machine.
|
|
||||||
The user that used the machine beforehand has to be taken from the last
|
|
||||||
user field using the API (via e.g. the mobile app)
|
|
||||||
"""
|
|
||||||
if args.verbose > 2:
|
|
||||||
print("on_tocheck called!")
|
|
||||||
if not args.quiet:
|
|
||||||
print("process called with unexpected combo id %s and state 'ToCheck'" % actor_name)
|
|
||||||
exit(-1)
|
|
||||||
|
|
||||||
def on_blocked(args, actor_name, user_id):
|
|
||||||
"""
|
|
||||||
Function called when an manager marks the connected machine as `Blocked`
|
|
||||||
|
|
||||||
user_id contains the UID of the manager that blocked the machine
|
|
||||||
"""
|
|
||||||
if args.verbose > 2:
|
|
||||||
print("on_blocked called!")
|
|
||||||
if not args.quiet:
|
|
||||||
print("process called with unexpected combo id %s and state 'Blocked'" % actor_name)
|
|
||||||
exit(-1)
|
|
||||||
|
|
||||||
def on_disabled(args, actor_name):
|
|
||||||
"""
|
|
||||||
Function called when the connected machine is marked `Disabled`
|
|
||||||
"""
|
|
||||||
if not args.quiet:
|
|
||||||
print("process called with unexpected combo id %s and state 'Disabled'" % actor_name)
|
|
||||||
exit(-1)
|
|
||||||
|
|
||||||
def on_reserve(args, actor_name, user_id):
|
|
||||||
"""
|
|
||||||
Function called when the connected machine has been reserved by somebody.
|
|
||||||
|
|
||||||
user_id contains the UID of the reserving user.
|
|
||||||
"""
|
|
||||||
if not args.quiet:
|
|
||||||
print("process called with unexpected combo id %s and state 'Reserved'" % actor_name)
|
|
||||||
exit(-1)
|
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
|
||||||
"""
|
|
||||||
Python example actor
|
|
||||||
|
|
||||||
This is an example how to use the `process` actor type to run a Python script.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if args.verbose is not None:
|
|
||||||
if args.verbose == 1:
|
|
||||||
print("verbose output enabled")
|
|
||||||
elif args.verbose == 2:
|
|
||||||
print("loud output enabled!")
|
|
||||||
elif args.verbose == 3:
|
|
||||||
print("LOUD output enabled!!!")
|
|
||||||
elif args.verbose > 4:
|
|
||||||
print("Okay stop you're being ridiculous.")
|
|
||||||
sys.exit(-2)
|
|
||||||
else:
|
|
||||||
args.verbose = 0
|
|
||||||
|
|
||||||
# You could also check the actor name here and call different functions
|
|
||||||
# depending on that variable instead of passing it to the state change
|
|
||||||
# methods.
|
|
||||||
|
|
||||||
new_state = args.state
|
|
||||||
if new_state == "free":
|
|
||||||
on_free(args, args.name)
|
|
||||||
elif new_state == "inuse":
|
|
||||||
on_use(args, args.name, args.userid)
|
|
||||||
elif new_state == "tocheck":
|
|
||||||
on_tocheck(args, args.name, args.userid)
|
|
||||||
elif new_state == "blocked":
|
|
||||||
on_blocked(args, args.name, args.userid)
|
|
||||||
elif new_state == "disabled":
|
|
||||||
on_disabled(args, args.name)
|
|
||||||
elif new_state == "reserved":
|
|
||||||
on_reserve(args, args.name, args.userid)
|
|
||||||
else:
|
|
||||||
print("Process actor called with unknown state %s" % new_state)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
# Parameters are passed to the Process actor as follows:
|
|
||||||
# 1. the contents of params.args, split by whitespace as separate args
|
|
||||||
# 2. the configured id of the actor (e.g. "DoorControl1")
|
|
||||||
# 3. the new state as one of [free|inuse|tocheck|blocked|disabled|reserved]
|
|
||||||
|
|
||||||
parser.add_argument("-q", "--quiet", help="be less verbose", action="store_true")
|
|
||||||
parser.add_argument("-v", "--verbose", help="be more verbose", action="count")
|
|
||||||
|
|
||||||
parser.add_argument("name",
|
|
||||||
help="name of this actor as configured in bffh.dhall"
|
|
||||||
)
|
|
||||||
|
|
||||||
# We parse the new state using subparsers so that we only require a userid
|
|
||||||
# in case it's a state that sets one.
|
|
||||||
subparsers = parser.add_subparsers(required=True, dest="state")
|
|
||||||
|
|
||||||
parser_free = subparsers.add_parser("free")
|
|
||||||
|
|
||||||
parser_inuse = subparsers.add_parser("inuse")
|
|
||||||
parser_inuse.add_argument("userid", help="The user that is now using the machine")
|
|
||||||
|
|
||||||
parser_tocheck = subparsers.add_parser("tocheck")
|
|
||||||
parser_tocheck.add_argument("userid", help="The user that should go check the machine")
|
|
||||||
|
|
||||||
parser_blocked = subparsers.add_parser("blocked")
|
|
||||||
parser_blocked.add_argument("userid", help="The user that marked the machine as blocked")
|
|
||||||
|
|
||||||
parser_disabled = subparsers.add_parser("disabled")
|
|
||||||
|
|
||||||
parser_reserved = subparsers.add_parser("reserved")
|
|
||||||
parser_reserved.add_argument("userid", help="The user that reserved the machine")
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
main(args)
|
|
@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
echo "Bash actor called with $@" > /tmp/act
|
|
||||||
echo "Bash actor called with: $@"
|
|
@ -1,234 +0,0 @@
|
|||||||
{- Main configuration file for bffh
|
|
||||||
- ================================
|
|
||||||
-
|
|
||||||
- In this configuration file you configure almost all parts of how bffh operates, but most importantly:
|
|
||||||
- * Machines
|
|
||||||
- * Initiators and Actors
|
|
||||||
- * Which Initiators and Actors relate to which machine(s)
|
|
||||||
- * Roles and the permissions granted by them
|
|
||||||
-}
|
|
||||||
|
|
||||||
-- The config is in the configuration format/language dhall. You can find more information about dhall over at
|
|
||||||
-- https://dhall-lang.org
|
|
||||||
|
|
||||||
-- (Our) Dhall is somewhat similar to JSON and YAML in that it expects a top-level object containing the
|
|
||||||
-- configuration values
|
|
||||||
{
|
|
||||||
-- Configure the addresses and ports bffh listens on
|
|
||||||
listens = [
|
|
||||||
-- BFFH binds a port for every listen object in this array.
|
|
||||||
-- Each listen object is of the format { address = <STRING>, port = <INTEGER> }
|
|
||||||
-- If you don't specify a port bffh will use the default of `59661`
|
|
||||||
-- 'address' can be a IP address or a hostname
|
|
||||||
-- If bffh can not bind a port for the specified combination if will log an error but *continue with the remaining ports*
|
|
||||||
{ address = "127.0.0.1", port = 59661 },
|
|
||||||
{ address = "::1", port = 59661 },
|
|
||||||
{ address = "steak.fritz.box", port = 59661 }
|
|
||||||
],
|
|
||||||
|
|
||||||
-- Configure TLS. BFFH requires a PEM-encoded certificate and the associated key as two separate files
|
|
||||||
certfile = "examples/self-signed-cert.pem",
|
|
||||||
keyfile = "examples/self-signed-key.pem",
|
|
||||||
|
|
||||||
-- BFFH right now requires a running MQTT broker.
|
|
||||||
mqtt_url = "tcp://localhost:1883",
|
|
||||||
|
|
||||||
-- Path to the database file for bffh. bffh will in fact create two files; ${db_path} and ${db_path}.lock.
|
|
||||||
-- BFFH will *not* create any directories so ensure that the directory exists and the user running bffh has write
|
|
||||||
-- access into them.
|
|
||||||
db_path = "/tmp/bffh",
|
|
||||||
|
|
||||||
-- Audit log path. Bffh will log state changes into this file, one per line.
|
|
||||||
-- Audit log entries are for now JSON:
|
|
||||||
-- {"timestamp":1641497361,"machine":"Testmachine","state":{"state":{"InUse":{"uid":"Testuser","subuid":null,"realm":null}}}}
|
|
||||||
auditlog_path = "/tmp/bffh.audit",
|
|
||||||
|
|
||||||
-- In dhall you can also easily import definitions from other files, e.g. you could write
|
|
||||||
-- roles = ./roles.dhall
|
|
||||||
roles = {
|
|
||||||
-- Role definitions
|
|
||||||
-- A role definition is of the form
|
|
||||||
-- rolename = {
|
|
||||||
-- parents = [<list of role names to inherit from>],
|
|
||||||
-- permissions = [<list of perm rules>],
|
|
||||||
-- }
|
|
||||||
--
|
|
||||||
-- Role names are case sensitive, so RoleName != rolename.
|
|
||||||
--
|
|
||||||
-- If you want either parents or permissions to be empty its best to completely skip it:
|
|
||||||
testrole = {
|
|
||||||
permissions = [ "lab.some.admin" ]
|
|
||||||
},
|
|
||||||
somerole = {
|
|
||||||
parents = ["testparent"],
|
|
||||||
-- "Permissions" are formatted as Perm Rules, so you can use the wildcards '*' and '+'
|
|
||||||
permissions = [ "lab.test.*" ]
|
|
||||||
},
|
|
||||||
-- Roles can inherit from each other. In that case a member of e.g. 'somerole' that inherits from
|
|
||||||
-- 'testparent' will have all the permissions of 'somerole' AND 'testparent' assigned to them.
|
|
||||||
-- Right now permissions are stricly additive so you can't take a permission away in a child role that a parent
|
|
||||||
-- role grants.
|
|
||||||
testparent = {
|
|
||||||
permissions = [
|
|
||||||
"lab.some.write",
|
|
||||||
"lab.some.read",
|
|
||||||
"lab.some.disclose"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
-- Configure machines
|
|
||||||
-- "Machines" (which in future will be more appropiately named "resources") are the main thing bffh is concerned
|
|
||||||
-- with.
|
|
||||||
-- You can define an almost limitless amount of machines (well 2^64 - 1, so 18_446_744_073_709_551_615 to be precise)
|
|
||||||
-- Each of these machines can then have several "actors" and "initiators" assigned
|
|
||||||
machines = {
|
|
||||||
Testmachine = {
|
|
||||||
-- A machine comes with two "names". The id above ("Testmachine") and the "name" ("MachineA").
|
|
||||||
-- The id is what you'll use in the config format and is strictly limited to alphanumeric characters and '_'
|
|
||||||
-- and must begin with a letter. Most importantly you CAN NOT use '-' or spaces in an identifier
|
|
||||||
-- (dhall makes this technically possible but you can break things in subtle ways)
|
|
||||||
|
|
||||||
-- REQUIRED. The "name" of a machine is what will be presented to humans. It can contain all unicode
|
|
||||||
-- including spaces and nonprintable characters.
|
|
||||||
-- A name SHOULD be short but unique.
|
|
||||||
name = "MachineA",
|
|
||||||
|
|
||||||
-- OPTIONAL. A description can be assigned to machines. It will also only be shown to humans. Thus it is
|
|
||||||
-- once again limited only to unicode. If you want to provide your users with important additional
|
|
||||||
-- information other than the name this is the place to do it.
|
|
||||||
description = "A test machine",
|
|
||||||
|
|
||||||
-- OPTIONAL. If you have a wiki going into more detail how to use a certain machine or what to keep in
|
|
||||||
-- mind when using it you can provide a URL here that will be presented to users.
|
|
||||||
wiki = "https://wiki.example.org/machineA",
|
|
||||||
|
|
||||||
-- OPTIONAL. You can assign categories to machines to allow clients to group/filter machines by them.
|
|
||||||
category = "Testcategory",
|
|
||||||
|
|
||||||
-- REQUIRED.
|
|
||||||
-- Each machine MUST have *all* Permission levels assigned to it.
|
|
||||||
-- Permissions aren't PermRules as used in the 'roles' definitions but must be precise without wildcards.
|
|
||||||
-- Permission levels aren't additive, so a user having 'manage' permission does not automatically get
|
|
||||||
-- 'read' or 'write' permission.
|
|
||||||
|
|
||||||
-- (Note, disclose is not fully implemented at the moment)
|
|
||||||
-- Users lacking 'disclose' will not be informed about this machine in any way and it will be hidden from
|
|
||||||
-- them in the client. Usually the best idea is to assign 'read' and 'disclose' to the same permission.
|
|
||||||
disclose = "lab.test.read",
|
|
||||||
|
|
||||||
-- Users lacking 'read' will be shown a machine including name, description, category and wiki but not
|
|
||||||
-- it's current state. The current user is not disclosed.
|
|
||||||
read = "lab.test.read",
|
|
||||||
|
|
||||||
-- The 'write' permission allows to 'use' the machine.
|
|
||||||
write = "lab.test.write",
|
|
||||||
|
|
||||||
-- Manage represents the 'superuser' permission. Users with this permission can force set any state and
|
|
||||||
-- read out the current user
|
|
||||||
manage = "lab.test.admin"
|
|
||||||
},
|
|
||||||
Another = {
|
|
||||||
wiki = "test_another",
|
|
||||||
category = "test",
|
|
||||||
disclose = "lab.test.read",
|
|
||||||
manage = "lab.test.admin",
|
|
||||||
name = "Another",
|
|
||||||
read = "lab.test.read",
|
|
||||||
write = "lab.test.write"
|
|
||||||
},
|
|
||||||
Yetmore = {
|
|
||||||
description = "Yet more test machines",
|
|
||||||
disclose = "lab.test.read",
|
|
||||||
manage = "lab.test.admin",
|
|
||||||
name = "Yetmore",
|
|
||||||
read = "lab.test.read",
|
|
||||||
write = "lab.test.write"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
-- Actor configuration. Actors are how bffh affects change in the real world by e.g. switching a power socket
|
|
||||||
-- using a shelly
|
|
||||||
actors = {
|
|
||||||
-- Actors similarly to machines have an 'id'. This id (here "Shelly1234") is limited to Alphanumeric ASCII
|
|
||||||
-- and must begin with a letter.
|
|
||||||
Shelly1234 = {
|
|
||||||
-- Actors are modular pieces of code that are loaded as required. The "Shelly" module will send
|
|
||||||
-- activation signals to a shelly switched power socket over MQTT
|
|
||||||
module = "Shelly",
|
|
||||||
-- Actors can have arbitrary parameters passed to them, varying by actor module.
|
|
||||||
params = {
|
|
||||||
-- For Shelly you can configure the MQTT topic segment it uses. Shellies listen to a specific topic
|
|
||||||
-- containing their name (which is usually of the form "shelly_<id>" but can be changed).
|
|
||||||
-- If you do not configure a topic here the actor will use it's 'id' (in this case "Shelly1234").
|
|
||||||
topic = "Topic1234"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Bash = {
|
|
||||||
-- The "Process" module runs a given script or command on state change.
|
|
||||||
-- bffh invoces the given cmd as `$ ${cmd} ${args} ${id} ${state}` so e.g. as
|
|
||||||
-- `$ ./examples/actor.sh your ad could be here Bash inuse`
|
|
||||||
module = "Process",
|
|
||||||
params = {
|
|
||||||
-- which is configured by the (required) 'cmd' parameter. Paths are relative to PWD of bffh. Systemd
|
|
||||||
-- and similar process managers may change this PWD so it's usually the most future-proof to use
|
|
||||||
-- absolute paths.
|
|
||||||
cmd = "./examples/actor.sh",
|
|
||||||
-- You can pass static args in here, these will be passed to every invocation of the command by this actor.
|
|
||||||
-- args passed here are split by whitespace, so these here will be passed as 5 separate arguments
|
|
||||||
args = "your ad could be here"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
DoorControl1 = {
|
|
||||||
-- This actor calls the actor.py script in examples/
|
|
||||||
-- It gets passed it's own name, so you can have several actors
|
|
||||||
-- from the same script.
|
|
||||||
-- If you need to pass more arguments to the command you can use the `args` key in
|
|
||||||
-- `params` as is done with the actor `Bash`
|
|
||||||
module = "Process",
|
|
||||||
-- the `args` are passed in front of all other parameters so they are best suited to
|
|
||||||
-- optional parameters like e.g. the verboseness
|
|
||||||
params = { cmd = "./examples/actor.py", args = "-vvv" }
|
|
||||||
},
|
|
||||||
DoorControl2 = {
|
|
||||||
module = "Process",
|
|
||||||
params = { cmd = "./examples/actor.py" }
|
|
||||||
},
|
|
||||||
DoorControl3 = {
|
|
||||||
-- This is an example for how it looks like if an actor is misconfigured.
|
|
||||||
-- the actor.py doesn't know anything about DoorControl3 and, if this actor is enabled,
|
|
||||||
-- will return with an error showing up in the server logs.
|
|
||||||
module = "Process",
|
|
||||||
params = { cmd = "./examples/actor.py" }
|
|
||||||
},
|
|
||||||
|
|
||||||
Bash2 = { module = "Process", params = { cmd = "./examples/actor.sh" , args = "this is a different one" }},
|
|
||||||
FailBash = { module = "Process", params = { cmd = "./examples/fail-actor.sh" }}
|
|
||||||
},
|
|
||||||
|
|
||||||
-- Linkng up machines to actors
|
|
||||||
-- Actors need to be connected to machines to be useful. A machine can be connected to multiple actors, but one
|
|
||||||
-- actor can only be connected to one machine.
|
|
||||||
actor_connections = [
|
|
||||||
{ machine = "Testmachine", actor = "Shelly1234" },
|
|
||||||
{ machine = "Another", actor = "Bash" },
|
|
||||||
{ machine = "Yetmore", actor = "Bash2" },
|
|
||||||
{ machine = "Yetmore", actor = "FailBash"}
|
|
||||||
],
|
|
||||||
|
|
||||||
-- Initiators are configured almost the same way as Actors, refer to actor documentation for more details
|
|
||||||
-- The below '{=}' is what you need if you want to define *no* initiators at all and only use the API with apps
|
|
||||||
-- to let people use machines.
|
|
||||||
initiators = {=},
|
|
||||||
-- The "Dummy" initiator will try to use and return a machine as the given user every few seconds. It's good to
|
|
||||||
-- test your system but will spam your log so is disabled by default.
|
|
||||||
--initiators = { Initiator = { module = "Dummy", params = { uid = "Testuser" } } },
|
|
||||||
|
|
||||||
-- Linking up machines to initiators. Similar to actors a machine can have several initiators assigned but an
|
|
||||||
-- initiator can only be assigned to one machine.
|
|
||||||
-- The below is once again how you have to define *no* initiators.
|
|
||||||
init_connections = [] : List { machine : Text, initiator : Text }
|
|
||||||
--init_connections = [{ machine = "Testmachine", initiator = "Initiator" }]
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
# API-Testsetup, aber mit Docker
|
|
||||||
|
|
||||||
wirklich nur um das API zu testen. ATM implementiert: machines::* & machine::read, authenticate
|
|
||||||
|
|
||||||
* run `docker-compose up` in this directory
|
|
@ -1,42 +0,0 @@
|
|||||||
-- { actor_connections = [] : List { _1 : Text, _2 : Text }
|
|
||||||
{ actor_connections = [{ _1 = "Testmachine", _2 = "Actor" }]
|
|
||||||
, actors =
|
|
||||||
{ Actor = { module = "Shelly", params = {=} }
|
|
||||||
}
|
|
||||||
, init_connections = [] : List { _1 : Text, _2 : Text }
|
|
||||||
--, init_connections = [{ _1 = "Initiator", _2 = "Testmachine" }]
|
|
||||||
, initiators =
|
|
||||||
{ Initiator = { module = "Dummy", params = {=} }
|
|
||||||
}
|
|
||||||
, listens =
|
|
||||||
[ { address = "::", port = Some 59661 }
|
|
||||||
]
|
|
||||||
, machines =
|
|
||||||
{ Testmachine =
|
|
||||||
{ description = Some "A test machine"
|
|
||||||
, disclose = "lab.test.read"
|
|
||||||
, manage = "lab.test.admin"
|
|
||||||
, name = "Testmachine"
|
|
||||||
, read = "lab.test.read"
|
|
||||||
, write = "lab.test.write"
|
|
||||||
},
|
|
||||||
Another =
|
|
||||||
{ description = Some "Another test machine"
|
|
||||||
, disclose = "lab.test.read"
|
|
||||||
, manage = "lab.test.admin"
|
|
||||||
, name = "Another"
|
|
||||||
, read = "lab.test.read"
|
|
||||||
, write = "lab.test.write"
|
|
||||||
},
|
|
||||||
Yetmore =
|
|
||||||
{ description = Some "Yet more test machines"
|
|
||||||
, disclose = "lab.test.read"
|
|
||||||
, manage = "lab.test.admin"
|
|
||||||
, name = "Yetmore"
|
|
||||||
, read = "lab.test.read"
|
|
||||||
, write = "lab.test.write"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
, mqtt_url = "tcp://mqtt:1883"
|
|
||||||
, db_path = "/tmp/bffh"
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
Testuser = "secret"
|
|
@ -1,19 +0,0 @@
|
|||||||
[anotherrole]
|
|
||||||
|
|
||||||
[testrole]
|
|
||||||
permissions = [
|
|
||||||
"lab.test.*"
|
|
||||||
]
|
|
||||||
|
|
||||||
[somerole]
|
|
||||||
parents = ["testparent/lmdb"]
|
|
||||||
permissions = [
|
|
||||||
"lab.some.admin"
|
|
||||||
]
|
|
||||||
|
|
||||||
[testparent]
|
|
||||||
permissions = [
|
|
||||||
"lab.some.write",
|
|
||||||
"lab.some.read",
|
|
||||||
"lab.some.disclose",
|
|
||||||
]
|
|
@ -1,11 +0,0 @@
|
|||||||
[Testuser]
|
|
||||||
# Define them in roles.toml as well
|
|
||||||
roles = ["somerole/lmdb", "testrole/lmdb"]
|
|
||||||
|
|
||||||
# If two or more users want to use the same machine at once the higher prio
|
|
||||||
# wins
|
|
||||||
priority = 0
|
|
||||||
|
|
||||||
# You can add whatever random data you want.
|
|
||||||
# It will get stored in the `kv` field in UserData.
|
|
||||||
noot = "noot!"
|
|
@ -1,13 +0,0 @@
|
|||||||
version: "3.8"
|
|
||||||
services:
|
|
||||||
bffh:
|
|
||||||
image: registry.gitlab.com/fabinfra/fabaccess/bffh:dev-latest
|
|
||||||
ports:
|
|
||||||
- "59661:59661"
|
|
||||||
volumes:
|
|
||||||
# generate a sample config.toml by running "docker run registry.gitlab.com/fabinfra/fabaccess/bffh:dev-latest --print-default > examples/config.toml" from the project root. You may have to delete the ipv6 listen section.
|
|
||||||
- "./config:/etc/bffh"
|
|
||||||
links:
|
|
||||||
- mqtt
|
|
||||||
mqtt:
|
|
||||||
image: eclipse-mosquitto:1.6.13
|
|
@ -1,11 +0,0 @@
|
|||||||
# Integration tests with Docker
|
|
||||||
|
|
||||||
## How it works
|
|
||||||
* spawns 2 instances of our bffh container and required mqqt broker
|
|
||||||
* spawns an additional debian to run a shell
|
|
||||||
* the containers can reach each other by their hostname
|
|
||||||
|
|
||||||
## How to start
|
|
||||||
|
|
||||||
* run `docker-compose up --exit-code-from test-manager` in this directory
|
|
||||||
* this will kill all containers when
|
|
@ -1,20 +0,0 @@
|
|||||||
{ actor_connections = [{ _1 = "Testmachine", _2 = "Actor" }]
|
|
||||||
, actors =
|
|
||||||
{ Actor = { module = "Shelly", params = {=} }
|
|
||||||
}
|
|
||||||
, init_connections = [{ _1 = "Initiator", _2 = "Testmachine" }]
|
|
||||||
, initiators =
|
|
||||||
{ Initiator = { module = "Dummy", params = {=} }
|
|
||||||
}
|
|
||||||
, listens = [{ address = "::", port = Some 59661 }]
|
|
||||||
, machines =
|
|
||||||
{ Testmachine =
|
|
||||||
{ description = Some "A test machine"
|
|
||||||
, disclose = "lab.test.read"
|
|
||||||
, manage = "lab.test.admin"
|
|
||||||
, name = "Testmachine"
|
|
||||||
, read = "lab.test.read"
|
|
||||||
, write = "lab.test.write"
|
|
||||||
} }
|
|
||||||
, mqtt_url = "tcp://mqtt-a:1883"
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user