2023-03-15 16:00:52 +00:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
2017-02-07 21:04:27 +00:00
package radius
import (
2018-01-08 18:31:38 +00:00
"context"
2017-02-07 21:04:27 +00:00
"strings"
2019-04-12 21:54:35 +00:00
"github.com/hashicorp/vault/sdk/framework"
2019-07-01 20:30:39 +00:00
"github.com/hashicorp/vault/sdk/helper/tokenutil"
2019-04-13 07:44:06 +00:00
"github.com/hashicorp/vault/sdk/logical"
2017-02-07 21:04:27 +00:00
)
func pathConfig ( b * backend ) * framework . Path {
2019-07-01 20:30:39 +00:00
p := & framework . Path {
2017-02-07 21:04:27 +00:00
Pattern : "config" ,
2023-04-07 17:14:44 +00:00
DisplayAttrs : & framework . DisplayAttributes {
OperationPrefix : operationPrefixRadius ,
Action : "Configure" ,
} ,
2017-02-07 21:04:27 +00:00
Fields : map [ string ] * framework . FieldSchema {
2021-04-08 16:43:39 +00:00
"host" : {
2017-02-07 21:04:27 +00:00
Type : framework . TypeString ,
Description : "RADIUS server host" ,
2019-06-21 15:08:08 +00:00
DisplayAttrs : & framework . DisplayAttributes {
Name : "Host" ,
} ,
2017-02-07 21:04:27 +00:00
} ,
2021-04-08 16:43:39 +00:00
"port" : {
2017-02-07 21:04:27 +00:00
Type : framework . TypeInt ,
Default : 1812 ,
Description : "RADIUS server port (default: 1812)" ,
2019-06-21 15:08:08 +00:00
DisplayAttrs : & framework . DisplayAttributes {
Value : 1812 ,
} ,
2017-02-07 21:04:27 +00:00
} ,
2021-04-08 16:43:39 +00:00
"secret" : {
2017-02-07 21:04:27 +00:00
Type : framework . TypeString ,
Description : "Secret shared with the RADIUS server" ,
} ,
2021-04-08 16:43:39 +00:00
"unregistered_user_policies" : {
2017-02-07 21:04:27 +00:00
Type : framework . TypeString ,
Default : "" ,
2023-04-19 16:16:30 +00:00
Description : "Comma-separated list of policies to grant upon successful RADIUS authentication of an unregistered user (default: empty)" ,
2019-06-21 15:08:08 +00:00
DisplayAttrs : & framework . DisplayAttributes {
2023-04-19 16:16:30 +00:00
Name : "Policies for unregistered users" ,
Description : "List of policies to grant upon successful RADIUS authentication of an unregistered user (default: empty)" ,
2019-06-21 15:08:08 +00:00
} ,
2017-02-07 21:04:27 +00:00
} ,
2021-04-08 16:43:39 +00:00
"dial_timeout" : {
2017-02-07 21:04:27 +00:00
Type : framework . TypeDurationSecond ,
Default : 10 ,
2017-02-07 21:06:27 +00:00
Description : "Number of seconds before connect times out (default: 10)" ,
2019-06-21 15:08:08 +00:00
DisplayAttrs : & framework . DisplayAttributes {
Value : 10 ,
} ,
2017-02-07 21:04:27 +00:00
} ,
2021-04-08 16:43:39 +00:00
"read_timeout" : {
2017-02-07 21:04:27 +00:00
Type : framework . TypeDurationSecond ,
Default : 10 ,
2017-12-01 15:18:26 +00:00
Description : "Number of seconds before response times out (default: 10)" ,
2019-06-21 15:08:08 +00:00
DisplayAttrs : & framework . DisplayAttributes {
Value : 10 ,
} ,
2017-02-07 21:04:27 +00:00
} ,
2021-04-08 16:43:39 +00:00
"nas_port" : {
2017-02-07 21:04:27 +00:00
Type : framework . TypeInt ,
Default : 10 ,
Description : "RADIUS NAS port field (default: 10)" ,
2019-06-21 15:08:08 +00:00
DisplayAttrs : & framework . DisplayAttributes {
Name : "NAS Port" ,
Value : 10 ,
} ,
2017-02-07 21:04:27 +00:00
} ,
2021-04-08 16:43:39 +00:00
"nas_identifier" : {
2018-10-18 17:41:14 +00:00
Type : framework . TypeString ,
Default : "" ,
Description : "RADIUS NAS Identifier field (optional)" ,
2019-06-21 15:08:08 +00:00
DisplayAttrs : & framework . DisplayAttributes {
Name : "NAS Identifier" ,
} ,
2018-10-21 01:09:51 +00:00
} ,
2017-02-07 21:04:27 +00:00
} ,
ExistenceCheck : b . configExistenceCheck ,
2023-04-07 17:14:44 +00:00
Operations : map [ logical . Operation ] framework . OperationHandler {
logical . ReadOperation : & framework . PathOperation {
Callback : b . pathConfigRead ,
DisplayAttrs : & framework . DisplayAttributes {
OperationSuffix : "configuration" ,
} ,
} ,
logical . CreateOperation : & framework . PathOperation {
Callback : b . pathConfigCreateUpdate ,
DisplayAttrs : & framework . DisplayAttributes {
OperationVerb : "configure" ,
} ,
} ,
logical . UpdateOperation : & framework . PathOperation {
Callback : b . pathConfigCreateUpdate ,
DisplayAttrs : & framework . DisplayAttributes {
OperationVerb : "configure" ,
} ,
} ,
2017-02-07 21:04:27 +00:00
} ,
HelpSynopsis : pathConfigHelpSyn ,
HelpDescription : pathConfigHelpDesc ,
}
2019-07-01 20:30:39 +00:00
tokenutil . AddTokenFields ( p . Fields )
p . Fields [ "token_policies" ] . Description += ". This will apply to all tokens generated by this auth method, in addition to any configured for specific users."
return p
2017-02-07 21:04:27 +00:00
}
// Establishes dichotomy of request operation between CreateOperation and UpdateOperation.
// Returning 'true' forces an UpdateOperation, CreateOperation otherwise.
2018-01-08 18:31:38 +00:00
func ( b * backend ) configExistenceCheck ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( bool , error ) {
2018-01-19 06:44:44 +00:00
entry , err := b . Config ( ctx , req )
2017-02-07 21:04:27 +00:00
if err != nil {
return false , err
}
return entry != nil , nil
}
/ *
* Construct ConfigEntry struct using stored configuration .
* /
2018-01-19 06:44:44 +00:00
func ( b * backend ) Config ( ctx context . Context , req * logical . Request ) ( * ConfigEntry , error ) {
storedConfig , err := req . Storage . Get ( ctx , "config" )
2017-02-07 21:04:27 +00:00
if err != nil {
return nil , err
}
if storedConfig == nil {
return nil , nil
}
var result ConfigEntry
if err := storedConfig . DecodeJSON ( & result ) ; err != nil {
return nil , err
}
return & result , nil
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathConfigRead ( ctx context . Context , req * logical . Request , d * framework . FieldData ) ( * logical . Response , error ) {
2018-01-19 06:44:44 +00:00
cfg , err := b . Config ( ctx , req )
2017-02-07 21:04:27 +00:00
if err != nil {
return nil , err
}
if cfg == nil {
return nil , nil
}
2019-07-01 20:30:39 +00:00
data := map [ string ] interface { } {
"host" : cfg . Host ,
"port" : cfg . Port ,
"unregistered_user_policies" : cfg . UnregisteredUserPolicies ,
"dial_timeout" : cfg . DialTimeout ,
"read_timeout" : cfg . ReadTimeout ,
"nas_port" : cfg . NasPort ,
"nas_identifier" : cfg . NasIdentifier ,
2017-02-07 21:04:27 +00:00
}
2019-07-01 20:30:39 +00:00
cfg . PopulateTokenData ( data )
return & logical . Response {
Data : data ,
} , nil
2017-02-07 21:04:27 +00:00
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathConfigCreateUpdate ( ctx context . Context , req * logical . Request , d * framework . FieldData ) ( * logical . Response , error ) {
2017-02-07 21:04:27 +00:00
// Build a ConfigEntry struct out of the supplied FieldData
2018-01-19 06:44:44 +00:00
cfg , err := b . Config ( ctx , req )
2017-02-07 21:04:27 +00:00
if err != nil {
return nil , err
}
if cfg == nil {
cfg = & ConfigEntry { }
}
2019-07-01 20:30:39 +00:00
if err := cfg . ParseTokenFields ( req , d ) ; err != nil {
return logical . ErrorResponse ( err . Error ( ) ) , logical . ErrInvalidRequest
}
2017-02-07 21:04:27 +00:00
host , ok := d . GetOk ( "host" )
if ok {
cfg . Host = strings . ToLower ( host . ( string ) )
} else if req . Operation == logical . CreateOperation {
cfg . Host = strings . ToLower ( d . Get ( "host" ) . ( string ) )
}
if cfg . Host == "" {
return logical . ErrorResponse ( "config parameter `host` cannot be empty" ) , nil
}
port , ok := d . GetOk ( "port" )
if ok {
cfg . Port = port . ( int )
} else if req . Operation == logical . CreateOperation {
cfg . Port = d . Get ( "port" ) . ( int )
}
secret , ok := d . GetOk ( "secret" )
if ok {
cfg . Secret = secret . ( string )
} else if req . Operation == logical . CreateOperation {
cfg . Secret = d . Get ( "secret" ) . ( string )
}
if cfg . Secret == "" {
return logical . ErrorResponse ( "config parameter `secret` cannot be empty" ) , nil
}
policies := make ( [ ] string , 0 )
unregisteredUserPoliciesRaw , ok := d . GetOk ( "unregistered_user_policies" )
if ok {
unregisteredUserPoliciesStr := unregisteredUserPoliciesRaw . ( string )
if strings . TrimSpace ( unregisteredUserPoliciesStr ) != "" {
policies = strings . Split ( unregisteredUserPoliciesStr , "," )
for _ , policy := range policies {
if policy == "root" {
2017-09-13 01:48:52 +00:00
return logical . ErrorResponse ( "root policy cannot be granted by an auth method" ) , nil
2017-02-07 21:04:27 +00:00
}
}
}
cfg . UnregisteredUserPolicies = policies
} else if req . Operation == logical . CreateOperation {
cfg . UnregisteredUserPolicies = policies
}
dialTimeout , ok := d . GetOk ( "dial_timeout" )
if ok {
cfg . DialTimeout = dialTimeout . ( int )
} else if req . Operation == logical . CreateOperation {
cfg . DialTimeout = d . Get ( "dial_timeout" ) . ( int )
}
readTimeout , ok := d . GetOk ( "read_timeout" )
if ok {
cfg . ReadTimeout = readTimeout . ( int )
} else if req . Operation == logical . CreateOperation {
cfg . ReadTimeout = d . Get ( "read_timeout" ) . ( int )
}
nasPort , ok := d . GetOk ( "nas_port" )
if ok {
cfg . NasPort = nasPort . ( int )
} else if req . Operation == logical . CreateOperation {
cfg . NasPort = d . Get ( "nas_port" ) . ( int )
}
2018-10-18 17:41:14 +00:00
nasIdentifier , ok := d . GetOk ( "nas_identifier" )
if ok {
cfg . NasIdentifier = nasIdentifier . ( string )
} else if req . Operation == logical . CreateOperation {
cfg . NasIdentifier = d . Get ( "nas_identifier" ) . ( string )
}
2017-02-07 21:04:27 +00:00
entry , err := logical . StorageEntryJSON ( "config" , cfg )
if err != nil {
return nil , err
}
2018-01-19 06:44:44 +00:00
if err := req . Storage . Put ( ctx , entry ) ; err != nil {
2017-02-07 21:04:27 +00:00
return nil , err
}
return nil , nil
}
type ConfigEntry struct {
2019-07-01 20:30:39 +00:00
tokenutil . TokenParams
2017-02-07 21:04:27 +00:00
Host string ` json:"host" structs:"host" mapstructure:"host" `
Port int ` json:"port" structs:"port" mapstructure:"port" `
Secret string ` json:"secret" structs:"secret" mapstructure:"secret" `
UnregisteredUserPolicies [ ] string ` json:"unregistered_user_policies" structs:"unregistered_user_policies" mapstructure:"unregistered_user_policies" `
DialTimeout int ` json:"dial_timeout" structs:"dial_timeout" mapstructure:"dial_timeout" `
ReadTimeout int ` json:"read_timeout" structs:"read_timeout" mapstructure:"read_timeout" `
NasPort int ` json:"nas_port" structs:"nas_port" mapstructure:"nas_port" `
2018-10-18 17:41:14 +00:00
NasIdentifier string ` json:"nas_identifier" structs:"nas_identifier" mapstructure:"nas_identifier" `
2017-02-07 21:04:27 +00:00
}
const pathConfigHelpSyn = `
Configure the RADIUS server to connect to , along with its options .
`
const pathConfigHelpDesc = `
This endpoint allows you to configure the RADIUS server to connect to and its
configuration options .
`