-
Notifications
You must be signed in to change notification settings - Fork 552
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
admin: implement create role API #17089
Conversation
ducktape was retried in https://buildkite.com/redpanda/redpanda/builds/46176#018e3cb2-a8a2-4046-a540-6f7a49252b1f ducktape was retried in https://buildkite.com/redpanda/redpanda/builds/46361#018e51da-2f39-4a8f-8f2a-c77dd7f4aff8 ducktape was retried in https://buildkite.com/redpanda/redpanda/builds/46361#018e51ec-7d88-42b8-bdc0-9ae96a2f1695 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Several minor comments, mostly carry-over from POC I think.
FWIW - if you see something on the POC branch that looks very janky, it probably is. I won't be offended if you want to blow some of it up 🙂
7c3c251
to
9037f4b
Compare
Force-pushed to address the comments above |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple things on the ducktape side and an error path I missed; otherwise looking good.
Probably a good idea to get one more set of eyes before merging :)
tests/rptest/tests/rbac_test.py
Outdated
with expect_role_error(RoleErrorCode.MALFORMED_DEF): | ||
self.superuser_admin.create_role(role=role_name0) | ||
res = self.superuser_admin.create_role(role=self.role_name0) | ||
created_role = res.json()['role'] | ||
assert created_role == self.role_name0, f"Incorrect create role response: {res.json()}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant with test_create_user
, right?
def test_create_role(self): | ||
res = self.superuser_admin.create_role(role=self.role_name0) | ||
created_role = res.json()['role'] | ||
assert created_role == self.role_name0, f"Incorrect create role response: {res.json()}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Meant to comment before - I think we'd be well served to test misuse of the API here, specifically the malformed body cases. Also idempotency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call. Because I used parse_json_body
, we return a 400 with a non-role-specific error if the body is missing / not well-formed json. I think that's fine and not worth the trouble changing (RPK/console should just send us well-formed requests anyway). But maybe we should call this out in the RFC. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM 👍
} | ||
|
||
co_await throw_on_error(*req, err, model::controller_ntp); | ||
co_return ss::json::json_return_type(j_res); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something else I've been meaning to address - we intended to return 201 (Created) on this endpoint because REST... I don't know how easy that is. It's possible that the seastar machinery is clever enough to glean it from the swagger, but I'm not sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's smart enough, unfortunately. It's returning 200 (OK) at the moment.
I spent a bit of time trying to refactor this to allow setting a result code, but it's not straightforward as far as I can tell. We'd have to at least refactor register_route
and replicate what seastar is doing for creating a json handler for this to work.
I'd be tempted to keep this returning 200 to be consistent with the other admin API endpoints (and to keep things simple), but let me know what you think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bummer. I'll add it my short list of minor API changes. Nothing for you to do, but I'll post an update on the channel today. I have a couple others, also arising from seastar::httpd implementation details
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be done, but you have to be a bit more manual about it: https://github.com/redpanda-data/redpanda/blob/dev/src/v/pandaproxy/reply.h#L75-L82
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice. We'd need access to the http_reply object, so we'd want to use register_route_raw_async to register the route IIUC
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My mistake, I think this is the correct overload:
redpanda/src/v/redpanda/admin/server.h
Lines 392 to 398 in dc7b8d8
template<auth_level required_auth> | |
void register_route( | |
ss::httpd::path_description const& path, request_handler_fn handler) { | |
path.set( | |
_server._routes, | |
new handler_impl<required_auth>{*this, std::move(handler)}); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created an issue to make this change as a follow up: https://github.com/redpanda-data/core-internal/issues/1173
9037f4b
to
b3255c3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good to me. As noted elsewhere, let's wait for a spot check from someone not me and
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good
src/v/redpanda/admin/security.cc
Outdated
if (err == cluster::errc::role_exists) { | ||
// Idempotency: if the empty role already exists, | ||
// suppress the role_exists error and return success. | ||
auto role = _controller->get_role_store().local().get(role_name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
auto role = _controller->get_role_store().local().get(role_name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now just duplicating the auto role = ...
above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I removed this now. I was thinking that it might be worth shortcutting a controller log entry replication by checking the existence of the role before calling create_role
. But it does simplify the code a bit if we don't double-check.
// Idempotency: if the empty role already exists, | ||
// suppress the role_exists error and return success. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this checked above? Or are you gearing up for something more than an empty role?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is just double-checking to guard against a race condition with a _controller->get_security_frontend().local().create_role(...)
on another node. It's mimicking the create_user_handler
:
redpanda/src/v/redpanda/admin/security.cc
Lines 361 to 370 in 066794c
if (err == cluster::errc::user_exists) { | |
// Idempotency: if user is same as one that already exists, | |
// suppress the user_exists error and return success. | |
const auto& credentials_store | |
= _controller->get_credential_store().local(); | |
std::optional<security::scram_credential> creds | |
= credentials_store.get<security::scram_credential>(username); | |
if (creds.has_value() && match_scram_credential(doc, creds.value())) { | |
co_return ss::json::json_return_type(ss::json::json_void()); | |
} |
} | ||
|
||
co_await throw_on_error(*req, err, model::controller_ntp); | ||
co_return ss::json::json_return_type(j_res); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be done, but you have to be a bit more manual about it: https://github.com/redpanda-data/redpanda/blob/dev/src/v/pandaproxy/reply.h#L75-L82
b3255c3
to
55b8670
Compare
55b8670
to
e8182ea
Compare
Force-pushed to simplify checking for existing roles based on feedback from @BenPope |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
src/v/redpanda/admin/security.cc
Outdated
auto err | ||
= co_await _controller->get_security_frontend().local().create_role( | ||
role_name, security::role{}, model::timeout_clock::now() + 5s); | ||
|
||
if (err == cluster::errc::role_exists) { | ||
// Idempotency: if the empty role already exists, | ||
// suppress the role_exists error and return success. | ||
auto role = _controller->get_role_store().local().get(role_name); | ||
if (role.has_value() && role.value().members().empty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a nitpick, which seems a little clearer to me, and will help with the role is not empty.
auto err | |
= co_await _controller->get_security_frontend().local().create_role( | |
role_name, security::role{}, model::timeout_clock::now() + 5s); | |
if (err == cluster::errc::role_exists) { | |
// Idempotency: if the empty role already exists, | |
// suppress the role_exists error and return success. | |
auto role = _controller->get_role_store().local().get(role_name); | |
if (role.has_value() && role.value().members().empty()) { | |
security::role role{}; | |
auto err | |
= co_await _controller->get_security_frontend().local().create_role( | |
role_name, role, model::timeout_clock::now() + 5s); | |
if (err == cluster::errc::role_exists) { | |
// Idempotency: if the empty role already exists, | |
// suppress the role_exists error and return success. | |
if (_controller->get_role_store().local().get(role_name) == role) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, I like this. Addressed
This implements the create_role_handler which will allow the creation of an empty role to which users can later be assigned to.
This tests the create_role_handler of the admin API, testing that an empty role can successfully be created. Additionally, it verifies that the validation on incorrect role names works.
e8182ea
to
02a391a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
This implements the
create_role_handler
which will allow the creation ofan empty role to which users can later be assigned to.
Closes https://github.com/redpanda-data/core-internal/issues/1104
Backports Required
Release Notes