%args> %args> <%once> use Data::Dumper; use OpenXPKI::Serialization::Simple; %once> <%init> ############################################################ # INIT - GLOBAL VARS ############################################################ our @errors; our $debug_msg; our $wf_type = 'I18N_OPENXPKI_WF_TYPE_SMARTCARD_PIN_UNBLOCK'; ############################################################ # INIT - SUBROUTINES ############################################################ # $wf = get_workflow_by_token_id( $token_id ) sub get_workflow_by_token_id { my $id = shift; my $wf; ### Check if workflow for this token ID exists # Search for workflows of type ...SMARTCARD_PIN_UNBLOCK # with token_id = current token ID my $msg = $context->{client}->send_receive_command_msg( 'search_workflow_instances', { TYPE => $wf_type, CONTEXT => [ { KEY => 'token_id', VALUE => $id } ] }); $m->comp('/lib/debug.mhtml', 'level' => 10, 'msg' => 'msg: ' . Dumper($msg), ); my @workflow_ids; warn "search for token '$id' returned ", Dumper($msg->{PARAMS}); if (defined $msg && $msg->{SERVICE_MSG} eq 'ERROR') { @errors = [ $m->comp('/lib/get_deep_error.mhtml', 'msg' => $msg) ]; } else { eval { @workflow_ids = @{$msg->{PARAMS}}; }; $m->comp('/lib/debug.mhtml', 'level' => 1, 'msg' => 'workflow_ids: ' . Dumper(\@workflow_ids), ); } # filter out workflows that are in state FAILURE or SUCCESS # # Important: $workflow does NOT contain a ref to a Workflow # instance, but instead it is an anonymous hash that contains # only a few params my @filtered_workflows; foreach my $workflow (@workflow_ids) { push @filtered_workflows, $workflow unless ($workflow->{'WORKFLOW.WORKFLOW_STATE'} =~ /^(FAILURE|SUCCESS)$/); } # Decide what to do based on the number of entries # 0 - create new workflow # >=1 - re-entry at given state of first workflow returned # TODO: catch the error of more than one workflow if (scalar @filtered_workflows == 0) { # no workflow available warn "no workflow available for token $id"; my $c_msg = $context->{client}->send_receive_command_msg( 'create_workflow_instance', { WORKFLOW => $wf_type, PARAMS => { token_id => $id, }, }, ); if ($c_msg->{SERVICE_MSG} eq 'ERROR') { $m->comp('/lib/get_deep_error.mhtml', 'msg' => $c_msg); return; } $wf = $c_msg->{PARAMS}->{WORKFLOW}; $debug_msg = $c_msg; } else { # re-entry into existing workflow warn "found workflow(s) for token $id"; $wf = &get_workflow_info( $filtered_workflows[0]->{'WORKFLOW.WORKFLOW_SERIAL'}); return unless $wf; } # $wf_id = $wf->{ID}; # warn "finished worflow fetch/create block, we have workflow '$wf_id'"; return $wf; } # $wf = get_workflow_info( $wf_id ); sub get_workflow_info { my $wf_id = shift; my $i_msg = $context->{client}->send_receive_command_msg( 'get_workflow_info', { WORKFLOW => $wf_type, ID => $wf_id, } ); if ($i_msg->{SERVICE_MSG} eq 'ERROR') { @errors = [ $m->comp('/lib/get_deep_error.mhtml', 'msg' => $i_msg) ]; return; } return $i_msg->{PARAMS}->{WORKFLOW}; } # my $idx = get_auth_idx( $curr_user, $context ) # Returns the index of the authorizing person: # 0 = given user is not an auth person # 1 = given user is the first auth person # 2 = given user is the second auth person sub get_auth_idx { my $user = shift; my $ctx = shift; return unless $user; # return undef if user is empty/undef return 1 if $user eq $ctx->{auth1_id}; return 2 if $user eq $ctx->{auth2_id}; return 0; } # my $hash = get_auth_hash( $curr_user, $wf ); # Returns the authorization code hash for the given user # TODO: implement actual hash -- at the moment, it's just # the secret itself. sub get_auth_hash { my $user = shift; my $wf = shift; if ( $user eq $wf->{CONTEXT}->{auth1_id} ) { return $wf->{CONTEXT}->{auth1_hash}; } elsif ( $user eq $wf->{CONTEXT}->{auth2_id} ) { return $wf->{CONTEXT}->{auth2_hash}; } else { return; } } # my $secret = gen_auth_hash( $curr_user, $wf ); # Returns the secret for the given user, setting the # hash field of the workflow instance. # TODO: implement actual hash -- at the moment, it's just # the secret itself. sub gen_auth_hash { my $user = shift; my $wf = shift; my $p = {}; my $secret = ''; my $i_msg = $context->{client}->send_receive_command_msg( 'execute_workflow_activity', { ID => $wf->{ID}, ACTIVITY => 'scpu_generate_activation_code', # PARAMS => { _user => $user }, PARAMS => { }, } ); if ($i_msg->{SERVICE_MSG} eq 'ERROR') { $m->comp('/lib/get_deep_error.mhtml', 'msg' => $i_msg); return; } warn "generate_activation_code returned: ", Dumper($i_msg->{PARAMS}); $secret = $i_msg->{PARAMS}->{WORKFLOW}->{CONTEXT}->{_password}; return $secret; } ############################################################ # INIT - MAIN BLOCK ############################################################ my ($token_id, $wf_id, $next_action); # params passed via GET/POST my ($curr_user); # param from Mason my ($wf, $c_msg, $wf_state); my ($need_token_id, $req_new_auth); # state info for displaying page $need_token_id = 0; # Novosec sends the ID with uppercase letters, but the XML file/workflow # has it in lowercase. Also, the arg name 'TokenID' is hard-coded by them. $token_id = lc($m->request_args()->{TokenID}); $wf_id = $m->request_args()->{WF_ID}; $next_action = $m->request_args()->{next_action}; $req_new_auth = $m->request_args()->{req_new_auth}; $curr_user = $context->{client}->send_receive_command_msg('get_user')->{PARAMS}; $m->request_args()->{next_action} = ''; # reset to keep the flow from getting too greedy warn "===== INIT BLOCK OF unblock_pin.html =====\n", "\trequest args = ", join(', ', %{ $m->request_args() }), "\n", "\ttoken_id = '$token_id'\n", "\twf_id = '$wf_id'\n", "\tnext_action = '$next_action'\n", "\tcurr_user = '$curr_user'\n", "\treq_new_auth = '$req_new_auth'\n", "==========================================\n"; ### ### See "Workflow - UI for Smart Card PIN Unlock and Auth.pdf" for details ### on the flow of logic ### if ( $wf_id ) { # TODO: get workflow details from DB $wf = get_workflow_info( $wf_id ); if ( $wf ) { warn "SCOTTY - wf: ", join(', ', %{ $wf }); if ( ($curr_user eq '' or $curr_user eq $wf->{CREATOR}) and not $token_id ) { $need_token_id++; } else { $wf_state = $wf->{STATE}; } } else { return; } } else { if ( $token_id ) { # As part of the development process, we simulate the connection to # the smart card. If the user entered the token_id, save it in the # file /tmp/sc_token_id so it is the default when asked the next time. # # BTW, no error checking here... just do it! If it fails, it doesn't matter. if (open(TOK, ">/tmp/sc_token_id") ) { print TOK $token_id; close TOK; } warn "==> calling get_workflow_by_token_id( $token_id )"; $wf = get_workflow_by_token_id( $token_id ); $wf_state = $wf->{STATE}; } else { $need_token_id++; # cause DLL to fetch token ID # this should cause a page reload, too. } } ### ### Last but not least - If we have a valid action from a preceeding ### GET request from this script, try to process it. ### ### Note: this only works if there is a current $wf ### if ( $wf and $next_action ) { warn "===> wf = ", join(', ', keys %{ $wf } ); warn "===> action = $next_action"; my %avail_actions = (); # Map button labels to their corresponding workflow actions my %map_action = ( "Restart" => 'initialize', ); $next_action = $map_action{$next_action} if $map_action{$next_action}; # Fetch list of available actions and their params my $msg = $context->{client}->send_receive_command_msg ( "get_workflow_activities_params", { WORKFLOW => $wf_type, ID => $wf->{ID}, } ); if ($msg->{SERVICE_MSG} eq 'ERROR') { $m->comp('/lib/get_deep_error.mhtml', 'msg' => $msg); return; } %avail_actions = @{ $msg->{PARAMS} }; warn "===> msg: ", Dumper( \$msg ); warn "===> avail_actions: ", Dumper( \%avail_actions ); if ( exists $avail_actions{$next_action} ) { my $p = {}; foreach ( @{ $avail_actions{$next_action} } ) { $p->{$_->{'name'}} = $m->request_args()->{$_->{'name'}} if exists $m->request_args()->{$_->{'name'}} } warn "===> would like to execute $next_action"; warn " p = ", Dumper(\$p); warn " ...and as Tiger says, \"...Just Do It!\""; my $i_msg = $context->{client}->send_receive_command_msg( 'execute_workflow_activity', { ID => $wf->{ID}, ACTIVITY => $next_action, PARAMS => $p, } ); warn "Execute '$next_action' returned ", Dumper(\$i_msg); if ($i_msg->{SERVICE_MSG} eq 'ERROR') { $m->comp('/lib/get_deep_error.mhtml', 'msg' => $i_msg); return; } # # After executing the action, re-read the ticket so we can # display the results based on the new state # $wf = get_workflow_info( $wf->{ID} ); $wf_state = $wf->{STATE}; } else { print "
Warning: it looks like you reposted data. The next action specified, $next_action, is not in list of valid actions
\n"; } } ############################################################ # END OF INIT BLOCK # # In the following section below, the page is actually presented. # The major decision is whether the token_id has been passed. If # not, run the java applet. Otherwise, display the appropriate # details for the current state of the workflow. ############################################################ %init> % ### % ### Need token ID? (it was set in <%init>) % ### % ### If so, just run applet to fetch it from smartcard % ### % my $debug_got_token_id_from_file = 0; % % if ( $need_token_id ) { % warn "SCOTTY: begin applet block"; % $token_id = ''; % if ( open(TOK, "; % close TOK; % chomp $token_id; % } <& /service/open_form.mhtml, action => 'unblock_pin.html' &> % if ( $wf_id ) { # Pass the workflow ID, if available <& /lib/html/hidden.mhtml, 'name' => 'WF_ID', value => $wf_id &> % }Please pretent to insert smartcard...
Token ID | <& /lib/html/input.mhtml, name => 'TokenID', value => $token_id, width => 25, &> |
Note: this is where the smartcard applet will run in the final version
% # NOTE: Be sure to pass the workflow ID (WF_ID), too!! % # <& '/service/reset_token/plugin.mhtml', % # params => { % # 'Request' => 'GetTokenID', % # 'PostURL' => $m->comp('/lib/rel2abs.mhtml', % # rel => $context->{menu}->get_root() . '/service/reset_token/unblock_pin.html'), % # }, % # &> % warn "SCOTTY: end applet block"; %################################################### %# At this point, we have a valid workflow and can decide how %# to continue based on the state of the workflow. %################################################### %# Fetch authorizing IDs from user % } elsif ( $wf->{STATE} eq 'HAVE_TOKEN_OWNER' ) { % warn "SCOTTY: begin HAVE_TOKEN_OWNER block"; <& /service/open_form.mhtml, action => 'unblock_pin.html' &> <& /lib/html/hidden.mhtml, 'name' => 'next_action', value => 'store_auth_ids' &> <& /lib/html/hidden.mhtml, 'name' => 'TokenID', value => $token_id &> <& /lib/html/hidden.mhtml, 'name' => 'WF_ID', value => $wf_id &>You are trying to unblock the Smartcard with the token ID <% $token_id %>, which is assigned in LDAP to <% $wf->{CONTEXT}->{token_owner} %>.
To continue the PIN unblock process, you must specify two persons that will authorize your request. They will each receive an e-mail containing details on how to fetch the authorization codes required for the next step.
First Authorizing Person | <& /lib/html/input.mhtml, name => 'auth1_id', width => 50, &> |
Second Authorizing Person | <& /lib/html/input.mhtml, name => 'auth2_id', width => 50, &> |
Your request is currently pending approval by the authorizing persons you specified. They have not yet fetched their activation codes. Please try again later.
Notification via RT not implemented yet. To access the authorization codes, copy this link and access the page after logging out first.
<& /service/open_form.mhtml, action => 'unblock_pin.html' &> <& /lib/html/hidden.mhtml, 'name' => 'TokenID', value => $token_id &> <& /lib/html/hidden.mhtml, 'name' => 'WF_ID', value => $wf_id &> <& /service/reset_token/unblock_reset.mhtml &> <& /service/close_form.mhtml &> % } else { % my $hash = get_auth_hash( $curr_user, $wf ); % if ( $req_new_auth or not $hash ) { % my $secret = gen_auth_hash( $curr_user, $wf );Your Authentication Code | <% $secret %> |
First Authorization Code: | <& /lib/html/input.mhtml, name => '_auth1_code', width => 25, &> |
---|---|
Second Authorization Code: | <& /lib/html/input.mhtml, name => '_auth2_code', width => 25, &> |
New PIN: | <& /lib/html/input.mhtml, name => '_new_pin1', width => 25, &> |
New PIN (repeat): | <& /lib/html/input.mhtml, name => '_new_pin2', width => 25, &> |
The requestor, <% $context->{creator} %>, may now continue with the unblocking process. If you have forgotten your authorization code, you may generate a new one by clicking the button below. The previous code will be no longer usable. % if ( $req_new_auth ) { % my $secret = gen_auth_hash( $curr_user, $wf );
Your Authentication Code | <% $secret %> |
The PIN has been written to the card
% my $i_msg = $context->{client}->send_receive_command_msg( % 'execute_workflow_activity', % { % ID => $wf->{ID}, % ACTIVITY => 'write_pin_ok', % } % ); % if ($i_msg->{SERVICE_MSG} eq 'ERROR') { % $m->comp('/lib/get_deep_error.mhtml', 'msg' => $i_msg); % } %################################################### %# Handle unimplemented states %################################################### % } else { % warn "SCOTTY: begin STATE_NOT_IMPLEMENTED block";Really cool user interface will go here...
% warn "SCOTTY: end STATE_NOT_IMPLEMENTED block"; % }Current Token ID: <% $token_id || 'unset' %>
Workflow State: <% $wf->{STATE} %>
Curr User = <% $curr_user %>
% if ( $wf->{ID} ) { <& '/service/workflow/show_instance.html', 'id' => $wf->{ID} &> % } % if ( $debug_got_token_id_from_file ) {WARN: debugging code for /tmp/sc_token_id is enabled
% }