package github import ( "context" "fmt" "net/url" "strings" "time" "github.com/hashicorp/errwrap" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/helper/tokenutil" "github.com/hashicorp/vault/sdk/logical" ) func pathConfig(b *backend) *framework.Path { p := &framework.Path{ Pattern: "config", Fields: map[string]*framework.FieldSchema{ "organization": { Type: framework.TypeString, Description: "The organization users must be part of", }, "base_url": { Type: framework.TypeString, Description: `The API endpoint to use. Useful if you are running GitHub Enterprise or an API-compatible authentication server.`, DisplayAttrs: &framework.DisplayAttributes{ Name: "Base URL", Group: "GitHub Options", }, }, "ttl": { Type: framework.TypeDurationSecond, Description: tokenutil.DeprecationText("token_ttl"), Deprecated: true, }, "max_ttl": { Type: framework.TypeDurationSecond, Description: tokenutil.DeprecationText("token_max_ttl"), Deprecated: true, }, }, Callbacks: map[logical.Operation]framework.OperationFunc{ logical.UpdateOperation: b.pathConfigWrite, logical.ReadOperation: b.pathConfigRead, }, } 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 } func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { 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) } if baseURLRaw, ok := data.GetOk("base_url"); ok { baseURL := baseURLRaw.(string) _, err := url.Parse(baseURL) if err != nil { return logical.ErrorResponse(fmt.Sprintf("Error parsing given base_url: %s", err)), nil } if !strings.HasSuffix(baseURL, "/") { baseURL += "/" } c.BaseURL = baseURL } if err := c.ParseTokenFields(req, data); err != nil { return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest } // Handle upgrade cases { if err := tokenutil.UpgradeValue(data, "ttl", "token_ttl", &c.TTL, &c.TokenTTL); err != nil { return logical.ErrorResponse(err.Error()), nil } if err := tokenutil.UpgradeValue(data, "max_ttl", "token_max_ttl", &c.MaxTTL, &c.TokenMaxTTL); err != nil { return logical.ErrorResponse(err.Error()), nil } } entry, err := logical.StorageEntryJSON("config", c) if err != nil { return nil, err } if err := req.Storage.Put(ctx, entry); err != nil { return nil, err } return nil, nil } func (b *backend) pathConfigRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { config, err := b.Config(ctx, req.Storage) if err != nil { return nil, err } if config == nil { return nil, nil } d := map[string]interface{}{ "organization": config.Organization, "base_url": config.BaseURL, } config.PopulateTokenData(d) if config.TTL > 0 { d["ttl"] = int64(config.TTL.Seconds()) } if config.MaxTTL > 0 { d["max_ttl"] = int64(config.MaxTTL.Seconds()) } return &logical.Response{ Data: d, }, nil } // Config returns the configuration for this backend. func (b *backend) Config(ctx context.Context, s logical.Storage) (*config, error) { entry, err := s.Get(ctx, "config") if err != nil { return nil, err } if entry == nil { return nil, nil } var result config if entry != nil { if err := entry.DecodeJSON(&result); err != nil { return nil, errwrap.Wrapf("error reading configuration: {{err}}", err) } } if result.TokenTTL == 0 && result.TTL > 0 { result.TokenTTL = result.TTL } if result.TokenMaxTTL == 0 && result.MaxTTL > 0 { result.TokenMaxTTL = result.MaxTTL } return &result, nil } type config struct { tokenutil.TokenParams 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"` }