// forbidden renders a simple forbidden error func forbidden(reason string, attributes authorizer.AuthorizationAttributes, w http.ResponseWriter, req *http.Request) { kind := "" name := "" // the attributes can be empty for two basic reasons: // 1. malformed API request // 2. not an API request at all // In these cases, just assume default that will work better than nothing if attributes != nil { kind = attributes.GetResource() if len(attributes.GetAPIGroup()) > 0 { kind = attributes.GetAPIGroup() + "." + kind } name = attributes.GetResourceName() } // Reason is an opaque string that describes why access is allowed or forbidden (forbidden by the time we reach here). // We don't have direct access to kind or name (not that those apply either in the general case) // We create a NewForbidden to stay close the API, but then we override the message to get a serialization // that makes sense when a human reads it. forbiddenError, _ := kapierrors.NewForbidden(unversioned.GroupResource{Group: attributes.GetAPIGroup(), Resource: attributes.GetResource()}, name, errors.New("") /*discarded*/).(*kapierrors.StatusError) forbiddenError.ErrStatus.Message = reason formatted := &bytes.Buffer{} output, err := runtime.Encode(kapi.Codecs.LegacyCodec(kapi.SchemeGroupVersion), &forbiddenError.ErrStatus) if err != nil { fmt.Fprintf(formatted, "%s", forbiddenError.Error()) } else { json.Indent(formatted, output, "", " ") } w.Header().Set("Content-Type", restful.MIME_JSON) w.WriteHeader(http.StatusForbidden) w.Write(formatted.Bytes()) }
func (a *testAuthorizer) Authorize(ctx kapi.Context, attributes authorizer.AuthorizationAttributes) (allowed bool, reason string, err error) { // allow the initial check for "can I run this RAR at all" if attributes.GetResource() == "localresourceaccessreviews" { return true, "", nil } return false, "", errors.New("Unsupported") }
func (a *testAuthorizer) Authorize(ctx kapi.Context, attributes authorizer.AuthorizationAttributes) (allowed bool, reason string, err error) { // allow the initial check for "can I run this RAR at all" if attributes.GetResource() == "localresourceaccessreviews" { if len(a.deniedNamespaces) != 0 && a.deniedNamespaces.Has(kapi.NamespaceValue(ctx)) { return false, "denied initial check", nil } return true, "", nil } return false, "", errors.New("unsupported") }
func (a *testAuthorizer) Authorize(ctx kapi.Context, passedAttributes authorizer.AuthorizationAttributes) (allowed bool, reason string, err error) { // allow the initial check for "can I run this SAR at all" if passedAttributes.GetResource() == "localsubjectaccessreviews" { return true, "", nil } attributes, ok := passedAttributes.(authorizer.DefaultAuthorizationAttributes) if !ok { return false, "ERROR", errors.New("unexpected type for test") } a.actualAttributes = attributes if len(a.err) == 0 { return a.allowed, a.reason, nil } return a.allowed, a.reason, errors.New(a.err) }
func getAction(namespace string, attributes authorizer.AuthorizationAttributes) authzapi.AuthorizationAttributes { return authzapi.AuthorizationAttributes{ Namespace: namespace, Verb: attributes.GetVerb(), Group: attributes.GetAPIGroup(), Version: attributes.GetAPIVersion(), Resource: attributes.GetResource(), ResourceName: attributes.GetResourceName(), // TODO: missing from authorizer.AuthorizationAttributes: // Content // TODO: missing from authzapi.AuthorizationAttributes // RequestAttributes (unserializable?) // IsNonResourceURL // URL (doesn't make sense for remote authz?) } }
// forbidden renders a simple forbidden error func forbidden(reason string, attributes authorizer.AuthorizationAttributes, w http.ResponseWriter, req *http.Request) { kind := "" name := "" apiVersion := klatest.DefaultVersionForLegacyGroup() // the attributes can be empty for two basic reasons: // 1. malformed API request // 2. not an API request at all // In these cases, just assume default that will work better than nothing if attributes != nil { apiVersion = attributes.GetAPIVersion() kind = attributes.GetResource() if len(attributes.GetAPIGroup()) > 0 { kind = attributes.GetAPIGroup() + "." + kind } name = attributes.GetResourceName() } // Reason is an opaque string that describes why access is allowed or forbidden (forbidden by the time we reach here). // We don't have direct access to kind or name (not that those apply either in the general case) // We create a NewForbidden to stay close the API, but then we override the message to get a serialization // that makes sense when a human reads it. forbiddenError, _ := kapierrors.NewForbidden(kind, name, errors.New("") /*discarded*/).(*kapierrors.StatusError) forbiddenError.ErrStatus.Message = reason // Not all API versions in valid API requests will have a matching codec in kubernetes. If we can't find one, // just default to the latest kube codec. codec := klatest.CodecForLegacyGroup() if requestedCodec, err := klatest.InterfacesForLegacyGroup(apiVersion); err == nil { codec = requestedCodec } formatted := &bytes.Buffer{} output, err := codec.Encode(&forbiddenError.ErrStatus) if err != nil { fmt.Fprintf(formatted, "%s", forbiddenError.Error()) } else { json.Indent(formatted, output, "", " ") } w.Header().Set("Content-Type", restful.MIME_JSON) w.WriteHeader(http.StatusForbidden) w.Write(formatted.Bytes()) }
func (impersonateAuthorizer) Authorize(ctx kapi.Context, a authorizer.AuthorizationAttributes) (allowed bool, reason string, err error) { user, exists := kapi.UserFrom(ctx) if !exists { return false, "missing user", nil } switch { case user.GetName() == "system:admin": return true, "", nil case user.GetName() == "tester": return false, "", fmt.Errorf("works on my machine") case user.GetName() == "deny-me": return false, "denied", nil } if len(user.GetGroups()) == 1 && user.GetGroups()[0] == "wheel" && a.GetVerb() == "impersonate" && a.GetResource() == "systemusers" { return true, "", nil } if len(user.GetGroups()) == 1 && user.GetGroups()[0] == "sa-impersonater" && a.GetVerb() == "impersonate" && a.GetResource() == "serviceaccounts" { return true, "", nil } if len(user.GetGroups()) == 1 && user.GetGroups()[0] == "regular-impersonater" && a.GetVerb() == "impersonate" && a.GetResource() == "users" { return true, "", nil } return false, "deny by default", nil }
func cacheKey(ctx kapi.Context, a authorizer.AuthorizationAttributes) (string, error) { if a.GetRequestAttributes() != nil { // TODO: see if we can serialize this? return "", errors.New("cannot cache request attributes") } keyData := map[string]interface{}{ "verb": a.GetVerb(), "apiVersion": a.GetAPIVersion(), "apiGroup": a.GetAPIGroup(), "resource": a.GetResource(), "resourceName": a.GetResourceName(), "nonResourceURL": a.IsNonResourceURL(), "url": a.GetURL(), } if namespace, ok := kapi.NamespaceFrom(ctx); ok { keyData["namespace"] = namespace } if user, ok := kapi.UserFrom(ctx); ok { keyData["user"] = user.GetName() keyData["groups"] = user.GetGroups() } key, err := json.Marshal(keyData) return string(key), err }