/
ServerAuthenticationContext.java
2578 lines (2307 loc) · 136 KB
/
ServerAuthenticationContext.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wildfly.security.auth.server;
import static org.wildfly.common.Assert.checkNotNullParam;
import static org.wildfly.security.auth.server._private.ElytronMessages.log;
import static org.wildfly.security.authz.RoleDecoder.KEY_SOURCE_ADDRESS;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Predicate;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.AuthorizeCallback;
import javax.security.sasl.RealmCallback;
import org.wildfly.common.Assert;
import org.wildfly.security.auth.SupportLevel;
import org.wildfly.security.auth.callback.AnonymousAuthorizationCallback;
import org.wildfly.security.auth.callback.AuthenticationCompleteCallback;
import org.wildfly.security.auth.callback.AuthenticationConfigurationCallback;
import org.wildfly.security.auth.callback.AvailableRealmsCallback;
import org.wildfly.security.auth.callback.CachedIdentityAuthorizeCallback;
import org.wildfly.security.auth.callback.CallbackUtil;
import org.wildfly.security.auth.callback.ChannelBindingCallback;
import org.wildfly.security.auth.callback.CredentialCallback;
import org.wildfly.security.auth.callback.CredentialUpdateCallback;
import org.wildfly.security.auth.callback.EvidenceDecodePrincipalCallback;
import org.wildfly.security.auth.callback.EvidenceVerifyCallback;
import org.wildfly.security.auth.callback.ExclusiveNameCallback;
import org.wildfly.security.auth.callback.FastUnsupportedCallbackException;
import org.wildfly.security.auth.callback.PrincipalAuthorizeCallback;
import org.wildfly.security.auth.callback.MechanismInformationCallback;
import org.wildfly.security.auth.callback.IdentityCredentialCallback;
import org.wildfly.security.auth.callback.PeerPrincipalCallback;
import org.wildfly.security.auth.callback.RequestInformationCallback;
import org.wildfly.security.auth.callback.SSLCallback;
import org.wildfly.security.auth.callback.SecurityIdentityCallback;
import org.wildfly.security.auth.callback.ServerCredentialCallback;
import org.wildfly.security.auth.callback.SocketAddressCallback;
import org.wildfly.security.auth.permission.LoginPermission;
import org.wildfly.security.auth.permission.RunAsPrincipalPermission;
import org.wildfly.security.auth.principal.AnonymousPrincipal;
import org.wildfly.security.auth.principal.NamePrincipal;
import org.wildfly.security.auth.server.event.RealmFailedAuthenticationEvent;
import org.wildfly.security.auth.server.event.RealmIdentityFailedAuthorizationEvent;
import org.wildfly.security.auth.server.event.RealmIdentitySuccessfulAuthorizationEvent;
import org.wildfly.security.auth.server.event.RealmSuccessfulAuthenticationEvent;
import org.wildfly.security.auth.server.event.SecurityAuthenticationFailedEvent;
import org.wildfly.security.auth.server.event.SecurityAuthenticationSuccessfulEvent;
import org.wildfly.security.auth.server.event.SecurityRealmUnavailableEvent;
import org.wildfly.security.authz.AggregateAttributes;
import org.wildfly.security.authz.Attributes;
import org.wildfly.security.authz.AuthorizationIdentity;
import org.wildfly.security.authz.MapAttributes;
import org.wildfly.security.credential.Credential;
import org.wildfly.security.credential.PasswordCredential;
import org.wildfly.security.credential.source.CredentialSource;
import org.wildfly.security.evidence.AlgorithmEvidence;
import org.wildfly.security.evidence.Evidence;
import org.wildfly.security.evidence.X509PeerCertificateChainEvidence;
import org.wildfly.security.password.Password;
import org.wildfly.security.password.PasswordFactory;
import org.wildfly.security.password.TwoWayPassword;
import org.wildfly.security.password.interfaces.DigestPassword;
import org.wildfly.security.password.spec.ClearPasswordSpec;
import org.wildfly.security.ssl.SSLConnection;
import org.wildfly.security.x500.X500;
import org.wildfly.security.auth.server._private.ElytronMessages;
/**
* Server-side authentication context. Instances of this class are used to perform all authentication and re-authorization
* operations that involve the usage of an identity in a {@linkplain SecurityDomain security domain}.
* <p>
* There are various effective states, described as follows:
* <ul>
* <li>The <em>inactive</em> state.</li>
* <li>
* The <em>unassigned</em> states:
* <ul>
* <li><em>Initial</em></li>
* <li><em>Realm-assigned</em></li>
* </ul>
* </li>
* <li>The <em>assigned</em> state</li>
* <li>
* The <em>authorized</em> states:
* <ul>
* <li><em>Anonymous-authorized</em></li>
* <li><em>Authorized</em></li>
* <li><em>Authorized-authenticated</em></li>
* </ul>
* </li>
* <li>
* The <em>terminal</em> states:
* <ul>
* <li><em>Complete</em></li>
* <li><em>Failed</em></li>
* </ul>
* </li>
* </ul>
*
* <p>
* When an instance of this class is first constructed, it is in the <em>inactive</em> state. In this state, the context retains
* a <em>captured {@linkplain SecurityIdentity identity}</em> and contains a reference to a
* <em>{@linkplain MechanismConfigurationSelector}</em>. The <em>captured identity</em> may be used for various
* context-sensitive authorization decisions. Additional mechanism information can be supplied to this state so that when
* authentication begins an appropriate <em>{@linkplain MechanismConfiguration}</em> can be selected.
* <p>
* Once authentication commences the state will automatically transition to the <em>initial</em> state. In this state, the
* context retains an <em>captured {@linkplain SecurityIdentity identity}</em> and a <em>{@linkplain MechanismConfiguration mechanism configuration}</em>
* which was resolved from the information supplied to the <em>inactive</em> state. The <em>captured identity</em> may be
* used for various context-sensitive authorization decisions. The <em>mechanism configuration</em> is used to associate
* an authentication mechanism-specific configuration, including rewriters, {@linkplain MechanismRealmConfiguration mechanism realms},
* server credential factories, and more.
* <p>
* When an authentication mechanism is "realm-aware" (that is, it has a notion of realms that is specific to that particular
* authentication mechanism, e.g. <a href="https://tools.ietf.org/html/rfc2831">the DIGEST-MD5 SASL mechanism</a>), it
* is necessary for the mechanism to relay the realm selection. This is done by way of the {@link #setMechanismRealmName(String) setMechanismRealmName()}
* method. Calling this method in the <em>initial</em> state causes a transition to the <em>realm-assigned</em> state,
* in which the method may be reinvoked idempotently as long as it is called with the same name (calling the method with
* a different name will result in an exception).
* <p>
* The <em>realm-assigned</em> state is nearly identical to the <em>initial</em> state, except that from this state, the
* mechanism realm-specific configuration is applied to all subsequent operation.
* <p>
* From these <em>unassigned</em> states, several possible actions may be taken, depending on the necessary progression
* of the authentication:
* <ul>
* <li>
* A <em>name</em> may be assigned by way of the {@link #setAuthenticationName(String)} method. The name is
* {@linkplain NameRewriter rewritten} and {@linkplain RealmMapper mapped to a realm} according to the
* domain settings, the <em>mechanism configuration</em>, and/or the <em>mechanism realm configuration</em>. The
* <em>{@linkplain SecurityRealm realm}</em> that is the resultant target of the mapping is queried for a
* <em>{@linkplain RealmIdentity realm identity}</em>. The <em>realm identity</em> may or may not be
* existent; this status will affect the outcome of certain operations in subsequent states (as described below).
* After the <em>realm identity</em> is selected, any final rewrite operations which are configured are applied,
* and the resultant name is transformed into a {@link NamePrincipal}, and associated as the
* <em>{@linkplain #getAuthenticationPrincipal() authentication principal}</em> which may subsequently be queried.
* </li>
* <li>
* A <em>principal</em> may be assigned using the {@link #setAuthenticationPrincipal(Principal)} method. The
* principal is {@linkplain PrincipalDecoder decoded} according to the configuration of the security domain (see
* the method documentation for input requirements and failure conditions). Once a name is decoded from the
* principal, it is assigned as described above.
* </li>
* <li>
* A unit of <em>{@linkplain Evidence evidence}</em> may be verified. This is mostly described below in the
* context of the <em>assigned</em> state, but with the important distinction the evidence is first examined
* to locate the corresponding evidence, in the following steps:
* <ul>
* <li>
* Firstly, the evidence is examined to determine whether it {@linkplain Evidence#getPrincipal() contains a principal}.
* If so, the principal name is first established using the procedure described above, and then the normal
* evidence verification procedure described below commences.
* </li>
* <li>
* Secondly, the evidence is socialized to each <em>realm</em> in turn, to see if a realm can recognize
* and {@linkplain SecurityRealm#getRealmIdentity(Principal) locate} an identity based on
* the evidence. If so, the <em>realm identity</em> is {@linkplain RealmIdentity#getRealmIdentityPrincipal() queried}
* for an authentication principal, which is then decoded and established as described above. Once this
* is done successfully, the evidence verification procedure described below commences.
* </li>
* <li>Finally, if none of these steps succeeds, the verification fails and no state transition occurs.</li>
* </ul>
* </li>
* <li>
* An <em>identity</em> may be {@linkplain #importIdentity(SecurityIdentity) imported}. In this process,
* a {@link SecurityIdentity} instance is examined to determine whether it can be used to complete an implicit
* authentication operation which would yield an <em>authorized identity</em>. The {@code SecurityIdentity} may
* be from the same <em>domain</em> or from a different one.
* <p>
* If the <em>identity</em> being imported is from the same security domain as this context, then the <em>identity</em>
* is implicitly <em>authorized</em> for usage, entering the <em>authorized</em> state described below.
* <p>
* If the <em>identity</em> being imported is not from the same security domain, then the principal is extracted
* from the identity and used to assign a <em>realm identity</em> in the same manner as {@link #setAuthenticationPrincipal(Principal)}.
* The <em>domain</em> is then {@linkplain SecurityDomain.Builder#setTrustedSecurityDomainPredicate(Predicate) queried}
* to determine whether the target identity's source <em>domain</em> is <em>trusted</em>. If so, a normal
* <em>authorization</em> is carried out as described below for the <em>assigned</em> state, resulting in an
* <em>authorized-authenticated</em> state. If not, then the <em>realm</em> of the <em>realm identity</em> is
* compared against the <em>realm</em> of the <em>identity</em> being imported. If they are the same, the
* identity is imported and a normal <em>authorization</em> is carried out as described below.
* </li>
* <li>
* An <em>anonymous authorization</em> may be carried out by way of the {@link #authorizeAnonymous()} method.
* If the <em>{@linkplain SecurityDomain#getAnonymousSecurityIdentity() anonymous identity}</em> has the
* {@link LoginPermission} granted to it, the context will transition into the <em>anonymous-authorized</em>
* state; otherwise no state transition occurs.
* </li>
* <li>
* An <em>external authorization</em> may be carried out using the {@link #authorize()} method. The
* <em>captured identity</em> (which may be <em>anonymous</em>) is queried for the presence of the
* {@link LoginPermission}; if present, the context will transition into the <em>authorized</em> or
* <em>anonymous-authorized</em> state (depending on whether the <em>captured identity</em> is <em>anonymous</em>);
* otherwise no state transition occurs.
* </li>
* <li>
* An <em>external run-as authorization</em> may be carried out using the {@link #authorize(String)} method.
* First, the given name is <em>rewritten</em> in the same manner as the {@link #setAuthenticationName(String)}
* method. Then, the <em>captured identity</em> (which may be <em>anonymous</em>) is queried for the presence of a
* {@link RunAsPrincipalPermission} for the target name. If present, the <em>authentication name</em> is assigned
* as described above, and the resultant <em>realm identity</em> is queried for {@link LoginPermission}. If present,
* the context will transition to the <em>authorized-authenticated</em> state. If any step fails, no state transition
* occurs.
* </li>
* <li>
* The authentication may be <em>failed</em> by way of the {@link #fail()} method. This method will dispose
* of all authentication resources and transition to the <em>failed</em> state.
* </li>
* </ul>
* <p>
* In the <em>name-assigned</em> (or, for brevity, <em>assigned</em>) state, the following actions may be performed:
* <ul>
* <li>
* A name or principal may be assigned as above, however the resultant <em>decoded</em> and <em>rewritten</em> name
* and <em>realm identity</em> must be identical to the previously selected name and identity.
* </li>
* <li>
* <em>Evidence</em> may be verified. The <em>realm identity</em> is queried directly and no state transitions
* will occur. Evidence verification will fail if the evidence has an <em>evidence principal</em> which does
* not result in the same <em>realm identity</em> as the current one after <em>decoding</em> and <em>rewriting</em>.
* </li>
* <li>
* An <em>authorization</em> may be performed via the {@link #authorize()} method. If the selected <em>realm identity</em>
* possesses the {@link LoginPermission}, then the context transitions to the <em>authorized-authenticated</em> state,
* otherwise no state transition occurs.
* </li>
* <li>
* A <em>run-as authorization</em> may be performed via the {@link #authorize(String)} method.
* First, the given name is <em>rewritten</em> in the same manner as the {@link #setAuthenticationName(String)} method.
* The current identity is then <em>authorized</em> as described above, and then the <em>authorized identity</em>
* is tested for a {@link RunAsPrincipalPermission} for the <em>rewritten</em> target name. If authorized,
* the context transitions to the <em>authorized</em> state for the <em>realm identity</em> corresponding to the
* <em>rewritten</em> name; otherwise no state transition occurs.
* </li>
* <li>
* The authentication may be <em>failed</em> by way of the {@link #fail()} method. This method will dispose
* of all authentication resources and transition to the <em>failed</em> state.
* </li>
* </ul>
* <p>
* There are three states related to authorization: the <em>anonymous-authorized</em> state, the <em>authorized</em> state,
* and the <em>authorized-authenticated</em> state. In all three states, the following actions may be taken:
* <ul>
* <li>
* As above, a name or principal may be assigned so long as it matches the existing identity. In particular,
* for the <em>anonymous-authorized</em> state, all names are rejected, and only the {@linkplain AnonymousPrincipal anonymous principal}
* is accepted.
* </li>
* <li>
* An <em>authorization</em> may be performed via the {@link #authorize()} method. Since the identity is
* always authorized, this is generally a no-op.
* </li>
* <li>
* A <em>run-as authorization</em> may be performed via the {@link #authorize(String)} method. The given
* name is <em>rewritten</em> as previously described, and then the <em>authorized identity</em>
* is tested for a {@link RunAsPrincipalPermission} for the <em>rewritten</em> target name. If authorized,
* the context transitions to the <em>authorized</em> state for the <em>realm identity</em> corresponding to the
* <em>rewritten</em> name; otherwise no state transition occurs.
* </li>
* <li>
* The authentication may be <em>completed</em> by way of the {@link #succeed()} method. This method will
* dispose of all authentication resources and transition to the <em>complete</em> state.
* </li>
* <li>
* The authentication may be <em>failed</em> by way of the {@link #fail()} method. This method will dispose
* of all authentication resources and transition to the <em>failed</em> state.
* </li>
* </ul>
* The <em>authorized-authenticated</em> state has the additional capability of verifying credentials as described above for
* the <em>assigned</em> state.
* <p>
* The <em>complete</em> state has only one capability: the retrieval of the final <em>authorized identity</em> by way
* of the {@link #getAuthorizedIdentity()} method.
* <p>
* The <em>failed</em> state has no capabilities and retains no reference to any identities or objects used during
* authentication.
*
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
*/
public final class ServerAuthenticationContext implements AutoCloseable {
private final AtomicReference<State> stateRef;
ServerAuthenticationContext(final SecurityDomain domain, final MechanismConfigurationSelector mechanismConfigurationSelector) {
this(domain.getCurrentSecurityIdentity(), mechanismConfigurationSelector);
}
ServerAuthenticationContext(final SecurityIdentity capturedIdentity, final MechanismConfigurationSelector mechanismConfigurationSelector) {
stateRef = new AtomicReference<>(new InactiveState(capturedIdentity, mechanismConfigurationSelector, IdentityCredentials.NONE, IdentityCredentials.NONE, Attributes.EMPTY));
}
/**
* Set information about the current mechanism and request for this authentication attempt. If the mechanism
* information cannot be resolved to a mechanism configuration, an exception is thrown.
*
* @param mechanismInformation the mechanism information about the current authentication attempt.
* @throws IllegalStateException if the mechanism information about the current authentication attempt cannot be
* resolved to a mechanism configuration
*/
public void setMechanismInformation(final MechanismInformation mechanismInformation) throws IllegalStateException {
stateRef.get().setMechanismInformation(mechanismInformation);
}
/**
* Get the authorized identity result of this authentication.
*
* @return the authorized identity
* @throws IllegalStateException if the authentication is incomplete
*/
public SecurityIdentity getAuthorizedIdentity() throws IllegalStateException {
return stateRef.get().getAuthorizedIdentity();
}
/**
* Set the authentication to anonymous, completing the authentication process.
*
* @throws IllegalStateException if the authentication is already complete
*/
public boolean authorizeAnonymous() throws IllegalStateException {
return authorizeAnonymous(true);
}
/**
* Set the authentication to anonymous, completing the authentication process.
*
* @param requireLoginPermission {@code true} if {@link LoginPermission} is required and {@code false} otherwise
* @throws IllegalStateException if the authentication is already complete
*/
public boolean authorizeAnonymous(boolean requireLoginPermission) throws IllegalStateException {
return stateRef.get().authorizeAnonymous(requireLoginPermission);
}
/**
* Set the authentication name for this authentication. If the name is already set, then the new name must be
* equal to the old name, or else an exception is thrown.
*
* @param name the authentication name
* @throws IllegalArgumentException if the name is syntactically invalid
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication name was already set and there is a mismatch
*/
public void setAuthenticationName(String name) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
setAuthenticationName(name, false);
}
/**
* Set the authentication name for this authentication. If the name is already set, then the new name must be
* equal to the old name, or else an exception is thrown.
*
* @param name the authentication name
* @param exclusive {@code true} if exclusive access to the backing identity is required
* @throws IllegalArgumentException if the name is syntactically invalid
* @throws RealmUnavailableException if the realm is not available or if exclusive access to the backing identity
* is required but could not be granted
* @throws IllegalStateException if the authentication name was already set and there is a mismatch
*/
public void setAuthenticationName(String name, boolean exclusive) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
Assert.checkNotNullParam("name", name);
setAuthenticationPrincipal(new NamePrincipal(name), exclusive);
}
/**
* Set the authentication principal for this authentication. Calling this method initiates authentication.
*
* @param principal the authentication principal
* @throws IllegalArgumentException if the principal cannot be mapped to a name, or the mapped name is syntactically invalid
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication name was already set
*/
public void setAuthenticationPrincipal(Principal principal) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
setAuthenticationPrincipal(principal, false);
}
/**
* Set the authentication principal for this authentication. Calling this method initiates authentication.
*
* @param principal the authentication principal
* @param exclusive {@code true} if exclusive access to the backing identity is required
* @throws IllegalArgumentException if the principal cannot be mapped to a name, or the mapped name is syntactically invalid
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication name was already set
*/
public void setAuthenticationPrincipal(Principal principal, boolean exclusive) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
Assert.checkNotNullParam("principal", principal);
stateRef.get().setPrincipal(principal, exclusive);
}
/**
* Determine if the given name refers to the same identity as the currently set authentication name.
*
* @param name the authentication name
* @return {@code true} if the name matches the current identity, {@code false} otherwise
* @throws IllegalArgumentException if the name is syntactically invalid
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication name was already set
*/
public boolean isSameName(String name) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
Assert.checkNotNullParam("name", name);
return isSamePrincipal(new NamePrincipal(name));
}
/**
* Determine if the given principal refers to the same identity as the currently set authentication name.
*
* @param principal the authentication name
* @return {@code true} if the name matches the current identity, {@code false} otherwise
* @throws IllegalArgumentException if the name is syntactically invalid
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication name was already set
*/
public boolean isSamePrincipal(Principal principal) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
Assert.checkNotNullParam("principal", principal);
return stateRef.get().isSamePrincipal(principal);
}
/**
* Determine if the current authentication identity actually exists in the realm.
*
* @return {@code true} if the identity exists, {@code false} otherwise
* @throws RealmUnavailableException if the realm failed to access the identity
* @throws IllegalStateException if there is no authentication name set
*/
public boolean exists() throws RealmUnavailableException, IllegalStateException {
return stateRef.get().getRealmIdentity().exists();
}
/**
* Mark this authentication as "failed". The context cannot be used after this method is called.
*
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public void fail() throws IllegalStateException {
stateRef.get().fail(true);
}
/**
* Attempt to authorize an authentication attempt. If the authorization is successful, {@code true} is returned and
* the context is placed in the "authorized" state with the new authorization identity. If the authorization fails,
* {@code false} is returned and the state of the context is unchanged.
*
* @return {@code true} if the authorization succeeded, {@code false} otherwise
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication name was not set or authentication was already complete
*/
public boolean authorize() throws RealmUnavailableException, IllegalStateException {
return authorize(true);
}
boolean authorize(boolean requireLoginPermission) throws RealmUnavailableException, IllegalStateException {
return stateRef.get().authorize(requireLoginPermission);
}
/**
* Attempt to authorize a change to a new user (possibly including an authentication attempt). If the authorization
* is successful, {@code true} is returned and the context is placed in the "authorized" state with the new authorization
* identity. If the authorization fails, {@code false} is returned and the state of the context is unchanged.
*
* @param name the authorization name (must not be {@code null})
* @return {@code true} if the authorization succeeded, {@code false} otherwise
* @throws IllegalArgumentException if the name is syntactically invalid
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication name was not set or authentication was already complete
*/
public boolean authorize(String name) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
checkNotNullParam("name", name);
return authorize(new NamePrincipal(name), true);
}
/**
* Attempt to authorize a change to a new user (possibly including an authentication attempt). If the authorization
* is successful, {@code true} is returned and the context is placed in the "authorized" state with the new authorization
* identity. If the authorization fails, {@code false} is returned and the state of the context is unchanged.
*
* @param principal the authorization principal (must not be {@code null})
* @return {@code true} if the authorization succeeded, {@code false} otherwise
* @throws IllegalArgumentException if the principal is syntactically invalid
* @throws RealmUnavailableException if the realm is not available
* @throws IllegalStateException if the authentication principal was not set or authentication was already complete
*/
public boolean authorize(Principal principal) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
return authorize(principal, true);
}
boolean authorize(Principal principal, boolean authorizeRunAs) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException {
checkNotNullParam("principal", principal);
return stateRef.get().authorize(principal, authorizeRunAs);
}
/**
* Mark this authentication as "successful". The context cannot be used after this method is called, however
* the authorized identity may thereafter be accessed via the {@link #getAuthorizedIdentity()} method. If no
* authentication actually happened, then authentication will complete anonymously.
*
* @throws IllegalStateException if authentication is already completed
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
*/
public void succeed() throws IllegalStateException, RealmUnavailableException {
stateRef.get().succeed();
}
/**
* Determine if authentication was already completed on this context.
*
* @return {@code true} if authentication was completed; {@code false} otherwise
*/
public boolean isDone() {
return stateRef.get().isDone();
}
/**
* Get the principal associated with the current authentication name. Only valid during authentication process.
*
* @return the principal
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public Principal getAuthenticationPrincipal() {
return stateRef.get().getAuthenticationPrincipal();
}
/**
* Determine whether a given credential is definitely obtainable, possibly obtainable, or definitely not obtainable.
*
* If an authentication identity is established this will be for that identity, otherwise this will be the general
* level of support advertised by the security domain.
*
* @param credentialType the credential type class (must not be {@code null})
* @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does
* not support algorithm names
* @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type
* does not support algorithm parameters
* @return the level of support for this credential type
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
Assert.checkNotNullParam("credentialType", credentialType);
return stateRef.get().getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec);
}
/**
* Determine whether a given credential is definitely obtainable, possibly obtainable, or definitely not obtainable.
*
* If an authentication identity is established this will be for that identity, otherwise this will be the general
* level of support advertised by the security domain.
*
* @param credentialType the credential type class (must not be {@code null})
* @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does
* not support algorithm names
* @return the level of support for this credential type
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName) throws RealmUnavailableException {
return getCredentialAcquireSupport(credentialType, algorithmName, null);
}
/**
* Determine whether a given credential is definitely obtainable, possibly obtainable, or definitely not obtainable.
*
* If an authentication identity is established this will be for that identity, otherwise this will be the general
* level of support advertised by the security domain.
*
* @param credentialType the credential type class (must not be {@code null})
* @return the level of support for this credential type
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType) throws RealmUnavailableException {
Assert.checkNotNullParam("credentialType", credentialType);
return getCredentialAcquireSupport(credentialType, null);
}
/**
* Determine whether a given piece of evidence is definitely verifiable, possibly verifiable, or definitely not verifiable.
*
* If an authentication identity is established this will be for that identity, otherwise this will be the general
* level of support advertised by the security domain.
*
* @param evidenceType the evidence type class (must not be {@code null})
* @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the evidence type does
* not support algorithm names
* @return the level of support for this credential type
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public SupportLevel getEvidenceVerifySupport(Class<? extends Evidence> evidenceType, String algorithmName) throws RealmUnavailableException {
Assert.checkNotNullParam("evidenceType", evidenceType);
return stateRef.get().getEvidenceVerifySupport(evidenceType, algorithmName);
}
/**
* Determine whether a given piece of evidence is definitely verifiable, possibly verifiable, or definitely not verifiable.
*
* If an authentication identity is established this will be for that identity, otherwise this will be the general
* level of support advertised by the security domain.
*
* @param evidenceType the evidence type class (must not be {@code null})
* @return the level of support for this credential type
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public SupportLevel getEvidenceVerifySupport(Class<? extends Evidence> evidenceType) throws RealmUnavailableException {
Assert.checkNotNullParam("evidenceType", evidenceType);
return getEvidenceVerifySupport(evidenceType, null);
}
/**
* Acquire a credential of the given type. The credential type is defined by its {@code Class} and an optional {@code algorithmName}. If the
* algorithm name is not given, then the query is performed for any algorithm of the given type.
*
* @param credentialType the credential type class (must not be {@code null})
* @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does
* not support algorithm names
* @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type
* does not support algorithm parameters
* @param <C> the credential type
*
* @return the credential, or {@code null} if the principal has no credential of that type
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public <C extends Credential> C getCredential(Class<C> credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
Assert.checkNotNullParam("credentialType", credentialType);
return stateRef.get().getCredential(credentialType, algorithmName, parameterSpec);
}
/**
* Acquire a credential of the given type. The credential type is defined by its {@code Class} and an optional {@code algorithmName}. If the
* algorithm name is not given, then the query is performed for any algorithm of the given type.
*
* @param credentialType the credential type class (must not be {@code null})
* @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does
* not support algorithm names
* @param <C> the credential type
*
* @return the credential, or {@code null} if the principal has no credential of that type
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public <C extends Credential> C getCredential(Class<C> credentialType, String algorithmName) throws RealmUnavailableException {
Assert.checkNotNullParam("credentialType", credentialType);
return stateRef.get().getCredential(credentialType, algorithmName, null);
}
/**
* Acquire a credential of the given type. The credential type is defined by its {@code Class} and an optional {@code algorithmName}. If the
* algorithm name is not given, then the query is performed for any algorithm of the given type.
*
* @param credentialType the credential type class (must not be {@code null})
* @param <C> the credential type
*
* @return the credential, or {@code null} if the principal has no credential of that type
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public <C extends Credential> C getCredential(Class<C> credentialType) throws RealmUnavailableException {
Assert.checkNotNullParam("credentialType", credentialType);
return stateRef.get().getCredential(credentialType, null, null);
}
/**
* Apply the given function to the acquired credential, if it is set and of the given type.
*
* @param credentialType the credential type class (must not be {@code null})
* @param function the function to apply (must not be {@code null})
* @param <C> the credential type
* @param <R> the return type
* @return the result of the function, or {@code null} if the criteria are not met
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public <C extends Credential, R> R applyToCredential(Class<C> credentialType, Function<C, R> function) throws RealmUnavailableException {
final Credential credential = getCredential(credentialType);
return credential == null ? null : credential.castAndApply(credentialType, function);
}
/**
* Apply the given function to the acquired credential, if it is set and of the given type and algorithm.
*
* @param credentialType the credential type class (must not be {@code null})
* @param algorithmName the algorithm name
* @param function the function to apply (must not be {@code null})
* @param <C> the credential type
* @param <R> the return type
* @return the result of the function, or {@code null} if the criteria are not met
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public <C extends Credential, R> R applyToCredential(Class<C> credentialType, String algorithmName, Function<C, R> function) throws RealmUnavailableException {
final Credential credential = getCredential(credentialType, algorithmName);
return credential == null ? null : credential.castAndApply(credentialType, algorithmName, function);
}
/**
* Apply the given function to the acquired credential, if it is set and of the given type and algorithm.
*
* @param credentialType the credential type class (must not be {@code null})
* @param algorithmName the algorithm name
* @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type
* does not support algorithm parameters
* @param function the function to apply (must not be {@code null})
* @param <C> the credential type
* @param <R> the return type
* @return the result of the function, or {@code null} if the criteria are not met
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public <C extends Credential, R> R applyToCredential(Class<C> credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec, Function<C, R> function) throws RealmUnavailableException {
final Credential credential = getCredential(credentialType, algorithmName, parameterSpec);
return credential == null ? null : credential.castAndApply(credentialType, algorithmName, parameterSpec, function);
}
/**
* Verify the given evidence.
*
* @param evidence the evidence to verify
*
* @return {@code true} if verification was successful, {@code false} otherwise
*
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
* @throws IllegalStateException if no authentication has been initiated or authentication is already completed
*/
public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException {
Assert.checkNotNullParam("evidence", evidence);
return stateRef.get().verifyEvidence(evidence);
}
/**
* Set the decoded evidence principal.
*
* @param evidence the evidence to decode and associate with a principal
* @since 1.10.0
*/
public void setDecodedEvidencePrincipal(Evidence evidence) throws RealmUnavailableException {
Assert.checkNotNullParam("evidence", evidence);
evidence.setDecodedPrincipal(stateRef.get().getSecurityDomain().getEvidenceDecoder().apply(evidence));
}
/**
* Add a public credential to the identity being authenticated.
*
* @param credential the credential to add (must not be {@code null})
*/
public void addPublicCredential(Credential credential) {
Assert.checkNotNullParam("credential", credential);
stateRef.get().addPublicCredential(credential);
}
/**
* Add a private credential to the identity being authenticated. This credential may be forwarded to outbound
* authentication mechanisms.
*
* @param credential the credential to add (must not be {@code null})
*/
public void addPrivateCredential(Credential credential) {
Assert.checkNotNullParam("credential", credential);
stateRef.get().addPrivateCredential(credential);
}
/**
* Add runtime attributes to the identity being authenticated.
*
* @param runtimeAttributes the runtime attributes to add (must not be {@code null})
*/
public void addRuntimeAttributes(Attributes runtimeAttributes) {
Assert.checkNotNullParam("runtimeAttributes", runtimeAttributes);
stateRef.get().addRuntimeAttributes(runtimeAttributes);
}
/**
* Attempt to import the given security identity as a trusted identity. If this method returns {@code true},
* the context will be in an authorized state, and the new identity can be retrieved.
*
* @param identity the identity to import (must not be {@code null})
* @return {@code true} if the identity is authorized, {@code false} otherwise
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
*/
public boolean importIdentity(SecurityIdentity identity) throws RealmUnavailableException {
Assert.checkNotNullParam("identity", identity);
return stateRef.get().importIdentity(identity);
}
/**
* Set the mechanism realm name to be equal to the given name. If no mechanism realms are configured, the realm
* name is ignored.
*
* @param realmName the selected realm name
* @throws IllegalStateException if a realm name was already selected or it is too late to choose a realm
* @throws IllegalArgumentException if the selected realm name was not offered
*/
public void setMechanismRealmName(String realmName) throws IllegalStateException, IllegalArgumentException {
Assert.checkNotNullParam("realmName", realmName);
stateRef.get().setMechanismRealmName(realmName);
}
/**
* Update the credential for the current authentication identity.
*
* @param credential the new credential (must not be {@code null})
* @throws RealmUnavailableException if the realm is not able to handle requests for any reason
*/
public void updateCredential(Credential credential) throws RealmUnavailableException {
Assert.checkNotNullParam("credential", credential);
stateRef.get().updateCredential(credential);
}
/**
* Close the server authentication context, failing any in-progress authentication and releasing any
* associated resources.
*/
public void close() {
stateRef.get().fail(false);
}
AtomicReference<State> getStateRef() {
return stateRef;
}
CallbackHandler createCallbackHandler() {
return new CallbackHandler() {
private SSLConnection sslConnection;
private X509Certificate[] peerCerts;
private boolean saslSkipCertificateVerification;
@Override
public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException {
handleOne(callbacks, 0);
}
private void handleOne(final Callback[] callbacks, final int idx) throws IOException, UnsupportedCallbackException {
if (idx == callbacks.length) {
return;
}
final AtomicReference<State> stateRef = getStateRef();
final Callback callback = callbacks[idx];
if (callback instanceof AnonymousAuthorizationCallback) {
boolean authorized = authorizeAnonymous();
log.tracef("Handling AnonymousAuthorizationCallback: authorized = %b", authorized);
((AnonymousAuthorizationCallback) callback).setAuthorized(authorized);
handleOne(callbacks, idx + 1);
} else if (callback instanceof AuthorizeCallback) {
final AuthorizeCallback authorizeCallback = (AuthorizeCallback) callback;
String authenticationID = authorizeCallback.getAuthenticationID();
if (authenticationID != null) {
// always re-set the authentication name to ensure it hasn't changed.
setAuthenticationName(authenticationID);
} else {
// This is a special case to support scenarios where the identity was already established by some
// external method (e.g.: EXTERNAL SASL and TLS) where only authorization is necessary. We delay authentication
// until we receive an authorization request.
// In the future, we may want to support external methods other than TLS peer authentication
if (stateRef.get().canVerifyEvidence() && peerCerts != null) {
log.tracef("Authentication ID is null but SSL peer certificates are available. Trying to authenticate peer");
// if SASL mechanism is used with skip-certificate-verification property then do not verifyEvidence against the security realm
if (saslSkipCertificateVerification) {
// Since evidence verification is being skipped here, ensure evidence decoding still takes place
X509PeerCertificateChainEvidence evidence = new X509PeerCertificateChainEvidence(peerCerts);
setDecodedEvidencePrincipal(evidence);
stateRef.get().setPrincipal(evidence.getDecodedPrincipal(), false);
}
else {
verifyEvidence(new X509PeerCertificateChainEvidence(peerCerts));
}
}
}
String authorizationID = authorizeCallback.getAuthorizationID();
boolean authorized = authorizationID != null ? authorize(authorizationID) : authorize();
log.tracef("Handling AuthorizeCallback: authenticationID = %s authorizationID = %s authorized = %b", authenticationID, authorizationID, authorized);
authorizeCallback.setAuthorized(authorized);
handleOne(callbacks, idx + 1);
} else if (callback instanceof ExclusiveNameCallback) {
final ExclusiveNameCallback exclusiveNameCallback = ((ExclusiveNameCallback) callback);
// login name
final String name = exclusiveNameCallback.getDefaultName();
try {
boolean exclusive = exclusiveNameCallback.needsExclusiveAccess();
log.tracef("Handling ExclusiveNameCallback: authenticationName = %s needsExclusiveAccess = %b", name, exclusive);
if (exclusive) {
setAuthenticationName(name, true);
exclusiveNameCallback.setExclusiveAccess(true);
} else {
setAuthenticationName(name);
}
} catch (Exception e) {
throw new IOException(e);
}
handleOne(callbacks, idx + 1);
} else if (callback instanceof NameCallback) {
// login name
final String name = ((NameCallback) callback).getDefaultName();
try {
log.tracef("Handling NameCallback: authenticationName = %s", name);
setAuthenticationName(name);
} catch (Exception e) {
throw new IOException(e);
}
handleOne(callbacks, idx + 1);
} else if (callback instanceof PeerPrincipalCallback) {
// login name
final Principal principal = ((PeerPrincipalCallback) callback).getPrincipal();
try {
log.tracef("Handling PeerPrincipalCallback: principal = %s", principal);
setAuthenticationPrincipal(principal);
} catch (Exception e) {
throw new IOException(e);
}
handleOne(callbacks, idx + 1);
} else if (callback instanceof PasswordCallback) {
final PasswordCallback passwordCallback = (PasswordCallback) callback;
if (getCredentialAcquireSupport(PasswordCredential.class).mayBeSupported()) {
final TwoWayPassword password = applyToCredential(PasswordCredential.class, c -> c.getPassword(TwoWayPassword.class));
if (password != null) {
final ClearPasswordSpec clearPasswordSpec;
try {
final PasswordFactory passwordFactory = PasswordFactory.getInstance(password.getAlgorithm());
clearPasswordSpec = passwordFactory.getKeySpec(password, ClearPasswordSpec.class);
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
log.trace("Unable to get key spec", e);
throw new FastUnsupportedCallbackException(callback);
}
log.tracef("Handling PasswordCallback: obtained successfully");
passwordCallback.setPassword(clearPasswordSpec.getEncodedPassword());
handleOne(callbacks, idx + 1);
return;
}
log.tracef("Handling PasswordCallback: failed to obtain PasswordCredential");
throw new FastUnsupportedCallbackException(callback);
}
// otherwise just fail out; some mechanisms will try again with different credentials
log.tracef("Handling PasswordCallback: PasswordCredential may not be supported");
throw new FastUnsupportedCallbackException(callback);
} else if (callback instanceof CredentialCallback) {
final CredentialCallback credentialCallback = (CredentialCallback) callback;
String requestedRealm = stateRef.get().getMechanismRealmConfiguration().getRealmName();
final Credential credential = getCredential(credentialCallback.getCredentialType(), credentialCallback.getAlgorithm(), credentialCallback.getParameterSpec());
if (credential != null) {
if (credential instanceof PasswordCredential) {
Password password = ((PasswordCredential) credential).getPassword();
if (password instanceof DigestPassword) {
String providedRealm = ((DigestPassword) password).getRealm();
if ( ! providedRealm.equals(requestedRealm)) {
log.tracef("Handling CredentialCallback: credential for realm \"%s\" is not available (\"%s\" provided)", requestedRealm, providedRealm);
throw new FastUnsupportedCallbackException(callback);
} else {
log.tracef("Handling CredentialCallback: obtained credential for correct realm \"%s\"", providedRealm);
}
}
}
log.tracef("Handling CredentialCallback: obtained credential: %s", credential);
credentialCallback.setCredential(credential);