source: BMXNET_RPMS_dotNET_UTILITIES-BMX/branch/BMX41000/IHS BMX Framework/IndianHealthService.BMXNet.WinForm/LoginProcess.cs@ 1453

Last change on this file since 1453 was 1193, checked in by Sam Habiel, 13 years ago

BMXNetEhrSessionConnection.cs implements ConnectionEncoding so that the project can compile.
BMXNetEhrSessionConnection.cs: ReceiveString TCP code refactored, AGAIN!

Updated dlls.

File size: 21.2 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Text;
4using IndianHealthService.BMXNet.WinForm.Configuration;
5using IndianHealthService.BMXNet.Net;
6using System.Security.Principal;
7using IndianHealthService.BMXNet.Forms;
8using IndianHealthService.BMXNet.WinForm.Forms;
9using System.Windows.Forms;
10using IndianHealthService.BMXNet.WinForm.Model;
11
12namespace IndianHealthService.BMXNet.WinForm
13{
14 /// <summary>
15 /// This class models the workflow of the LoginProcess. It correographs UI and non-UI
16 /// login methods, login cancelling, management dialogs, and provides hooks (events) to
17 /// customize the login workflow.
18 /// </summary>
19 /// <example>
20 /// See the SDK for other examples.
21 /// <code>
22 /// this.Framework = WinFramework.CreateWithNetworkBroker(true);
23 /// this.Framework.LoadSettings(LocalPersistentStore.CreateIn(Environment.SpecialFolder.LocalApplicationData, EntryAssemblyInfo.AssemblyCompany + "/" + EntryAssemblyInfo.AssemblyProduct, false), "settings");
24 /// this.Framework.LoadConnectionSpecs(LocalPersistentStore.CreateIn(Environment.SpecialFolder.LocalApplicationData, EntryAssemblyInfo.AssemblyCompany + "/" + EntryAssemblyInfo.AssemblyProduct, false), "connectiosn");
25 /// LoginProcess login = this.Framework.CreateLoginProcess();
26 ///
27 /// //Attempt a non-UI WindowsAuth if and only if there is a default connection with WindowsAuth
28 /// //Of course, an application can set its own policy of when to AttemptWindowsAuthLogin()
29 ///
30 /// if (login.HasDefaultConnectionWithUseWindowsAuth)
31 /// {
32 /// login.AttemptWindowsAuthLogin();
33 /// }
34 ///
35 /// //If not attempted yet, i.e. skipped the AttemptWindowsAuthLogin(), or was unsuccessul, try and UI login
36 /// if (!login.WasLoginAttempted || !login.WasSuccessful)
37 /// {
38 /// login.AttemptUserInputLogin(IndianHealthService.BMXNet.Util.EntryAssemblyInfo.AssemblyTitle+" Login", 3,!this.Framework.BootStrapSettings.Get("lockedit",false), this);
39 /// }
40 ///
41 /// //If the login process was unable to login after the max tries (or fow other configuration reasons), exit the application
42 /// if (!login.WasSuccessful)
43 /// {
44 /// this.Close();
45 /// return;
46 /// }
47 ///
48 /// // Making sure that the user's division is set. Can use AttemptUserInputSetDivision() or the application can devise another solution
49 /// if ((this.Framework.User.Division == null) &amp;&amp; !this.Framework.AttemptUserInputSetDivision("Set Initial Division", this))
50 /// {
51 /// this.Close();
52 /// return;
53 /// }
54 ///
55 /// // Logged in with valid user and division
56 /// this.RemoteSession = this.Framework.PrimaryRemoteSession;
57 /// </code></example>
58 public class LoginProcess
59 {
60 /// <summary>
61 /// Triggered before every login attempt. See <see cref="AttemptingLoginEventArgs"/> for details .
62 /// </summary>
63 public event EventHandler<AttemptingLoginEventArgs> AttemptingLogin;
64
65 /// <summary>
66 /// Triggered after every login attempt. See <see cref="LoginAttemptedEventArgs"/> for details .
67 /// </summary>
68 public event EventHandler<LoginAttemptedEventArgs> LoginAttempted;
69
70 private bool _cancel = false;
71
72 /// <summary>
73 /// During a LoginAttempted the LoginProcess can be Cancelled by setting Cancel to True.
74 /// </summary>
75 public bool Cancel
76 {
77 get { return _cancel; }
78 set { _cancel = value; }
79 }
80
81 private bool _IsSwitchServerModeEnabled = false;
82
83 /// <summary>
84 /// If set to True, the Connection combo box on the Login screen with be dropped down when the
85 /// dialog is displayed. This is useful for applications that have an option to change Connections.
86 /// The default is False.
87 /// </summary>
88 public bool IsSwitchServerModeEnabled
89 {
90 get { return _IsSwitchServerModeEnabled; }
91 set { _IsSwitchServerModeEnabled = value; }
92 }
93
94 private bool _autoSetDivisionToLastLookup = true;
95
96 /// <summary>
97 /// If set to True, RPMS is checked and if there was a previously set division for the user it will be used, otherwise
98 /// MustResolveDivision will be set to True and the division will need to be set for the user.
99 /// The default is True
100 /// </summary>
101 public bool AutoSetDivisionToLastLookup
102 {
103 get { return _autoSetDivisionToLastLookup; }
104 set { _autoSetDivisionToLastLookup = value; }
105 }
106
107 private bool _mustResolveDivision = false;
108
109 /// <summary>
110 /// If the division for the user has not been determine after the LoginProcess, MustResolveDivision will be set to True.
111 /// The default if False.
112 /// </summary>
113 public bool MustResolveDivision
114 {
115 get { return _mustResolveDivision; }
116 set { _mustResolveDivision = value; }
117 }
118
119 private int _loginAttempts = 0;
120
121 /// <summary>
122 /// The number of login attempts so far. This value can be modified during the AttemptingLogin and LoginAttempted events.
123 /// </summary>
124 public int LoginAttempts
125 {
126 get { return _loginAttempts; }
127 set { _loginAttempts = value; }
128 }
129
130 private int _maxAttempts = 3;
131
132 /// <summary>
133 /// The number of login attempts before cancelling the LoginProcess. The default value is 3. This value can be modified during the AttemptingLogin and LoginAttempted events.
134 /// </summary>
135 public int MaxAttempts
136 {
137 get { return _maxAttempts; }
138 set { _maxAttempts = value; }
139 }
140
141 private bool _wasLoginAttempted = false;
142
143 /// <summary>
144 /// True if a login was attempted. False if the user is presented with a LoginDialog and "Cancel" is selected.
145 /// The value is changed during every login attempt cycle.
146 /// </summary>
147 public bool WasLoginAttempted
148 {
149 get { return _wasLoginAttempted; }
150 set { _wasLoginAttempted = value; }
151 }
152
153 private bool _wasSuccessful = false;
154
155 /// <summary>
156 /// True if the most recent login attempt was successful.
157 /// </summary>
158 public bool WasSuccessful
159 {
160 get { return _wasSuccessful; }
161 set { _wasSuccessful = value; }
162 }
163 private String _failureMessage = null;
164
165 /// <summary>
166 /// A reasonable message to display to the user if the last login attempt failed.
167 /// </summary>
168 public String FailureMessage
169 {
170 get { return _failureMessage; }
171 set { _failureMessage = value; }
172 }
173
174 private Exception _failureException = null;
175
176 /// <summary>
177 /// If an exception occured during the last login attempt, FailureException will be set to it.
178 /// </summary>
179 public Exception FailureException
180 {
181 get { return _failureException; }
182 set { _failureException = value; }
183 }
184
185 private RpmsConnectionSpec _connectionSpec = null;
186
187 /// <summary>
188 /// The active ConnectionSpec being used to login. With care, the property can be changed or the instance can be modified during the AttemptingLogin and LoginAttempted events
189 /// with care.
190 /// </summary>
191 public RpmsConnectionSpec ConnectionSpec
192 {
193 get { return _connectionSpec; }
194 set { _connectionSpec = value; }
195 }
196
197 internal LoginProcess(WinFramework aFramework)
198 {
199 this.Framework = aFramework;
200 }
201
202 internal RpmsConnectionSettings Settings
203 {
204 get { return this.Framework.ConnectionSettings; }
205 }
206
207 private WinFramework _framework = null;
208
209 internal protected WinFramework Framework
210 {
211 get { return _framework; }
212 set { _framework = value; }
213 }
214
215 /// <summary>
216 /// Answer True if there is a default managed connection that uses WindowsAuthenication
217 /// </summary>
218 public bool HasDefaultConnectionWithUseWindowsAuth
219 {
220 get
221 {
222 RpmsConnectionSpec spec = this.Settings.DefaultConnectionSpec;
223
224 return spec == null ? false : spec.UseWindowsAuthentication;
225 }
226
227
228 }
229
230
231 /// <summary>
232 /// Attempt a WindowsAuthentication Login and answer true if it was successful.
233 /// </summary>
234 /// <returns>True if login was successful</returns>
235 public bool AttemptWindowsAuthLogin(RpmsConnectionSpec aConnectionSpec)
236 {
237 RpmsConnectionSpec spec = aConnectionSpec;
238 if (spec == null)
239 {
240 return this.Failed("A default connection spec must be set for automatic Windows Authenication");
241 }
242
243 if (spec.UseWindowsAuthentication)
244 {
245 return this.PrimitiveAttemptWindowsAuthLogin(spec);
246 }
247 else
248 {
249 return this.Failed("Connection spec must be set for automatic Windows Authenication for auto-login");
250 }
251 }
252
253 /// <summary>
254 /// Using the current default connection spec, attempt a WindowsAuthentication Login and answer true if it was successful.
255 /// </summary>
256 /// <remarks>
257 /// Most common approach when using the DefaultConnection feature the RpmsConnection spec class.
258 /// </remarks>
259 /// <returns>True if login was successful</returns>
260 public bool AttemptWindowsAuthLogin()
261 {
262 return this.AttemptWindowsAuthLogin(this.Settings.DefaultConnectionSpec);
263 }
264
265 private bool Failed(string aMessage)
266 {
267 this.WasLoginAttempted = true;
268 this.WasSuccessful = false;
269 this.FailureMessage = aMessage;
270
271 return this.WasSuccessful;
272 }
273
274 /// <summary>
275 /// Attempt an interactive UI login. There are several useful arguments to control the process and appearance of the this process.
276 /// </summary>
277 /// <param name="aDialogTitle">The title of the login. Customize to be something like "MyApplication Login"</param>
278 /// <param name="maxAttempts">The number of attempts before cancelling</param>
279 /// <param name="enableConnectionManagement">If false, the user will not be able to change the connections. This is useful for lockdown situations</param>
280 /// <param name="aUiOwnerForPositioningDialog">Provide your main application as the window owner so the LoginDialog box is correctly managed.</param>
281 /// <returns>True if login was successful</returns>
282 public bool AttemptUserInputLogin(string aDialogTitle, int maxAttempts, bool enableConnectionManagement,IWin32Window aUiOwnerForPositioningDialog)
283 {
284 //test sam
285 //Framework.SocketBroker.Open(
286 //Framework.SocketBroker.PrimaryRemoteSession.TransmitRPC("XUS INTRO TEXT","","XUS SIGNON");
287 //test sam
288
289 this.MaxAttempts = maxAttempts;
290 RpmsLoginView view = (RpmsLoginView)new RpmsLoginDialog();
291
292 view.Title = aDialogTitle;
293
294 RpmsLoginPresenter presenter = new RpmsLoginPresenter();
295 presenter.EnableConnectionManagement = enableConnectionManagement;
296 presenter.View = view;
297 presenter.Model = this;
298 presenter.UiOwner = aUiOwnerForPositioningDialog;
299
300 presenter.Open();
301
302
303 return this.WasSuccessful;
304 }
305
306 /// <summary>
307 /// If the application is managing the actual login, send Succeeded() to indicate success.
308 /// </summary>
309 /// <remarks>
310 /// Do not set WasSuccessful to true.
311 /// </remarks>
312 /// <returns>True if successful</returns>
313 public bool Succeeded()
314 {
315 this.WasLoginAttempted = true;
316 this.WasSuccessful = true;
317 this.FailureMessage = "";
318 this.Framework.Login = this;
319
320 return this.WasSuccessful;
321 }
322
323
324
325 internal bool PrimitiveAttemptWindowsAuthLogin(RpmsConnectionSpec aSpec)
326 {
327 BMXNetBroker broker = this.Framework.SocketBroker;
328 this.ConnectionSpec = aSpec;
329
330 if (this.AttemptingLogin != null)
331 {
332 AttemptingLoginEventArgs args = new AttemptingLoginEventArgs();
333 args.Process = this;
334 this.AttemptingLogin(this, args);
335 if (args.Handled)
336 {
337 return this.WasSuccessful;
338 }
339
340 if (args.Cancel)
341 {
342 this.Cancel = true;
343 return false;
344 }
345 }
346
347 try
348 {
349 if (broker.Open(aSpec.Server, aSpec.Port, aSpec.NameSpace, WindowsIdentity.GetCurrent(),aSpec.SendTimeout,aSpec.ReceiveTimeout))
350 {
351 this.Succeeded();
352 this.ResolveDivision(broker);
353 return this.WasSuccessful;
354 }
355 else
356 {
357 return this.Failed("Unable to authenicate. Use may need to login");
358 }
359 }
360 catch (BMXNetException problem)
361 {
362 this.FailureException = problem;
363 return this.Failed(problem.Message);
364 }
365 catch (Exception anException)
366 {
367 this.FailureException = anException;
368 return this.Failed("Critical issue: " + anException.Message);
369 }
370 }
371
372
373 public VerifyCodeUpdateResult AttemptVerifyCodeChange(WinFramework bmxFramework, RpmsConnectionSpec bmxConnectionSpec, string strAccessCode)
374 {
375
376 // Open the verify code upate dialog
377 VerifyCodeUpdateDialog vcd = new VerifyCodeUpdateDialog(bmxFramework, bmxConnectionSpec, strAccessCode);
378 DialogResult dr = new DialogResult();
379 VerifyCodeUpdateResult vr = new VerifyCodeUpdateResult();
380
381 while (vr.DialogResult != DialogResult.Cancel && !(vr.WasVerifyCodeUpdatedSuccessfully ))
382 {
383 vr = vcd.ShowVerifyChangeDialog(null);
384 }
385
386 return vr;
387
388 }
389
390 /// <summary>
391 /// Attempt a headless non-interactive UI login. This would be useful for an ASP.NET or NT-service type application
392 /// </summary>
393 /// <param name="aSpec">The RpmsConnectionSpec to use during the login process</param>
394 /// <param name="anAccessCode">The clear text access code</param>
395 /// <param name="aVerifyCode">The clear text verify code</param>
396 /// <returns>True if the login was successful</returns>
397 public bool AttemptAccessVerifyLogin(RpmsConnectionSpec aSpec, String anAccessCode, String aVerifyCode)
398 {
399 BMXNetBroker broker = this.Framework.SocketBroker;
400
401 this.ConnectionSpec = aSpec;
402
403
404 if (this.AttemptingLogin != null)
405 {
406 AttemptingLoginEventArgs args = new AttemptingLoginEventArgs();
407 args.Process = this;
408 this.AttemptingLogin(this, args);
409 if (args.Handled)
410 {
411 return this.WasSuccessful;
412 }
413
414 if (args.Cancel)
415 {
416 this.Cancel = true;
417 return false;
418 }
419 }
420
421
422 try
423 {
424 this.LoginAttempts++;
425
426 if (broker.Open(aSpec.Server, aSpec.Port, aSpec.NameSpace, anAccessCode, aVerifyCode,aSpec.SendTimeout,aSpec.ReceiveTimeout))
427 {
428 this.Succeeded();
429 if (aSpec.UseWindowsAuthentication)
430 {
431 String result = broker.RegisterWindowsIdentityForWindowsAuthenication(WindowsIdentity.GetCurrent());
432 }
433 this.ResolveDivision(broker);
434
435 return this.WasSuccessful;
436 }
437 else
438 {
439 return this.Failed("Unable to authenicate. Use may need to login");
440 }
441 }
442
443 catch (BMXNetException problem)
444 {
445 this.FailureException = problem;
446
447 if ( problem.Message.Contains("VERIFY CODE MUST be changed before continued use."))
448 {
449 string message = "Your Verify Code has expired. Do you want to submit a new Verify Code now? Click Yes to continue, Click No to exit the sign in process.";
450 string caption = "Verify Code Expired";
451 MessageBoxButtons buttons = MessageBoxButtons.YesNo;
452
453 DialogResult result;
454
455 // Displays the MessageBox.
456
457 result = MessageBox.Show(message, caption, buttons);
458
459 if (result == System.Windows.Forms.DialogResult.Yes)
460 {
461 // Open the verify code upate dialog
462 VerifyCodeUpdateDialog vcd =new VerifyCodeUpdateDialog(this.Framework,this.ConnectionSpec,anAccessCode);
463 DialogResult dr = new DialogResult();
464
465 while ( (dr != DialogResult.Cancel) && (!vcd.IsUpdateSuccessful))
466 {
467 dr = vcd.ShowDialog();
468 }
469
470
471 if (dr == DialogResult.OK)
472 {
473 bool bLogin = AttemptAccessVerifyLogin(this.ConnectionSpec,vcd.AccessCode, vcd.NewVerifyCode);
474 return bLogin;
475 }
476 else
477 {
478 this.FailureMessage = "VERIFY CODE MUST be changed before continued use.";
479 return this.WasSuccessful;
480 }
481 }
482 else
483 {
484 return this.Failed(problem.Message);
485 }
486 }
487 else
488 {
489 return this.Failed(problem.Message);
490 }
491
492 }
493 catch (Exception anException)
494 {
495 this.FailureException = anException;
496 return this.Failed("Critical issue: " + anException.Message);
497 }
498 }
499
500 private void TriggerAttemptingLogin()
501 {
502 throw new NotImplementedException();
503 }
504
505 private void ResolveDivision(BMXNetBroker aBroker)
506 {
507 List<SelectableDivision> divisions = this.Framework.Divisions(aBroker.Duz);
508 if (divisions.Count == 1)
509 {
510 this.Framework.SetDivision(divisions[0]);
511 }
512 else
513 {
514 if (this.AutoSetDivisionToLastLookup)
515 {
516 WinDivision last = null;
517 foreach (WinDivision each in divisions)
518 {
519 if (each.MostRecentLookup)
520 {
521 last = each;
522 break;
523 }
524 }
525 if (last == null)
526 {
527 this.MustResolveDivision = true;
528 }
529 else
530 {
531 this.Framework.SetDivision(last);
532 }
533 }
534 else
535 {
536 this.MustResolveDivision = true;
537 }
538 }
539 }
540
541 /// <summary>
542 /// Will trigger the LoginAttempted event
543 /// </summary>
544 /// <returns>Answer true if the LoginProcess has been Canceled or handled</returns>
545 public bool HandleLoginAttemptedFailed()
546 {
547 if (this.LoginAttempted != null)
548 {
549 LoginAttemptedEventArgs args = new LoginAttemptedEventArgs();
550 args.Process = this;
551 args.Handled = false;
552 args.WasSuccessful = this.WasLoginAttempted;
553 this.LoginAttempted(this, args);
554 if (args.Cancel)
555 {
556 this.Cancel = true;
557 }
558 return args.Handled || this.Cancel;
559
560 }
561 else
562 {
563 return false;
564 }
565 }
566 }
567}
Note: See TracBrowser for help on using the repository browser.