Skip to content

Commit

Permalink
Support app-defined ACLs via user-data table and whoAmI RPC (#590)
Browse files Browse the repository at this point in the history
* Add whoAmI and whoIs RPCs

* Name cert files [user|member]0-2, not 1-3

* Remove manual offsetting from txregulatorclient

* Separate local name from CCF-retrieved ID

* Remove manual offset from demo script too

* Auto stringify

* Add user_data field in UserInfo, settable by members

* USERS table should be whitelisted

* Trying to log most types from lua is an error

* Add privilege model to txregulator app - WIP

* Semantic indentation

* Format

* Add permissions for demo script

* who_is should return a WhoIs::Out

* Schema for new RPCs

* Fix IDs in memberclient

* Address PR comments

* Document adding users + user-data

* Move new info to existing Adding Users section
  • Loading branch information
eddyashton authored Nov 26, 2019
1 parent 8ad9299 commit 14167d3
Show file tree
Hide file tree
Showing 21 changed files with 559 additions and 120 deletions.
55 changes: 46 additions & 9 deletions samples/apps/txregulator/app/txregulator.lua
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,34 @@ return {
return tx_id
end
function env.get_privileges(caller_id)
local users_table = gov_tables["ccf.users"]
local user_info = users_table:get(caller_id)
if user_info ~= nil then
local user_data = user_info.user_data
if user_data ~= nil then
return user_data.privileges
end
end
return nil
end
function env.can_register_regulators(caller_id)
local privileges = env.get_privileges(caller_id)
if privileges ~= nil then
return privileges.REGISTER_REGULATORS == true
end
return false
end
function env.can_register_banks(caller_id)
local privileges = env.get_privileges(caller_id)
if privileges ~= nil then
return privileges.REGISTER_BANKS == true
end
return false
end
--
-- BANK ENDPOINTS
--
Expand Down Expand Up @@ -129,12 +157,17 @@ return {
end
function env.register_bank()
reg_v = env.reg_table():get(args.caller_id)
if not env.can_register_banks(args.caller_id) then
return env.jerr(env.error_codes.INVALID_CALLER_ID, "User " .. args.caller_id .. " is not permitted to register new banks")
end
local bank_id = args.params.bank_id
reg_v = env.reg_table():get(bank_id)
if reg_v then
return env.jerr(env.error_codes.INVALID_CALLER_ID, "User is already registered as a regulator")
return env.jerr(env.error_codes.INVALID_PARAMS, "User " .. bank_id .. " is already registered as a regulator - not permitted to also be a bank")
end
env.bank_table():put(args.caller_id, args.params.country)
return env.jsucc(args.caller_id)
env.bank_table():put(bank_id, args.params.country)
return env.jsucc(bank_id)
end
function env.get_bank()
Expand Down Expand Up @@ -172,13 +205,17 @@ return {
--
function env.register_regulator()
bank_v = env.bank_table():get(args.caller_id)
if bank_v then
return env.jerr(env.error_codes.INVALID_CALLER_ID, "User is already registered as a bank")
if not env.can_register_regulators(args.caller_id) then
return env.jerr(env.error_codes.INVALID_CALLER_ID, "User " .. args.caller_id .. " is not permitted to register new regulators")
end
env.reg_table():put(args.caller_id, {args.params.country, args.params.script, args.params.name})
return env.jsucc(args.caller_id)
local reg_id = args.params.regulator_id
bank_v = env.bank_table():get(reg_id)
if bank_v then
return env.jerr(env.error_codes.INVALID_PARAMS, "User " .. reg_id .. " is already registered as a bank - not permitted to also be a regulator")
end
env.reg_table():put(reg_id, {args.params.country, args.params.script, args.params.name})
return env.jsucc(reg_id)
end
function env.get_regulator()
Expand Down
109 changes: 77 additions & 32 deletions samples/apps/txregulator/clients/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,23 @@
import subprocess


class AppUser:
def __init__(self, network, name, country):
self.name = name
self.country = country

primary, _ = network.find_primary()

network.create_users([self.name])
network.consortium.add_users(primary, [self.name])

with primary.user_client(user_id=self.name) as client:
self.ccf_id = client.rpc("whoAmI", {}).result["caller_id"]

def __str__(self):
return f"{self.ccf_id} ({self.name})"


def run(args):
hosts = ["localhost"]

Expand All @@ -21,6 +38,34 @@ def run(args):
network.start_and_join(args)
primary, others = network.find_nodes()

regulators = [AppUser(network, "FCA", "GB"), AppUser(network, "SEC", "FR")]
banks = [
AppUser(network, f"bank{country}", country)
for country in ("US", "GB", "GR", "FR")
]

# Give regulators permissions to register regulators and banks
for regulator in regulators:
proposal_result, error = network.consortium.propose(
0,
primary,
f"""
return Calls:call(
"set_user_data",
{{
user_id = {regulator.ccf_id},
user_data = {{
privileges = {{
REGISTER_REGULATORS = true,
REGISTER_BANKS = true,
}}
}}
}}
)
""",
)
network.consortium.vote_using_majority(primary, proposal_result["id"])

if args.run_poll:
with open("revealed.log", "a+") as stdout:
subprocess.Popen(
Expand All @@ -29,6 +74,8 @@ def run(args):
f"{os.path.realpath(os.path.dirname(__file__))}/poll.py",
f"--host={primary.host}",
f"--port={primary.rpc_port}",
f"--regulator-name={regulators[0].name}",
f"--bank-name={banks[0].name}",
],
stdout=stdout,
)
Expand All @@ -48,65 +95,63 @@ def run(args):
data = []
with open(args.lua_script, "r") as f:
data = f.readlines()
script = "".join(data)

regulators = [
(0, "GB", script, "FCA"),
(
1,
"FR",
"if tonumber(amt) > 15000 then return true else return false end",
"SEC",
),
]
banks = [(2, "US", 99), (3, "GB", 29), (4, "GR", 99), (5, "FR", 29)]

scripts = {}
scripts["FCA"] = "".join(data)
scripts[
"SEC"
] = "if tonumber(amt) > 15000 then return true else return false end"

for regulator in regulators:
with primary.user_client(format="msgpack", user_id=regulator[0] + 1) as c:
with primary.user_client(format="msgpack", user_id=regulator.name) as c:
check = infra.checker.Checker()

check(
c.rpc(
"REG_register",
{
"country": regulator[1],
"script": regulator[2],
"name": regulator[3],
"regulator_id": regulator.ccf_id,
"country": regulator.country,
"script": scripts[regulator.name],
"name": regulator.name,
},
),
result=regulator[0],
result=regulator.ccf_id,
)
check(
c.rpc("REG_get", {"id": regulator[0]}),
c.rpc("REG_get", {"id": regulator.ccf_id}),
result=[
regulator[1].encode(),
regulator[2].encode(),
regulator[3].encode(),
regulator.country,
scripts[regulator.name],
regulator.name,
],
)

LOG.debug(f"User {regulator[0]} successfully registered as regulator")
LOG.debug(f"User {regulator} successfully registered as regulator")

for bank in banks:
with primary.user_client(format="msgpack", user_id=bank[0] + 1) as c:
with primary.user_client(format="msgpack", user_id=regulators[0].name) as c:
for bank in banks:
check = infra.checker.Checker()

check(c.rpc("BK_register", {"country": bank[1]}), result=bank[0])
check(c.rpc("BK_get", {"id": bank[0]}), result=bank[1].encode())
LOG.debug(f"User {bank[0]} successfully registered as bank")
check(
c.rpc(
"BK_register", {"bank_id": bank.ccf_id, "country": bank.country}
),
result=bank.ccf_id,
)
check(c.rpc("BK_get", {"id": bank.ccf_id}), result=bank.country)
LOG.debug(f"User {bank} successfully registered as bank")

LOG.success(
f"{len(regulators)} regulator and {len(banks)} bank(s) successfully setup"
)

tx_id = 0 # Tracks how many transactions have been issued
bank_id = banks[0][0] + 1
LOG.info(f"Loading scenario file as bank {bank_id}")

with primary.user_client(format="msgpack", user_id=regulator[0] + 1) as reg_c:
LOG.info(f"Loading scenario file as bank {banks[0].ccf_id} ({banks[0].name})")

with primary.user_client(format="msgpack", user_id=regulators[0].name) as reg_c:
with primary.user_client(
format="msgpack", user_id=bank_id, log_file=None
format="msgpack", user_id=banks[0].name, log_file=None
) as c:
with open(args.datafile, newline="") as f:
start_time = perf_counter()
Expand Down
20 changes: 12 additions & 8 deletions samples/apps/txregulator/clients/poll.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,22 @@ def convert(data):


def run(args):
regulator = (0, "gbr", None)
banks = [(1, "us", 99), (1, "gbr", 29), (2, "grc", 99), (2, "fr", 29)]
revealed = []

reg_id = regulator[0] + 1
with client(
host=args.host,
port=args.port,
format="msgpack",
cert="user{}_cert.pem".format(reg_id),
key="user{}_privk.pem".format(reg_id),
cert="user{}_cert.pem".format(args.regulator_name),
key="user{}_privk.pem".format(args.regulator_name),
cafile="networkcert.pem",
) as reg_c:
bank_id = user_id = banks[0][0] + 1
with client(
host=args.host,
port=args.port,
format="msgpack",
cert="user{}_cert.pem".format(bank_id),
key="user{}_privk.pem".format(bank_id),
cert="user{}_cert.pem".format(args.bank_name),
key="user{}_privk.pem".format(args.bank_name),
cafile="networkcert.pem",
) as c:
while True:
Expand Down Expand Up @@ -81,6 +77,14 @@ def run(args):
parser = argparse.ArgumentParser()
parser.add_argument("--host", help="Hostname that service is running on", type=str)
parser.add_argument("--port", help="Port that service is running on", type=int)
parser.add_argument(
"--regulator-name",
help="Name of cert/key to use for regulator connection",
type=str,
)
parser.add_argument(
"--bank-name", help="Name of cert/key to user for bank connection", type=str
)

args = parser.parse_args()
run(args)
Loading

0 comments on commit 14167d3

Please sign in to comment.