This is an automated email from the ASF dual-hosted git repository. billyliu pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/kylin.git
commit 7c9892b2f8163d63536d4b5e76759186584c3d60 Author: Jiatao Tao <245915...@qq.com> AuthorDate: Thu Feb 8 15:11:09 2018 +0800 KYLIN-3246, add manager for user. minor, remove notify project ACL update. --- .../kylin/rest/security/KylinUserManager.java | 141 +++++++++++++++++++++ .../apache/kylin/rest/security/ManagedUser.java | 5 + .../kylin/rest/service/KylinUserService.java | 72 +++-------- .../org/apache/kylin/rest/service/UserService.java | 2 - .../kylin/rest/security/KylinUserManagerTest.java | 51 ++++++++ .../apache/kylin/rest/service/UserServiceTest.java | 11 -- 6 files changed, 217 insertions(+), 65 deletions(-) diff --git a/server-base/src/main/java/org/apache/kylin/rest/security/KylinUserManager.java b/server-base/src/main/java/org/apache/kylin/rest/security/KylinUserManager.java new file mode 100644 index 0000000..c4b457d --- /dev/null +++ b/server-base/src/main/java/org/apache/kylin/rest/security/KylinUserManager.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.kylin.rest.security; + +import static org.apache.kylin.common.persistence.ResourceStore.USER_ROOT; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.apache.kylin.common.KylinConfig; +import org.apache.kylin.common.persistence.ResourceStore; +import org.apache.kylin.common.util.AutoReadWriteLock; +import org.apache.kylin.metadata.cachesync.Broadcaster; +import org.apache.kylin.metadata.cachesync.CachedCrudAssist; +import org.apache.kylin.metadata.cachesync.CaseInsensitiveStringCache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class KylinUserManager { + + private static final Logger logger = LoggerFactory.getLogger(KylinUserManager.class); + + public static KylinUserManager getInstance(KylinConfig config) { + return config.getManager(KylinUserManager.class); + } + + // called by reflection + static KylinUserManager newInstance(KylinConfig config) throws IOException { + return new KylinUserManager(config); + } + + // ============================================================================ + + private KylinConfig config; + // user ==> ManagedUser + private CaseInsensitiveStringCache<ManagedUser> userMap; + private CachedCrudAssist<ManagedUser> crud; + private AutoReadWriteLock lock = new AutoReadWriteLock(); + + public KylinUserManager(KylinConfig config) throws IOException { + logger.info("Initializing KylinUserManager with config " + config); + this.config = config; + this.userMap = new CaseInsensitiveStringCache<>(config, "user"); + this.crud = new CachedCrudAssist<ManagedUser>(getStore(), USER_ROOT, "", ManagedUser.class, userMap, false) { + @Override + protected ManagedUser initEntityAfterReload(ManagedUser user, String resourceName) { + return user; + } + }; + + crud.reloadAll(); + Broadcaster.getInstance(config).registerListener(new ManagedUserSyncListener(), "user"); + } + + private class ManagedUserSyncListener extends Broadcaster.Listener { + + @Override + public void onEntityChange(Broadcaster broadcaster, String entity, Broadcaster.Event event, String cacheKey) + throws IOException { + try (AutoReadWriteLock.AutoLock l = lock.lockForWrite()) { + if (event == Broadcaster.Event.DROP) + userMap.removeLocal(cacheKey); + else + crud.reloadQuietly(cacheKey); + } + } + } + + public KylinConfig getConfig() { + return config; + } + + public ResourceStore getStore() { + return ResourceStore.getStore(this.config); + } + + public ManagedUser get(String name) { + try (AutoReadWriteLock.AutoLock l = lock.lockForRead()) { + return userMap.get(name); + } + } + + public List<ManagedUser> list() { + try (AutoReadWriteLock.AutoLock l = lock.lockForRead()) { + List<ManagedUser> users = new ArrayList<>(); + users.addAll(userMap.values()); + Collections.sort(users, new Comparator<ManagedUser>() { + @Override + public int compare(ManagedUser o1, ManagedUser o2) { + return o1.getUsername().compareToIgnoreCase(o2.getUsername()); + } + }); + return users; + } + } + + public void update(ManagedUser user) { + try (AutoReadWriteLock.AutoLock l = lock.lockForWrite()) { + ManagedUser exist = userMap.get(user.getUsername()); + if (exist != null) { + user.setLastModified(exist.getLastModified()); + } + crud.save(user); + } catch (IOException e) { + throw new RuntimeException("Can not update user.", e); + } + } + + public void delete(String username) { + try (AutoReadWriteLock.AutoLock l = lock.lockForWrite()) { + crud.delete(username); + } catch (IOException e) { + throw new RuntimeException("Can not delete user.", e); + } + } + + public boolean exists(String username) { + try (AutoReadWriteLock.AutoLock l = lock.lockForRead()) { + return userMap.containsKey(username); + } + } +} diff --git a/server-base/src/main/java/org/apache/kylin/rest/security/ManagedUser.java b/server-base/src/main/java/org/apache/kylin/rest/security/ManagedUser.java index 0fb2791..00e0045 100644 --- a/server-base/src/main/java/org/apache/kylin/rest/security/ManagedUser.java +++ b/server-base/src/main/java/org/apache/kylin/rest/security/ManagedUser.java @@ -113,6 +113,11 @@ public class ManagedUser extends RootPersistentEntity implements UserDetails { caterLegacy(); } + @Override + public String resourceName() { + return username; + } + public String getUsername() { return username; } diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/KylinUserService.java b/server-base/src/main/java/org/apache/kylin/rest/service/KylinUserService.java index c35d737..a67fb4c 100644 --- a/server-base/src/main/java/org/apache/kylin/rest/service/KylinUserService.java +++ b/server-base/src/main/java/org/apache/kylin/rest/service/KylinUserService.java @@ -22,7 +22,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nullable; import javax.annotation.PostConstruct; import org.apache.kylin.common.KylinConfig; @@ -33,6 +32,7 @@ import org.apache.kylin.rest.constant.Constant; import org.apache.kylin.rest.exception.InternalErrorException; import org.apache.kylin.rest.msg.Message; import org.apache.kylin.rest.msg.MsgPicker; +import org.apache.kylin.rest.security.KylinUserManager; import org.apache.kylin.rest.security.ManagedUser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,9 +40,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; -import com.google.common.base.Function; import com.google.common.base.Preconditions; -import com.google.common.collect.Lists; public class KylinUserService implements UserService { @@ -84,29 +82,20 @@ public class KylinUserService implements UserService { public void updateUser(UserDetails user) { Preconditions.checkState(user instanceof ManagedUser, "User {} is not ManagedUser", user); ManagedUser managedUser = (ManagedUser) user; - try { - String id = getId(user.getUsername()); - aclStore.putResourceWithoutCheck(id, managedUser, System.currentTimeMillis(), SERIALIZER); - logger.trace("update user : {}", user.getUsername()); - setEvictCacheFlag(true); - } catch (IOException e) { - throw new InternalErrorException(e); - } + getKylinUserManager().update(managedUser); + logger.trace("update user : {}", user.getUsername()); + setEvictCacheFlag(true); } @Override public void deleteUser(String userName) { - if (userName.equals(SUPER_ADMIN)) + if (userName.equals(SUPER_ADMIN)) { throw new InternalErrorException("User " + userName + " is not allowed to be deleted."); - - try { - String id = getId(userName); - aclStore.deleteResource(id); - logger.trace("delete user : {}", userName); - setEvictCacheFlag(true); - } catch (IOException e) { - throw new InternalErrorException(e); } + + getKylinUserManager().delete(userName); + logger.trace("delete user : {}", userName); + setEvictCacheFlag(true); } @Override @@ -116,12 +105,8 @@ public class KylinUserService implements UserService { @Override public boolean userExists(String userName) { - try { - logger.trace("judge user exist: {}", userName); - return aclStore.exists(getId(userName)); - } catch (IOException e) { - throw new InternalErrorException(e); - } + logger.trace("judge user exist: {}", userName); + return getKylinUserManager().exists(userName); } /** @@ -131,37 +116,17 @@ public class KylinUserService implements UserService { @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { Message msg = MsgPicker.getMsg(); - try { - ManagedUser managedUser = aclStore.getResource(getId(userName), ManagedUser.class, SERIALIZER); - if (managedUser == null) { - throw new UsernameNotFoundException(String.format(msg.getUSER_NOT_FOUND(), userName)); - } - logger.trace("load user : {}", userName); - return managedUser; - } catch (IOException e) { - throw new InternalErrorException(e); + ManagedUser managedUser = getKylinUserManager().get(userName); + if (managedUser == null) { + throw new UsernameNotFoundException(String.format(msg.getUSER_NOT_FOUND(), userName)); } + logger.trace("load user : {}", userName); + return managedUser; } @Override public List<ManagedUser> listUsers() throws IOException { - return aclStore.getAllResources(DIR_PREFIX, ManagedUser.class, SERIALIZER); - } - - @Override - public List<String> listUsernames() throws IOException { - List<String> paths = new ArrayList<>(); - paths.addAll(aclStore.listResources(ResourceStore.USER_ROOT)); - List<String> users = Lists.transform(paths, new Function<String, String>() { - @Nullable - @Override - public String apply(@Nullable String input) { - String[] path = input.split("/"); - Preconditions.checkArgument(path.length == 3); - return path[2]; - } - }); - return users; + return getKylinUserManager().list(); } @Override @@ -183,4 +148,7 @@ public class KylinUserService implements UserService { return DIR_PREFIX + userName; } + private KylinUserManager getKylinUserManager() { + return KylinUserManager.getInstance(KylinConfig.getInstanceFromEnv()); + } } diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/UserService.java b/server-base/src/main/java/org/apache/kylin/rest/service/UserService.java index 5b50456..21c4cf9 100644 --- a/server-base/src/main/java/org/apache/kylin/rest/service/UserService.java +++ b/server-base/src/main/java/org/apache/kylin/rest/service/UserService.java @@ -32,8 +32,6 @@ public interface UserService extends UserDetailsManager { List<ManagedUser> listUsers() throws IOException; - List<String> listUsernames() throws IOException; - List<String> listAdminUsers() throws IOException; //For performance consideration, list all users may be incomplete(eg. not load user's authorities until authorities has benn used). diff --git a/server/src/test/java/org/apache/kylin/rest/security/KylinUserManagerTest.java b/server/src/test/java/org/apache/kylin/rest/security/KylinUserManagerTest.java new file mode 100644 index 0000000..7024c4f --- /dev/null +++ b/server/src/test/java/org/apache/kylin/rest/security/KylinUserManagerTest.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.kylin.rest.security; + +import java.io.IOException; + +import org.apache.kylin.rest.constant.Constant; +import org.apache.kylin.rest.util.MultiNodeManagerTestBase; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +import com.google.common.collect.Lists; + +public class KylinUserManagerTest extends MultiNodeManagerTestBase { + @Test + public void testBasic() throws IOException, InterruptedException { + final KylinUserManager managerA = new KylinUserManager(configA); + final KylinUserManager managerB = new KylinUserManager(configB); + ManagedUser u1 = new ManagedUser("u1", "skippped", false, Lists.<GrantedAuthority> newArrayList()); + managerA.update(u1); + Thread.sleep(1000); + ManagedUser u11 = new ManagedUser("u1", "password", false, + Lists.<GrantedAuthority> newArrayList(new SimpleGrantedAuthority(Constant.ROLE_ANALYST))); + managerB.update(u11); + Thread.sleep(1000); + Assert.assertEquals("password", managerA.get("u1").getPassword()); + Assert.assertEquals("password", managerB.get("u1").getPassword()); + managerB.delete("u1"); + Thread.sleep(1000); + Assert.assertNull(managerA.get("u1")); + Assert.assertNull(managerB.get("u1")); + } +} diff --git a/server/src/test/java/org/apache/kylin/rest/service/UserServiceTest.java b/server/src/test/java/org/apache/kylin/rest/service/UserServiceTest.java index 2b3e0f5..284469c 100644 --- a/server/src/test/java/org/apache/kylin/rest/service/UserServiceTest.java +++ b/server/src/test/java/org/apache/kylin/rest/service/UserServiceTest.java @@ -22,7 +22,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import com.google.common.collect.Lists; import org.apache.kylin.rest.constant.Constant; import org.apache.kylin.rest.exception.InternalErrorException; import org.apache.kylin.rest.security.ManagedUser; @@ -34,9 +33,6 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; -/** - * - */ public class UserServiceTest extends ServiceTestBase { @Autowired @@ -65,13 +61,6 @@ public class UserServiceTest extends ServiceTestBase { } @Test - public void testGetAllUserNames() throws IOException { - List<String> users = userService.listUsernames(); - List<String> expected = Lists.newArrayList("ADMIN", "ANALYST", "MODELER"); - Assert.assertEquals(expected, users); - } - - @Test public void testDeleteAdmin() throws IOException { try { userService.deleteUser("ADMIN"); -- To stop receiving notification emails like this one, please contact billy...@apache.org.