I am writing roles in ansible, which use the ansible.builtin.uri method to do my bidding against the api of the service.
As I don't want to POST/PUT every time I run the Playbook, I check if the item I want to create already exists.
Does this make sense? In the end I introduce an extra step to GET the status, to skip the POST/PUT, where the POST/PUT itself would simply set what I want in the end.
For example, I wrote an Ansible Role which gives a User a role in Nexus.
Every time I run the Role, it first checks if the User already has the role and if not, it provides it.
If I don't check before and the User would already have the Role, it would simply apply it again.
But as I would like to know exactly whats going to happen, I believe it is better to explicitly check before applying.
What is the best practice for my scenario and are there any reasons against checking before or directly applying the changes?
Related
So I'm sending all users through apache with mod_auth_kerb. All users come in with a default userRolesHeader of users.
I'd like to add extra roles for specific accounts, but I'm not seeing a good way to do that. If you could define the users in realm.properties and it would combine with the userRolesHeader, that would be useful.
Is there another way to do this? I don't see how it can be done with apache alone since REMOTE_USER isn't available during if/else logic processing.
#rundeck
rundeck.security.authorization.preauthenticated.userNameHeader=X-Forwarded-Uuid
rundeck.security.authorization.preauthenticated.userRolesHeader=X-Forwarded-Roles
#apache
RequestHeader set "X-Forwarded-Uuid" %{REMOTE_USER}s
RequestHeader set X-Forwarded-Roles users
Internally Rundeck gets only one method once, if you configure Rundeck to get the users from the realm.properties file, Rundeck seeks the roles from that file. Currently You can combine methods but the user/role in different methods doesn't.
Given the example of a shop API with an orders resource. You would want to delete one order by id
DELETE /orders/:orderId
Under the hood you run an update query and set canceled to true. But what if
A customer calls this endpoint:
You need a canceledByCustomer database flag
No additional permissions are required
An administrator calls this endpoint?
You need a rejectedByAdministrator database flag
Additional permissions are required
Would you keep the endpoint posted above and check internally, if the calling user tries to cancel the order of another user and if true, this is a reject action?
Would you add two query parameters cancel and reject and one of them MUST be true and one of them MUST be null/false?
Would you violate the design rules, create two different endpoints and add verbs to them like so?
DELETE /orders/:orderId/cancel => customer can call it
DELETE /orders/:orderId/reject => only administrators can call it
Does someone know about the best practises for such "domain driven" problems?
API endpoints don't have to correlate on what happens closer to the core, for example in your Aggregate Root or CommandHandler. In my opinion, make the API routes as verbose as possible, which means creating their own separate routes for each use case. Push the logic on what database flag to use (canceledByCustomer vs rejectedByAdministrator) closer down to the entity.
Odata V4 Spec says that Actions MAY have observable side effects and should be invoked using HTTP POST. But we do have scenarios where we need to use actions which just modifies some status.
For example :
1.You might want to mark status of a document identified by a id as locked
Endpoint - .../Documents({id})/lock().
Since I am doing a partial update here, In my opinion PATCH is more suitable.2. You might want to offer two ways of deleting a document
a) Just Hide Endpoint - ...../Documents({id}) This is with HTTP DELETE (no disputes)
b) Delete PermanentlyEndpoint - ...../Documents({id})/permanentDelete() This as an ODATA action. In my opinion, HTTP Delete would be more appropriate here instead of HTTP POST.
What is the recommended way to do this from Odata standpoint? Any help here is much appreciated.
Below is the information from SPEC.
SPEC
11.5.4 Actions
Actions are operations exposed by an OData service that MAY have side effects when invoked. Actions MAY return data but MUST NOT be further composed with additional path segments.
11.5.4.1 Invoking an Action
To invoke an action bound to a resource, the client issues a POST request to an action URL. An action URL may be obtained from a previously returned entity representation or constructed by appending the namespace- or alias-qualified action name to a URL that identifies a resource whose type is the same as, or derives from, the type of the binding parameter of the action. The value for the binding parameter is the value of the resource identified by the URL prior to appending the action name, and any non-binding parameter values are passed in the request body according to the particular format.
Thanks in advance
--ksp
From an OData standpoint, you always have to invoke an action with a POST, with any other verb, it won't work.
Both of your examples are things that I personally don't think are well suited to an action as there are already ways to do these things with OData and they use the verbs that you are mentioning.Updating a property is supported with a PATCH and deleting an object is supported with a DELETE. You mention that you have two different types of delete operations, this is more difficult but you could use a custom header to distinguish between them.
An action tends to be something that doesn't fit into the normal CRUD operations so it isn't always clear which HTTP verb should be used for this.
In your examples, it does feel right to want to use DELETE and PATCH. However, the problem comes because we need to have a standard to follow, bear in mind that OData actions are all discoverable through the metadata so could be consumed by a client with no knowledge of what the actions actually do and in this case, we need to have something defined. Since we know that actions affect the server in some way, to me, it seems like POST is the least bad option that is consistent for all actions.
At the end of the day, as the author, your API is yours to command. Just as we shouldn't use properties in C# class design that cause side affects in other properties, that doesn't mean we cant, and it can be pretty common.
From an OData standpoint, PATCH is analogous to using property accessors and actions or POST is for accessing methods.
Making the decision between PATCH or POST to affect change is therefor the same design decision between using property mutators (making them writable) or forcing the caller to set their values through related methods.
You might want to mark status of a document identified by a id as locked
Endpoint - .../Documents({id})/lock().
Since I am doing a partial update here, In my opinion PATCH is more suitable.
If your resource has a property called Locked, and your current lock() Action only sets the Locked property to true, then you could simply use PATCH to update just that Locked field.
Some common reasons to use an Action specifically for a lock process:
You want to execute specific logic when lock() is called and you want to maintain this logic in it's own method, rather than having complex conditional logic inside your patch handler.
You don't want the reverse logic, unlock() to be available to everyone, when a resource is locked, presumably only the user who locked it can release the lock or other conditions need to be satisfied.
as with the first point, this sort of logic is generally easier to maintain in its own method, and therefore Action.
You want to use Security Attributes to restrict access to lock() to certain security groups, yet you want all users to have read access the the Locked state field.
When a resource is locked other fields are also set, like LockedBy and perhaps DateLocked.
While all of that logic could be managed in your single PATCH endpoint logic for the controller, I can't stress enough how this can easily make your solution unmanageable over time or as the complexity of your resource increases.
More importantly: the documented and accepted convention is that PATCH will NOT have side effects, and as such there is no need to execute a GET after a PATCH because the same change on the client has been accepted on the server. Conversely, because a POST MAY have side effects, it is reasonable for the client to expect to execute a GET to get all the related changes if the response from the POST does not contain the updated resource.
In the case of Delete vs PermanentlyDelete now things become personal / opinionated...
By convention, the expectation of DELETE is two fold:
After a delete the resource should no longer appear in collection query results from a GET
After a delete the resource should no longer appear in item requests via GET
From a server point of view, if my resource has a soft delete, that simply sets a flag, and a permanent delete that deletes the record from the underlying store, then by convention I would use an Action called Delete() for the soft delete, and the permanent delete should use the DELETE http verb.
The general reasoning for this is the same as for the discussion about locked().
However, if the intention of soft vs permanent delete is to intercept the client's standard delete workflow, such that the permanent delete concept is hidden or otherwise abstracted in a way that it is not part of the usual workflow (the user has to go somewhere different, like the recycle bin to view deleted records and restore or permanently delete), in that scenario, use the HTTP verb DELETE for soft delete, and make a specific, perhaps collection bound Action to accept the request for permanent delete
This action may have to be unbound, or bound to the collection depending on how you implement your filtering, if we have deleted record 'xyz' such the GET: ~/document('xyz') returns NOT-FOUND then we can't really expect POST: ~/document('xyz')/delete() to execute...
We are building a REST API with the following resources: Users, UserGroups. Currently we are supporting the following URI's:
/BASEAPI/VERSION/Users/
/BASEAPI/VERSION/Users/{id}/UserGroups
/BASEAPI/VERSION/UserGroups/
/BASEAPI/VERSION/UserGroups/{id}/Users
I like this better than including references in the objects which then have to be pulled on subsequent requests. It also means that we can avoid query params to filter the results. i.e. we don't have to support:
/BASEAPI/VERSION/UserGroups/{id}?user_id={user_id}
The problem is that it doesn't make creation and deletion semantics very clear. i.e. should a DELETE request to:
/BASEAPI/VERSION/Users/{id}/UserGroups/{group_id}
remove the UserGroup, or remove the user from the user group?
We've considered adding:
/BASEAPI/VERSION/UserGroupUsers
But something doesn't quite feel right about that, but maybe it's the best way to go. What do others think are best practices?
You need to figure out how you intend to represent the membership relationship between user and user group. It can be an attribute of the user, an attribute of the group, or a separate resource. Those are the only choices. How users are added to and removed from groups falls out naturally from your choice. Membership management becomes a PUT/DELETE to the user, the group, or the membership resource.
Personally, I find the separate resource to be the cleanest way to handle the issue, but you then need query parameters to poll for a specific user or group. Also, you'd need to change your second-level resource names, because it makes no sense for /userGroups/{id}/users to return a collection of userGroupUsers resources.
A URL addresses a resource. A GET on this URL returns the resource and a DELETE deletes it. If the DELETE would delete something different than the GET is returning something really is broken.
So if /BASEAPI/VERSION/Users/4711/UserGroups would return the UserGroups with the ID 0815 and 0816 the DELETE should delete both userGroups.
Question is: Does this make sense? What is happening to the other users in both userGroups?
If you want to remove a user from a group I would provide a PATCH Method.
I'm trying to wrap my head around CQRS. I'm drawing from the code example provided here. Please be gentle I'm very new to this pattern.
I'm looking at a logon scenario. I like this scenario because it's not really demonstrated in any examples i've read. In this case I do not know what the aggregate id of the user is or even if there is one as all I start with is a username and password.
In the fohjin example events are always fired from the domain (if needed) and the command handler calls some method on the domain. However if a user logon is invalid I have no domain to call anything on. Also most, if not all of the base Command/Event classes defined in the fohjin project pass around an aggregate id.
In the case of the event LogonFailure I may want to update a LogonAudit report.
So my question is: how to handle commands that do not resolve to a particular aggregate? How would that flow?
public void Execute(UserLogonCommand command)
{
var user = null;//user looked up by username somehow, should i query the report database to resolve the username to an id?
if (user == null || user.Password != command.Password)
;//What to do here? I want to raise an event somehow that doesn't target a specific user
else
user.LogonSuccessful();
}
You should take into account that it most cases CQRS and DDD is suitable just for some parts of the system. It is very uncommon to model entire system with CQRS concepts - it fits best to the parts with complex business domain and I wouldn't call logging user in a particularly complex business scenario. In fact, in most cases it's not business-related at all. The actual business domain starts when user is already identified.
Another thing to remember is that due to eventual consistency it is extremely beneficial to check as much as we can using only query-side, without event creating any commands/events.
Assuming however, that the information about successful / failed user log-ins is meaningful I'd model your scenario with following steps
User provides name and password
Name/password is validated against some kind of query database
When provided credentials are valid RegisterValidUserCommand(userId) is executed which results in proper event
If provided credentials are not valid
RegisterInvalidCredentialsCommand(providedUserName) is executed which results in proper event
The point is that checking user credentials is not necessarily part of business domain.
That said, there is another related concept, in which not every command or event needs to be business - related, thus it is possible to handle events that don't need aggregates to be loaded.
For example you want to change data that is informational-only and in no way affects business concepts of your system, like information about person's sex (once again, assuming that it has no business meaning).
In that case when you handle SetPersonSexCommand there's actually no need to load aggregate as that information doesn't even have to be located on entities, instead you create PersonSexSetEvent, register it, and publish so the query side could project it to the screen/raport.