You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by li...@apache.org on 2017/06/29 05:48:37 UTC

[30/50] kylin git commit: minor, introduce ManagedUser to replace Spring User

minor, introduce ManagedUser to replace Spring User

temp


Project: http://git-wip-us.apache.org/repos/asf/kylin/repo
Commit: http://git-wip-us.apache.org/repos/asf/kylin/commit/cf2f27a3
Tree: http://git-wip-us.apache.org/repos/asf/kylin/tree/cf2f27a3
Diff: http://git-wip-us.apache.org/repos/asf/kylin/diff/cf2f27a3

Branch: refs/heads/master
Commit: cf2f27a3a8ffc41d5d909ff2b4fd38887305fe9d
Parents: 071f3b9
Author: Hongbin Ma <ma...@apache.org>
Authored: Thu Jun 22 14:52:28 2017 +0800
Committer: Hongbin Ma <ma...@kyligence.io>
Committed: Thu Jun 22 18:26:32 2017 +0800

----------------------------------------------------------------------
 .../rest/controller2/QueryControllerV2.java     |   6 +
 .../security/KylinAuthenticationProvider.java   |  16 +-
 .../kylin/rest/security/LdapProvider.java       | 107 ---------
 .../apache/kylin/rest/security/ManagedUser.java | 235 +++++++++++++++++++
 .../rest/service/AclTableMigrationTool.java     |  64 ++---
 .../rest/service/UserGrantedAuthority.java      |   5 +
 .../org/apache/kylin/rest/service/UserInfo.java |  82 -------
 .../apache/kylin/rest/service/UserService.java  |  48 ++--
 server/src/main/resources/kylinSecurity.xml     |   4 +-
 .../rest/controller/UserControllerTest.java     |   4 +-
 .../kylin/rest/service/ServiceTestBase.java     |  15 +-
 .../kylin/rest/service/UserServiceTest.java     |   8 +-
 12 files changed, 318 insertions(+), 276 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kylin/blob/cf2f27a3/server-base/src/main/java/org/apache/kylin/rest/controller2/QueryControllerV2.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller2/QueryControllerV2.java b/server-base/src/main/java/org/apache/kylin/rest/controller2/QueryControllerV2.java
index a641a53..a1b65a0 100644
--- a/server-base/src/main/java/org/apache/kylin/rest/controller2/QueryControllerV2.java
+++ b/server-base/src/main/java/org/apache/kylin/rest/controller2/QueryControllerV2.java
@@ -23,10 +23,13 @@ import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import javax.servlet.http.HttpServletResponse;
 
+import com.google.common.collect.Maps;
 import org.apache.commons.io.IOUtils;
+import org.apache.kylin.common.debug.BackdoorToggles;
 import org.apache.kylin.metadata.querymeta.SelectedColumnMeta;
 import org.apache.kylin.rest.controller.BasicController;
 import org.apache.kylin.rest.exception.InternalErrorException;
@@ -84,6 +87,9 @@ public class QueryControllerV2 extends BasicController {
             "application/vnd.apache.kylin-v2+json" })
     @ResponseBody
     public EnvelopeResponse prepareQueryV2(@RequestBody PrepareSqlRequest sqlRequest) {
+        Map<String, String> toggles = Maps.newHashMap();
+        toggles.put(BackdoorToggles.DEBUG_TOGGLE_PREPARE_ONLY, "true");
+        BackdoorToggles.addToggles(toggles);
 
         return new EnvelopeResponse(ResponseCode.CODE_SUCCESS, queryService.doQueryWithCache(sqlRequest), "");
     }

http://git-wip-us.apache.org/repos/asf/kylin/blob/cf2f27a3/server-base/src/main/java/org/apache/kylin/rest/security/KylinAuthenticationProvider.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/security/KylinAuthenticationProvider.java b/server-base/src/main/java/org/apache/kylin/rest/security/KylinAuthenticationProvider.java
index dc475c9..7322b84 100644
--- a/server-base/src/main/java/org/apache/kylin/rest/security/KylinAuthenticationProvider.java
+++ b/server-base/src/main/java/org/apache/kylin/rest/security/KylinAuthenticationProvider.java
@@ -18,8 +18,6 @@
 
 package org.apache.kylin.rest.security;
 
-import com.google.common.hash.HashFunction;
-import com.google.common.hash.Hashing;
 import org.apache.kylin.common.util.ByteArray;
 import org.apache.kylin.rest.service.UserService;
 import org.slf4j.Logger;
@@ -30,11 +28,13 @@ import org.springframework.security.authentication.AuthenticationProvider;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.core.userdetails.User;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.util.Assert;
 
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hashing;
+
 import net.sf.ehcache.Cache;
 import net.sf.ehcache.CacheManager;
 import net.sf.ehcache.Element;
@@ -87,17 +87,19 @@ public class KylinAuthenticationProvider implements AuthenticationProvider {
 
             logger.debug("Authenticated user " + authed.toString());
 
-            UserDetails user;
+            ManagedUser user;
 
             if (authed.getDetails() == null) {
                 //authed.setAuthenticated(false);
-                throw new UsernameNotFoundException("User not found in LDAP, check whether he/she has been added to the groups.");
+                throw new UsernameNotFoundException(
+                        "User not found in LDAP, check whether he/she has been added to the groups.");
             }
 
             if (authed.getDetails() instanceof UserDetails) {
-                user = (UserDetails) authed.getDetails();
+                UserDetails details = (UserDetails) authed.getDetails();
+                user = new ManagedUser(details.getUsername(), details.getPassword(), false, details.getAuthorities());
             } else {
-                user = new User(authentication.getName(), "skippped-ldap", authed.getAuthorities());
+                user = new ManagedUser(authentication.getName(), "skippped-ldap", false, authed.getAuthorities());
             }
             Assert.notNull(user, "The UserDetail is null.");
 

http://git-wip-us.apache.org/repos/asf/kylin/blob/cf2f27a3/server-base/src/main/java/org/apache/kylin/rest/security/LdapProvider.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/security/LdapProvider.java b/server-base/src/main/java/org/apache/kylin/rest/security/LdapProvider.java
deleted file mode 100644
index c76301b..0000000
--- a/server-base/src/main/java/org/apache/kylin/rest/security/LdapProvider.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-
-import org.apache.kylin.rest.service.UserService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.core.userdetails.User;
-import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
-import org.springframework.security.ldap.authentication.LdapAuthenticator;
-import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.CacheManager;
-import net.sf.ehcache.Element;
-
-/**
- * @author xduo
- * @deprecated replaced by KylinAuthenticationProvider
- * 
- */
-public class LdapProvider extends LdapAuthenticationProvider {
-
-    private static final Logger logger = LoggerFactory.getLogger(LdapProvider.class);
-
-    @Autowired
-    @Qualifier("userService")
-    UserService userService;
-
-    @Autowired
-    private CacheManager cacheManager;
-
-    MessageDigest md = null;
-
-    /**
-     * @param authenticator
-     * @param authoritiesPopulator
-     */
-    public LdapProvider(LdapAuthenticator authenticator, LdapAuthoritiesPopulator authoritiesPopulator) {
-        super(authenticator, authoritiesPopulator);
-
-        try {
-            md = MessageDigest.getInstance("MD5");
-        } catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException("Failed to init Message Digest ", e);
-        }
-    }
-
-    @Override
-    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
-        Authentication authed = null;
-        Cache userCache = cacheManager.getCache("UserCache");
-        md.reset();
-        byte[] hashKey = md.digest((authentication.getName() + authentication.getCredentials()).getBytes());
-        String userKey = Arrays.toString(hashKey);
-
-        Element authedUser = userCache.get(userKey);
-        if (null != authedUser) {
-            authed = (Authentication) authedUser.getObjectValue();
-            SecurityContextHolder.getContext().setAuthentication(authed);
-        } else {
-            try {
-                authed = super.authenticate(authentication);
-                userCache.put(new Element(userKey, authed));
-            } catch (AuthenticationException e) {
-                logger.error("Failed to auth user: " + authentication.getName(), e);
-                throw e;
-            }
-
-            UserDetails user = new User(authentication.getName(), "skippped-ldap", authed.getAuthorities());
-
-            if (!userService.userExists(authentication.getName())) {
-                userService.createUser(user);
-            } else {
-                userService.updateUser(user);
-            }
-        }
-
-        return authed;
-    }
-}

http://git-wip-us.apache.org/repos/asf/kylin/blob/cf2f27a3/server-base/src/main/java/org/apache/kylin/rest/security/ManagedUser.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..4805d5c
--- /dev/null
+++ b/server-base/src/main/java/org/apache/kylin/rest/security/ManagedUser.java
@@ -0,0 +1,235 @@
+/*
+ * 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.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.apache.kylin.common.persistence.RootPersistentEntity;
+import org.apache.kylin.rest.service.UserGrantedAuthority;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+
+@SuppressWarnings("serial")
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class ManagedUser extends RootPersistentEntity implements UserDetails {
+
+    @JsonProperty
+    private String username;
+    @JsonProperty
+    private String password;
+    @JsonProperty
+    private List<String> authorities = Lists.newArrayList();
+    @JsonProperty
+    private boolean disabled = false;
+    @JsonProperty
+    private boolean defaultPassword = false;
+    @JsonProperty
+    private boolean locked = false;
+    @JsonProperty
+    private long lockedTime = 0L;
+    @JsonProperty
+    private int wrongTime = 0;
+
+    private Boolean legacyCatered = false;
+    //DISABLED_ROLE is a ancient way to represent disabled user
+    //now we no longer support such way, however legacy metadata may still contain it
+    private static final String DISABLED_ROLE = "--disabled--";
+
+    //this is computed
+    private List<UserGrantedAuthority> grantedAuthorities = null;
+
+    public ManagedUser() {
+    }
+
+    public ManagedUser(String username, String password, Boolean defaultPassword, String... authorities) {
+        this.username = username;
+        this.password = password;
+        this.setDefaultPassword(defaultPassword);
+
+        this.authorities = Lists.newArrayList(authorities);
+        this.grantedAuthorities = null;
+    }
+
+    public ManagedUser(String username, String password, Boolean defaultPassword,
+            Collection<? extends GrantedAuthority> grantedAuthorities) {
+        this.username = username;
+        this.password = password;
+        this.setDefaultPassword(defaultPassword);
+
+        this.setGrantedAuthorities(grantedAuthorities);
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String userName) {
+        this.username = userName;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    private void caterLegacy() {
+        if (!legacyCatered) {
+            synchronized (legacyCatered) {
+                Iterator<String> iterator = authorities.iterator();
+                while (iterator.hasNext()) {
+                    if (DISABLED_ROLE.equals(iterator.next())) {
+                        iterator.remove();
+                        this.disabled = true;
+                    }
+                }
+                legacyCatered = true;
+            }
+        }
+    }
+
+    public List<UserGrantedAuthority> getAuthorities() {
+        caterLegacy();
+        if (grantedAuthorities == null) {
+            grantedAuthorities = Lists.newArrayList();
+            for (String a : authorities) {
+                this.grantedAuthorities.add(new UserGrantedAuthority(a));
+            }
+        }
+        return grantedAuthorities;
+    }
+
+    public void setGrantedAuthorities(Collection<? extends GrantedAuthority> grantedAuthorities) {
+        this.authorities = Lists
+                .newArrayList(Collections2.transform(grantedAuthorities, new Function<GrantedAuthority, String>() {
+                    @Nullable
+                    @Override
+                    public String apply(@Nullable GrantedAuthority input) {
+                        return input.getAuthority();
+                    }
+                }));
+        this.grantedAuthorities = null;
+    }
+
+    public boolean isDisabled() {
+        caterLegacy();
+        return disabled;
+    }
+
+    public void setDisabled(boolean disabled) {
+        this.disabled = disabled;
+    }
+
+    public boolean isDefaultPassword() {
+        return defaultPassword;
+    }
+
+    public void setDefaultPassword(boolean defaultPassword) {
+        this.defaultPassword = defaultPassword;
+    }
+
+    public boolean isLocked() {
+        return locked;
+    }
+
+    public void setLocked(boolean locked) {
+        this.locked = locked;
+    }
+
+    public int getWrongTime() {
+        return wrongTime;
+    }
+
+    public long getLockedTime() {
+        return lockedTime;
+    }
+
+    public void increaseWrongTime() {
+        int wrongTime = this.getWrongTime();
+        if (wrongTime == 2) {
+            this.setLocked(true);
+            this.lockedTime = System.currentTimeMillis();
+            this.wrongTime = 0;
+        } else {
+            this.wrongTime = wrongTime + 1;
+        }
+    }
+
+    @Override
+    public boolean isAccountNonExpired() {
+        return true;
+    }
+
+    @Override
+    public boolean isAccountNonLocked() {
+        return !locked;
+    }
+
+    @Override
+    public boolean isCredentialsNonExpired() {
+        return true;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return !disabled;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((username == null) ? 0 : username.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        ManagedUser other = (ManagedUser) obj;
+        if (username == null) {
+            if (other.username != null)
+                return false;
+        } else if (!username.equals(other.username))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "KapManagedUser [username=" + username + ", authorities=" + grantedAuthorities + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/kylin/blob/cf2f27a3/server-base/src/main/java/org/apache/kylin/rest/service/AclTableMigrationTool.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/AclTableMigrationTool.java b/server-base/src/main/java/org/apache/kylin/rest/service/AclTableMigrationTool.java
index fc50410..64bac23 100644
--- a/server-base/src/main/java/org/apache/kylin/rest/service/AclTableMigrationTool.java
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/AclTableMigrationTool.java
@@ -19,7 +19,6 @@
 package org.apache.kylin.rest.service;
 
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -40,6 +39,7 @@ import org.apache.kylin.common.persistence.ResourceStore;
 import org.apache.kylin.common.persistence.StringEntity;
 import org.apache.kylin.common.util.Bytes;
 import org.apache.kylin.rest.security.AclConstant;
+import org.apache.kylin.rest.security.ManagedUser;
 import org.apache.kylin.rest.util.Serializer;
 import org.apache.kylin.storage.hbase.HBaseConnection;
 import org.apache.kylin.storage.hbase.HBaseResourceStore;
@@ -53,11 +53,13 @@ public class AclTableMigrationTool {
 
     private static final Serializer<SidInfo> sidSerializer = new Serializer<SidInfo>(SidInfo.class);
 
-    private static final Serializer<DomainObjectInfo> domainObjSerializer = new Serializer<DomainObjectInfo>(DomainObjectInfo.class);
+    private static final Serializer<DomainObjectInfo> domainObjSerializer = new Serializer<DomainObjectInfo>(
+            DomainObjectInfo.class);
 
     private static final Serializer<AceInfo> aceSerializer = new Serializer<AceInfo>(AceInfo.class);
 
-    private static final Serializer<UserGrantedAuthority[]> ugaSerializer = new Serializer<UserGrantedAuthority[]>(UserGrantedAuthority[].class);
+    private static final Serializer<UserGrantedAuthority[]> ugaSerializer = new Serializer<UserGrantedAuthority[]>(
+            UserGrantedAuthority[].class);
 
     public static final String MIGRATE_OK_PREFIX = "MIGRATE_OK_";
 
@@ -69,7 +71,8 @@ public class AclTableMigrationTool {
             return;
         } else {
             if (!kylinConfig.getServerMode().equals("all")) {
-                throw new IllegalStateException("Please make sure that you have config kylin.server.mode=all before migrating data");
+                throw new IllegalStateException(
+                        "Please make sure that you have config kylin.server.mode=all before migrating data");
             }
             logger.info("Start to migrate acl table data");
             ResourceStore store = ResourceStore.getStore(kylinConfig);
@@ -144,11 +147,9 @@ public class AclTableMigrationTool {
                         return;
                     Result result = rs.next();
                     while (result != null) {
-                        User user = hbaseRowToUser(result);
-                        UserInfo userInfo = convert(user);
-                        store.deleteResource(UserService.getId(userInfo.getUsername()));
-                        store.putResource(UserService.getId(userInfo.getUsername()), userInfo, 0,
-                                UserService.SERIALIZER);
+                        ManagedUser user = hbaseRowToUser(result);
+                        store.deleteResource(UserService.getId(user.getUsername()));
+                        store.putResource(UserService.getId(user.getUsername()), user, 0, UserService.SERIALIZER);
                         result = rs.next();
                     }
                 }
@@ -172,7 +173,8 @@ public class AclTableMigrationTool {
         return store.exists(MIGRATE_OK_PREFIX + tableName);
     }
 
-    private void convertToResourceStore(KylinConfig kylinConfig, String tableName, ResourceStore store, ResultConverter converter) throws IOException {
+    private void convertToResourceStore(KylinConfig kylinConfig, String tableName, ResourceStore store,
+            ResultConverter converter) throws IOException {
 
         Table table = null;
         ResultScanner rs = null;
@@ -181,7 +183,8 @@ public class AclTableMigrationTool {
             table = HBaseConnection.get(kylinConfig.getStorageUrl()).getTable(TableName.valueOf(tableName));
             rs = table.getScanner(scan);
             converter.convertResult(rs, store);
-            store.putResource(MIGRATE_OK_PREFIX + tableName, new StringEntity(tableName + " migrated"), StringEntity.serializer);
+            store.putResource(MIGRATE_OK_PREFIX + tableName, new StringEntity(tableName + " migrated"),
+                    StringEntity.serializer);
         } finally {
             IOUtils.closeQuietly(rs);
             IOUtils.closeQuietly(table);
@@ -190,7 +193,8 @@ public class AclTableMigrationTool {
     }
 
     private DomainObjectInfo getDomainObjectInfoFromRs(Result result) {
-        String type = String.valueOf(result.getValue(Bytes.toBytes(AclConstant.ACL_INFO_FAMILY), Bytes.toBytes(AclConstant.ACL_INFO_FAMILY_TYPE_COLUMN)));
+        String type = String.valueOf(result.getValue(Bytes.toBytes(AclConstant.ACL_INFO_FAMILY),
+                Bytes.toBytes(AclConstant.ACL_INFO_FAMILY_TYPE_COLUMN)));
         String id = new String(result.getRow());
         DomainObjectInfo newInfo = new DomainObjectInfo();
         newInfo.setId(id);
@@ -199,17 +203,20 @@ public class AclTableMigrationTool {
     }
 
     private DomainObjectInfo getParentDomainObjectInfoFromRs(Result result) throws IOException {
-        DomainObjectInfo parentInfo = domainObjSerializer.deserialize(result.getValue(Bytes.toBytes(AclConstant.ACL_INFO_FAMILY), Bytes.toBytes(AclConstant.ACL_INFO_FAMILY_PARENT_COLUMN)));
+        DomainObjectInfo parentInfo = domainObjSerializer.deserialize(result.getValue(
+                Bytes.toBytes(AclConstant.ACL_INFO_FAMILY), Bytes.toBytes(AclConstant.ACL_INFO_FAMILY_PARENT_COLUMN)));
         return parentInfo;
     }
 
     private boolean getInheriting(Result result) {
-        boolean entriesInheriting = Bytes.toBoolean(result.getValue(Bytes.toBytes(AclConstant.ACL_INFO_FAMILY), Bytes.toBytes(AclConstant.ACL_INFO_FAMILY_ENTRY_INHERIT_COLUMN)));
+        boolean entriesInheriting = Bytes.toBoolean(result.getValue(Bytes.toBytes(AclConstant.ACL_INFO_FAMILY),
+                Bytes.toBytes(AclConstant.ACL_INFO_FAMILY_ENTRY_INHERIT_COLUMN)));
         return entriesInheriting;
     }
 
     private SidInfo getOwnerSidInfo(Result result) throws IOException {
-        SidInfo owner = sidSerializer.deserialize(result.getValue(Bytes.toBytes(AclConstant.ACL_INFO_FAMILY), Bytes.toBytes(AclConstant.ACL_INFO_FAMILY_OWNER_COLUMN)));
+        SidInfo owner = sidSerializer.deserialize(result.getValue(Bytes.toBytes(AclConstant.ACL_INFO_FAMILY),
+                Bytes.toBytes(AclConstant.ACL_INFO_FAMILY_OWNER_COLUMN)));
         return owner;
     }
 
@@ -226,27 +233,14 @@ public class AclTableMigrationTool {
         return allAceInfoMap;
     }
 
-    private UserInfo convert(User user) {
-        if (user == null)
-            return null;
-        UserInfo newInfo = new UserInfo();
-        newInfo.setUsername(user.getUserName());
-        newInfo.setPassword(user.getPassword());
-        List<String> authorities = new ArrayList<>();
-        for (String auth : user.getAuthorities()) {
-            authorities.add(auth);
-        }
-        newInfo.setAuthorities(authorities);
-        return newInfo;
-    }
-
-    private User hbaseRowToUser(Result result) throws JsonParseException, JsonMappingException, IOException {
+    private ManagedUser hbaseRowToUser(Result result) throws JsonParseException, JsonMappingException, IOException {
         if (null == result || result.isEmpty())
             return null;
 
         String username = Bytes.toString(result.getRow());
 
-        byte[] valueBytes = result.getValue(Bytes.toBytes(AclConstant.USER_AUTHORITY_FAMILY), Bytes.toBytes(AclConstant.USER_AUTHORITY_COLUMN));
+        byte[] valueBytes = result.getValue(Bytes.toBytes(AclConstant.USER_AUTHORITY_FAMILY),
+                Bytes.toBytes(AclConstant.USER_AUTHORITY_COLUMN));
         UserGrantedAuthority[] deserialized = ugaSerializer.deserialize(valueBytes);
 
         String password = "";
@@ -261,13 +255,7 @@ public class AclTableMigrationTool {
                 authorities = Arrays.asList(deserialized);
             }
         }
-        List<String> authoritiesStr = new ArrayList<>();
-        for (UserGrantedAuthority auth : authorities) {
-            if (auth != null) {
-                authoritiesStr.add(auth.getAuthority());
-            }
-        }
-        return new User(username, password, authoritiesStr);
+        return new ManagedUser(username, password, false, authorities);
     }
 
     interface ResultConverter {

http://git-wip-us.apache.org/repos/asf/kylin/blob/cf2f27a3/server-base/src/main/java/org/apache/kylin/rest/service/UserGrantedAuthority.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/UserGrantedAuthority.java b/server-base/src/main/java/org/apache/kylin/rest/service/UserGrantedAuthority.java
index 4c2a392..1227177 100644
--- a/server-base/src/main/java/org/apache/kylin/rest/service/UserGrantedAuthority.java
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/UserGrantedAuthority.java
@@ -20,10 +20,15 @@ package org.apache.kylin.rest.service;
 
 import org.springframework.security.core.GrantedAuthority;
 
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
 
+@SuppressWarnings("serial")
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
 public class UserGrantedAuthority implements GrantedAuthority {
     private static final long serialVersionUID = -5128905636841891058L;
 
+    @JsonProperty
     private String authority;
 
     public UserGrantedAuthority() {

http://git-wip-us.apache.org/repos/asf/kylin/blob/cf2f27a3/server-base/src/main/java/org/apache/kylin/rest/service/UserInfo.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/UserInfo.java b/server-base/src/main/java/org/apache/kylin/rest/service/UserInfo.java
deleted file mode 100644
index 644883d..0000000
--- a/server-base/src/main/java/org/apache/kylin/rest/service/UserInfo.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.service;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.kylin.common.persistence.RootPersistentEntity;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.userdetails.UserDetails;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-
-@SuppressWarnings("serial")
-public class UserInfo extends RootPersistentEntity {
-
-    @JsonProperty()
-    private String username;
-    @JsonProperty()
-    private String password;
-    @JsonProperty()
-    private List<String> authorities = new ArrayList<>();
-
-    public UserInfo(String username, String password, List<String> authorities) {
-        this.username = username;
-        this.password = password;
-        this.authorities = authorities;
-    }
-
-    public UserInfo(UserDetails user) {
-        this.username = user.getUsername();
-        this.password = user.getPassword();
-        for (GrantedAuthority a : user.getAuthorities()) {
-            this.authorities.add(a.getAuthority());
-        }
-    }
-
-    public UserInfo() {
-    }
-
-    public String getUsername() {
-        return username;
-    }
-
-    public void setUsername(String username) {
-        this.username = username;
-    }
-
-    public String getPassword() {
-        return password;
-    }
-
-    public void setPassword(String password) {
-        this.password = password;
-    }
-
-    public List<String> getAuthorities() {
-        return authorities;
-    }
-
-    public void setAuthorities(List<String> authorities) {
-        this.authorities = authorities;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/kylin/blob/cf2f27a3/server-base/src/main/java/org/apache/kylin/rest/service/UserService.java
----------------------------------------------------------------------
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 e803040..504c035 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
@@ -31,15 +31,17 @@ import org.apache.kylin.common.persistence.Serializer;
 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.ManagedUser;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.userdetails.User;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.security.provisioning.UserDetailsManager;
 import org.springframework.stereotype.Component;
 
+import com.google.common.base.Preconditions;
+
 /**
  */
 @Component("userService")
@@ -49,7 +51,7 @@ public class UserService implements UserDetailsManager {
 
     public static final String DIR_PREFIX = "/user/";
 
-    public static final Serializer<UserInfo> SERIALIZER = new JsonSerializer<>(UserInfo.class);
+    public static final Serializer<ManagedUser> SERIALIZER = new JsonSerializer<>(ManagedUser.class);
 
     protected ResourceStore aclStore;
 
@@ -67,11 +69,13 @@ public class UserService implements UserDetailsManager {
     @Override
     //@PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN) --- DON'T DO THIS, CAUSES CIRCULAR DEPENDENCY BETWEEN UserService & AclService
     public void updateUser(UserDetails user) {
+        Preconditions.checkState(user instanceof ManagedUser, "User {} is not ManagedUser", user);
+        ManagedUser managedUser = (ManagedUser) user;
         try {
             deleteUser(user.getUsername());
             String id = getId(user.getUsername());
-            aclStore.putResource(id, new UserInfo(user), 0, SERIALIZER);
-            logger.debug("update user : {}", user.getUsername());
+            aclStore.putResource(id, managedUser, 0, SERIALIZER);
+            logger.trace("update user : {}", user.getUsername());
         } catch (IOException e) {
             throw new InternalErrorException(e);
         }
@@ -82,7 +86,7 @@ public class UserService implements UserDetailsManager {
         try {
             String id = getId(userName);
             aclStore.deleteResource(id);
-            logger.debug("delete user : {}", userName);
+            logger.trace("delete user : {}", userName);
         } catch (IOException e) {
             throw new InternalErrorException(e);
         }
@@ -96,23 +100,27 @@ public class UserService implements UserDetailsManager {
     @Override
     public boolean userExists(String userName) {
         try {
-            logger.debug("judge user exist: {}", userName);
+            logger.trace("judge user exist: {}", userName);
             return aclStore.exists(getId(userName));
         } catch (IOException e) {
             throw new InternalErrorException(e);
         }
     }
 
+    /**
+     * 
+     * @return a ManagedUser
+     */
     @Override
     public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
         Message msg = MsgPicker.getMsg();
         try {
-            UserInfo userInfo = aclStore.getResource(getId(userName), UserInfo.class, SERIALIZER);
-            if (userInfo == null) {
+            ManagedUser managedUser = aclStore.getResource(getId(userName), ManagedUser.class, SERIALIZER);
+            if (managedUser == null) {
                 throw new UsernameNotFoundException(String.format(msg.getUSER_NOT_FOUND(), userName));
             }
-            logger.debug("load user : {}", userName);
-            return wrap(userInfo);
+            logger.trace("load user : {}", userName);
+            return managedUser;
         } catch (IOException e) {
             throw new InternalErrorException(e);
         }
@@ -130,28 +138,12 @@ public class UserService implements UserDetailsManager {
         return all;
     }
 
-    public List<UserDetails> listUsers() throws IOException {
-        List<UserDetails> all = new ArrayList<UserDetails>();
-        List<UserInfo> userInfos = aclStore.getAllResources(DIR_PREFIX, UserInfo.class, SERIALIZER);
-        for (UserInfo info : userInfos) {
-            all.add(wrap(info));
-        }
-        return all;
+    public List<ManagedUser> listUsers() throws IOException {
+        return aclStore.getAllResources(DIR_PREFIX, ManagedUser.class, SERIALIZER);
     }
 
     public static String getId(String userName) {
         return DIR_PREFIX + userName;
     }
 
-    protected User wrap(UserInfo userInfo) {
-        if (userInfo == null)
-            return null;
-        List<GrantedAuthority> authorities = new ArrayList<>();
-        List<String> auths = userInfo.getAuthorities();
-        for (String str : auths) {
-            authorities.add(new UserGrantedAuthority(str));
-        }
-        return new User(userInfo.getUsername(), userInfo.getPassword(), authorities);
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/kylin/blob/cf2f27a3/server/src/main/resources/kylinSecurity.xml
----------------------------------------------------------------------
diff --git a/server/src/main/resources/kylinSecurity.xml b/server/src/main/resources/kylinSecurity.xml
index 039bded..53ed511 100644
--- a/server/src/main/resources/kylinSecurity.xml
+++ b/server/src/main/resources/kylinSecurity.xml
@@ -246,7 +246,7 @@
             <scr:intercept-url pattern="/api/admin*/**" access="hasRole('ROLE_ADMIN')"/>
             <scr:intercept-url pattern="/api/**" access="isAuthenticated()"/>
 
-            <scr:logout invalidate-session="true" delete-cookies="JSESSIONID"/>
+            <scr:logout invalidate-session="true" delete-cookies="JSESSIONID" logout-url="/j_spring_security_logout" logout-success-url="/." />
             <scr:session-management session-fixation-protection="newSession"/>
         </scr:http>
     </beans>
@@ -288,7 +288,7 @@
             <scr:intercept-url pattern="/api/admin*/**" access="hasRole('ROLE_ADMIN')"/>
             <scr:intercept-url pattern="/api/**" access="isAuthenticated()"/>
 
-            <scr:logout invalidate-session="true" delete-cookies="JSESSIONID"/>
+            <scr:logout invalidate-session="true" delete-cookies="JSESSIONID" logout-url="/j_spring_security_logout" logout-success-url="/." />
             <scr:session-management session-fixation-protection="newSession"/>
         </scr:http>
 

http://git-wip-us.apache.org/repos/asf/kylin/blob/cf2f27a3/server/src/test/java/org/apache/kylin/rest/controller/UserControllerTest.java
----------------------------------------------------------------------
diff --git a/server/src/test/java/org/apache/kylin/rest/controller/UserControllerTest.java b/server/src/test/java/org/apache/kylin/rest/controller/UserControllerTest.java
index 767aaf1..f6b4ae1 100644
--- a/server/src/test/java/org/apache/kylin/rest/controller/UserControllerTest.java
+++ b/server/src/test/java/org/apache/kylin/rest/controller/UserControllerTest.java
@@ -23,6 +23,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.kylin.rest.constant.Constant;
+import org.apache.kylin.rest.security.ManagedUser;
 import org.apache.kylin.rest.service.ServiceTestBase;
 import org.junit.Assert;
 import org.junit.Before;
@@ -32,7 +33,6 @@ import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.core.userdetails.User;
 import org.springframework.security.core.userdetails.UserDetails;
 
 /**
@@ -46,7 +46,7 @@ public class UserControllerTest extends ServiceTestBase {
     public static void setupResource() {
         staticCreateTestMetadata();
         List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
-        User user = new User("ADMIN", "ADMIN", authorities);
+        ManagedUser user = new ManagedUser("ADMIN", "ADMIN", false, authorities);
         Authentication authentication = new TestingAuthenticationToken(user, "ADMIN", Constant.ROLE_ADMIN);
         SecurityContextHolder.getContext().setAuthentication(authentication);
     }

http://git-wip-us.apache.org/repos/asf/kylin/blob/cf2f27a3/server/src/test/java/org/apache/kylin/rest/service/ServiceTestBase.java
----------------------------------------------------------------------
diff --git a/server/src/test/java/org/apache/kylin/rest/service/ServiceTestBase.java b/server/src/test/java/org/apache/kylin/rest/service/ServiceTestBase.java
index b45b27b..1d60a53 100644
--- a/server/src/test/java/org/apache/kylin/rest/service/ServiceTestBase.java
+++ b/server/src/test/java/org/apache/kylin/rest/service/ServiceTestBase.java
@@ -24,6 +24,7 @@ import org.apache.kylin.common.KylinConfig;
 import org.apache.kylin.common.util.LocalFileMetadataTestCase;
 import org.apache.kylin.metadata.cachesync.Broadcaster;
 import org.apache.kylin.rest.constant.Constant;
+import org.apache.kylin.rest.security.ManagedUser;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
@@ -35,7 +36,6 @@ import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.core.userdetails.User;
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -71,18 +71,19 @@ public class ServiceTestBase extends LocalFileMetadataTestCase {
         Broadcaster.getInstance(config).notifyClearAll();
 
         if (!userService.userExists("ADMIN")) {
-            userService.createUser(new User("ADMIN", "KYLIN", Arrays.asList(//
-                new UserGrantedAuthority(Constant.ROLE_ADMIN), new UserGrantedAuthority(Constant.ROLE_ANALYST), new UserGrantedAuthority(Constant.ROLE_MODELER))));
+            userService.createUser(new ManagedUser("ADMIN", "KYLIN", false, Arrays.asList(//
+                    new UserGrantedAuthority(Constant.ROLE_ADMIN), new UserGrantedAuthority(Constant.ROLE_ANALYST),
+                    new UserGrantedAuthority(Constant.ROLE_MODELER))));
         }
 
         if (!userService.userExists("MODELER")) {
-            userService.createUser(new User("MODELER", "MODELER", Arrays.asList(//
-                new UserGrantedAuthority(Constant.ROLE_ANALYST), new UserGrantedAuthority(Constant.ROLE_MODELER))));
+            userService.createUser(new ManagedUser("MODELER", "MODELER", false, Arrays.asList(//
+                    new UserGrantedAuthority(Constant.ROLE_ANALYST), new UserGrantedAuthority(Constant.ROLE_MODELER))));
         }
 
         if (!userService.userExists("ANALYST")) {
-            userService.createUser(new User("ANALYST", "ANALYST", Arrays.asList(//
-                new UserGrantedAuthority(Constant.ROLE_ANALYST))));
+            userService.createUser(new ManagedUser("ANALYST", "ANALYST", false, Arrays.asList(//
+                    new UserGrantedAuthority(Constant.ROLE_ANALYST))));
         }
     }
 

http://git-wip-us.apache.org/repos/asf/kylin/blob/cf2f27a3/server/src/test/java/org/apache/kylin/rest/service/UserServiceTest.java
----------------------------------------------------------------------
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 0d4b580..c49b552 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
@@ -23,13 +23,13 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.kylin.rest.constant.Constant;
+import org.apache.kylin.rest.security.ManagedUser;
 import org.junit.Assert;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.security.core.userdetails.User;
 import org.springframework.security.core.userdetails.UserDetails;
 
 /**
@@ -44,11 +44,12 @@ public class UserServiceTest extends ServiceTestBase {
     @Test
     public void testBasics() throws IOException {
         userService.deleteUser("ADMIN");
+
         Assert.assertTrue(!userService.userExists("ADMIN"));
 
         List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
         authorities.add(new SimpleGrantedAuthority(Constant.ROLE_ADMIN));
-        User user = new User("ADMIN", "PWD", authorities);
+        ManagedUser user = new ManagedUser("ADMIN", "PWD", false, authorities);
         userService.createUser(user);
 
         Assert.assertTrue(userService.userExists("ADMIN"));
@@ -59,7 +60,8 @@ public class UserServiceTest extends ServiceTestBase {
         Assert.assertEquals(Constant.ROLE_ADMIN, ud.getAuthorities().iterator().next().getAuthority());
         Assert.assertEquals(1, ud.getAuthorities().size());
 
-        Assert.assertTrue(userService.listUserAuthorities().contains(Constant.ROLE_ADMIN));
+        List<String> strings = userService.listUserAuthorities();
+        Assert.assertTrue(strings.contains(Constant.ROLE_ADMIN));
     }
 
 }