mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-10-07 03:17:50 +00:00
Merge branch 'master' into staticcheck-issues
This commit is contained in:
commit
30b45adb30
6 changed files with 323 additions and 0 deletions
38
auth/auth.go
Normal file
38
auth/auth.go
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2017 Pilosa Corp.
|
||||
//
|
||||
// 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 auth
|
||||
|
||||
type Auth struct {
|
||||
// Enable AuthZ/AuthN for featurebase server
|
||||
Enable bool `toml:"enable"`
|
||||
|
||||
// Application/Client ID
|
||||
ClientId string `toml:"client-id"`
|
||||
|
||||
// Client Secret
|
||||
ClientSecret string `toml:"client-secret"`
|
||||
|
||||
// Authorize URL
|
||||
AuthorizeURL string `toml:"authorize-url"`
|
||||
|
||||
// Token URL
|
||||
TokenURL string `toml:"token-url"`
|
||||
|
||||
// Group Endpoint URL
|
||||
GroupEndpointURL string `toml:"group-endpoint-url"`
|
||||
|
||||
// Scope URL
|
||||
ScopeURL string `toml:"scope-url"`
|
||||
}
|
||||
|
|
@ -121,4 +121,14 @@ func BuildServerFlags(cmd *cobra.Command, srv *server.Command) {
|
|||
|
||||
// Toggle /schema/details endpoint.
|
||||
flags.BoolVar(&srv.Config.SchemaDetailsOn, "schema-details-on", true, "Disable /schema/details endpoint")
|
||||
|
||||
// OAuth2.0 identity provider configuration
|
||||
flags.BoolVar(&srv.Config.Auth.Enable, "auth.enable", false, "Enable AuthN/AuthZ of featurebase, disabled by default.")
|
||||
flags.StringVar(&srv.Config.Auth.ClientId, "auth.client-id", srv.Config.Auth.ClientId, "Identity Provider's Application/Client ID.")
|
||||
flags.StringVar(&srv.Config.Auth.ClientSecret, "auth.client-secret", srv.Config.Auth.ClientSecret, "Identity Provider's Client Secret.")
|
||||
flags.StringVar(&srv.Config.Auth.AuthorizeURL, "auth.authorize-url", srv.Config.Auth.AuthorizeURL, "Identity Provider's Authorize URL.")
|
||||
flags.StringVar(&srv.Config.Auth.TokenURL, "auth.token-url", srv.Config.Auth.TokenURL, "Identity Provider's Token URL.")
|
||||
flags.StringVar(&srv.Config.Auth.GroupEndpointURL, "auth.group-endpoint-url", srv.Config.Auth.GroupEndpointURL, "Identity Provider's Group endpoint URL.")
|
||||
flags.StringVar(&srv.Config.Auth.ScopeURL, "auth.scope-url", srv.Config.Auth.ScopeURL, "Identity Provider's Scope URL.")
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -371,3 +371,13 @@ log-path = "/var/log/molecula/featurebase.log"
|
|||
|
||||
|
||||
# ==============================================================================
|
||||
# Enable/Disable AuthN/AuthZ for featurebase
|
||||
# Can choose identity provider, pass authorize and user-info endpoints, and client id
|
||||
# [auth]
|
||||
# enable = false
|
||||
# client-id = ""
|
||||
# client-secret = ""
|
||||
# authorize-url = ""
|
||||
# token-url = ""
|
||||
# group-endpoint-url = ""
|
||||
# scope-url = ""
|
||||
|
|
@ -19,11 +19,13 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/molecula/featurebase/v2/auth"
|
||||
petcd "github.com/molecula/featurebase/v2/etcd"
|
||||
rbfcfg "github.com/molecula/featurebase/v2/rbf/cfg"
|
||||
"github.com/molecula/featurebase/v2/storage"
|
||||
|
|
@ -240,6 +242,9 @@ type Config struct {
|
|||
|
||||
// Toggles /schema/details endpoint. If off, it returns empty.
|
||||
SchemaDetailsOn bool `toml:"schema-details-on"`
|
||||
|
||||
// Enable AuthZ/AuthN
|
||||
Auth auth.Auth `toml:"auth"`
|
||||
}
|
||||
|
||||
// Namespace returns the namespace to use based on the Future flag.
|
||||
|
|
@ -273,6 +278,7 @@ func (c *Config) validate() error {
|
|||
"Etcd.ClusterURL", c.Etcd.ClusterURL,
|
||||
"Postgres.Bind", c.Postgres.Bind,
|
||||
}
|
||||
|
||||
ports := make(map[int]bool)
|
||||
n := len(hostPort)
|
||||
for i := 0; i < n; i += 2 {
|
||||
|
|
@ -602,3 +608,46 @@ func lookupAddr(ctx context.Context, resolver *net.Resolver, host string) (strin
|
|||
// No IPv4 address, return the first resolved address instead.
|
||||
return addrs[0].String(), nil
|
||||
}
|
||||
|
||||
func (c *Config) ValidateAuth() ([]error, error) {
|
||||
if !c.Auth.Enable {
|
||||
return []error{}, nil
|
||||
}
|
||||
authConfig := map[string]string{
|
||||
"ClientId": c.Auth.ClientId,
|
||||
"ClientSecret": c.Auth.ClientSecret,
|
||||
"AuthorizeURL": c.Auth.AuthorizeURL,
|
||||
"TokenURL": c.Auth.TokenURL,
|
||||
"GroupEndpointURL": c.Auth.GroupEndpointURL,
|
||||
"ScopeURL": c.Auth.ScopeURL,
|
||||
}
|
||||
|
||||
errors := make([]error, 0)
|
||||
for name, value := range authConfig {
|
||||
if value == "" {
|
||||
errors = append(errors, fmt.Errorf("Empty string for auth config %s", name))
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(name, "URL") {
|
||||
_, err := url.ParseRequestURI(value)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("Invalid URL for auth config %s: %s", name, err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(errors) > 0 {
|
||||
return errors, fmt.Errorf("there were errors validating config")
|
||||
}
|
||||
return errors, nil
|
||||
}
|
||||
|
||||
func (c *Config) MustValidateAuth() {
|
||||
if errors, err := c.ValidateAuth(); err != nil {
|
||||
for _, e := range errors {
|
||||
log.Println(e)
|
||||
}
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/molecula/featurebase/v2/auth"
|
||||
)
|
||||
|
||||
type addrs struct{ bind, advertise string }
|
||||
|
|
@ -288,3 +290,213 @@ func TestConfig_validateAddrsGRPC(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_validateAuth(t *testing.T) {
|
||||
errorMesgEmpty := "Empty string"
|
||||
errorMesgURL := "Invalid URL"
|
||||
validTestURL := "https://url.com/"
|
||||
validClientID := "clientid"
|
||||
validClientSecret := "clientSecret"
|
||||
notValidURL := "not-a-url"
|
||||
emptyString := ""
|
||||
enable := true
|
||||
disable := false
|
||||
|
||||
tests := []struct {
|
||||
expErrs []string
|
||||
input auth.Auth
|
||||
}{
|
||||
|
||||
{
|
||||
// Auth enabled, all configs are set to empty string
|
||||
[]string{
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
},
|
||||
auth.Auth{
|
||||
Enable: enable,
|
||||
ClientId: emptyString,
|
||||
ClientSecret: emptyString,
|
||||
AuthorizeURL: emptyString,
|
||||
TokenURL: emptyString,
|
||||
GroupEndpointURL: emptyString,
|
||||
ScopeURL: emptyString,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Auth enabled, some configs are set to empty string
|
||||
[]string{
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
},
|
||||
auth.Auth{
|
||||
Enable: enable,
|
||||
ClientId: validClientID,
|
||||
ClientSecret: emptyString,
|
||||
AuthorizeURL: emptyString,
|
||||
TokenURL: emptyString,
|
||||
GroupEndpointURL: emptyString,
|
||||
ScopeURL: emptyString,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Auth enabled, some configs are set to empty string
|
||||
[]string{
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
},
|
||||
auth.Auth{
|
||||
Enable: enable,
|
||||
ClientId: emptyString,
|
||||
ClientSecret: validClientSecret,
|
||||
AuthorizeURL: emptyString,
|
||||
TokenURL: emptyString,
|
||||
GroupEndpointURL: emptyString,
|
||||
ScopeURL: emptyString,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Auth enabled, some configs are set to empty string
|
||||
[]string{
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
},
|
||||
auth.Auth{
|
||||
Enable: enable,
|
||||
ClientId: validClientID,
|
||||
ClientSecret: validClientSecret,
|
||||
AuthorizeURL: emptyString,
|
||||
TokenURL: emptyString,
|
||||
GroupEndpointURL: emptyString,
|
||||
ScopeURL: emptyString,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Auth enabled, some configs are set to empty string
|
||||
[]string{
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
},
|
||||
auth.Auth{
|
||||
Enable: enable,
|
||||
ClientId: validClientID,
|
||||
ClientSecret: validClientSecret,
|
||||
AuthorizeURL: validTestURL,
|
||||
TokenURL: emptyString,
|
||||
GroupEndpointURL: emptyString,
|
||||
ScopeURL: emptyString,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Auth enabled, some configs are set to empty string
|
||||
[]string{
|
||||
errorMesgEmpty,
|
||||
errorMesgEmpty,
|
||||
},
|
||||
auth.Auth{
|
||||
Enable: enable,
|
||||
ClientId: validClientID,
|
||||
ClientSecret: validClientSecret,
|
||||
AuthorizeURL: validTestURL,
|
||||
TokenURL: validTestURL,
|
||||
GroupEndpointURL: emptyString,
|
||||
ScopeURL: emptyString,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Auth enabled, some strings are set to invalid URL
|
||||
[]string{
|
||||
errorMesgURL,
|
||||
},
|
||||
auth.Auth{
|
||||
Enable: enable,
|
||||
ClientId: validClientID,
|
||||
ClientSecret: validClientSecret,
|
||||
AuthorizeURL: notValidURL,
|
||||
TokenURL: validTestURL,
|
||||
GroupEndpointURL: validTestURL,
|
||||
ScopeURL: validTestURL,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Auth enabled, some strings are set to invalid URL
|
||||
[]string{
|
||||
errorMesgURL,
|
||||
errorMesgURL,
|
||||
},
|
||||
auth.Auth{
|
||||
Enable: enable,
|
||||
ClientId: validClientID,
|
||||
ClientSecret: validClientSecret,
|
||||
AuthorizeURL: validTestURL,
|
||||
TokenURL: notValidURL,
|
||||
GroupEndpointURL: notValidURL,
|
||||
ScopeURL: validTestURL,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Auth enabled, all configs are set properly
|
||||
[]string{},
|
||||
auth.Auth{
|
||||
Enable: enable,
|
||||
ClientId: validClientID,
|
||||
ClientSecret: validClientSecret,
|
||||
AuthorizeURL: validTestURL,
|
||||
TokenURL: validTestURL,
|
||||
GroupEndpointURL: validTestURL,
|
||||
ScopeURL: validTestURL,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Auth disabled, all configs are set to empty string
|
||||
[]string{},
|
||||
auth.Auth{
|
||||
Enable: disable,
|
||||
ClientId: emptyString,
|
||||
ClientSecret: emptyString,
|
||||
AuthorizeURL: emptyString,
|
||||
TokenURL: emptyString,
|
||||
GroupEndpointURL: emptyString,
|
||||
ScopeURL: emptyString,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
c := NewConfig()
|
||||
c.Auth = test.input
|
||||
|
||||
errors, err := c.ValidateAuth()
|
||||
if len(test.expErrs) > 0 {
|
||||
if err == nil {
|
||||
t.Fatal("expected errors, but none were found")
|
||||
}
|
||||
}
|
||||
|
||||
if len(errors) != len(test.expErrs) {
|
||||
fmt.Printf("%+v\n", errors)
|
||||
t.Fatalf("expected %v errors but got %v", len(test.expErrs), len(errors))
|
||||
}
|
||||
|
||||
for i, e := range errors {
|
||||
if !strings.Contains(e.Error(), test.expErrs[i]) {
|
||||
t.Errorf("expected error to contain %s, but got %s", test.expErrs[i], e.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -234,6 +234,10 @@ func (m *Command) Start() (err error) {
|
|||
return errors.Wrap(err, "setting resource limits")
|
||||
}
|
||||
|
||||
if m.Config.Auth.Enable == true {
|
||||
m.Config.MustValidateAuth()
|
||||
}
|
||||
|
||||
// Initialize server.
|
||||
if err = m.Server.Open(); err != nil {
|
||||
return errors.Wrap(err, "opening server")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue