1use strict; 2use warnings; 3use Carp; 4use English; 5use Test::More qw(no_plan); 6 7#plan tests => 8; 8 9use OpenXPKI::Tests; 10use OpenXPKI::Client; 11use Data::Dumper; 12use OpenXPKI::Serialization::Simple; 13 14diag("Smartcard Unblock workflow\n") if $ENV{VERBOSE}; 15our $debug = 0; 16my $sleep = 0; # set to '1' to cause pause between transactions 17 18# reuse the already deployed server 19#my $instancedir = 't/60_workflow/test_instance'; 20my $instancedir = ''; 21my $socketfile = $instancedir . '/var/openxpki/openxpki.socket'; 22my $pidfile = $instancedir . '/var/openxpki/openxpki.pid'; 23 24my $tok_id; 25my $wf_type = 'I18N_OPENXPKI_WF_TYPE_SMARTCARD_PIN_UNBLOCK'; 26my ( $msg, $wf_id, $client ); 27 28my %act_test = ( 29 selfserve => { 30 name => 'selfserve', 31 role => 'User', 32 }, 33 user => { 34 name => 'user002@local', 35 role => 'User', 36 newpin => '1234', 37 token => 'gem2_002', 38 }, 39 auth1 => { 40 name => 'user003@local', 41 role => 'User', 42 code => '', 43 }, 44 auth2 => { 45 name => 'user004@local', 46 role => 'User', 47 code => '', 48 }, 49); 50 51# 52# $client = wfconnect( USER, PASS [, REALM] ); 53# 54sub wfconnect { 55 my ( $u, $p, $r ) = @_; 56 my $c = OpenXPKI::Client->new( 57 { 58 TIMEOUT => 100, 59 SOCKETFILE => $instancedir . '/var/openxpki/openxpki.socket', 60 } 61 ); 62 login( 63 { 64 CLIENT => $c, 65 USER => $u, 66 PASSWORD => $p, 67 } 68 ) or croak "Login as $c failed: $@"; 69 return $client = $c; 70} 71 72sub wfdisconnect { 73 eval { $client && $client->send_receive_service_msg('LOGOUT'); }; 74 $client = undef; 75} 76 77# 78# usage: my $msg = wfexec( ID, ACTIVITY, { PARAMS } ); 79# 80sub wfexec { 81 my ( $id, $act, $params ) = @_; 82 my $msg; 83 84 croak("Unable to exec action '$act' on closed connection") 85 unless defined $client; 86 87 $msg = $client->send_receive_command_msg( 88 'execute_workflow_activity', 89 { 90 'ID' => $id, 91 'ACTIVITY' => $act, 92 'PARAMS' => $params, 93 'WORKFLOW' => $wf_type, 94 }, 95 ); 96 return $msg; 97 98} 99 100# 101# usage: my $state = wfstate( ID ); 102# Note: $@ contains either error message or Dumper($msg) 103# 104sub wfstate { 105 my ($id) = @_; 106 my ( $msg, $state ); 107 my $disc = 0; 108 $@ = ''; 109 110 unless ($client) { 111 $disc++; 112 unless ( $client = 113 wfconnect( $act_test{user}->{name}, $act_test{user}->{role} ) ) 114 { 115 $@ = "Failed to connect as " . $act_test{user}->{name}; 116 return; 117 } 118 } 119 $msg = 120 $client->send_receive_command_msg( 'get_workflow_info', 121 { 'WORKFLOW' => $wf_type, 'ID' => $id, } ); 122 if ( is_error_response($msg) ) { 123 $@ = "Error running get_workflow_info: " . Dumper($msg); 124 return; 125 } 126 $@ = Dumper($msg); 127 if ($disc) { 128 wfdisconnect(); 129 } 130 return $msg->{PARAMS}->{WORKFLOW}->{STATE}; 131} 132 133################################################### 134# The wftask_* routines represent individual tasks 135# done by a user. If there is an error in a single 136# step, undef is returned. The reason is in $@ and 137# on success, $@ contains Dumper($msg) if $msg is 138# not normally returned. 139# 140# Each routine takes care of login and logout. 141################################################### 142# 143# usage: my $id = wftask_create( USER, PASS, TOKENID, AUTH1, AUTH2 ); 144# 145sub wftask_create { 146 my ( $u, $p, $t, $a1, $a2 ) = @_; 147 my ( $id, $msg ); 148 149 unless ( $client = wfconnect( $u, $p ) ) { 150 $@ = "Failed to connect as $u"; 151 return; 152 } 153 154 $msg = $client->send_receive_command_msg( 155 'create_workflow_instance', 156 { 157 PARAMS => { token_id => $t }, 158 WORKFLOW => $wf_type, 159 } 160 ); 161 if ( is_error_response($msg) ) { 162 $@ = "Error creating workflow instance: " . Dumper($msg); 163 return; 164 } 165 166 $id = $msg->{PARAMS}->{WORKFLOW}->{ID}; 167 168 unless ( $msg->{PARAMS}->{WORKFLOW}->{STATE} eq 'HAVE_TOKEN_OWNER' ) { 169 $@ = "Error - new workflow in wrong state: " . Dumper($msg); 170 return; 171 } 172 173 $msg = 174 wfexec( $id, 'store_auth_ids', { auth1_id => $a1, auth2_id => $a2 } ); 175 if ( is_error_response($msg) ) { 176 $@ = "Error storing auth IDs: " . Dumper($msg); 177 return; 178 } 179 unless ( $msg->{PARAMS}->{WORKFLOW}->{STATE} eq 'PEND_ACT_CODE' ) { 180 $@ = "Error - new workflow in wrong state: " . Dumper($msg); 181 return; 182 } 183 184 wfdisconnect(); 185 186 # eval { 187 # $msg = $client->send_receive_service_msg('LOGOUT'); 188 # }; 189 190 return $id; 191} 192 193# 194# usage: my $code = wftask_getcode( ID, USER, PASS ); 195# 196sub wftask_getcode { 197 my ( $id, $u, $p ) = @_; 198 199 my ( $ret, $msg ); 200 unless ( $client = wfconnect( $u, $p ) ) { 201 $@ = "Failed to connect as $u"; 202 return; 203 } 204 sleep 1 if $sleep; 205 206 $msg = 207 $client->send_receive_command_msg( 'get_workflow_info', 208 { 'WORKFLOW' => $wf_type, 'ID' => $id, } ); 209 if ( is_error_response($msg) ) { 210 $@ = "Error running get_workflow_info: " . Dumper($msg); 211 return; 212 } 213 sleep 1 if $sleep; 214 215 unless ( $msg->{PARAMS}->{WORKFLOW}->{STATE} eq 'PEND_ACT_CODE' ) { 216 $@ = "Error: workflow state must be PEND_ACT_CODE to get code"; 217 diag( $@, Dumper($msg) ); 218 return; 219 } 220 221 # $msg = wfexec( $id, 'scpu_generate_activation_code', { _user => $u }, ); 222 $msg = wfexec( $id, 'scpu_generate_activation_code', {}, ); 223 if ( is_error_response($msg) ) { 224 $@ = "Error running scpu_generate_activation_code: " . Dumper($msg); 225 return; 226 } 227 sleep 1 if $sleep; 228 229 $ret = $msg->{PARAMS}->{WORKFLOW}->{CONTEXT}->{_password}; 230 diag( "$id/$u code: " . $ret ); 231 wfdisconnect(); 232 233 # eval { 234 # $msg = $client->send_receive_service_msg('LOGOUT'); 235 # }; 236 return $ret; 237} 238 239# 240# usage: my $ret = wftask_verifycodes( ID, USER, PASS, CODE1, CODE2, PIN1, PIN2 ); 241# 242sub wftask_verifycodes { 243 my ( $id, $u, $p, $ac1, $ac2, $pin1, $pin2 ) = @_; 244 my ( $ret, $msg, $state ); 245 246 unless ( $client = wfconnect( $u, $p ) ) { 247 $@ = "Failed to connect as $u"; 248 return; 249 } 250 251 $state = wfstate($id) or return; 252 253 unless ( $state eq 'PEND_PIN_CHANGE' ) { 254 $@ = "Error - wrong state ($state) for pin change"; 255 return; 256 } 257 258 $msg = wfexec( 259 $id, 260 'post_codes_and_pin', 261 { 262 _auth1_code => $ac1, 263 _auth2_code => $ac2, 264 _new_pin1 => $pin1, 265 _new_pin2 => $pin2, 266 } 267 ); 268 if ( is_error_response($msg) ) { 269 $@ = "Error running post_codes_and_pin: " . Dumper($msg); 270 return; 271 } 272 273 $state = wfstate($wf_id); 274 unless ( $state eq 'CAN_WRITE_PIN' ) { 275 $@ = "Error - wrong state ($state) for pin change"; 276 return; 277 } 278 279 # Wrap it up by changing state to success 280 $msg = wfexec( $wf_id, 'write_pin_ok', {} ); 281 if ( is_error_response($msg) ) { 282 $@ = "Error running write_pin_ok: " . Dumper($msg); 283 return; 284 } 285 286 $state = wfstate($wf_id); 287 unless ( $state eq 'SUCCESS' ) { 288 $@ = "Error - wrong state ($state) for finish"; 289 return; 290 } 291 292 wfdisconnect(); 293 294 # eval { 295 # $msg = $client->send_receive_service_msg('LOGOUT'); 296 # }; 297 298 return 1; 299} 300 301############################################################ 302# START TESTS 303############################################################ 304 305$wf_id = wftask_create( 306 $act_test{user}->{name}, $act_test{user}->{role}, 307 $act_test{user}->{token}, $act_test{auth1}->{name}, 308 $act_test{auth2}->{name} 309); 310croak $@ unless defined $wf_id; 311 312# Get activation codes 313foreach my $a ( qw( auth1 auth2 ) ) { 314 my $code = 315 wftask_getcode( $wf_id, $act_test{$a}->{name}, $act_test{$a}->{role} ); 316 croak $@ unless defined $code; 317 $act_test{$a}->{code} = $code; 318} 319 320 321# Now, provide the correct details for the post 322ok( 323 wftask_verifycodes( 324 $wf_id, $act_test{user}->{name}, 325 $act_test{user}->{role}, $act_test{auth1}->{code}, 326 $act_test{auth2}->{code}, $act_test{user}->{newpin}, 327 $act_test{user}->{newpin}, 328 ), 329 'Verify codes and pin using correct codes' 330); 331 332is( wfstate($wf_id), 'SUCCESS', 'Workflow state after write_pin_ok' ) 333 or diag($@); 334 335# LOGOUT 336wfdisconnect(); 337 338