State-machine workflow in youtrack - restrict state changes for certain roles - youtrack

I have created a simple state-machine workflow in youtrack to reflect our process. It uses three State values (Submitted, In Progress, Fixed) and allows to move through them sequentially.
Is it possible to restrict certain state changes for specific roles? For example role Reporter should only be able to create issue and move from 'Fixed' to 'In Progress' if something is wrong.

UPDATE:
An even better way to do this task is the following right inside the Statemachine:
initial state Submitted {
on Approve[always] do {
assert loggedInUser.hasRole("Project Admin"): "Only Project Admins can Approve tasks.";
} transit to Open
}
OLD ANSWER:
Straighforward way (inside the Statemachine itself):
initial state Submitted {
on Approve[loggedInUser.hasRole("Project Admin")] do {<define statements>} transit to Open
}
Althought it will work, it will fail silently, so user will not know why does it not work.
A much better approach will look like the following (for this you will have to create Stateless Rule):
when State.oldValue == {Submitted} && State.becomes({Open}) {
assert loggedInUser.hasRole("Project Admin"): "Only Project Admins can Approve tasks.";
}
In this case user will get an error message that you specify.
Remember to delete the condition in the Statemachine, since it is checked earlier and you will not have any error messages as assertion will not run at all.

A pretty old question, but I'll try to answer.
You can specify a guard expression that will be invoked upon transition to/from a specific state. In this expression you can validate user permissions.

Related

Can commands be intercepted based on their type?

In Axon, Command Interceptors seem to intercept any and every command. For permission checking, I need to base my decision on the type of command to see whether it is allowed in the current context.
#Override
public BiFunction<Integer, CommandMessage<?>, CommandMessage<?>> handle(List<? extends CommandMessage<?>> messages) {
return (index, command) -> {
// Check permissions
return command;
};
}
Usually I would prefer to register an interceptor per command type and handle the permission checks in dedicated objects. How would I do it with Axon? Will I need to have a load of instanceof if-statements in a single interceptor? Does anyone have a good example of permission checking with Axon?
At this point (Axon 4.0) the framework does not allow you the option to register DispatchInterceptors per command payload type directly. So what you're suggesting to do, the if-blocks/switch-statements, is the best thing you could do at this point.
Although I am not certain how fine grained your permission checks are, what you could do is have some form of permission annotation on the commands, with a specific value/enum you need to set on it. That way you do not have to tie in your payload type into the dispatcher directly, but could just check if the annotation exists and if so, check it's value for the kind of permission check which should be performed on it.
That would very likely result in a far smaller if-block/switch-statement than checking per payload type.

Lync 2013 UCMA WCF Web Service

What I want to do is create a WCF service just to get the availability of a user. I have gone through the following quick-start example:
Name: SubscribePresence
http://msdn.microsoft.com/en-us/library/office/dn454835(v=office.15).aspx
I have managed to do this but i feel that its not the most efficient way to just get a users availability.
At the moment I create a end point subscribe to a users presence and wait for the response to come back and from that i get the users availability. (I'm simplifying this down).
What I would ideally like though is just to quickly get a users availability without subscribing to a users presence and close the connection as soon as i have retrieved the availability.
I was wondering if anyone knows of an example that i can have a look at or that they have implemented themselves
any advice would be appreciated
You can also do a one-time presence query. From MSDN:
If a one-time presence query to a remote presentity is desired, creating a view and tearing it down is a suboptimal solution for an application. In addition, the application needs to wait and track whether all presence information has been received.
An alternative is to use the BeginPresenceQuery(IEnumerable<String>, [], EventHandler<RemotePresentitiesNotificationEventArgs>, AsyncCallback, Object) and EndPresenceQuery(IAsyncResult) methods on the endpoint’s PresenceServices property.
See http://msdn.microsoft.com/en-us/library/office/hh383136%28v=office.14%29.aspx
Example
You can call the presence query like this. The null argument on 3rd position is the event handler which will fire when presence is recieved, it's not required since we process the results of the EndPresenceQuery instead. You could also pass an eventhandler and not care about the results of the EndPresenceQuery, thats up to you.
endpoint.PresenceServices.BeginPresenceQuery(
new[] { "sip:user#example.com" }, // Collection of sip addresses to query
new[] { "state" }, // Collection of presence catrgories to query
null, // The eventhandler to call when presence is recieved
(ar) => {
var result = endpoint.PresenceServices.EndPresenceQuery(ar);
// process the recieved containers in 'result' here.
},
null); // The state object
However, when you run a WCF service for presence which will be queried multiple times, I would say it might be better to subscribe to presence than to do single queries every time. I build a similar system once with the following logic:
Get an incoming presence request on WCF.
If this SIP uri presence is known to the WCF service (is subscribed), return immediately the cached presence.
If it is not known, subscribe to the presence.
When presence is recieved, return the result and add the presence to the cache.
Any time a subscribed user updates their presence, an event is fired to update the cache.
If no presence queries are recieved for a single user for a certain period of time, unsubscribe from the presence and remove from cache.
The main advantage here is that for multiple subsequent queries for the same user's presence, you do not query the Lync server each time. Your service responses will be a lot faster, and you get presence pushed rather than having to poll for it each time.

CQS and CRUD operation

I working on high-scalability web site for learning purpose. I decided to use CQS pattern and some ideas of CQRS. I have separate write and read layers used by command handlers and event handlers which system sends and receives from message buses (two separate message- buses).
I have some problem dealing with commands. I read that command shouldn't return anything. And now the point is: for example I have a form on with user can create an event or for example change something in his profile (photo or name). After user clicked save i want to show him, his profile or add a new event to his wall. But how can I know that his profile has been already updated when command is only send to the bus ? I How connect idea of command and CRUD operations ? Or maybe this wrong idea at all ?
Well first off, the split should not be between commands and events, but rather between domain and read models. You can't really map CQRS commands to CRUD operations as a general rule, although most of the commands in your system will change the state of your repositories. I will give you a general overview of how this works. Say you want to add a user, you create a command AddUserCommand and assign an id to that message. On the back end, you have an handler for that command and you're right that the command does not return nothing. Once that command is handled you should publish and event reflecting the change: UserWasAddedEvent. The id of this message will be unique, but it can and should have an id related to the command which you created in the UI. Your read models should handle the event and update a read model with the command status (waiting, processing, completedOnError, completedSuccessfully) depending on the event you published. On the UI after you submited the command, you should start querying the read models whith the ID of the command you created to get the status and then update your UI accordigly.
Your right that CQRS handlers return void, but you should bear in mind that typically in an architecture like this, the backend should return the validation results of the submited commands, not the handler itself but the infrastucture around your cqrs handlers.
Just update te UI on the assumption the command succeeds - which most of the time it will.
If validation is required on the user input, you could run validation as the user types or tabs to increase the likelyhood the command will succeed.

Deep level access control in DataMapper ORM

Introduction
I'm currently building an access control system in my DataMapper ORM installation (with CodeIgniter 2.*). I have the initial injection of the User's rights (Root/Anonymous layers too) working perfectly. When a User logs in the DataMapper calls that are done in the system will automatically be marked with the Userrights the User has.
So until this point it works perfectly, but now I'm a bit in a bind. The problem is that I need some way to catch and filter each method-call on the Object that is instantiated.
I have two special calls so I can disable the Userrights-checks too. This is particularly handy at the exact moment I want to login a User and need to do initial checks;
DataMapper::disable_userrights();
$this->_user = new User($this->session->userdata('_user_id'));
$this->_userrights = ($this->_user ? $this->_user->userrights(TRUE) : NULL);
DataMapper::enable_userrights();
The above makes sure I can do the initial User (and it's Userrights) injection. Inside the DataMapper library I use the $CI =& get_instance(); to access the _ globals I use. The general rule in this installment I'm building is that $this->_ is reserved for a "globals" system that always gets loaded (or can sometimes be NULL/FALSE) so I can easily access information that's almost always required on each page/call.
Details
Ok, so image the above my logged-in User has the Userrights: Create/Read/Update on the User Entity. So now if I call a simple:
$test = new User();
$test->get_where('name', 'Allendar');
The $_rights Array inside the DataMapper instance will know that the current logged-in User is allowed to perform certain tasks on "this" instance;
protected $_rights = array(
'Create' => TRUE,
'Read' => TRUE,
'Update' => TRUE,
'Delete' => FALSE,
);
The issue
Now comes my problem. I want to control these Userrights by validating them over each action that is performed. I have the following ideas;
Super redundant; make a global validation method that is executed at the start of each other method in the DataMapper Class.
Problem 1: I have to spam the whole DataMapper Class with the same calls
Problem 2: I have no control over DataMapper extension methods
Problem 3: How to detect relational includes? They should be validated too
Low level binding on certain Core DataMapper calls where I can clearly detect what kind of action is executed on the database (C/R/U/D).
So I'm aiming for Option 2 (and 1.) Problem 2), as it will also solve 1.) Problem 2.
The problem is that DataMapper is so massive and it's pretty complex to discern what actually happens when on it's deepest calling level. Furthermore it looks like all methods are very scattered and hardly ever use each other ($this->get() is often not used to do an eventual call to get a dataset).
So my goal is:
User (logged-in, Anonymous, Root) makes a DataMapper istance
$user_test = new User;
User wants to get $user-test (Read)
$user_test->get(1);
DataMapper will validate the actual call that is done at the database
IF it is only SELECT; OK
IF something else than SELECT (or JOINs to other Model that the User doesn't have access to that/those Models, it will fail with a clear error message)
IF JOINed Models also validate; OK
Return the actual instance;
IF OK: continue DataMapper's normal workflow
IF not OK: inform the User and return the normal empty DataMapper instance of that Model
Furthermore, for this system I think I will need to add some customization for the raw_sql (etc.) SQL calls so that I have to inject the rights manually related to that SQL statement or only allow the Root User to do those things.
Recap
I'm curious if someone ever attempted something like this in DataMapper or has some hints how I can use/intercept those lowest level calls in DataMapper.
If I can get some clearance on the deepest level of DataMapper's actual final query-call I can probably get a long way myself too.
I would like to suggest not to do this in Datamapper itself (mainly due to the complexity of the code, as you have already noticed yourself).
Instead, use a base model, and have that extend Datamapper. Then add the code to the base model required for your ACL checks, and then overload every Datamapper method that needs an ACL check. Have it call your ACL, deal with an access denied, and if access is granted, simply return the result of parent::method();.
Instead of extending Datamapper, your application models should then extend this base model, so they will inherit the ACL features.

How to get tcmid of currently logged user in Tridion?

private void Subscribe()
{
EventSystem.Subscribe<User, LoadEventArgs>(GetInfo, EventPhases.Initiated);
}
public void GetInfo(User user, LoadEventArgs args, EventPhases phase)
{
TcmUri id = user.Id;
string name = user.Title;
Console.WriteLine(id.ToString());
Console.WriteLine(name);
}
I wrote above code and add the assembly in config file in Tridion server but no console window is coming on login of a user
The event you were initially subscribing to is the processed phase of any identifiable object with any of its actions, that will trigger basically on every transaction happening in the SDL Tridion CMS, so it won't give you any indication of when a user logs in (it's basically everything which happens all the time).
Probably one of the first things which is happening after a user logs in, is that its user info and application data is read. So what you should try is something along the lines of:
private void Subscribe()
{
EventSystem.Subscribe<User, LoadEventArgs>(GetInfo, EventPhases.Initiated);
}
public void GetInfo(User user, LoadEventArgs args, EventPhases phase)
{
TcmUri id = user.Id;
string name = user.Title;
}
But do keep in mind that this will also be triggered by other actions, things like viewing history, checking publish transactions and possibly a lot more. I don't know how you can distinguish this action to be part of a user login, since there isn't an event triggered specifically for that.
You might want to check out if you can find anything specific for a login in the LoadEventArgs for instance in its ContextVariables, EventStack, FormerLoadState or LoadFlags.
Edit:
Please note that the Event System is running inside the SDL Tridion core, so you won't ever see a console window popup from anywhere. If you want to log information, you can include the following using statement:
using Tridion.Logging;
After adding a reference to the Tridion.Logging.dll which you can find in your ..\Tridion\bin\client directory. Then you can use the following logging statement in your code:
Logger.Write("message", "name", LoggingCategory.General, TraceEventType.Information);
Which you will find back in your Tridion Event log (provided you have set the logging level to show information messages too).
But probably the best option here is to just debug your event system, so you can directly inspect your object when the event is triggered. Here you can find a nice blog article about how to setup debugging of your event system.
If you want to get the TCM URI of the current user, you can do so in a number of ways.
I would recommend one of these:
Using the Core Service, call GetCurrentUser and read the Id property.
Using TOM.NET, read the User.Id property of the current Session.
It looks like you want #2 in this case as your code is in the event system.