blob: 202b5f56e5217683eae1dbff5393ac3db8a6d2f7 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"strings"
"v.io/v23/security"
"v.io/v23/vdl"
"v.io/x/ref/lib/vdl/build"
"v.io/x/ref/lib/vdl/compile"
"v.io/x/ref/lib/vdl/parse"
)
// caveatsFlag defines a flag.Value for receiving multiple caveat definitions.
type caveatsFlag struct {
caveatInfos []caveatInfo
}
type caveatInfo struct {
expr, params string
}
// Implements flag.Value.Get
func (c caveatsFlag) Get() interface{} {
return c.caveatInfos
}
// Implements flag.Value.Set
// Set expects s to be of the form:
// caveatExpr=VDLExpressionOfParam
func (c *caveatsFlag) Set(s string) error {
exprAndParam := strings.SplitN(s, "=", 2)
if len(exprAndParam) != 2 {
return fmt.Errorf("incorrect caveat format: %s", s)
}
c.caveatInfos = append(c.caveatInfos, caveatInfo{exprAndParam[0], exprAndParam[1]})
return nil
}
// Implements flag.Value.String
func (c caveatsFlag) String() string {
return fmt.Sprint(c.caveatInfos)
}
func (c caveatsFlag) usage() string {
return `"package/path".CaveatName:VDLExpressionParam to attach to this blessing`
}
func (c caveatsFlag) Compile() ([]security.Caveat, error) {
if len(c.caveatInfos) == 0 {
return nil, nil
}
env := compile.NewEnv(-1)
if err := buildPackages(c.caveatInfos, env); err != nil {
return nil, err
}
var caveats []security.Caveat
for _, info := range c.caveatInfos {
caveat, err := newCaveat(info, env)
if err != nil {
return nil, err
}
caveats = append(caveats, caveat)
}
return caveats, nil
}
func buildPackages(caveatInfos []caveatInfo, env *compile.Env) error {
var (
pkgNames []string
exprs []string
)
for _, info := range caveatInfos {
exprs = append(exprs, info.expr, info.params)
}
for _, pexpr := range parse.ParseExprs(strings.Join(exprs, ","), env.Errors) {
pkgNames = append(pkgNames, parse.ExtractExprPackagePaths(pexpr)...)
}
if !env.Errors.IsEmpty() {
return fmt.Errorf("can't build expressions %v:\n%v", exprs, env.Errors)
}
pkgs := build.TransitivePackages(pkgNames, build.UnknownPathIsError, build.Opts{}, env.Errors)
if !env.Errors.IsEmpty() {
return fmt.Errorf("failed to get transitive packages %v: %s", pkgNames, env.Errors)
}
for _, p := range pkgs {
if build.BuildPackage(p, env); !env.Errors.IsEmpty() {
return fmt.Errorf("failed to build package(%v): %v", p, env.Errors)
}
}
return nil
}
func newCaveat(info caveatInfo, env *compile.Env) (security.Caveat, error) {
caveatDesc, err := compileCaveatDesc(info.expr, env)
if err != nil {
return security.Caveat{}, err
}
param, err := compileParams(info.params, caveatDesc.ParamType, env)
if err != nil {
return security.Caveat{}, err
}
return security.NewCaveat(caveatDesc, param)
}
func compileCaveatDesc(expr string, env *compile.Env) (security.CaveatDescriptor, error) {
vdlValues := build.BuildExprs(expr, []*vdl.Type{vdl.TypeOf(security.CaveatDescriptor{})}, env)
if err := env.Errors.ToError(); err != nil {
return security.CaveatDescriptor{}, fmt.Errorf("can't build caveat desc %s:\n%v", expr, err)
}
if len(vdlValues) == 0 {
return security.CaveatDescriptor{}, fmt.Errorf("no caveat descriptors were built")
}
var desc security.CaveatDescriptor
if err := vdl.Convert(&desc, vdlValues[0]); err != nil {
return security.CaveatDescriptor{}, err
}
return desc, nil
}
func compileParams(paramData string, vdlType *vdl.Type, env *compile.Env) (interface{}, error) {
params := build.BuildExprs(paramData, []*vdl.Type{vdlType}, env)
if !env.Errors.IsEmpty() {
return nil, fmt.Errorf("can't build param data %s:\n%v", paramData, env.Errors)
}
if len(params) == 0 {
return security.CaveatDescriptor{}, fmt.Errorf("no caveat params were built")
}
return params[0], nil
}