|
|
|
|
@@ -364,8 +364,9 @@ func (s *nodeServiceImpl) Add(input AddInput) (*models.Node, error) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Edge rels.
|
|
|
|
|
// Edge rels. Track the namespace the node is placed in for ownership.
|
|
|
|
|
hasCreated := false
|
|
|
|
|
var actualNsID string
|
|
|
|
|
for _, ri := range input.Rels {
|
|
|
|
|
if ri.Target == "" {
|
|
|
|
|
continue // already stored as tag
|
|
|
|
|
@@ -377,6 +378,9 @@ func (s *nodeServiceImpl) Add(input AddInput) (*models.Node, error) {
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if ri.Type == models.RelInNamespace {
|
|
|
|
|
actualNsID = resolved
|
|
|
|
|
}
|
|
|
|
|
if ri.Type == models.RelHasOwnership {
|
|
|
|
|
// Ownership transfer: remove existing owner of the target.
|
|
|
|
|
existingOwners, _ := st.FindNodes([]*models.Rel{{Type: models.RelHasOwnership, Target: resolved}})
|
|
|
|
|
@@ -398,6 +402,7 @@ func (s *nodeServiceImpl) Add(input AddInput) (*models.Node, error) {
|
|
|
|
|
if err := st.AddRel(id, string(models.RelInNamespace), nsID); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
actualNsID = nsID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Default created.
|
|
|
|
|
@@ -411,12 +416,19 @@ func (s *nodeServiceImpl) Add(input AddInput) (*models.Node, error) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Grant creator ownership of the new node.
|
|
|
|
|
// Grant ownership of the new node.
|
|
|
|
|
// Namespace nodes are owned by their creator. All other nodes are owned
|
|
|
|
|
// by the namespace they belong to — the user retains transitive ownership
|
|
|
|
|
// through the namespace's own ownership chain (e.g. user→owns→default-ns→owns→node).
|
|
|
|
|
creatorID, err := s.resolveUserRef(st, s.userID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := st.AddRel(creatorID, string(models.RelHasOwnership), id); err != nil {
|
|
|
|
|
ownerID := creatorID
|
|
|
|
|
if tmp.GetProperty("type") != "namespace" && actualNsID != "" {
|
|
|
|
|
ownerID = actualNsID
|
|
|
|
|
}
|
|
|
|
|
if err := st.AddRel(ownerID, string(models.RelHasOwnership), id); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -810,6 +822,37 @@ func (s *nodeServiceImpl) resolveUserRef(st store.GraphStore, ref string) (strin
|
|
|
|
|
return s.ensureUser(st, ref)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const globalNamespace = "_global"
|
|
|
|
|
|
|
|
|
|
func (s *nodeServiceImpl) ensureGlobalNamespace(st store.GraphStore) (string, error) {
|
|
|
|
|
nsID, err := s.resolveIDByNameAndType(st, globalNamespace, "namespace")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
if nsID != "" {
|
|
|
|
|
return nsID, nil
|
|
|
|
|
}
|
|
|
|
|
id, err := st.GenerateID()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
now := time.Now().UTC().Format(time.RFC3339)
|
|
|
|
|
if err := st.AddNode(id, globalNamespace, "", nil, now, now); err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
if err := st.AddRel(id, "_type::namespace", ""); err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
if err := st.AddRel(id, string(models.RelInNamespace), id); err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
// Self-owned so no single user controls it.
|
|
|
|
|
if err := st.AddRel(id, string(models.RelHasOwnership), id); err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
return id, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *nodeServiceImpl) ensureUser(st store.GraphStore, username string) (string, error) {
|
|
|
|
|
userID, err := s.resolveIDByNameAndType(st, username, "user")
|
|
|
|
|
if err != nil {
|
|
|
|
|
@@ -833,6 +876,17 @@ func (s *nodeServiceImpl) ensureUser(st store.GraphStore, username string) (stri
|
|
|
|
|
if err := st.AddRel(id, string(models.RelHasOwnership), id); err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
// Every user gets can_create_rel access to the global namespace (inclusive
|
|
|
|
|
// of can_read), providing a shared space where all users can see and interact with
|
|
|
|
|
// nodes that are published there. User nodes themselves are already
|
|
|
|
|
// globally readable via the post-BFS identity override in getPermContext.
|
|
|
|
|
globalNsID, err := s.ensureGlobalNamespace(st)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
if err := st.AddRel(id, string(models.RelCanCreateRel), globalNsID); err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
return id, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|