PostgreSQLAuthenticationHandler.java 3.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * 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.shardingsphere.proxy.frontend.postgresql.auth;

20
import com.google.common.base.Strings;
21 22
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
23
import org.apache.commons.collections4.CollectionUtils;
24 25 26 27 28
import org.apache.shardingsphere.db.protocol.postgresql.constant.PostgreSQLErrorCode;
import org.apache.shardingsphere.db.protocol.postgresql.packet.handshake.PostgreSQLPasswordMessagePacket;
import org.apache.shardingsphere.infra.auth.ProxyUser;
import org.apache.shardingsphere.proxy.backend.schema.ProxySchemaContexts;

29 30 31 32
import java.security.MessageDigest;
import java.util.Collection;
import java.util.Map;

33 34 35 36 37 38 39 40
/**
 * Authentication handler for PostgreSQL.
 */
public class PostgreSQLAuthenticationHandler {
    
    /**
     * Login.
     *
41 42 43
     * @param username username
     * @param databaseName database name
     * @param md5Salt MD5 salt
44
     * @param passwordMessagePacket password message packet
45
     * @return PostgreSQL login result
46
     */
47
    public static PostgreSQLLoginResult loginWithMd5Password(final String username, final String databaseName, final byte[] md5Salt, final PostgreSQLPasswordMessagePacket passwordMessagePacket) {
48 49
        ProxyUser proxyUser = null;
        for (Map.Entry<String, ProxyUser> entry : ProxySchemaContexts.getInstance().getSchemaContexts().getAuthentication().getUsers().entrySet()) {
50
            if (entry.getKey().equals(username)) {
51 52 53 54 55
                proxyUser = entry.getValue();
                break;
            }
        }
        if (null == proxyUser) {
56
            return new PostgreSQLLoginResult(PostgreSQLErrorCode.INVALID_AUTHORIZATION_SPECIFICATION, "unknown username: " + username);
57 58
        }
        String md5Digest = passwordMessagePacket.getMd5Digest();
59
        String expectedMd5Digest = md5Encode(username, proxyUser.getPassword(), md5Salt);
60
        if (!expectedMd5Digest.equals(md5Digest)) {
61
            return new PostgreSQLLoginResult(PostgreSQLErrorCode.INVALID_PASSWORD, "password authentication failed for user \"" + username + "\"");
62
        }
63
        if (!isAuthorizedSchema(proxyUser.getAuthorizedSchemas(), databaseName)) {
64
            return new PostgreSQLLoginResult(PostgreSQLErrorCode.PRIVILEGE_NOT_GRANTED, String.format("Access denied for user '%s' to database '%s'", username, databaseName));
65 66 67 68
        }
        return new PostgreSQLLoginResult(PostgreSQLErrorCode.SUCCESSFUL_COMPLETION, null);
    }
    
69 70
    private static String md5Encode(final String username, final String password, final byte[] md5Salt) {
        String passwordHash = new String(Hex.encodeHex(DigestUtils.md5(password + username), true));
71 72 73 74 75 76
        MessageDigest messageDigest = DigestUtils.getMd5Digest();
        messageDigest.update(passwordHash.getBytes());
        messageDigest.update(md5Salt);
        return "md5" + new String(Hex.encodeHex(messageDigest.digest(), true));
    }
    
77 78 79
    private static boolean isAuthorizedSchema(final Collection<String> authorizedSchemas, final String schema) {
        return Strings.isNullOrEmpty(schema) || CollectionUtils.isEmpty(authorizedSchemas) || authorizedSchemas.contains(schema);
    }
80
}