mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2025-04-21 19:56:25 +02:00
Compare commits
No commits in common. "b08d57d14de255da6704e87134b82c8619d7e369" and "646aae8ef59a37a632692d535a9f0f89201eb5fa" have entirely different histories.
b08d57d14d
...
646aae8ef5
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,4 +1,4 @@
|
|||||||
[submodule "schema"]
|
[submodule "schema"]
|
||||||
path = api/schema
|
path = api/schema
|
||||||
url = https://gitlab.com/fabinfra/fabaccess/fabaccess-api
|
url = ../fabaccess-api
|
||||||
branch = main
|
branch = main
|
||||||
|
11
examples/README.md
Normal file
11
examples/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# 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
|
179
examples/actor.py
Executable file
179
examples/actor.py
Executable file
@ -0,0 +1,179 @@
|
|||||||
|
#!/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)
|
4
examples/actor.sh
Executable file
4
examples/actor.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
echo "Bash actor called with $@" > /tmp/act
|
||||||
|
echo "Bash actor called with: $@"
|
237
examples/bffh.dhall
Normal file
237
examples/bffh.dhall
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
{- 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" }]
|
||||||
|
|
||||||
|
instanceurl = "https://example.com",
|
||||||
|
spacename = "examplespace"
|
||||||
|
}
|
5
examples/docker/basic/README.md
Normal file
5
examples/docker/basic/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# 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
|
42
examples/docker/basic/config/bffh.dhall
Normal file
42
examples/docker/basic/config/bffh.dhall
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
-- { 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
examples/docker/basic/config/pass.toml
Normal file
1
examples/docker/basic/config/pass.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
Testuser = "secret"
|
19
examples/docker/basic/config/roles.toml
Normal file
19
examples/docker/basic/config/roles.toml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[anotherrole]
|
||||||
|
|
||||||
|
[testrole]
|
||||||
|
permissions = [
|
||||||
|
"lab.test.*"
|
||||||
|
]
|
||||||
|
|
||||||
|
[somerole]
|
||||||
|
parents = ["testparent/lmdb"]
|
||||||
|
permissions = [
|
||||||
|
"lab.some.admin"
|
||||||
|
]
|
||||||
|
|
||||||
|
[testparent]
|
||||||
|
permissions = [
|
||||||
|
"lab.some.write",
|
||||||
|
"lab.some.read",
|
||||||
|
"lab.some.disclose",
|
||||||
|
]
|
11
examples/docker/basic/config/users.toml
Normal file
11
examples/docker/basic/config/users.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[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!"
|
13
examples/docker/basic/docker-compose.yaml
Normal file
13
examples/docker/basic/docker-compose.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
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
|
11
examples/docker/integration/README.md
Normal file
11
examples/docker/integration/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# 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
|
20
examples/docker/integration/config_a/bffh.dhall
Normal file
20
examples/docker/integration/config_a/bffh.dhall
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{ 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"
|
||||||
|
}
|
1
examples/docker/integration/config_a/pass.toml
Normal file
1
examples/docker/integration/config_a/pass.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
Testuser = "secret"
|
20
examples/docker/integration/config_a/roles.toml
Normal file
20
examples/docker/integration/config_a/roles.toml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[testrole]
|
||||||
|
name = "Testrole"
|
||||||
|
permissions = [
|
||||||
|
"lab.test.*"
|
||||||
|
]
|
||||||
|
|
||||||
|
[somerole]
|
||||||
|
name = "Somerole"
|
||||||
|
parents = ["testparent%lmdb"]
|
||||||
|
permissions = [
|
||||||
|
"lab.some.admin"
|
||||||
|
]
|
||||||
|
|
||||||
|
[testparent]
|
||||||
|
name = "Testparent"
|
||||||
|
permissions = [
|
||||||
|
"lab.some.write",
|
||||||
|
"lab.some.read",
|
||||||
|
"lab.some.disclose",
|
||||||
|
]
|
11
examples/docker/integration/config_a/users.toml
Normal file
11
examples/docker/integration/config_a/users.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[Testuser]
|
||||||
|
# Define them in roles.toml as well
|
||||||
|
roles = []
|
||||||
|
|
||||||
|
# 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!"
|
20
examples/docker/integration/config_b/bffh.dhall
Normal file
20
examples/docker/integration/config_b/bffh.dhall
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{ 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-b:1883"
|
||||||
|
}
|
1
examples/docker/integration/config_b/pass.toml
Normal file
1
examples/docker/integration/config_b/pass.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
Testuser = "secret"
|
20
examples/docker/integration/config_b/roles.toml
Normal file
20
examples/docker/integration/config_b/roles.toml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[testrole]
|
||||||
|
name = "Testrole"
|
||||||
|
permissions = [
|
||||||
|
"lab.test.*"
|
||||||
|
]
|
||||||
|
|
||||||
|
[somerole]
|
||||||
|
name = "Somerole"
|
||||||
|
parents = ["testparent%lmdb"]
|
||||||
|
permissions = [
|
||||||
|
"lab.some.admin"
|
||||||
|
]
|
||||||
|
|
||||||
|
[testparent]
|
||||||
|
name = "Testparent"
|
||||||
|
permissions = [
|
||||||
|
"lab.some.write",
|
||||||
|
"lab.some.read",
|
||||||
|
"lab.some.disclose",
|
||||||
|
]
|
11
examples/docker/integration/config_b/users.toml
Normal file
11
examples/docker/integration/config_b/users.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[Testuser]
|
||||||
|
# Define them in roles.toml as well
|
||||||
|
roles = []
|
||||||
|
|
||||||
|
# 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!"
|
26
examples/docker/integration/docker-compose.yaml
Normal file
26
examples/docker/integration/docker-compose.yaml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
version: "3.8"
|
||||||
|
services:
|
||||||
|
bffh-a:
|
||||||
|
image: registry.gitlab.com/fabinfra/fabaccess/bffh:dev-latest
|
||||||
|
command: ["sh", "-c", "difluoroborane -c /etc/bffh/bffh.dhall --load=/etc/bffh; difluoroborane -c /etc/bffh/bffh.dhall"]
|
||||||
|
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_a:/etc/bffh"
|
||||||
|
links:
|
||||||
|
- mqtt-a
|
||||||
|
mqtt-a:
|
||||||
|
image: eclipse-mosquitto
|
||||||
|
bffh-b:
|
||||||
|
image: registry.gitlab.com/fabinfra/fabaccess/bffh:dev-latest
|
||||||
|
command: ["sh", "-c", "difluoroborane -c /etc/bffh/bffh.dhall --load=/etc/bffh; difluoroborane -c /etc/bffh/bffh.dhall"]
|
||||||
|
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_b:/etc/bffh"
|
||||||
|
links:
|
||||||
|
- mqtt-b
|
||||||
|
mqtt-b:
|
||||||
|
image: eclipse-mosquitto
|
||||||
|
|
||||||
|
test-manager:
|
||||||
|
image: debian
|
||||||
|
tty: true
|
4
examples/fail-actor.sh
Executable file
4
examples/fail-actor.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
echo "This is some error output" > /dev/stderr
|
||||||
|
exit 115
|
13
examples/init.py
Executable file
13
examples/init.py
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
while True:
|
||||||
|
print('{ "state": { "1.3.6.1.4.1.48398.612.2.4": { "state": "Free" } } }')
|
||||||
|
sys.stdout.flush()
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
print('{ "state": { "1.3.6.1.4.1.48398.612.2.4": { "state": { "InUse": { "id": "Testuser" } } } } }')
|
||||||
|
sys.stdout.flush()
|
||||||
|
time.sleep(2)
|
19
examples/self-signed-cert.pem
Normal file
19
examples/self-signed-cert.pem
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDFzCCAf+gAwIBAgIUDr+1F3zzyza+soLtiurKEXW9pGIwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwGzEZMBcGA1UEAwwQYmZmaC1kZXZlbG9wbWVudDAeFw0yMTEyMDkxODQ2Mjla
|
||||||
|
Fw0zMTEyMDkxODQ2MjlaMBsxGTAXBgNVBAMMEGJmZmgtZGV2ZWxvcG1lbnQwggEi
|
||||||
|
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGwNy7yGaURR08dWfoDmnJeyx1
|
||||||
|
0FVRmozoGCIb3Oj6c2t+84QUxqTdknE7Cdcz5Wi1o0x2CWPZG4z1vgTaCcJVhcME
|
||||||
|
hxn+7eK1NtDQEjs8Ojs7uaraVvooIe8R7jat0qs7Dmf8RO9P0I4MMZlvijhI7aLw
|
||||||
|
0C6vNsr1ebeppIiwO5aUuCGuKqxJGghHeqZv18ZcPayunyNrxMC6uyX7y6nUVkfq
|
||||||
|
x0m9gDsN112Iv9Dd/ZE5Gxivm8jZvVUGZgJD2szK7zbeCDeo5aU3cRWfYaoN0QDx
|
||||||
|
AKmo4bjciJzfMDDgvcIBq9lGS3FxEv394Mc5YX/ZdP+KRTiHcYCXfBzr3B6HAgMB
|
||||||
|
AAGjUzBRMB0GA1UdDgQWBBTtUvvWXlo5tU8cEoxbs5UJdodOVDAfBgNVHSMEGDAW
|
||||||
|
gBTtUvvWXlo5tU8cEoxbs5UJdodOVDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
|
||||||
|
DQEBCwUAA4IBAQAB3IxRnWi/LrxCvlHNaWEi3ZvlbN+KpegWZeKtjHwQoizhR/Fi
|
||||||
|
SMC7z4y6trJE7LXUOSb9Pu8QQSvpVQZd3W4XCPZMl10Lt7iV8vc3pVviCseDnT9r
|
||||||
|
X1gXdbeuyYm9lE8KtlhX03jD/CiEx7Qe/q8Rc20AQIKAAJzvl7sXU2tmJ5kpzMEO
|
||||||
|
v5czlLaX2ajlD/QMgNBUuDyw6wPo3wx9Khph484RygN2LHeT6kfu/PBiF0dGDTUu
|
||||||
|
mgxg4K0GfwTcHgtz5Bag/HyuuJEKx8Wv7jth59PyKPT+lMVBznxIm3gLS5U+Nnr1
|
||||||
|
uAws8dgLXRlPo5HJORuJCcZWVBClruZUpyDT
|
||||||
|
-----END CERTIFICATE-----
|
28
examples/self-signed-key.pem
Normal file
28
examples/self-signed-key.pem
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDGwNy7yGaURR08
|
||||||
|
dWfoDmnJeyx10FVRmozoGCIb3Oj6c2t+84QUxqTdknE7Cdcz5Wi1o0x2CWPZG4z1
|
||||||
|
vgTaCcJVhcMEhxn+7eK1NtDQEjs8Ojs7uaraVvooIe8R7jat0qs7Dmf8RO9P0I4M
|
||||||
|
MZlvijhI7aLw0C6vNsr1ebeppIiwO5aUuCGuKqxJGghHeqZv18ZcPayunyNrxMC6
|
||||||
|
uyX7y6nUVkfqx0m9gDsN112Iv9Dd/ZE5Gxivm8jZvVUGZgJD2szK7zbeCDeo5aU3
|
||||||
|
cRWfYaoN0QDxAKmo4bjciJzfMDDgvcIBq9lGS3FxEv394Mc5YX/ZdP+KRTiHcYCX
|
||||||
|
fBzr3B6HAgMBAAECggEAS5DGG6ssvRCt9e+ZatQYCl+HXt+voJAHJLMQPNG3zokV
|
||||||
|
hLXnMNL5mbh0zoKGTJfbQLvudS5KxR/BbykoxRFSzptFszH+gztEp6tIpuNXnCVz
|
||||||
|
odiMiejpwVptf763EU14hsKKbJJ0/j6H00EEWjEOB0Q6YB52sW0+qyf02U3SHlZN
|
||||||
|
k2PZYkpHi3YCONtOGj7jOdW7M3RfvcBNg9EW7fZc1KkiRAlscUAYLMkKcOLevil0
|
||||||
|
lUuF/JWj4iH22Oq6+JeSiecf6izF6lyIGvMXPry+woa8Iq0BBdmbZsK7r/Pa+wlz
|
||||||
|
+E6xHGn2rcyrnYB2pPc+RfHhYlodaOo69DxAYlRYaQKBgQDxnKySmlcfHe8lMqR4
|
||||||
|
2LvqMyGjNRo2+q9VZYej2mvr6+TGyd7Op/fRJ1t5f9DgtINomNQYSOtYAsPiEnl+
|
||||||
|
43z2N/Rdh6XSmOj4gLkDeiYNSpy86L/F39uCWZpkkqiy2zxYLWOs15MA0GWOtQGh
|
||||||
|
dz4cM0b/jyZOdHZR//L5WiMLawKBgQDSltEfQKqCEKJTqp4SU9BCHQmyheIjepY2
|
||||||
|
eKakgcsjpFjRBK2VrUalDLOQV74rtd7wp8F8kqqPJpRshb+oVuDCg6JB17UxYd34
|
||||||
|
iB+5cMdLRpg8f0HOqcYz4KOql2QhJQhFc3jzY7n1piPEhFO/MqFwmlUB4RdgJ3ix
|
||||||
|
HqX+F/T8VQKBgGos76l9KcwC25T9LEnu9KV20tFmBJ8kiuh8NZ9L3SFQCLlS/RbT
|
||||||
|
uZOwOAKsqJ4WtajBgHMrmECU9n/inoGkdsW80SZI9hYWHEsYRjXA9/ffUgGyRpQu
|
||||||
|
S8h8l9yalogC0AHv8F2EXpV8/yQ3ZwAN5r19yzWDMtJHW7etQplRgxUBAoGAGeOy
|
||||||
|
t+3iSHU1D6YlIsmtC8O4Int1LrluaCnzCrxuNeaJiMDTelhAHCBwnuk6lvMYAmwN
|
||||||
|
THxXfZvXmXPj+RUdMqyuMPwM6ZJHkLtjcw/bYHTAWIeolnimxk/yrxFHnQ+Jcchd
|
||||||
|
cUasYPfY49sE1Lerw0Ul+EIs9oRDwTqsW42kb7UCgYEA2y+oc7Fz2eq38hSbTfy7
|
||||||
|
OcATtny+xQ1+4IELtQIP7VctkMInJs57J+vS//IT41P0L2K1YjvL8RacnvG7yMvP
|
||||||
|
GnwHBcKgvL6zuoy11I3zPPYtbKGwcJoVGomPX7W0csfl4gdST3uugd9iCDEB8NsS
|
||||||
|
QmOYM/dk8x8aWpndBjRF5ig=
|
||||||
|
-----END PRIVATE KEY-----
|
16
examples/users.toml
Normal file
16
examples/users.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[Testuser]
|
||||||
|
# These roles have to be defined in 'bffh.dhall'.
|
||||||
|
# Non-existant roles will not crash the server but print a `WARN` level message in the
|
||||||
|
# server log in the form "Did not find role somerole/internal while trying to tally".
|
||||||
|
roles = ["somerole", "testrole"]
|
||||||
|
|
||||||
|
# The password will be hashed using argon2id on load time and is not available in plaintext afterwards.
|
||||||
|
passwd = "secret"
|
||||||
|
|
||||||
|
# You can add whatever random data you want.
|
||||||
|
# It will get stored in the `kv` field in UserData.
|
||||||
|
# This is not used for anything at the moment
|
||||||
|
noot = "noot!"
|
||||||
|
|
||||||
|
# Store the card specific AES key in kv userdata
|
||||||
|
cardkey = "7ab8704a61b5317e1fe4cae9e3e1fd8d"
|
Loading…
x
Reference in New Issue
Block a user