1- using System ;
1+ using MultiFactor . IIS . Adapter . Services ;
2+ using System ;
23using System . Collections . Specialized ;
34using System . Configuration ;
45using System . Linq ;
@@ -7,22 +8,42 @@ namespace MultiFactor.IIS.Adapter
78{
89 public class Configuration
910 {
10- public string ApiKey { get ; private set ; }
11- public string ApiSecret { get ; private set ; }
12- public string ApiUrl { get ; private set ; }
13- public string ApiProxy { get ; private set ; }
11+ private readonly string _activeDirectoryDomain ;
12+ public string [ ] ActiveDirectoryDomains =>
13+ ( _activeDirectoryDomain ?? string . Empty ) . Split ( new [ ] { ';' } , StringSplitOptions . RemoveEmptyEntries )
14+ . Distinct ( )
15+ . ToArray ( ) ;
16+
17+ public string ApiUrl { get ; }
18+ public string ApiKey { get ; }
19+ public string ApiSecret { get ; }
20+ public string ApiProxy { get ; }
21+
1422 public bool BypassSecondFactorWhenApiUnreachable { get ; private set ; }
1523
1624 public string ActiveDirectory2FaGroup { get ; private set ; }
17- public TimeSpan ActiveDirectoryCacheTimout { get ; private set ; }
18- public TimeSpan ApiLifeCheckInterval { get ; private set ; }
19- public bool UseUpnAsIdentity { get ; private set ; }
20- public string [ ] PhoneAttributes { get ; private set ; } = new string [ 0 ] ;
25+ public TimeSpan ActiveDirectoryCacheTimout { get ; private set ; }
26+ public TimeSpan ApiLifeCheckInterval { get ; private set ; }
2127
28+ //Lookup for some attribute and use it for 2fa instead of uid
29+ public bool UseIdentityAttribute => ! string . IsNullOrWhiteSpace ( TwoFaIdentityAttribute ) ;
30+ public string TwoFaIdentityAttribute { get ; private set ; }
31+ public string [ ] PhoneAttributes { get ; private set ; } = new string [ 0 ] ;
32+
33+
2234 private static readonly Lazy < Configuration > _current = new Lazy < Configuration > ( Load ) ;
2335 public static Configuration Current => _current . Value ;
36+
37+ protected Configuration ( ) { }
2438
25- protected Configuration ( ) { }
39+ private Configuration ( string activeDirectoryDomain , string apiUrl , string apiKey , string apiSecret , string apiProxy )
40+ {
41+ _activeDirectoryDomain = activeDirectoryDomain ;
42+ ApiUrl = apiUrl ;
43+ ApiKey = apiKey ;
44+ ApiSecret = apiSecret ;
45+ ApiProxy = apiProxy ;
46+ }
2647
2748 protected static Configuration Load ( )
2849 {
@@ -34,35 +55,31 @@ protected static Configuration Load()
3455 var apiProxySetting = appSettings [ ConfigurationKeys . ApiProxy ] ;
3556
3657 var activeDirectory2FaGroupSetting = appSettings [ ConfigurationKeys . ActiveDirectory2FAGroup ] ;
37- var useUpnAsIdentitySetting = appSettings [ ConfigurationKeys . UseUpnAsIdentity ] ;
58+ var activeDirectoryDomain = appSettings [ ConfigurationKeys . ActiveDirectoryDomain ] ;
3859
39- if ( string . IsNullOrEmpty ( apiUrlSetting ) )
60+ var domain = GetDomain ( activeDirectoryDomain ) ;
61+
62+ if ( string . IsNullOrWhiteSpace ( apiUrlSetting ) )
4063 {
4164 throw new Exception ( $ "Configuration error: '{ ConfigurationKeys . ApiUrl } ' element not found or empty") ;
4265 }
43- if ( string . IsNullOrEmpty ( apiKeySetting ) )
66+ if ( string . IsNullOrWhiteSpace ( apiKeySetting ) )
4467 {
4568 throw new Exception ( $ "Configuration error: '{ ConfigurationKeys . ApiKey } ' element not found or empty") ;
4669 }
47- if ( string . IsNullOrEmpty ( apiSecretSetting ) )
70+ if ( string . IsNullOrWhiteSpace ( apiSecretSetting ) )
4871 {
4972 throw new Exception ( $ "Configuration error: '{ ConfigurationKeys . ApiSecret } ' element not found or empty") ;
5073 }
5174
52- var config = new Configuration
53- {
54- ApiUrl = apiUrlSetting ,
55- ApiKey = apiKeySetting ,
56- ApiSecret = apiSecretSetting ,
57- ApiProxy = apiProxySetting ,
58- ActiveDirectory2FaGroup = activeDirectory2FaGroupSetting
59- } ;
75+ var config = new Configuration ( domain , apiUrlSetting , apiKeySetting , apiSecretSetting , apiProxySetting ) ;
6076
61- if ( bool . TryParse ( useUpnAsIdentitySetting , out var useUpnAsIdentity ) )
77+ if ( ! string . IsNullOrWhiteSpace ( activeDirectory2FaGroupSetting ) )
6278 {
63- config . UseUpnAsIdentity = useUpnAsIdentity ;
79+ config . ActiveDirectory2FaGroup = activeDirectory2FaGroupSetting ;
6480 }
6581
82+ ReadTwoFaIdentityAttributeSetting ( appSettings , config ) ;
6683 ReadActiveDirectoryCacheTimoutSetting ( appSettings , config ) ;
6784 ReadPhoneAttributeSetting ( appSettings , config ) ;
6885 ReadBypassWhenApiUnreachableSetting ( appSettings , config ) ;
@@ -71,6 +88,43 @@ protected static Configuration Load()
7188 return config ;
7289 }
7390
91+ private static void ReadTwoFaIdentityAttributeSetting ( NameValueCollection appSettings , Configuration configuration )
92+ {
93+ var useUpnAsIdentitySetting = appSettings [ ConfigurationKeys . UseUpnAsIdentity ] ;
94+ var twoFaIdentityAttributeSetting = appSettings [ ConfigurationKeys . TwoFAIdentityAttribyte ] ;
95+
96+ // MUST be before 'use-upn-as-identity' check
97+ if ( ! string . IsNullOrWhiteSpace ( twoFaIdentityAttributeSetting ) )
98+ {
99+ configuration . TwoFaIdentityAttribute = twoFaIdentityAttributeSetting ;
100+ }
101+
102+ //legacy settings for 2fa identity
103+ if ( bool . TryParse ( useUpnAsIdentitySetting , out var useUpnAsIdentity ) )
104+ {
105+ if ( ! string . IsNullOrWhiteSpace ( twoFaIdentityAttributeSetting ) )
106+ {
107+ throw new Exception ( "Configuration error: Using settings 'use-upn-as-identity' and 'use-attribute-as-identity' together is unacceptable. Prefer using 'use-attribute-as-identity'." ) ;
108+ }
109+
110+ Logger . Owa . Warn ( "The setting 'use-upn-as-identity' is deprecated, use 'use-attribute-as-identity' instead" ) ;
111+ if ( useUpnAsIdentity )
112+ {
113+ configuration . TwoFaIdentityAttribute = "userPrincipalName" ;
114+ }
115+ }
116+ }
117+
118+ private static string GetDomain ( string activeDirectoryDomain )
119+ {
120+ if ( ! string . IsNullOrWhiteSpace ( activeDirectoryDomain ) )
121+ {
122+ return activeDirectoryDomain ;
123+ }
124+
125+ return System . DirectoryServices . ActiveDirectory . Domain . GetComputerDomain ( ) . Name ;
126+ }
127+
74128 private static void ReadPhoneAttributeSetting ( NameValueCollection appSettings , Configuration configuration )
75129 {
76130 var value = appSettings [ ConfigurationKeys . PhoneAttribute ] ;
@@ -80,7 +134,7 @@ private static void ReadPhoneAttributeSetting(NameValueCollection appSettings, C
80134 }
81135
82136 var parsed = value . Split ( new [ ] { ';' } , StringSplitOptions . RemoveEmptyEntries ) . Select ( attr => attr . Trim ( ) ) . ToArray ( ) ;
83- if ( parsed . Length != 0 ) configuration . PhoneAttributes = parsed ;
137+ if ( parsed . Length != 0 ) configuration . PhoneAttributes = parsed ;
84138 }
85139
86140 private static void ReadActiveDirectoryCacheTimoutSetting ( NameValueCollection appSettings , Configuration configuration )
@@ -89,13 +143,13 @@ private static void ReadActiveDirectoryCacheTimoutSetting(NameValueCollection ap
89143
90144 var legacyValue = appSettings [ ConfigurationKeys . ActiveDirectory2FAGroupMembershipCacheTimeout ] ;
91145 if ( int . TryParse ( legacyValue , out var legVal ) ) ttl = TimeSpan . FromMinutes ( legVal ) ;
92-
146+
93147 var value = appSettings [ ConfigurationKeys . ActiveDirectoryCacheTimeout ] ;
94148 if ( int . TryParse ( value , out var val ) ) ttl = TimeSpan . FromMinutes ( val ) ;
95-
149+
96150 configuration . ActiveDirectoryCacheTimout = ttl > TimeSpan . Zero ? ttl : TimeSpan . FromMinutes ( 15 ) ;
97151 }
98-
152+
99153 private static void ReadApiLifeCheckIntervalSetting ( NameValueCollection appSettings , Configuration configuration )
100154 {
101155 var defaultValue = TimeSpan . FromMinutes ( 15 ) ;
@@ -114,7 +168,7 @@ private static void ReadApiLifeCheckIntervalSetting(NameValueCollection appSetti
114168
115169 configuration . ApiLifeCheckInterval = parsed > 0 ? TimeSpan . FromMinutes ( parsed ) : defaultValue ;
116170 }
117-
171+
118172 private static void ReadBypassWhenApiUnreachableSetting ( NameValueCollection appSettings , Configuration configuration )
119173 {
120174 var value = appSettings [ ConfigurationKeys . BypassSecondFactorWhenApiUnreachable ] ;
0 commit comments