| // 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(%v): %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 |
| } |