12
12
use Fhp \Model \TanRequestChallengeImage ;
13
13
use InvalidArgumentException ;
14
14
use RuntimeException ;
15
+ use Symfony \Component \Console \Output \OutputInterface ;
15
16
16
17
class ActionService
17
18
{
18
- public static function login (FinTs $ finTs ): void
19
+ private ?OutputInterface $ output ;
20
+
21
+ public function setOutput (OutputInterface $ output ): void
22
+ {
23
+ $ this ->output = $ output ;
24
+ }
25
+
26
+ public function login (FinTs $ finTs ): void
19
27
{
20
28
$ login = $ finTs ->login ();
21
29
22
30
if ($ login ->needsTan ()) {
23
- self :: handleStrongAuthentication ($ finTs , $ login );
31
+ $ this -> handleStrongAuthentication ($ finTs , $ login );
24
32
}
25
33
}
26
34
27
- public static function action (FinTs $ finTs , BaseAction $ action ): void
35
+ public function action (FinTs $ finTs , BaseAction $ action ): void
28
36
{
29
37
$ finTs ->execute ($ action );
30
38
31
39
if ($ action ->needsTan ()) {
32
- self :: handleStrongAuthentication ($ finTs , $ action );
40
+ $ this -> handleStrongAuthentication ($ finTs , $ action );
33
41
}
34
42
}
35
43
@@ -57,12 +65,12 @@ public static function action(FinTs $finTs, BaseAction $action): void
57
65
*
58
66
* @param BaseAction $action Some action that requires strong authentication.
59
67
*/
60
- public static function handleStrongAuthentication (FinTs $ finTs , BaseAction $ action ): void
68
+ public function handleStrongAuthentication (FinTs $ finTs , BaseAction $ action ): void
61
69
{
62
70
if ($ finTs ->getSelectedTanMode ()?->isDecoupled() === true ) {
63
- self :: handleDecoupled ($ finTs , $ action );
71
+ $ this -> handleDecoupled ($ finTs , $ action );
64
72
} else {
65
- self :: handleTan ($ finTs , $ action );
73
+ $ this -> handleTan ($ finTs , $ action );
66
74
}
67
75
}
68
76
@@ -71,39 +79,40 @@ public static function handleStrongAuthentication(FinTs $finTs, BaseAction $acti
71
79
* application.
72
80
* @param BaseAction $action Some action that requires a TAN.
73
81
*/
74
- protected static function handleTan (FinTs $ finTs , BaseAction $ action ): void
82
+ protected function handleTan (FinTs $ finTs , BaseAction $ action ): void
75
83
{
76
84
// Find out what sort of TAN we need, tell the user about it.
77
85
$ tanRequest = $ action ->getTanRequest ();
78
- echo 'The bank requested a TAN. ' ;
86
+ $ this -> output ( 'The bank requested a TAN. ' ) ;
79
87
if ($ tanRequest ?->getChallenge() !== null ) {
80
- echo ' Instructions: ' . $ tanRequest ?->getChallenge();
88
+ $ this -> output ( ' Instructions: ' . $ tanRequest ?->getChallenge() );
81
89
}
82
90
83
- echo "\n" ;
84
91
if ($ tanRequest ?->getTanMediumName() !== null ) {
85
- echo 'Please use this device: ' . $ tanRequest ?->getTanMediumName() . "\n" ;
92
+ $ this -> output ( 'Please use this device: ' . $ tanRequest ?->getTanMediumName()) ;
86
93
}
87
94
88
95
// Challenge Image for PhotoTan/ChipTan
89
96
if ($ tanRequest ?->getChallengeHhdUc() instanceof \Fhp \Syntax \Bin) {
90
97
try {
91
98
$ flicker = new TanRequestChallengeFlicker ($ tanRequest ?->getChallengeHhdUc());
92
- echo 'There is a challenge flicker. ' . PHP_EOL ;
99
+ $ this -> output ( 'There is a challenge flicker. ' ) ;
93
100
// save or output svg
94
101
$ flickerPattern = $ flicker ->getFlickerPattern ();
95
102
// other renderers can be implemented with this pattern
96
103
$ svg = new SvgRenderer ($ flickerPattern );
97
- echo $ svg ->getImage ();
104
+ $ this -> output ( $ svg ->getImage () );
98
105
} catch (InvalidArgumentException ) {
99
106
// was not a flicker
100
107
$ challengeImage = new TanRequestChallengeImage ($ tanRequest ?->getChallengeHhdUc());
101
- echo 'There is a challenge image. ' . PHP_EOL ;
108
+ $ this -> output ( 'There is a challenge image. ' ) ;
102
109
// Save the challenge image somewhere
103
110
// Alternative: HTML sample code
104
- echo '<img src="data: ' . htmlspecialchars ($ challengeImage ->getMimeType ()) . ';base64, ' . base64_encode (
105
- $ challengeImage ->getData ()
106
- ) . '" /> ' . PHP_EOL ;
111
+ $ this ->output (
112
+ '<img src="data: ' . htmlspecialchars ($ challengeImage ->getMimeType ()) . ';base64, ' . base64_encode (
113
+ $ challengeImage ->getData ()
114
+ ) . '" /> '
115
+ );
107
116
}
108
117
}
109
118
@@ -115,10 +124,10 @@ protected static function handleTan(FinTs $finTs, BaseAction $action): void
115
124
// handling, not just one like in this simplified example). You *only* need to carry over the $persistedInstance
116
125
// and the $persistedAction (which are simple strings) by storing them in some database or file where you can load
117
126
// them again in a new PHP process when the user sends the TAN.
118
- echo "Please enter the TAN: \n" ;
127
+ $ this -> output ( "Please enter the TAN: " ) ;
119
128
$ tan = trim (fgets (STDIN ));
120
129
121
- echo sprintf ('Submitting TAN: %s%s ' , $ tan, PHP_EOL );
130
+ $ this -> output ( sprintf ('Submitting TAN: %s ' , $ tan) );
122
131
$ finTs ->submitTan ($ action , $ tan );
123
132
}
124
133
@@ -128,18 +137,17 @@ protected static function handleTan(FinTs $finTs, BaseAction $action): void
128
137
* authentication at all, i.e. you could filter out any decoupled TanModes when letting the user choose.
129
138
* @param BaseAction $action Some action that requires decoupled authentication.
130
139
*/
131
- protected static function handleDecoupled (FinTs $ finTs , BaseAction $ action ): void
140
+ protected function handleDecoupled (FinTs $ finTs , BaseAction $ action ): void
132
141
{
133
142
$ tanMode = $ finTs ->getSelectedTanMode ();
134
143
$ tanRequest = $ action ->getTanRequest ();
135
- echo 'The bank requested authentication on another device. ' ;
144
+ $ this -> output ( 'The bank requested authentication on another device. ' ) ;
136
145
if ($ tanRequest ?->getChallenge() !== null ) {
137
- echo ' Instructions: ' . $ tanRequest ?->getChallenge();
146
+ $ this -> output ( ' Instructions: ' . $ tanRequest ?->getChallenge() );
138
147
}
139
148
140
- echo "\n" ;
141
149
if ($ tanRequest ?->getTanMediumName() !== null ) {
142
- echo 'Please check this device: ' . $ tanRequest ?->getTanMediumName() . "\n" ;
150
+ $ this -> output ( 'Please check this device: ' . $ tanRequest ?->getTanMediumName()) ;
143
151
}
144
152
145
153
// IMPORTANT: In your real application, you don't have to use sleep() in PHP. You can persist the state in the same
@@ -148,18 +156,18 @@ protected static function handleDecoupled(FinTs $finTs, BaseAction $action): voi
148
156
// without polling entirely and just let the user confirm manually in all cases (i.e. only implement the `else`
149
157
// branch below).
150
158
if ($ tanMode ?->allowsAutomatedPolling() === true ) {
151
- echo "Polling server to detect when the decoupled authentication is complete. \n" ;
159
+ $ this -> output ( "Polling server to detect when the decoupled authentication is complete. " ) ;
152
160
sleep ($ tanMode ?->getFirstDecoupledCheckDelaySeconds());
153
161
for ($ attempt = 0 ;
154
162
$ tanMode ?->getMaxDecoupledChecks() === 0 || $ attempt < $ tanMode ?->getMaxDecoupledChecks();
155
163
++$ attempt
156
164
) {
157
165
if ($ finTs ->checkDecoupledSubmission ($ action )) {
158
- echo "Confirmed. \n" ;
166
+ $ this -> output ( "Confirmed. " ) ;
159
167
return ;
160
168
}
161
169
162
- echo "Still waiting... \n" ;
170
+ $ this -> output ( "Still waiting... " ) ;
163
171
sleep ($ tanMode ?->getPeriodicDecoupledCheckDelaySeconds());
164
172
}
165
173
@@ -168,17 +176,28 @@ protected static function handleDecoupled(FinTs $finTs, BaseAction $action): voi
168
176
169
177
if ($ tanMode ?->allowsManualConfirmation() === true ) {
170
178
do {
171
- echo "Please type 'done' and hit Return when you've completed the authentication on the other device. \n" ;
179
+ $ this ->output (
180
+ "Please type 'done' and hit Return when you've completed the authentication on the other device. "
181
+ );
172
182
while (trim (fgets (STDIN )) !== 'done ' ) {
173
- echo "Try again. \n" ;
183
+ $ this -> output ( "Try again. " ) ;
174
184
}
175
185
176
- echo "Confirming that the action is done. \n" ;
186
+ $ this -> output ( "Confirming that the action is done. " ) ;
177
187
} while (! $ finTs ->checkDecoupledSubmission ($ action ));
178
188
179
- echo "Confirmed \n" ;
189
+ $ this -> output ( "Confirmed " ) ;
180
190
} else {
181
191
throw new AssertionError ('Server allows neither automated polling nor manual confirmation ' );
182
192
}
183
193
}
194
+
195
+ private function output (string $ string ): void
196
+ {
197
+ if ($ this ->output instanceof OutputInterface) {
198
+ $ this ->output ->writeln ($ string );
199
+ } else {
200
+ echo $ string . PHP_EOL ;
201
+ }
202
+ }
184
203
}
0 commit comments