144 lines
6.9 KiB
Go
144 lines
6.9 KiB
Go
// Copyright 2024 PingCAP, Inc.
|
|
//
|
|
// Licensed 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 extension
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"slices"
|
|
|
|
"github.com/pingcap/errors"
|
|
"github.com/pingcap/tidb/pkg/parser/auth"
|
|
"github.com/pingcap/tidb/pkg/parser/mysql"
|
|
"github.com/pingcap/tidb/pkg/privilege/conn"
|
|
)
|
|
|
|
// AuthPlugin contains attributes needed for an authentication plugin.
|
|
type AuthPlugin struct {
|
|
// Name is the name of the auth plugin. It will be registered as a system variable in TiDB which can be used inside the `CREATE USER ... IDENTIFIED WITH 'plugin_name'` statement.
|
|
Name string
|
|
|
|
// RequiredClientSidePlugin is the name of the client-side plugin required by the server-side plugin. It will be used to check if the client has the required plugin installed and require the client to use it if installed.
|
|
// The user can require default MySQL plugins such as 'caching_sha2_password' or 'mysql_native_password'.
|
|
// If this is empty then `AuthPlugin.Name` is used as the required client-side plugin.
|
|
RequiredClientSidePlugin string
|
|
|
|
// AuthenticateUser is called when a client connects to the server as a user and the server authenticates the user.
|
|
// If an error is returned, the login attempt fails, otherwise it succeeds.
|
|
// request: The request context for the authentication plugin to authenticate a user
|
|
AuthenticateUser func(request AuthenticateRequest) error
|
|
|
|
// GenerateAuthString is a function for user to implement customized ways to encode the password (e.g. hash/salt/clear-text). The returned string will be stored as the encoded password in the mysql.user table.
|
|
// If the input password is considered as invalid, this should return an error.
|
|
// pwd: User's input password in CREATE/ALTER USER statements in clear-text
|
|
GenerateAuthString func(pwd string) (string, bool)
|
|
|
|
// ValidateAuthString checks if the password hash stored in the mysql.user table or passed in from `IDENTIFIED AS` is valid.
|
|
// This is called when retrieving an existing user to make sure the password stored is valid and not modified and make sure user is passing a valid password hash in `IDENTIFIED AS`.
|
|
// pwdHash: hash of the password stored in the internal user table
|
|
ValidateAuthString func(pwdHash string) bool
|
|
|
|
// VerifyPrivilege is called for each user queries, and serves as an extra check for privileges for the user.
|
|
// It will only be executed if the user has already been granted the privilege in SQL layer.
|
|
// Returns true if user has the requested privilege.
|
|
// request: The request context for the authorization plugin to authorize a user's static privilege
|
|
VerifyPrivilege func(request VerifyStaticPrivRequest) bool
|
|
|
|
// VerifyDynamicPrivilege is called for each user queries, and serves as an extra check for dynamic privileges for the user.
|
|
// It will only be executed if the user has already been granted the dynamic privilege in SQL layer.
|
|
// Returns true if user has the requested privilege.
|
|
// request: The request context for the authorization plugin to authorize a user's dynamic privilege
|
|
VerifyDynamicPrivilege func(request VerifyDynamicPrivRequest) bool
|
|
}
|
|
|
|
// AuthenticateRequest contains the context for the authentication plugin to authenticate a user.
|
|
type AuthenticateRequest struct {
|
|
// User The username in the connect attempt
|
|
User string
|
|
// StoredAuthString The user's auth string stored in mysql.user table
|
|
StoredAuthString string
|
|
// InputAuthString The user's auth string passed in from the connection attempt in bytes
|
|
InputAuthString []byte
|
|
// Salt Randomly generated salt for the current connection
|
|
Salt []byte
|
|
// ConnState The TLS connection state (contains the TLS certificate) if client is using TLS. It will be nil if the client is not using TLS
|
|
ConnState *tls.ConnectionState
|
|
// AuthConn Interface for the plugin to communicate with the client
|
|
AuthConn conn.AuthConn
|
|
}
|
|
|
|
// VerifyStaticPrivRequest contains the context for the plugin to authorize a user's static privilege.
|
|
type VerifyStaticPrivRequest struct {
|
|
// User The username in the connect attempt
|
|
User string
|
|
// Host The host that the user is connecting from
|
|
Host string
|
|
// DB The database to check for privilege
|
|
DB string
|
|
// Table The table to check for privilege
|
|
Table string
|
|
// Column The column to check for privilege (currently just a placeholder in TiDB as column-level privilege is not supported by TiDB yet)
|
|
Column string
|
|
// StaticPriv The privilege type of the SQL statement that will be executed
|
|
StaticPriv mysql.PrivilegeType
|
|
// ConnState The TLS connection state (contains the TLS certificate) if client is using TLS. It will be nil if the client is not using TLS
|
|
ConnState *tls.ConnectionState
|
|
// ActiveRoles List of active MySQL roles for the current user
|
|
ActiveRoles []*auth.RoleIdentity
|
|
}
|
|
|
|
// VerifyDynamicPrivRequest contains the context for the plugin to authorize a user's dynamic privilege.
|
|
type VerifyDynamicPrivRequest struct {
|
|
// User The username in the connect attempt
|
|
User string
|
|
// Host The host that the user is connecting from
|
|
Host string
|
|
// DynamicPriv the dynamic privilege required by the user's SQL statement
|
|
DynamicPriv string
|
|
// ConnState The TLS connection state (contains the TLS certificate) if client is using TLS. It will be nil if the client is not using TLS
|
|
ConnState *tls.ConnectionState
|
|
// ActiveRoles List of active MySQL roles for the current user
|
|
ActiveRoles []*auth.RoleIdentity
|
|
// WithGrant Whether the statement to be executed is granting the user privilege for executing GRANT statements
|
|
WithGrant bool
|
|
}
|
|
|
|
// validateAuthPlugin validates the auth plugin functions and attributes.
|
|
func validateAuthPlugin(m *Manifest) error {
|
|
pluginNames := make(map[string]bool)
|
|
// Validate required functions for the auth plugins
|
|
for _, p := range m.authPlugins {
|
|
if p.Name == "" {
|
|
return errors.Errorf("auth plugin name cannot be empty for %s", p.Name)
|
|
}
|
|
if pluginNames[p.Name] {
|
|
return errors.Errorf("auth plugin name %s has already been registered", p.Name)
|
|
}
|
|
pluginNames[p.Name] = true
|
|
if slices.Contains(mysql.DefaultAuthPlugins, p.Name) {
|
|
return errors.Errorf("auth plugin name %s is a reserved name for default auth plugins", p.Name)
|
|
}
|
|
if p.AuthenticateUser == nil {
|
|
return errors.Errorf("auth plugin AuthenticateUser function cannot be nil for %s", p.Name)
|
|
}
|
|
if p.GenerateAuthString == nil {
|
|
return errors.Errorf("auth plugin GenerateAuthString function cannot be nil for %s", p.Name)
|
|
}
|
|
if p.ValidateAuthString == nil {
|
|
return errors.Errorf("auth plugin ValidateAuthString function cannot be nil for %s", p.Name)
|
|
}
|
|
}
|
|
return nil
|
|
}
|