| // Go MySQL Driver - A MySQL-Driver for Go's database/sql package |
| // |
| // Copyright 2023 The Go-MySQL-Driver Authors. All rights reserved. |
| // |
| // This Source Code Form is subject to the terms of the Mozilla Public |
| // License, v. 2.0. If a copy of the MPL was not distributed with this file, |
| // You can obtain one at http://mozilla.org/MPL/2.0/. |
| |
| package mysql |
| |
| import ( |
| "context" |
| "sync" |
| ) |
| |
| // AuthPlugin represents an authentication plugin for MySQL/MariaDB. |
| // The driver creates an instance for each authentication exchange and calls its |
| // methods sequentially. ctx is the connection's context and may be canceled |
| // while a method is running. Plugins should use it to cancel blocking work. |
| type AuthPlugin interface { |
| // InitAuth initializes the authentication process and returns the initial response. |
| // authData is the challenge data from the server. It must not be modified |
| // and must be copied if retained after the call. |
| // auth is the snapshot for this exchange, shared with ContinuationAuth. |
| // A nil or empty response sends an empty authentication response. |
| // Plugins must check any transport requirements here before returning credentials. |
| InitAuth(ctx context.Context, authData []byte, auth *AuthContext) ([]byte, error) |
| |
| // ContinuationAuth processes the server's continuation payload after the |
| // driver has handled OK, ERR, and auth switch packets and stripped any |
| // AuthMoreData prefix. authData is the challenge passed to InitAuth. |
| // Neither slice may be modified; copy them if retaining them after the call. |
| // A nil response reads the next server packet without sending. A non-nil |
| // empty response sends an empty packet. An error aborts authentication. |
| // Only a server OK packet completes authentication successfully. |
| ContinuationAuth(ctx context.Context, packet, authData []byte, auth *AuthContext) (nextPacket []byte, err error) |
| } |
| |
| // simpleAuth provides the default continuation behavior for authentication |
| // plugins that complete after their initial response. The driver handles OK, |
| // ERR, and authentication switch packets before dispatching to the plugin, so |
| // any call to ContinuationAuth represents an unexpected packet. |
| type simpleAuth struct{} |
| |
| func (s simpleAuth) ContinuationAuth(ctx context.Context, packet, authData []byte, auth *AuthContext) ([]byte, error) { |
| return nil, ErrMalformPkt |
| } |
| |
| // pluginRegistry is a registry of available authentication plugins |
| type pluginRegistry struct { |
| mu sync.RWMutex |
| plugins map[string]func() AuthPlugin |
| } |
| |
| var globalPluginRegistry = newPluginRegistry() |
| |
| // newPluginRegistry creates a new plugin registry. |
| func newPluginRegistry() *pluginRegistry { |
| registry := &pluginRegistry{ |
| plugins: make(map[string]func() AuthPlugin), |
| } |
| return registry |
| } |
| |
| // Register adds a plugin factory to the registry |
| func (r *pluginRegistry) Register(name string, factory func() AuthPlugin) { |
| if factory == nil { |
| panic("auth plugin factory cannot be nil") |
| } |
| r.mu.Lock() |
| r.plugins[name] = factory |
| r.mu.Unlock() |
| } |
| |
| // GetPlugin returns a new plugin instance for the given name |
| func (r *pluginRegistry) GetPlugin(name string) (AuthPlugin, bool) { |
| r.mu.RLock() |
| factory, ok := r.plugins[name] |
| r.mu.RUnlock() |
| if !ok { |
| return nil, false |
| } |
| p := factory() |
| return p, p != nil |
| } |
| |
| // RegisterAuthPlugin registers a factory for the server's authentication plugin |
| // name, replacing any existing registration for that name, including built-in |
| // plugins. Authentication-method permissions still apply to the registered name. |
| // Registration does not call the factory. The driver calls factory for each |
| // authentication exchange, possibly concurrently for different connections. |
| // The factory must return a new, non-nil AuthPlugin whose mutable state is not |
| // shared with other exchanges. |
| func RegisterAuthPlugin(name string, factory func() AuthPlugin) { |
| globalPluginRegistry.Register(name, factory) |
| } |