88
99import org .apache .logging .log4j .LogManager ;
1010import org .apache .logging .log4j .Logger ;
11- import org .elasticsearch .Assertions ;
1211import org .elasticsearch .action .ActionListener ;
1312import org .elasticsearch .common .Strings ;
1413import org .elasticsearch .common .collect .MapBuilder ;
3029import org .elasticsearch .xpack .security .authc .esnative .ReservedRealm ;
3130
3231import java .util .ArrayList ;
33- import java .util .Arrays ;
3432import java .util .Collections ;
3533import java .util .HashMap ;
3634import java .util .HashSet ;
@@ -59,12 +57,11 @@ public class Realms implements Iterable<Realm> {
5957 private final ThreadContext threadContext ;
6058 private final ReservedRealm reservedRealm ;
6159
62- protected List <Realm > realms ;
63- // a list of realms that are considered standard in that they are provided by x-pack and
64- // interact with a 3rd party source on a limited basis
65- List <Realm > standardRealmsOnly ;
66- // a list of realms that are considered native, that is they only interact with x-pack and no 3rd party auth sources
67- List <Realm > nativeRealmsOnly ;
60+ // All realms that were configured from the node settings, some of these may not be enabled due to licensing
61+ private final List <Realm > allConfiguredRealms ;
62+
63+ // the realms in current use. This list will change dynamically as the license changes
64+ private volatile List <Realm > activeRealms ;
6865
6966 public Realms (Settings settings , Environment env , Map <String , Realm .Factory > factories , XPackLicenseState licenseState ,
7067 ThreadContext threadContext , ReservedRealm reservedRealm ) throws Exception {
@@ -74,77 +71,78 @@ public Realms(Settings settings, Environment env, Map<String, Realm.Factory> fac
7471 this .licenseState = licenseState ;
7572 this .threadContext = threadContext ;
7673 this .reservedRealm = reservedRealm ;
74+
7775 assert XPackSettings .SECURITY_ENABLED .get (settings ) : "security must be enabled" ;
7876 assert factories .get (ReservedRealm .TYPE ) == null ;
77+
7978 final List <RealmConfig > realmConfigs = buildRealmConfigs ();
80- this .realms = initRealms (realmConfigs );
81- assert realms .get (0 ) == reservedRealm : "the first realm must be reserved realm" ;
82- // pre-computing a list of internal only realms allows us to have much cheaper iteration than a custom iterator
83- // and is also simpler in terms of logic. These lists are small, so the duplication should not be a real issue here
84- List <Realm > standardRealms = new ArrayList <>(List .of (reservedRealm ));
85- List <Realm > basicRealms = new ArrayList <>(List .of (reservedRealm ));
86- for (Realm realm : realms ) {
87- // don't add the reserved realm here otherwise we end up with only this realm...
88- if (InternalRealms .isStandardRealm (realm .type ())) {
89- standardRealms .add (realm );
90- }
79+ this .allConfiguredRealms = initRealms (realmConfigs );
80+ this .allConfiguredRealms .forEach (r -> r .initialize (allConfiguredRealms , licenseState ));
81+ assert allConfiguredRealms .get (0 ) == reservedRealm : "the first realm must be reserved realm" ;
9182
92- if (InternalRealms .isBuiltinRealm (realm .type ())) {
93- basicRealms .add (realm );
94- }
95- }
83+ recomputeActiveRealms ();
84+ licenseState .addListener (this ::recomputeActiveRealms );
85+ }
9686
97- if (Assertions .ENABLED ) {
98- for (List <Realm > realmList : Arrays .asList (standardRealms , basicRealms )) {
99- assert realmList .get (0 ) == reservedRealm : "the first realm must be reserved realm" ;
100- }
87+ protected void recomputeActiveRealms () {
88+ final XPackLicenseState licenseStateSnapshot = licenseState .copyCurrentLicenseState ();
89+ final List <Realm > licensedRealms = calculateLicensedRealms (licenseStateSnapshot );
90+ logger .info (
91+ "license mode is [{}], currently licensed security realms are [{}]" ,
92+ licenseStateSnapshot .getOperationMode ().description (),
93+ Strings .collectionToCommaDelimitedString (licensedRealms )
94+ );
95+
96+ // Stop license-tracking for any previously-active realms that are no longer allowed
97+ if (activeRealms != null ) {
98+ activeRealms .stream ().filter (r -> licensedRealms .contains (r ) == false ).forEach (realm -> {
99+ if (InternalRealms .isStandardRealm (realm .type ())) {
100+ Security .STANDARD_REALMS_FEATURE .stopTracking (licenseStateSnapshot , realm .name ());
101+ } else {
102+ Security .ALL_REALMS_FEATURE .stopTracking (licenseStateSnapshot , realm .name ());
103+ }
104+ });
101105 }
102106
103- this .standardRealmsOnly = Collections .unmodifiableList (standardRealms );
104- this .nativeRealmsOnly = Collections .unmodifiableList (basicRealms );
105- realms .forEach (r -> r .initialize (this , licenseState ));
107+ activeRealms = licensedRealms ;
106108 }
107109
108110 @ Override
109111 public Iterator <Realm > iterator () {
110- return asList ().iterator ();
112+ return getActiveRealms ().iterator ();
111113 }
112114
113115 /**
114116 * Returns a list of realms that are configured, but are not permitted under the current license.
115117 */
116118 public List <Realm > getUnlicensedRealms () {
117- final XPackLicenseState licenseStateSnapshot = licenseState .copyCurrentLicenseState ();
118-
119- // If all realms are allowed, then nothing is unlicensed
120- if (Security .ALL_REALMS_FEATURE .checkWithoutTracking (licenseStateSnapshot )) {
121- return Collections .emptyList ();
122- }
123-
124- final List <Realm > allowedRealms = this .asList ();
125- // Shortcut for the typical case, all the configured realms are allowed
126- if (allowedRealms .equals (this .realms )) {
119+ final List <Realm > activeSnapshot = activeRealms ;
120+ if (activeSnapshot .equals (allConfiguredRealms )) {
127121 return Collections .emptyList ();
128122 }
129123
130124 // Otherwise, we return anything in "all realms" that is not in the allowed realm list
131- return realms .stream ().filter (r -> allowedRealms .contains (r ) == false ).collect (Collectors .toUnmodifiableList ());
125+ return allConfiguredRealms .stream ().filter (r -> activeSnapshot .contains (r ) == false ).collect (Collectors .toUnmodifiableList ());
132126 }
133127
134128 public Stream <Realm > stream () {
135129 return StreamSupport .stream (this .spliterator (), false );
136130 }
137131
138- public List <Realm > asList () {
139- // TODO : Recalculate this when the license changes rather than on every call
140- return realms .stream ().filter (r -> checkLicense (r , licenseState )).collect (Collectors .toUnmodifiableList ());
132+ public List <Realm > getActiveRealms () {
133+ assert activeRealms != null : "Active realms not configured" ;
134+ return activeRealms ;
135+ }
136+
137+ // Protected for testing
138+ protected List <Realm > calculateLicensedRealms (XPackLicenseState licenseStateSnapshot ) {
139+ return allConfiguredRealms .stream ()
140+ .filter (r -> checkLicense (r , licenseStateSnapshot ))
141+ .collect (Collectors .toUnmodifiableList ());
141142 }
142143
143144 private static boolean checkLicense (Realm realm , XPackLicenseState licenseState ) {
144- if (ReservedRealm .TYPE .equals (realm .type ())) {
145- return true ;
146- }
147- if (InternalRealms .isBuiltinRealm (realm .type ())) {
145+ if (isBasicLicensedRealm (realm .type ())) {
148146 return true ;
149147 }
150148 if (InternalRealms .isStandardRealm (realm .type ())) {
@@ -153,8 +151,22 @@ private static boolean checkLicense(Realm realm, XPackLicenseState licenseState)
153151 return Security .ALL_REALMS_FEATURE .checkAndStartTracking (licenseState , realm .name ());
154152 }
155153
154+ public static boolean isRealmTypeAvailable (XPackLicenseState licenseState , String type ) {
155+ if (Security .ALL_REALMS_FEATURE .checkWithoutTracking (licenseState )) {
156+ return true ;
157+ } else if (Security .STANDARD_REALMS_FEATURE .checkWithoutTracking (licenseState )) {
158+ return InternalRealms .isStandardRealm (type ) || ReservedRealm .TYPE .equals (type );
159+ } else {
160+ return isBasicLicensedRealm (type );
161+ }
162+ }
163+
164+ private static boolean isBasicLicensedRealm (String type ) {
165+ return ReservedRealm .TYPE .equals (type ) || InternalRealms .isBuiltinRealm (type );
166+ }
167+
156168 public Realm realm (String name ) {
157- for (Realm realm : realms ) {
169+ for (Realm realm : activeRealms ) {
158170 if (name .equals (realm .name ())) {
159171 return realm ;
160172 }
@@ -213,7 +225,7 @@ public void usageStats(ActionListener<Map<String, Object>> listener) {
213225 final XPackLicenseState licenseStateSnapshot = licenseState .copyCurrentLicenseState ();
214226 Map <String , Object > realmMap = new HashMap <>();
215227 final AtomicBoolean failed = new AtomicBoolean (false );
216- final List <Realm > realmList = asList ().stream ()
228+ final List <Realm > realmList = getActiveRealms ().stream ()
217229 .filter (r -> ReservedRealm .TYPE .equals (r .type ()) == false )
218230 .collect (Collectors .toList ());
219231 final Set <String > realmTypes = realmList .stream ().map (Realm ::type ).collect (Collectors .toSet ());
@@ -382,15 +394,4 @@ private static Map<String, Object> convertToMapOfLists(Map<String, Object> map)
382394 }
383395 return converted ;
384396 }
385-
386- public static boolean isRealmTypeAvailable (XPackLicenseState licenseState , String type ) {
387- if (Security .ALL_REALMS_FEATURE .checkWithoutTracking (licenseState )) {
388- return true ;
389- } else if (Security .STANDARD_REALMS_FEATURE .checkWithoutTracking (licenseState )) {
390- return InternalRealms .isStandardRealm (type ) || ReservedRealm .TYPE .equals (type );
391- } else {
392- return InternalRealms .isBuiltinRealm (type );
393- }
394- }
395-
396397}
0 commit comments