blob: 2f98264034b01e00576b84ca66ed7e21ef2942d3 [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"
"regexp"
"strings"
"v.io/v23/security"
"v.io/v23/vdl"
"v.io/x/ref/lib/vdl/build"
"v.io/x/ref/lib/vdl/compile"
)
// caveatsFlag defines a flag.Value for receiving multiple caveat definitions.
type caveatsFlag struct {
caveatInfos []caveatInfo
}
type caveatInfo struct {
pkg, 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)
}
expr, param := exprAndParam[0], exprAndParam[1]
// If the caveatExpr is of the form "path/to/package".ConstName we
// need to extract the package to later obtain the imports.
var pkg string
pkgre, err := regexp.Compile(`\A"(.*)"\.`)
if err != nil {
return fmt.Errorf("failed to parse pkgregex: %v", err)
}
match := pkgre.FindStringSubmatch(expr)
if len(match) == 2 {
pkg = match[1]
}
c.caveatInfos = append(c.caveatInfos, caveatInfo{pkg, expr, param})
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
}
var caveats []security.Caveat
env := compile.NewEnv(-1)
var pkgs []string
for _, info := range c.caveatInfos {
if len(info.pkg) > 0 {
pkgs = append(pkgs, info.pkg)
}
}
if err := buildPackages(pkgs, env); err != nil {
return nil, err
}
for _, info := range c.caveatInfos {
caveat, err := newCaveat(info, env)
if err != nil {
return nil, err
}
caveats = append(caveats, caveat)
}
return caveats, nil
}
func newCaveat(info caveatInfo, env *compile.Env) (security.Caveat, error) {
caveatDesc, err := compileCaveatDesc(info.pkg, 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 compileParams(paramData string, vdlType *vdl.Type, env *compile.Env) (interface{}, error) {
params := build.BuildExprs(paramData, []*vdl.Type{vdlType}, env)
if err := env.Errors.ToError(); err != nil {
return nil, fmt.Errorf("can't parse param data %s:\n%v", paramData, err)
}
return params[0], nil
}
func buildPackages(packages []string, env *compile.Env) error {
pkgs := build.TransitivePackages(packages, build.UnknownPathIsError, build.Opts{}, env.Errors)
if !env.Errors.IsEmpty() {
return fmt.Errorf("failed to get transitive packages packages: %s", env.Errors)
}
for _, p := range pkgs {
build.BuildPackage(p, env)
if !env.Errors.IsEmpty() {
return fmt.Errorf("failed to build package(%s): %s", p, env.Errors)
}
}
return nil
}
func compileCaveatDesc(pkg, expr string, env *compile.Env) (security.CaveatDescriptor, error) {
var vdlValue *vdl.Value
// In the case that the expr is of the form "path/to/package".ConstName we need to get the
// resolve the const name instead of building the expressions.
// TODO(suharshs,toddw): We need to fix BuildExprs so that both these cases will work with it.
// The issue is that BuildExprs returns a dummy file with no imports instead and errors out instead of
// looking at the imports in the env.
if len(pkg) > 0 {
spl := strings.SplitN(expr, "\".", 2)
constdef := env.ResolvePackage(pkg).ResolveConst(spl[1])
if constdef == nil {
return security.CaveatDescriptor{}, fmt.Errorf("failed to find caveat %v", expr)
}
vdlValue = constdef.Value
} else {
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")
}
vdlValue = vdlValues[0]
}
var desc security.CaveatDescriptor
if err := vdl.Convert(&desc, vdlValue); err != nil {
return security.CaveatDescriptor{}, err
}
return desc, nil
}