2023-03-15 16:00:52 +00:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
2015-04-01 22:46:21 +00:00
package github
import (
2018-01-08 18:31:38 +00:00
"context"
2015-04-01 22:46:21 +00:00
"fmt"
2015-08-28 13:28:35 +00:00
"net/url"
2023-02-21 20:17:35 +00:00
"os"
2019-07-01 20:31:30 +00:00
"strings"
2019-02-01 16:23:40 +00:00
"time"
2015-04-01 22:46:21 +00:00
2021-12-14 22:37:19 +00:00
"github.com/google/go-github/github"
2019-04-12 21:54:35 +00:00
"github.com/hashicorp/vault/sdk/framework"
2019-07-01 20:31:30 +00:00
"github.com/hashicorp/vault/sdk/helper/tokenutil"
2019-04-13 07:44:06 +00:00
"github.com/hashicorp/vault/sdk/logical"
2015-04-01 22:46:21 +00:00
)
2015-10-02 17:33:19 +00:00
func pathConfig ( b * backend ) * framework . Path {
2019-07-01 20:31:30 +00:00
p := & framework . Path {
2015-04-01 22:46:21 +00:00
Pattern : "config" ,
2023-04-07 17:30:26 +00:00
DisplayAttrs : & framework . DisplayAttributes {
OperationPrefix : operationPrefixGithub ,
} ,
2015-04-01 22:46:21 +00:00
Fields : map [ string ] * framework . FieldSchema {
2021-04-08 16:43:39 +00:00
"organization" : {
2015-04-01 22:46:21 +00:00
Type : framework . TypeString ,
Description : "The organization users must be part of" ,
2021-12-14 22:37:19 +00:00
Required : true ,
} ,
"organization_id" : {
Type : framework . TypeInt64 ,
Description : "The ID of the organization users must be part of" ,
2015-04-01 22:46:21 +00:00
} ,
2021-04-08 16:43:39 +00:00
"base_url" : {
2015-08-28 13:28:35 +00:00
Type : framework . TypeString ,
Description : ` The API endpoint to use . Useful if you
are running GitHub Enterprise or an
API - compatible authentication server . ` ,
2019-06-21 15:08:08 +00:00
DisplayAttrs : & framework . DisplayAttributes {
Name : "Base URL" ,
Group : "GitHub Options" ,
} ,
2015-08-28 13:28:35 +00:00
} ,
2021-04-08 16:43:39 +00:00
"ttl" : {
2019-07-01 20:31:30 +00:00
Type : framework . TypeDurationSecond ,
Description : tokenutil . DeprecationText ( "token_ttl" ) ,
Deprecated : true ,
2019-02-01 16:23:40 +00:00
} ,
2021-04-08 16:43:39 +00:00
"max_ttl" : {
2019-07-01 20:31:30 +00:00
Type : framework . TypeDurationSecond ,
Description : tokenutil . DeprecationText ( "token_max_ttl" ) ,
Deprecated : true ,
2019-02-01 16:23:40 +00:00
} ,
2015-04-01 22:46:21 +00:00
} ,
2023-04-07 17:30:26 +00:00
Operations : map [ logical . Operation ] framework . OperationHandler {
logical . UpdateOperation : & framework . PathOperation {
Callback : b . pathConfigWrite ,
DisplayAttrs : & framework . DisplayAttributes {
OperationPrefix : operationPrefixGithub ,
OperationVerb : "configure" ,
} ,
} ,
logical . ReadOperation : & framework . PathOperation {
Callback : b . pathConfigRead ,
DisplayAttrs : & framework . DisplayAttributes {
OperationSuffix : "configuration" ,
} ,
} ,
2015-04-01 22:46:21 +00:00
} ,
}
2019-07-01 20:31:30 +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 policies configured for specific users/groups."
return p
2015-04-01 22:46:21 +00:00
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathConfigWrite ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2021-12-14 22:37:19 +00:00
var resp logical . Response
2019-07-01 20:31:30 +00:00
c , err := b . Config ( ctx , req . Storage )
if err != nil {
return nil , err
}
if c == nil {
c = & config { }
}
if organizationRaw , ok := data . GetOk ( "organization" ) ; ok {
c . Organization = organizationRaw . ( string )
}
2021-12-14 22:37:19 +00:00
if c . Organization == "" {
return logical . ErrorResponse ( "organization is a required parameter" ) , nil
}
2019-07-01 20:31:30 +00:00
2021-12-14 22:37:19 +00:00
if organizationRaw , ok := data . GetOk ( "organization_id" ) ; ok {
c . OrganizationID = organizationRaw . ( int64 )
}
var parsedURL * url . URL
2019-07-01 20:31:30 +00:00
if baseURLRaw , ok := data . GetOk ( "base_url" ) ; ok {
baseURL := baseURLRaw . ( string )
if ! strings . HasSuffix ( baseURL , "/" ) {
baseURL += "/"
}
2021-12-14 22:37:19 +00:00
parsedURL , err = url . Parse ( baseURL )
if err != nil {
return logical . ErrorResponse ( fmt . Sprintf ( "error parsing given base_url: %s" , err ) ) , nil
}
2019-07-01 20:31:30 +00:00
c . BaseURL = baseURL
2015-08-28 13:28:35 +00:00
}
2021-12-14 22:37:19 +00:00
if c . OrganizationID == 0 {
2023-02-21 20:17:35 +00:00
githubToken := os . Getenv ( "VAULT_AUTH_CONFIG_GITHUB_TOKEN" )
client , err := b . Client ( githubToken )
2021-12-14 22:37:19 +00:00
if err != nil {
return nil , err
}
// ensure our client has the BaseURL if it was provided
if parsedURL != nil {
client . BaseURL = parsedURL
}
// we want to set the Org ID in the config so we can use that to verify
// the credentials on login
err = c . setOrganizationID ( ctx , client )
if err != nil {
errorMsg := fmt . Errorf ( "unable to fetch the organization_id, you must manually set it in the config: %s" , err )
b . Logger ( ) . Error ( errorMsg . Error ( ) )
return nil , errorMsg
}
}
2019-07-01 20:31:30 +00:00
if err := c . ParseTokenFields ( req , data ) ; err != nil {
return logical . ErrorResponse ( err . Error ( ) ) , logical . ErrInvalidRequest
2015-10-02 19:41:35 +00:00
}
2019-07-01 20:31:30 +00:00
// Handle upgrade cases
{
2019-07-02 13:52:05 +00:00
if err := tokenutil . UpgradeValue ( data , "ttl" , "token_ttl" , & c . TTL , & c . TokenTTL ) ; err != nil {
return logical . ErrorResponse ( err . Error ( ) ) , nil
2019-02-01 16:23:40 +00:00
}
2015-10-02 17:33:19 +00:00
2019-07-02 13:52:05 +00:00
if err := tokenutil . UpgradeValue ( data , "max_ttl" , "token_max_ttl" , & c . MaxTTL , & c . TokenMaxTTL ) ; err != nil {
return logical . ErrorResponse ( err . Error ( ) ) , nil
2019-07-01 20:31:30 +00:00
}
}
2015-10-02 17:33:19 +00:00
2019-07-01 20:31:30 +00:00
entry , err := logical . StorageEntryJSON ( "config" , c )
2015-04-01 22:46:21 +00:00
if err != nil {
return nil , err
}
2018-01-19 06:44:44 +00:00
if err := req . Storage . Put ( ctx , entry ) ; err != nil {
2015-04-01 22:46:21 +00:00
return nil , err
}
2021-12-14 22:37:19 +00:00
if len ( resp . Warnings ) == 0 {
return nil , nil
}
return & resp , nil
2015-04-01 22:46:21 +00:00
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathConfigRead ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2018-01-19 06:44:44 +00:00
config , err := b . Config ( ctx , req . Storage )
2017-01-11 00:21:31 +00:00
if err != nil {
return nil , err
}
2017-01-11 17:04:15 +00:00
if config == nil {
2019-07-02 16:57:48 +00:00
return nil , nil
2017-01-11 17:04:15 +00:00
}
2019-07-01 20:31:30 +00:00
d := map [ string ] interface { } {
2021-12-14 22:37:19 +00:00
"organization_id" : config . OrganizationID ,
"organization" : config . Organization ,
"base_url" : config . BaseURL ,
2019-07-01 20:31:30 +00:00
}
config . PopulateTokenData ( d )
2019-01-30 21:23:28 +00:00
2019-07-01 20:31:30 +00:00
if config . TTL > 0 {
d [ "ttl" ] = int64 ( config . TTL . Seconds ( ) )
2019-02-01 16:23:40 +00:00
}
2019-07-01 20:31:30 +00:00
if config . MaxTTL > 0 {
d [ "max_ttl" ] = int64 ( config . MaxTTL . Seconds ( ) )
}
return & logical . Response {
Data : d ,
} , nil
2017-01-11 00:21:31 +00:00
}
2015-04-01 22:46:21 +00:00
// Config returns the configuration for this backend.
2018-01-19 06:44:44 +00:00
func ( b * backend ) Config ( ctx context . Context , s logical . Storage ) ( * config , error ) {
entry , err := s . Get ( ctx , "config" )
2015-04-01 22:46:21 +00:00
if err != nil {
return nil , err
}
2019-07-01 20:31:30 +00:00
if entry == nil {
return nil , nil
}
2015-04-01 22:46:21 +00:00
var result config
if entry != nil {
if err := entry . DecodeJSON ( & result ) ; err != nil {
2021-04-22 15:20:59 +00:00
return nil , fmt . Errorf ( "error reading configuration: %w" , err )
2015-04-01 22:46:21 +00:00
}
}
2019-07-01 20:31:30 +00:00
if result . TokenTTL == 0 && result . TTL > 0 {
result . TokenTTL = result . TTL
}
if result . TokenMaxTTL == 0 && result . MaxTTL > 0 {
result . TokenMaxTTL = result . MaxTTL
}
2015-04-01 22:46:21 +00:00
return & result , nil
}
type config struct {
2019-07-01 20:31:30 +00:00
tokenutil . TokenParams
2021-12-14 22:37:19 +00:00
OrganizationID int64 ` json:"organization_id" structs:"organization_id" mapstructure:"organization_id" `
Organization string ` json:"organization" structs:"organization" mapstructure:"organization" `
BaseURL string ` json:"base_url" structs:"base_url" mapstructure:"base_url" `
TTL time . Duration ` json:"ttl" structs:"ttl" mapstructure:"ttl" `
MaxTTL time . Duration ` json:"max_ttl" structs:"max_ttl" mapstructure:"max_ttl" `
}
func ( c * config ) setOrganizationID ( ctx context . Context , client * github . Client ) error {
org , _ , err := client . Organizations . Get ( ctx , c . Organization )
if err != nil {
return err
}
orgID := org . GetID ( )
if orgID == 0 {
return fmt . Errorf ( "organization_id not found for %s" , c . Organization )
}
c . OrganizationID = orgID
return nil
2015-04-01 22:46:21 +00:00
}