Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add p/nestedpkg #2342

Merged
merged 11 commits into from
Jul 6, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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() + "/"
deelawn marked this conversation as resolved.
Show resolved Hide resolved
)
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]
deelawn marked this conversation as resolved.
Show resolved Hide resolved
}
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")
}
}
1 change: 1 addition & 0 deletions examples/gno.land/r/demo/tests/gno.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module gno.land/r/demo/tests

require (
gno.land/p/demo/nestedpkg v0.0.0-latest
gno.land/p/demo/testutils 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()
}
Loading