Skip to content

Commit

Permalink
feat: add p/nestedpkg (gnolang#2342)
Browse files Browse the repository at this point in the history
This package demonstrates a new access control pattern relying on
package paths.

It considers that the gnolang#1107 feature is already implemented to rely on
the built-in security.

You can see an ongoing usage in gnolang#2334.

---------

Signed-off-by: moul <[email protected]>
  • Loading branch information
moul authored and gfanton committed Jul 23, 2024
1 parent 0a52045 commit 8d3cfba
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 1 deletion.
3 changes: 3 additions & 0 deletions examples/gno.land/p/demo/nestedpkg/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module nestedpkg

require gno.land/r/demo/tests v0.0.0-latest
84 changes: 84 additions & 0 deletions examples/gno.land/p/demo/nestedpkg/nestedpkg.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Package nestedpkg provides helpers for package-path based access control.
// It is useful for upgrade patterns relying on namespaces.
package nestedpkg

import (
"std"
"strings"
)

// IsCallerSubPath checks if the caller realm is located in a subfolder of the current realm.
func IsCallerSubPath() bool {
var (
cur = std.CurrentRealm().PkgPath() + "/"
prev = std.PrevRealm().PkgPath() + "/"
)
return strings.HasPrefix(prev, cur)
}

// AssertCallerIsSubPath panics if IsCallerSubPath returns false.
func AssertCallerIsSubPath() {
var (
cur = std.CurrentRealm().PkgPath() + "/"
prev = std.PrevRealm().PkgPath() + "/"
)
if !strings.HasPrefix(prev, cur) {
panic("call restricted to nested packages. current realm is " + cur + ", previous realm is " + prev)
}
}

// IsCallerParentPath checks if the caller realm is located in a parent location of the current realm.
func IsCallerParentPath() bool {
var (
cur = std.CurrentRealm().PkgPath() + "/"
prev = std.PrevRealm().PkgPath() + "/"
)
return strings.HasPrefix(cur, prev)
}

// AssertCallerIsParentPath panics if IsCallerParentPath returns false.
func AssertCallerIsParentPath() {
var (
cur = std.CurrentRealm().PkgPath() + "/"
prev = std.PrevRealm().PkgPath() + "/"
)
if !strings.HasPrefix(cur, prev) {
panic("call restricted to parent packages. current realm is " + cur + ", previous realm is " + prev)
}
}

// IsSameNamespace checks if the caller realm and the current realm are in the same namespace.
func IsSameNamespace() bool {
var (
cur = nsFromPath(std.CurrentRealm().PkgPath()) + "/"
prev = nsFromPath(std.PrevRealm().PkgPath()) + "/"
)
return cur == prev
}

// AssertIsSameNamespace panics if IsSameNamespace returns false.
func AssertIsSameNamespace() {
var (
cur = nsFromPath(std.CurrentRealm().PkgPath()) + "/"
prev = nsFromPath(std.PrevRealm().PkgPath()) + "/"
)
if cur != prev {
panic("call restricted to packages from the same namespace. current realm is " + cur + ", previous realm is " + prev)
}
}

// nsFromPath extracts the namespace from a package path.
func nsFromPath(pkgpath string) string {
parts := strings.Split(pkgpath, "/")

// Specifically for gno.land, potential paths are in the form of DOMAIN/r/NAMESPACE/...
// XXX: Consider extra checks.
// XXX: Support non gno.land domains, where p/ and r/ won't be enforced.
if len(parts) >= 3 {
return parts[2]
}
return ""
}

// XXX: Consider adding IsCallerDirectlySubPath
// XXX: Consider adding IsCallerDirectlyParentPath
75 changes: 75 additions & 0 deletions examples/gno.land/p/demo/nestedpkg/nestedpkg_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package nestedpkg

import (
"std"
"testing"

"gno.land/r/demo/tests"
)

func TestPackage(t *testing.T) {
// direct child
cur := "gno.land/r/demo/tests/foo"
std.TestSetRealm(std.NewCodeRealm(cur))
if !tests.IsCallerSubPath() {
t.Errorf(cur + " should be a sub path")
}
if tests.IsCallerParentPath() {
t.Errorf(cur + " should not be a parent path")
}
if !tests.HasCallerSameNamespace() {
t.Errorf(cur + " should be from the same namespace")
}

// grand-grand-child
cur = "gno.land/r/demo/tests/foo/bar/baz"
std.TestSetRealm(std.NewCodeRealm(cur))
if !tests.IsCallerSubPath() {
t.Errorf(cur + " should be a sub path")
}
if tests.IsCallerParentPath() {
t.Errorf(cur + " should not be a parent path")
}
if !tests.HasCallerSameNamespace() {
t.Errorf(cur + " should be from the same namespace")
}

// direct parent
cur = "gno.land/r/demo"
std.TestSetRealm(std.NewCodeRealm(cur))
if tests.IsCallerSubPath() {
t.Errorf(cur + " should not be a sub path")
}
if !tests.IsCallerParentPath() {
t.Errorf(cur + " should be a parent path")
}
if !tests.HasCallerSameNamespace() {
t.Errorf(cur + " should be from the same namespace")
}

// fake parent (prefix)
cur = "gno.land/r/dem"
std.TestSetRealm(std.NewCodeRealm(cur))
if tests.IsCallerSubPath() {
t.Errorf(cur + " should not be a sub path")
}
if tests.IsCallerParentPath() {
t.Errorf(cur + " should not be a parent path")
}
if tests.HasCallerSameNamespace() {
t.Errorf(cur + " should not be from the same namespace")
}

// different namespace
cur = "gno.land/r/foo"
std.TestSetRealm(std.NewCodeRealm(cur))
if tests.IsCallerSubPath() {
t.Errorf(cur + " should not be a sub path")
}
if tests.IsCallerParentPath() {
t.Errorf(cur + " should not be a parent path")
}
if tests.HasCallerSameNamespace() {
t.Errorf(cur + " should not be from the same namespace")
}
}
5 changes: 4 additions & 1 deletion examples/gno.land/r/demo/tests/gno.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
module gno.land/r/demo/tests

require gno.land/r/demo/tests/subtests v0.0.0-latest
require (
gno.land/p/demo/nestedpkg v0.0.0-latest
gno.land/r/demo/tests/subtests v0.0.0-latest
)
13 changes: 13 additions & 0 deletions examples/gno.land/r/demo/tests/tests.gno
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tests
import (
"std"

"gno.land/p/demo/nestedpkg"
rsubtests "gno.land/r/demo/tests/subtests"
)

Expand Down Expand Up @@ -99,3 +100,15 @@ func GetRSubtestsPrevRealm() std.Realm {
func Exec(fn func()) {
fn()
}

func IsCallerSubPath() bool {
return nestedpkg.IsCallerSubPath()
}

func IsCallerParentPath() bool {
return nestedpkg.IsCallerParentPath()
}

func HasCallerSameNamespace() bool {
return nestedpkg.IsSameNamespace()
}

0 comments on commit 8d3cfba

Please sign in to comment.