View Javadoc
1   package com.atlassian.sal.core.features;
2   
3   import com.atlassian.sal.api.features.DarkFeatureManager;
4   import com.atlassian.sal.api.features.EnabledDarkFeatures;
5   import com.atlassian.sal.api.features.EnabledDarkFeaturesBuilder;
6   import com.atlassian.sal.api.features.MissingPermissionException;
7   import com.atlassian.sal.api.features.SiteDarkFeaturesStorage;
8   import com.atlassian.sal.api.features.ValidFeatureKeyPredicate;
9   import com.atlassian.sal.api.user.UserKey;
10  import com.atlassian.sal.api.user.UserManager;
11  
12  import javax.annotation.Nonnull;
13  import javax.annotation.Nullable;
14  
15  import java.util.Optional;
16  
17  import static com.atlassian.sal.api.features.ValidFeatureKeyPredicate.checkFeatureKey;
18  
19  /**
20   * Default implementation of DarkFeatureManager - sufficient for any product which does not already have its own dark
21   * feature framework. Does not implement per-user enabling.
22   */
23  public class DefaultDarkFeatureManager implements DarkFeatureManager {
24      private final UserManager userManager;
25      private final SiteDarkFeaturesStorage siteDarkFeaturesStorage;
26      private final SystemDarkFeatureInitializer.SystemDarkFeatures systemDarkFeatures;
27  
28      public DefaultDarkFeatureManager(final UserManager userManager, final SiteDarkFeaturesStorage siteDarkFeaturesStorage) {
29          this.userManager = userManager;
30          this.siteDarkFeaturesStorage = siteDarkFeaturesStorage;
31          this.systemDarkFeatures = SystemDarkFeatureInitializer.getSystemStartupDarkFeatures();
32      }
33  
34      @Override
35      @Nonnull
36      public Optional<Boolean> isEnabledForAllUsers(@Nonnull String featureKey) {
37          if (!ValidFeatureKeyPredicate.isValidFeatureKey(featureKey)) {
38              return Optional.empty();
39          }
40          if (systemDarkFeatures.getEnabled().contains(featureKey)
41                  || siteDarkFeaturesStorage.contains(featureKey)) {
42              return Optional.of(Boolean.TRUE);
43          }
44          if (systemDarkFeatures.getDisabled().contains(featureKey)) {
45              return Optional.of(Boolean.FALSE);
46          }
47          return Optional.empty();
48      }
49  
50      @Override
51      @Nonnull
52      public Optional<Boolean> isEnabledForCurrentUser(@Nonnull String featureKey) {
53          return isEnabledForAllUsers(featureKey);
54      }
55  
56      @Override
57      @Nonnull
58      public Optional<Boolean> isEnabledForUser(@Nullable UserKey userKey, @Nonnull String featureKey) {
59          if (isUserAnonymous(userKey) || isUserExisting(userKey)) {
60              return isEnabledForAllUsers(featureKey);
61          } else {
62              throw new IllegalArgumentException("The user does not exist");
63          }
64      }
65  
66      @Override
67      @Deprecated
68      public boolean isFeatureEnabledForAllUsers(final String featureKey) {
69          return ValidFeatureKeyPredicate.isValidFeatureKey(featureKey) && getFeaturesEnabledForAllUsers().isFeatureEnabled(featureKey);
70      }
71  
72      @Override
73      @Deprecated
74      public boolean isFeatureEnabledForCurrentUser(final String featureKey) {
75          return isFeatureEnabledForAllUsers(featureKey);
76      }
77  
78      @Override
79      @Deprecated
80      public boolean isFeatureEnabledForUser(@Nullable final UserKey userKey, final String featureKey) {
81          if (isUserAnonymous(userKey) || isUserExisting(userKey)) {
82              return isFeatureEnabledForAllUsers(featureKey);
83          } else {
84              throw new IllegalArgumentException("The user does not exist");
85          }
86      }
87  
88      @Override
89      public boolean canManageFeaturesForAllUsers() {
90          try {
91              final UserKey remoteUserKey = userManager.getRemoteUserKey();
92              return userManager.isSystemAdmin(remoteUserKey);
93          } catch (RuntimeException e) {
94              /*
95               * Applying the principle of least surprise here so the caller wouldn't have to deal with undeclared
96               * runtime exceptions. Determining that a user has the proper permissions is very unlikely to be successful
97               * when an exception is thrown.
98               */
99              return false;
100         }
101     }
102 
103     @Override
104     public void enableFeatureForAllUsers(final String featureKey) {
105         checkFeatureKey(featureKey);
106         checkCurrentUserCanManageFeaturesForAllUsers();
107         siteDarkFeaturesStorage.enable(featureKey);
108     }
109 
110     @Override
111     public void disableFeatureForAllUsers(final String featureKey) {
112         checkFeatureKey(featureKey);
113         checkCurrentUserCanManageFeaturesForAllUsers();
114         siteDarkFeaturesStorage.disable(featureKey);
115     }
116 
117     @Override
118     public void enableFeatureForCurrentUser(final String featureKey) {
119         throwUnsupportedPerUserOperationException();
120     }
121 
122     @Override
123     public void enableFeatureForUser(final UserKey userKey, final String featureKey) {
124         throwUnsupportedPerUserOperationException();
125     }
126 
127     @Override
128     public void disableFeatureForCurrentUser(final String featureKey) {
129         throwUnsupportedPerUserOperationException();
130     }
131 
132     @Override
133     public void disableFeatureForUser(final UserKey userKey, final String featureKey) {
134         throwUnsupportedPerUserOperationException();
135     }
136 
137     public EnabledDarkFeatures getFeaturesEnabledForAllUsers() {
138         if (systemDarkFeatures.isDisableAll()) {
139             return EnabledDarkFeatures.NONE;
140         } else {
141             return new EnabledDarkFeaturesBuilder()
142                     .unmodifiableFeaturesEnabledForAllUsers(systemDarkFeatures.getEnabled())
143                     .featuresEnabledForAllUsers(siteDarkFeaturesStorage.getEnabledDarkFeatures())
144                     .build();
145         }
146     }
147 
148     @Override
149     public EnabledDarkFeatures getFeaturesEnabledForCurrentUser() {
150         return getFeaturesEnabledForAllUsers();
151     }
152 
153     @Override
154     public EnabledDarkFeatures getFeaturesEnabledForUser(@Nullable final UserKey userKey) {
155         if (isUserAnonymous(userKey) || isUserExisting(userKey)) {
156             return getFeaturesEnabledForAllUsers();
157         } else {
158             throw new IllegalArgumentException("The user does not exist");
159         }
160     }
161 
162     private boolean isUserExisting(@Nullable final UserKey userKey) {
163         return (userKey != null) && userManager.getUserProfile(userKey) != null;
164     }
165 
166     private boolean isUserAnonymous(@Nullable final UserKey userKey) {
167         return userKey == null;
168     }
169 
170     private void checkCurrentUserCanManageFeaturesForAllUsers() {
171         if (!canManageFeaturesForAllUsers()) {
172             throw new MissingPermissionException("The current user is not allowed to change dark features affecting all users.");
173         }
174     }
175 
176     private void throwUnsupportedPerUserOperationException() {
177         throw new UnsupportedOperationException("The default implementation doesn't support per-user dark features.");
178     }
179 }