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