vitess.io/vitess@v0.16.2/go/acl/acl.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package acl contains functions to enforce access control lists.
    18  // It allows you to register multiple security policies for enforcing
    19  // ACLs for users or HTTP requests. The specific policy to use must be
    20  // specified from a command line argument and cannot be changed on-the-fly.
    21  //
    22  // For actual authentication and authorization, you would need to implement your
    23  // own policy as a package that calls RegisterPolicy(), and compile it into all
    24  // Vitess binaries that you use.
    25  //
    26  // By default (when no security_policy is specified), everyone is allowed to do
    27  // anything.
    28  //
    29  // For convenience, there are two other built-in policies that also do NOT do
    30  // any authentication, but allow you to globally disable some roles entirely:
    31  //   - `deny-all` disallows all roles for everyone. Note that access is still
    32  //     allowed to endpoints that are considered "public" (no ACL check at all).
    33  //   - `read-only` allows anyone to act as DEBUGGING or MONITORING, but no one
    34  //     is allowed to act as ADMIN. It also disallows any other custom roles that
    35  //     are requested.
    36  package acl
    37  
    38  import (
    39  	"fmt"
    40  	"net/http"
    41  	"sync"
    42  
    43  	"github.com/spf13/pflag"
    44  
    45  	"vitess.io/vitess/go/vt/log"
    46  )
    47  
    48  // This is a list of predefined roles. Applications are free
    49  // to invent more roles, as long as the acl policies they use can
    50  // understand what they mean.
    51  const (
    52  	ADMIN      = "admin"
    53  	DEBUGGING  = "debugging"
    54  	MONITORING = "monitoring"
    55  )
    56  
    57  var (
    58  	securityPolicy string
    59  	policies       = make(map[string]Policy)
    60  	once           sync.Once
    61  	currentPolicy  Policy
    62  )
    63  
    64  // Policy defines the interface that needs to be satisfied by
    65  // ACL policy implementors.
    66  type Policy interface {
    67  	// CheckAccessActor can be called to verify if an actor
    68  	// has access to the role.
    69  	CheckAccessActor(actor, role string) error
    70  	// CheckAccessHTTP can be called to verify if an actor in
    71  	// the http request has access to the role.
    72  	CheckAccessHTTP(req *http.Request, role string) error
    73  }
    74  
    75  func RegisterFlags(fs *pflag.FlagSet) {
    76  	fs.StringVar(&securityPolicy, "security_policy", securityPolicy, "the name of a registered security policy to use for controlling access to URLs - empty means allow all for anyone (built-in policies: deny-all, read-only)")
    77  }
    78  
    79  // RegisterPolicy registers a security policy. This function must be called
    80  // before the first call to CheckAccess happens, preferably through an init.
    81  // This will ensure that the requested policy can be found by other acl
    82  // functions when needed.
    83  func RegisterPolicy(name string, policy Policy) {
    84  	if _, ok := policies[name]; ok {
    85  		log.Fatalf("policy %s is already registered", name)
    86  	}
    87  	policies[name] = policy
    88  }
    89  
    90  func savePolicy() {
    91  	if securityPolicy == "" {
    92  		// Setting the policy to nil means Allow All from Anyone.
    93  		currentPolicy = nil
    94  		return
    95  	}
    96  	if policy, ok := policies[securityPolicy]; ok {
    97  		currentPolicy = policy
    98  		return
    99  	}
   100  	log.Warningf("security_policy %q not found; using fallback policy (deny-all)", securityPolicy)
   101  	currentPolicy = denyAllPolicy{}
   102  }
   103  
   104  // CheckAccessActor uses the current security policy to
   105  // verify if an actor has access to the role.
   106  func CheckAccessActor(actor, role string) error {
   107  	once.Do(savePolicy)
   108  	if currentPolicy != nil {
   109  		return currentPolicy.CheckAccessActor(actor, role)
   110  	}
   111  	return nil
   112  }
   113  
   114  // CheckAccessHTTP uses the current security policy to
   115  // verify if an actor in an http request has access to
   116  // the role.
   117  func CheckAccessHTTP(req *http.Request, role string) error {
   118  	once.Do(savePolicy)
   119  	if currentPolicy != nil {
   120  		return currentPolicy.CheckAccessHTTP(req, role)
   121  	}
   122  	return nil
   123  }
   124  
   125  // SendError is a convenience function that sends an ACL
   126  // error as an HTTP response.
   127  func SendError(w http.ResponseWriter, err error) {
   128  	w.WriteHeader(http.StatusForbidden)
   129  	fmt.Fprintf(w, "Access denied: %v\n", err)
   130  }