roll back a case to previous step - processmaker

in process maker I assign some tasks usedStart Custom Task action to some users,
I want to know that is there any way to roll back to previous tasks in such a workflow,
for example if one of users (user1) reject task by mistake and workflow create another task
for next user (user2), is it possible to roll back workflow to previous step and assign task
to user1 again?
I'll be appreciated if anyone could help me.

should delete record from end list of sub_application and app_delegation until record required
with this code i can restore case from anywhere to anywhere
foreach($GLOBALS['gridHistory'] as $cuurentTask)
{
$cuurentTask["PRO_UID"] =$dbHandle->Convertnumber2english(trim($cuurentTask["PRO_UID"]));
$cuurentTask["TAS_UID"] =$dbHandle->Convertnumber2english(trim($cuurentTask["TAS_UID"]));
$cuurentTask["DEL_INDEX"]=$dbHandle->Convertnumber2english(trim($cuurentTask["DEL_INDEX"]));
if($cuurentTask["PRO_UID"]!==$to_PRO_UID || $cuurentTask["TAS_UID"]!==$to_TAS_UID || $cuurentTask["DEL_INDEX"]!==$to_DEL_INDEX)
{
$task_type=task_type($cuurentTask["TAS_UID"],$cuurentTask["PRO_UID"]);
$inPath=checkForExistInPath($to_APP_UID,$cuurentTask["APP_UID"],$to_DEL_INDEX,$cuurentTask["DEL_INDEX"]);
if($task_type=="NORMAL" && $inPath )
{
if(rollbackAppDelegate($cuurentTask["APP_UID"],$cuurentTask["DEL_INDEX"])!=1)
die('خطا در بازگرداندن کار نرمال '.$cuurentTask["APP_UID"]);
}
else if($task_type=="SUBPROCESS" && $inPath)
{
$sub_application=$baseClass->query("select APP_UID from sub_application where app_parent='".$cuurentTask["APP_UID"]."' and DEL_INDEX_PARENT='".$cuurentTask["DEL_INDEX"]."'");
if(deleteSubProcess($sub_application[0]["APP_UID"])!=1)
die('خطا در حذف زیر فرآیند'.$sub_application[0]["APP_UID"]);
if(rollbackAppDelegate($cuurentTask["APP_UID"],$cuurentTask["DEL_INDEX"])!=1)
die('خطا در بازگرداندن کار موازی'.$cuurentTask["APP_UID"]);
}
else
echo 'امکان انتقال وجود ندارد.';
}
else
{
if($APP_CUR_USER=$baseClass->query("select USR_UID from app_delegation where app_delegation.APP_UID='{$cuurentTask['APP_UID']}' order by DEL_INDEX desc LIMIT 1"))
if($baseClass->query("update app_delegation set DEL_THREAD_STATUS='OPEN' , DEL_FINISH_DATE=null where app_delegation.APP_UID='{$cuurentTask['APP_UID']}' and app_delegation.DEL_INDEX='{$cuurentTask["DEL_INDEX"]}';"))
if($baseClass->query("update application set APP_STATUS='TO_DO' , APP_FINISH_DATE=null,APP_CUR_USER='{$APP_CUR_USER[0]["USR_UID"]}' where application.APP_UID='{$cuurentTask['APP_UID']}';"))
{
echo 'true';
break;
}
else
echo 'false';
}
}

Related

FreeRadius 3.0.1 Unlang Policy to Dynamically match User -> Client -> LDAP group

I have my Radius working with AD + Google OTP working fine. What I am trying to accomplish now is to specify user-to-client-to-ADgroup in a policy and/or unlang within the post-auth.
How it works today:
client performs request
Radius sends first half of the password to AD
Radius sends the second half of the password to Google OTP
If both come back good, then auth is successful
Post-auth does some checking if user is memberof ADgroup -> assign
class -> accept
OR if not part of ADgroup -> reject
The part I need assistance with is I have over 30 sites with equipment in each one. We distinguish our users based on per site access. E.g. NetworkAdmin01 is allowed to access site01 but not site02.
So the only way I can think of doing this:
Each site has it's own virtual server (VS)
Each client has the "virtual server" attribute set
Within each VS there is post-auth unlang like:
if (LDAP-Group == "NetworkAdmins_site01") {
[do something] (update control, update reply, etc..)
else
reject
This setup would require me to have 30+ VS running on the Radius and is not manageable.
If I was able to run this within a few VS (separated based on equipment vendor)Want within the post-auth to grant/assign based on;
if (%{client:shortname} =~ /regex/) #grab the portion of the variable between "." (site01)
if (LDAP-Group =~ /regex/) # grab the portion of the variable after last "_" (site01)
if (%{0} == %{1}) {
if (LDAP-Group == NetworkAdmins_site01) {
update reply {
Juniper-Local-User-Name := "admins_group"
}
}
else {
update control {
Auth-type := "Reject"
}
}
}
}
}
So after a lot of looking around, it appears that runtime dynamic variables are the biggest limitation to building any type of policies/rules.
so I went a different direction. I have basically matched the NAS-IP-Address to the IP subnet/site I expect the request to come from.
So this is placed into the Post-Auth section of each VS. Not the most manageable way when you have 30+ sites but, best I could find at this point (without running 30+ VS).
# SITE01 site
if (&NAS-IP-Address < 10.1.0.0/16) {
if (LDAP-Group == "Radius_NetworkAdmins_SITE01") {
update reply {
Juniper-Local-User-Name := "ad-super-users"
}
}
elsif (LDAP-Group == "Radius_NetworkAdminsRO_SITE01") {
update reply {
Juniper-Local-User-Name := "ad-readonly-users"
}
}
}
# SITE02 site
if (&NAS-IP-Address < 10.2.0.0/16) {
if (LDAP-Group == "Radius_NetworkAdmins_SITE02") {
update reply {
Juniper-Local-User-Name := "ad-super-users"
}
}
elsif (LDAP-Group == "SG_Uni_Radius_NetworkAdminsRO_SITE02") {
update reply {
Juniper-Local-User-Name := "ad-readonly-users"
}
}
}
else {
update reply {
Reply-Message := "Not authorized to access this system"
}
update control {
Auth-Type := "Reject"
}
}
#
Post-Auth-Type REJECT {
-sql
attr_filter.access_reject
eap
remove_reply_message_if_eap
}
}
Post-Auth-Type Challenge {
}
#
pre-proxy {
}
#
post-proxy {
eap
}

Loop through array, checking each element with networking call/callback function, and then go to default if desired element is not found

Because of the very specific nature of this question, I could not find an answer anywhere. Basically I want to create a messaging conversation with a specific user, but only if a conversation with that user doesn't already exist. I am looping through an array of conversations, and for each conversation I fetch the identity of the other user via a call to my backend. However, if no conversation is found with a particular user, then I want to create a new conversation. This is what I am doing:
for convo in convos {
HTTPManager.getOtherUserFromConversation(conversation: convo, success: { (otherUser) in
if desiredUser == otherUser {
//Found the desired conversation, so bring the user
//to it instead of creating a new one
}
}, failure: {
//Networking failure
})
}
//IF WE DIDN'T FIND IT, CREATE A NEW CONVERSATION HERE
I have thought of making a boolean value called "found" and setting it to true if we find the desired conversation, but I don't know how to wait until the last callback has executed before checking this boolean in order to avoid calling the check too early. Can anyone point me in the right direction?
The classic solution for this is using dispatch-group
https://developer.apple.com/documentation/dispatch/dispatchgroup
There are many code examples for this. The idea is that each network call should be in a separate task and the system lets you know when all tasks are done (this is where you check "found").
How about create callback function that will called when the all the request to check the convos is done, or when the correct convo is found.
func checkConvoIfExist(convos: [Convo]){
var found = false
var countCheck = 0
for convo in convos {
HTTPManager.getOtherUserFromConversation(conversation: convo, success: { (otherUser) in
countCheck += 1
if desiredUser == otherUser {
//Found the desired conversation
found = true
callbackCheckConvo(result: found, convo: convo)
break // to stop the loop
}else{
if (countCheck == convos.count){
callbackCheckConvo(result: found)
}
}
}, failure: {
//Networking failure
countCheck += 1
if (countCheck == convos.count){
callbackCheckConvo(result: found)
}
})
}
}
func callbackCheckConvo(result: Bool, convo: Convo = nil){
if (result){
//found the desired conversation, so bring the user to it instead of creating a new one
}else{
//convo not found, create new one
}
}

Jenkins timeout/abort exception

We have a Jenkins pipeline script that requests approval from the user after all the preparatory steps are complete, before it actually applies the changes.
We want to add a timeout to this step, so that if there is no input from the user then the build is aborted, and are currently working on using this kind of method:
try {
timeout(time: 30, unit: 'SECONDS') {
userInput = input("Apply changes?")
}
} catch(err) {
def user = err.getCauses()[0].getUser()
if (user.toString == 'SYSTEM') { // if it's system it's a timeout
didTimeout = true
echo "Build timed out at approval step"
} else if (userInput == false) { // if not and input is false it's the user
echo "Build aborted by: [${user}]"
}
}
This code is based on examples found here: https://support.cloudbees.com/hc/en-us/articles/226554067-Pipeline-How-to-add-an-input-step-with-timeout-that-continues-if-timeout-is-reached-using-a-default-value and other places online, but I really dislike catching all errors then working out what's caused the exception using err.getCauses()[0].getUser(). I'd rather explicitly catch(TimeoutException) or something like that.
So my question is, what are the actual exceptions that would be thrown by either the approval step timing out or the userInput being false? I haven't been able to find anything in the docs or Jenkins codebase so far about this.
The exception class they are referring to is org.jenkinsci.plugins.workflow.steps.FlowInterruptedException.
Cannot believe that this is an example provided by CloudBeeds.
Most (or probably all?) other exceptions won't even have the getCauses() method which of course would throw another exception then from within the catch block.
Furthermore as you already mentioned it is not a good idea to just catch all exceptions.
Edit:
By the way: Scrolling further down that post - in the comments - there you'll find an example catching a FlowInterruptedException.
Rather old topic, but it helped me, and I've done some more research on it.
As I figured out, FlowInterruptedException's getCauses()[0] has .getUser() only when class of getCauses()[0] is org.jenkinsci.plugins.workflow.support.steps.input.Rejection. It is so only when timeout occured while input was active. But, if timeout occured not in input, getCause()[0] will contain object of another class: org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution$ExceededTimeout (directly mentioning timeout).
So, I end up with this:
def is_interrupted_by_timeout(org.jenkinsci.plugins.workflow.steps.FlowInterruptedException e, Boolean throw_again=true) {
// if cause is not determined, re-throw exception
try {
def cause = e.getCauses()[0]
def cause_class = cause.getClass()
//echo("cause ${cause} class: ${cause_class}")
if( cause_class == org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution$ExceededTimeout ) {
// strong detection
return true
} else if( cause_class == org.jenkinsci.plugins.workflow.support.steps.input.Rejection ) {
// indirect detection
def user = cause.getUser()
if( user.toString().equals('SYSTEM') ) {
return true
} else {
return false
}
}
} catch(org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException e_access) {
// here, we may deal with situation when restricted methods are not approved:
// show message and Jengins' admin will copy/paste and execute them only once per Jenkins installation.
error('''
To run this job, Jenkins admin needs to approve some Java methods.
There are two possible ways to do this:
1. (better) run this code in Jenkins Console (URL: /script):
import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval;
def scriptApproval = ScriptApproval.get()
scriptApproval.approveSignature('method org.jenkinsci.plugins.workflow.steps.FlowInterruptedException getCauses')
scriptApproval.approveSignature('method org.jenkinsci.plugins.workflow.support.steps.input.Rejection getUser')
scriptApproval.save()
'''.stripIndent())
return null
}
if( throw_again ) {
throw e
} else {
return null
}
}
And now, you may catch it with something like this:
try {
...
} catch (org.jenkinsci.plugins.workflow.steps.FlowInterruptedException err) {
if( is_interrupted_by_timeout(err) ) {
echo('It is timeout!')
}
}
P.S. I agree, this is bad Jenkins design.

FireBase - maintain/guarantee data consistency

I'm trying to understand what is the right approach for this following scenario :
Multiplayer game,each game structured only with two players. Each game/match will be completely randomized
Lets assume 5 users "logs" the same time into my app, each one of them "searching" for a match. Each user hold a property named opponent which equal the the opponent uniqueID(initial value equal "". so far so good.
assuming user 1 matched with user 3. user 1 will update his own oppoent value to user 3 uniqueID and will do the same to user 3
Problem
1) What if at the same moment, user 2 tried to to the same to user 3?
2) What if at the same moment, user 3 tried to do so to user 4?
Main Point
Is it possible to "lock" a user values? or freeze them once they changed? Am i going in the wrong approach?
I was thinking using Security Rules and Validation in order to create consistency but i just may picked the wrong tech(FireBase). Any thoughts?
EDIT
Security rules i have tried, which still for some reason enable a third device change "already changed opponent" value.
{
"rules": {
".read": true,
".write": true,
"Users" :
{
"$uid" : {
"opponent" :
{
".write" : "data.val() == 'empty' || data.val() == null",
".validate": "data.val() == null || data.val() == 'empty' || newData.parent().parent().child(newData.val())
.child('opponent').val() == $uid"
}
,".indexOn": "state"
}
}
}
}
You can validate many things with Firebase security rules.
For example, you can say that an opponent can only be written if there currently is no opponent for the user:
"users": {
"$uid": {
"opponent: {
".write": "!data.exists()"
}
}
}
With this and the following operations:
ref.child('users').child(auth.uid).child('opponent').set('uid:1234');
ref.child('users').child(auth.uid).child('opponent').set('uid:2345');
The second set() operation will fail, because the opponent property already has a value at that point.
You can expand that to also validate that the opponents must refer to each other:
"users": {
"$uid": {
"opponent: {
".write": "!data.exists()"
".validate": "newData.parent().parent().child(newData.val())
.child('opponent').val() == $uid"
}
}
}
From the opponent that is being written, we go up two levels back to users: newData.parent().parent().
Then we go down into the opponent's node: child(newData.val()).
And we then validate that the opponent's opponent property matches our uid: child('opponent').val() == $uid.
Now both of the write operations from above will fail, because they're only setting the opponent one at a time. To fix this, you'll need to perform a so-called multi-location update:
var updates = {};
updates['users/'+auth.uid+'/opponent'] = 'uid:1234';
updates['users/uid:1234/opponent'] = auth.uid;
ref.update(updates);
We're now sending a single update() command to the Firebase server that writes the uids to both opponents. This will satisfy the security rule.
A few notes:
these are just some examples to get you started. While they should work, you'll need to write your own rules that meet your security needs.
these rules just handle writing of opponents. You'll probably also want to testing what happens when the game is over and you need to clear the opponents.
You might also look at the transaction operation.
Firebase transactions make sure that the current set of data you are acting on is really what is in the database, guaranteeing that you are updating data that is in the right condition. The docs indicate that this is the recommended way to avoid race conditions such as you describe.
Something like this (in IOS, and warning - not tested):
NSString* user1Key = #"-JRHTHaIs-jNPLXOQivY";
NSString* user2Key = #"-NFHUaIs-kNPLJDHuvY";
Firebase *user1Ref = [[Firebase alloc] initWithUrl: #"https://docs-examples.firebaseio.com.users/-JRHTHaIs-jNPLXOQivY/opponent"];
Firebase *user2Ref = [[Firebase alloc] initWithUrl: #"https://docs-examples.firebaseio.com.users/-NFHUaIs-kNPLJDHuvY/opponent"];
//See if the proposed opponent does not yet have a match
[user2Ref runTransactionBlock:^FTransactionResult *(FMutableData *opponent) {
if (opponent.value == [NSNull null]) {
//They have no match - update with our key and signal success
[opponent setValue:user1Key];
return [FTransactionResult successWithValue: opponent];
} else {
return [FTransactionResult abort]; //They already have an opponent - fail
//Notify the user that the match didn't happen
}
} andCompletionBlock:^(NSError *error, BOOL committed, FDataSnapshot *snapshot) {
if (!error && committed) {
//The transaction above was committed with no error
//Update our record with the other player - we're matched!
[user1ref setValue:user2Key];
//Do whatever notification you want
} else {
//Notify that the matchup failed
}
}];

How to check if purchase status is complete

I need to check into a arquivo.phtml if the purchase is complete. If it's with status complete, it will be displayed, if not with full status, displays a message "You do not have complete purchase"
To call all purchases this code is used
<php $ _orders = $ this-> GetOrders ();
And to call only those with full status, how do?
This will check the orders to see if they are complete, adjust to fit your needs:
$_orders = $this->getOrders();
foreach($_orders as $_order){
if($_order->getStatusLabel()=='Complete'){
echo 'You have a complete purchase';
}
else {
echo 'You do not have a complete purchase';
}
}
I got personal, I had to change the core for this.
With the help of a friend (http://www.icefusion.com.br) ..
in app / code / core / Mage / Sales / Block / Order / history.php
placed this construct he sent me instead of what's there!
public function __construct()
{
parent::__construct();
$this->setTemplate('sales/order/history.phtml');
//TODO: add full name logic
$orders = Mage::getResourceModel('sales/order_collection')
->addAttributeToSelect('*')
->joinAttribute('shipping_firstname', 'order_address/firstname', 'shipping_address_id', null, 'left')
->joinAttribute('shipping_lastname', 'order_address/lastname', 'shipping_address_id', null, 'left')
->addAttributeToFilter('customer_id', Mage::getSingleton('customer/session')->getCustomer()->getId())
->addAttributeToFilter('state', array('in' => Mage::getSingleton('sales/order_config')->getVisibleOnFrontStates()))
->addAttributeToFilter('status', array('eq' => 'complete'))
->addAttributeToSort('created_at', 'desc');
$this->setOrders($orders);
Mage::app()->getFrontController()->getAction()->getLayout()->getBlock('root')->setHeaderTitle(Mage::helper('sales')->__('My Orders'));
}
Done, done that, on page seudominio.com.br / history.phtml Shopping with displays only the status of Complete.
Thanks to everyone who helped or tried.

Resources