This feature allows users to assign execution context to Lua scripts. The supported contexts are:
-- ``prerequest`` which will execute a script before each operation is performed
-- ``postrequest`` which will execute after each operation is performed
-- ``background`` which will execute within a specified time interval
-- ``getdata`` which will execute on objects' data when objects are downloaded
-- ``putdata`` which will execute on objects' data when objects are uploaded
+ - ``prerequest`` which will execute a script before each operation is performed
+ - ``postauth`` which will execute a script after each operation is authorized but before it is performed
+ - ``postrequest`` which will execute after each operation is performed
+ - ``background`` which will execute within a specified time interval
+ - ``getdata`` which will execute on objects' data when objects are downloaded
+ - ``putdata`` which will execute on objects' data when objects are uploaded
A request (pre or post) or data (get or put) context script may be constrained to operations belonging to a specific tenant's users.
The request context script can also access fields in the request and modify certain fields, as well as the `Global RGW Table`_.
::
- # radosgw-admin script put --infile={lua-file-path} --context={prerequest|postrequest|background|getdata|putdata} [--tenant={tenant-name}]
+ # radosgw-admin script put --infile={lua-file-path} --context={prerequest|postauth|postrequest|background|getdata|putdata} [--tenant={tenant-name}]
* When uploading a script with the ``background`` context, a tenant name should not be specified.
::
- # radosgw-admin script get --context={preRequest|postRequest|background|getdata|putdata} [--tenant={tenant-name}]
+ # radosgw-admin script get --context={preRequest|postAuth|postRequest|background|getdata|putdata} [--tenant={tenant-name}]
To remove the script:
::
- # radosgw-admin script rm --context={preRequest|postRequest|background|getdata|putdata} [--tenant={tenant-name}]
+ # radosgw-admin script rm --context={preRequest|postAuth|postRequest|background|getdata|putdata} [--tenant={tenant-name}]
Package Management via CLI
Return Value Context
~~~~~~~~~~~~~~~~~~~~
-The Lua script’s return value is evaluated only during the prerequest context and is ignored in any other RGW request-processing context.
+The Lua script’s return value is evaluated only during the prerequest and postauth context and is ignored in any other RGW request-processing context.
The HTTP response status code is 403 (Forbidden) by default when a request is blocked by Lua. The response code can be changed using ``Request.Response.HTTPStatusCode`` and ``Request.Response.HTTPStatus``.
If a request is aborted this way, the ``data`` and ``postrequest`` context will also be aborted.
if (rate_limit(driver, s)) {
return -ERR_RATE_LIMITED;
}
+
+ bool is_health_request = (op->get_type() == RGW_OP_GET_HEALTH_CHECK);
+ {
+ if (!is_health_request) {
+ std::string script;
+ auto rc = rgw::lua::read_script(s, s->penv.lua.manager.get(),
+ s->bucket_tenant, s->yield,
+ rgw::lua::context::postAuth, script);
+ if (rc == -ENOENT) {
+ // no script, nothing to do
+ } else if (rc < 0) {
+ ldpp_dout(op, 5) <<
+ "WARNING: failed to execute post authorization script. "
+ "error: " << rc << dendl;
+ } else {
+ int script_return_code = 0;
+ rc = rgw::lua::request::execute(s->penv.rest, s->penv.olog.get(), s, op, script, script_return_code);
+ if (rc < 0) {
+ ldpp_dout(op, 5) <<
+ "WARNING: failed to execute post authorization script. "
+ "error: " << rc << dendl;
+ }
+ if (script_return_code == -EPERM) {
+ return script_return_code;
+ }
+ }
+ }
+ }
ldpp_dout(op, 2) << "executing" << dendl;
{
auto span = tracing::rgw::tracer.add_span("execute", s->trace);
@pytest.mark.basic_test
def test_script_management():
- contexts = ['prerequest', 'postrequest', 'background', 'getdata', 'putdata']
+ contexts = ['prerequest', 'postauth', 'postrequest', 'background', 'getdata', 'putdata']
scripts = {}
for context in contexts:
script = 'print("hello from ' + context + '")'
def test_script_management_with_tenant():
tenant = 'mytenant'
conn2 = another_user(tenant)
- contexts = ['prerequest', 'postrequest', 'getdata', 'putdata']
+ contexts = ['prerequest', 'postauth', 'postrequest', 'getdata', 'putdata']
scripts = {}
for context in contexts:
for t in ['', tenant]:
# cleanup
conn.delete_object(Bucket=bucket_name, Key=key)
conn.delete_bucket(Bucket=bucket_name)
- contexts = ['prerequest', 'postrequest', 'getdata', 'putdata']
+ contexts = ['prerequest', 'postauth', 'postrequest', 'getdata', 'putdata']
for context in contexts:
result = admin(['script', 'rm', '--context', context])
assert result[1] == 0
RGWDebugLog("op was: "..Request.RGWOp)
'''
- contexts = ['prerequest', 'postrequest', 'getdata', 'putdata']
+ contexts = ['prerequest', 'postauth', 'postrequest', 'getdata', 'putdata']
for context in contexts:
footer = '\nRGWDebugLog("context was: '+context+'\\n\\n")'
result = put_script(script+footer, context)
# cleanup
delete_all_objects(conn, bucket_name)
conn.delete_bucket(Bucket=bucket_name)
- contexts = ['prerequest', 'postrequest', 'getdata', 'putdata']
+ contexts = ['prerequest', 'postauth', 'postrequest', 'getdata', 'putdata']
for context in contexts:
result = admin(['script', 'rm', '--context', context])
assert result[1] == 0
# cleanup
delete_all_objects(conn, bucket_name)
conn.delete_bucket(Bucket=bucket_name)
- contexts = ['prerequest', 'postrequest', 'background', 'getdata', 'putdata']
+ contexts = ['prerequest', 'postauth', 'postrequest', 'background', 'getdata', 'putdata']
for context in contexts:
result = admin(['script', 'rm', '--context', context])
assert result[1] == 0
socket_server.shutdown()
delete_all_objects(conn, bucket_name)
conn.delete_bucket(Bucket=bucket_name)
- contexts = ['prerequest', 'postrequest', 'background', 'getdata', 'putdata']
+ contexts = ['prerequest', 'postauth', 'postrequest', 'background', 'getdata', 'putdata']
for context in contexts:
result = admin(['script', 'rm', '--context', context])
assert result[1] == 0
out, err = admin(['script', 'rm', '--context', 'prerequest'])
assert err == 0
+ try:
+ conn.get_object(Bucket=bucket_name, Key=key)
+ pytest.fail("The object was written to the bucket despite the error.")
+ except Exception as e:
+ assert e.response['Error']['Code'] == 'NoSuchKey'
+ log.info("Successfully confirmed that the request was interrupted.")
+
+ conn.delete_bucket(Bucket=bucket_name)
+
+@pytest.mark.example_test
+def test_interrupt_request_postauth():
+ script = '''
+ return RGW_ABORT_REQUEST
+ '''
+
+ conn = connection()
+ bucket_name = gen_bucket_name()
+ conn.create_bucket(Bucket=bucket_name)
+
+ result = put_script(script, "postauth")
+ assert result[1] == 0
+ key = "hello"
+
+ try:
+ conn.put_object(Body="this should be blocked".encode("ascii"), Bucket=bucket_name, Key=key)
+ pytest.fail("The put_object operation was not blocked by the Lua script.")
+ except Exception as e:
+ pass
+
+ out, err = admin(['script', 'rm', '--context', 'postauth'])
+ assert err == 0
+
try:
conn.get_object(Bucket=bucket_name, Key=key)
pytest.fail("The object was written to the bucket despite the error.")