diff --git a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconControllerModule.java b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconControllerModule.java index 3f7e99056e44..9a9dfb48e74b 100644 --- a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconControllerModule.java +++ b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconControllerModule.java @@ -92,8 +92,15 @@ public class ReconControllerModule extends AbstractModule { private static final Logger LOG = LoggerFactory.getLogger(ReconControllerModule.class); + private final ReconServer reconServer; + + public ReconControllerModule(ReconServer reconServer) { + this.reconServer = reconServer; + } + @Override protected void configure() { + bind(ReconServer.class).toInstance(reconServer); bind(OzoneConfiguration.class).toProvider(ConfigurationProvider.class); bind(ReconHttpServer.class).in(Singleton.class); bind(ReconStorageConfig.class).in(Singleton.class); diff --git a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconServer.java b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconServer.java index c9fc0ab3470b..9b5f78c083a2 100644 --- a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconServer.java +++ b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconServer.java @@ -31,6 +31,7 @@ import com.google.inject.Injector; import java.io.IOException; import java.net.InetSocketAddress; +import java.util.Collection; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; import javax.sql.DataSource; @@ -38,9 +39,11 @@ import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolClientSideTranslatorPB; import org.apache.hadoop.hdds.recon.ReconConfig; +import org.apache.hadoop.hdds.recon.ReconConfigKeys; import org.apache.hadoop.hdds.scm.server.OzoneStorageContainerManager; import org.apache.hadoop.hdds.security.SecurityConfig; import org.apache.hadoop.hdds.security.x509.certificate.client.CertificateClient; +import org.apache.hadoop.hdds.server.OzoneAdmins; import org.apache.hadoop.hdds.utils.HddsServerUtil; import org.apache.hadoop.ozone.OzoneSecurityUtil; import org.apache.hadoop.ozone.recon.api.types.FeatureProvider; @@ -85,6 +88,7 @@ public class ReconServer extends GenericCli implements Callable { private ReconStorageConfig reconStorage; private CertificateClient certClient; private ReconTaskStatusMetrics reconTaskStatusMetrics; + private OzoneAdmins reconAdmins; private volatile boolean isStarted = false; @@ -104,9 +108,23 @@ public Void call() throws Exception { ReconServer.class, originalArgs, LOG, configuration); ConfigurationProvider.setConfiguration(configuration); + String reconStarterUser = UserGroupInformation.getCurrentUser().getShortUserName(); + Collection adminUsers = + OzoneAdmins.getOzoneAdminsFromConfig(configuration, reconStarterUser); + adminUsers.addAll( + configuration.getStringCollection(ReconConfigKeys.OZONE_RECON_ADMINISTRATORS)); + + Collection adminGroups = + OzoneAdmins.getOzoneAdminsGroupsFromConfig(configuration); + adminGroups.addAll( + configuration.getStringCollection(ReconConfigKeys.OZONE_RECON_ADMINISTRATORS_GROUPS)); + + reconAdmins = new OzoneAdmins(adminUsers, adminGroups); + LOG.info("Recon start with adminUsers: {}", reconAdmins.getAdminUsernames()); + LOG.info("Initializing Recon server..."); try { - injector = Guice.createInjector(new ReconControllerModule(), + injector = Guice.createInjector(new ReconControllerModule(this), new ReconRestServletModule(configuration), new ReconSchemaGenerationModule()); @@ -427,4 +445,14 @@ public ReconTaskController getReconTaskController() { ReconHttpServer getHttpServer() { return httpServer; } + + /** + * Check if a user is a Recon administrator. + * + * @param user UserGroupInformation + * @return true if the user is an admin, false otherwise + */ + public boolean isAdmin(UserGroupInformation user) { + return reconAdmins.isAdmin(user); + } } diff --git a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/filters/ReconAdminFilter.java b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/filters/ReconAdminFilter.java index 546a0b1949b2..f4ae82b7d613 100644 --- a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/filters/ReconAdminFilter.java +++ b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/filters/ReconAdminFilter.java @@ -21,7 +21,6 @@ import com.google.inject.Singleton; import java.io.IOException; import java.security.Principal; -import java.util.Collection; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -30,10 +29,7 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.hadoop.hdds.conf.OzoneConfiguration; -import org.apache.hadoop.hdds.recon.ReconConfigKeys; -import org.apache.hadoop.hdds.server.OzoneAdmins; -import org.apache.hadoop.ozone.OzoneConfigKeys; +import org.apache.hadoop.ozone.recon.ReconServer; import org.apache.hadoop.security.UserGroupInformation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,15 +44,17 @@ public class ReconAdminFilter implements Filter { private static final Logger LOG = LoggerFactory.getLogger(ReconAdminFilter.class); - private final OzoneConfiguration conf; + private final ReconServer reconServer; @Inject - ReconAdminFilter(OzoneConfiguration conf) { - this.conf = conf; + ReconAdminFilter(ReconServer reconServer) { + this.reconServer = reconServer; } @Override - public void init(FilterConfig filterConfig) throws ServletException { } + public void init(FilterConfig filterConfig) throws ServletException { + LOG.info("ReconAdminFilter initialized"); + } @Override public void doFilter(ServletRequest servletRequest, @@ -100,15 +98,6 @@ public void doFilter(ServletRequest servletRequest, public void destroy() { } private boolean hasPermission(UserGroupInformation user) { - Collection admins = - conf.getStringCollection(OzoneConfigKeys.OZONE_ADMINISTRATORS); - admins.addAll( - conf.getStringCollection(ReconConfigKeys.OZONE_RECON_ADMINISTRATORS)); - Collection adminGroups = - conf.getStringCollection(OzoneConfigKeys.OZONE_ADMINISTRATORS_GROUPS); - adminGroups.addAll( - conf.getStringCollection( - ReconConfigKeys.OZONE_RECON_ADMINISTRATORS_GROUPS)); - return new OzoneAdmins(admins, adminGroups).isAdmin(user); + return reconServer.isAdmin(user); } } diff --git a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/filters/TestAdminFilter.java b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/filters/TestAdminFilter.java index b6aa522d4e70..c8c8797b1986 100644 --- a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/filters/TestAdminFilter.java +++ b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/filters/TestAdminFilter.java @@ -20,12 +20,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.google.common.collect.Sets; +import java.io.IOException; import java.security.Principal; +import java.util.Collection; import java.util.HashSet; import java.util.Set; import javax.servlet.FilterChain; @@ -34,7 +37,9 @@ import javax.ws.rs.Path; import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.recon.ReconConfigKeys; +import org.apache.hadoop.hdds.server.OzoneAdmins; import org.apache.hadoop.ozone.OzoneConfigKeys; +import org.apache.hadoop.ozone.recon.ReconServer; import org.apache.hadoop.ozone.recon.api.AdminOnly; import org.apache.hadoop.ozone.recon.api.ClusterStateEndpoint; import org.apache.hadoop.ozone.recon.api.MetricsProxyEndpoint; @@ -170,8 +175,31 @@ public void testAdminFilterNoAdmins() throws Exception { testAdminFilterWithPrincipal(new OzoneConfiguration(), "reject", false); } + @Test + public void testAdminFilterStarterUserAutoAdmin() throws Exception { + OzoneConfiguration conf = new OzoneConfiguration(); + String currentUser = UserGroupInformation.getCurrentUser().getShortUserName(); + + testAdminFilterWithPrincipal(conf, currentUser, true); + testAdminFilterWithPrincipal(conf, "otheruser", false); + } + + @Test + public void testAdminFilterStarterUserPlusConfiguredAdmins() throws Exception { + OzoneConfiguration conf = new OzoneConfiguration(); + conf.setStrings(OzoneConfigKeys.OZONE_ADMINISTRATORS, "configadmin"); + + String currentUser = UserGroupInformation.getCurrentUser().getShortUserName(); + + testAdminFilterWithPrincipal(conf, currentUser, true); + testAdminFilterWithPrincipal(conf, "configadmin", true); + testAdminFilterWithPrincipal(conf, "reject", false); + } + private void testAdminFilterWithPrincipal(OzoneConfiguration conf, String principalToUse, boolean shouldPass) throws Exception { + ReconServer mockReconServer = createMockReconServer(conf); + Principal mockPrincipal = mock(Principal.class); when(mockPrincipal.getName()).thenReturn(principalToUse); HttpServletRequest mockRequest = mock(HttpServletRequest.class); @@ -180,8 +208,9 @@ private void testAdminFilterWithPrincipal(OzoneConfiguration conf, HttpServletResponse mockResponse = mock(HttpServletResponse.class); FilterChain mockFilterChain = mock(FilterChain.class); - new ReconAdminFilter(conf).doFilter(mockRequest, mockResponse, - mockFilterChain); + ReconAdminFilter filter = new ReconAdminFilter(mockReconServer); + filter.init(null); + filter.doFilter(mockRequest, mockResponse, mockFilterChain); if (shouldPass) { verify(mockFilterChain).doFilter(mockRequest, mockResponse); @@ -189,4 +218,31 @@ private void testAdminFilterWithPrincipal(OzoneConfiguration conf, verify(mockResponse).setStatus(HttpServletResponse.SC_FORBIDDEN); } } + + /** + * Creates a mock ReconServer that mimics the actual admin initialization logic. + */ + private ReconServer createMockReconServer(OzoneConfiguration conf) throws IOException { + String reconStarterUser = UserGroupInformation.getCurrentUser().getShortUserName(); + Collection adminUsers = + OzoneAdmins.getOzoneAdminsFromConfig(conf, reconStarterUser); + adminUsers.addAll( + conf.getStringCollection(ReconConfigKeys.OZONE_RECON_ADMINISTRATORS)); + + Collection adminGroups = + OzoneAdmins.getOzoneAdminsGroupsFromConfig(conf); + adminGroups.addAll( + conf.getStringCollection(ReconConfigKeys.OZONE_RECON_ADMINISTRATORS_GROUPS)); + + OzoneAdmins reconAdmins = new OzoneAdmins(adminUsers, adminGroups); + + ReconServer mockReconServer = mock(ReconServer.class); + when(mockReconServer.isAdmin(any(UserGroupInformation.class))) + .thenAnswer(invocation -> { + UserGroupInformation user = invocation.getArgument(0); + return reconAdmins.isAdmin(user); + }); + + return mockReconServer; + } }