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.

Reply via email to