cojson 0.8.18 → 0.8.19-group-inheritance.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +12 -0
- package/.turbo/turbo-lint.log +4 -0
- package/.turbo/turbo-test.log +1001 -0
- package/CHANGELOG.md +6 -0
- package/dist/native/coValueCore.js +34 -3
- package/dist/native/coValueCore.js.map +1 -1
- package/dist/native/coValues/group.js +89 -3
- package/dist/native/coValues/group.js.map +1 -1
- package/dist/native/permissions.js +174 -145
- package/dist/native/permissions.js.map +1 -1
- package/dist/native/storage/index.js +8 -4
- package/dist/native/storage/index.js.map +1 -1
- package/dist/native/sync.js +5 -0
- package/dist/native/sync.js.map +1 -1
- package/dist/web/coValueCore.js +34 -3
- package/dist/web/coValueCore.js.map +1 -1
- package/dist/web/coValues/group.js +89 -3
- package/dist/web/coValues/group.js.map +1 -1
- package/dist/web/permissions.js +174 -145
- package/dist/web/permissions.js.map +1 -1
- package/dist/web/storage/index.js +8 -4
- package/dist/web/storage/index.js.map +1 -1
- package/dist/web/sync.js +5 -0
- package/dist/web/sync.js.map +1 -1
- package/package.json +1 -1
- package/src/coValueCore.ts +50 -4
- package/src/coValues/group.ts +159 -4
- package/src/permissions.ts +244 -203
- package/src/storage/index.ts +12 -4
- package/src/sync.ts +5 -0
- package/src/tests/permissions.test.ts +748 -0
|
@@ -1708,3 +1708,751 @@ test("Can give write permissions to 'everyone' (high-level)", async () => {
|
|
|
1708
1708
|
childContent2.set("foo", "bar2", "private");
|
|
1709
1709
|
expect(childContent2.get("foo")).toEqual("bar2");
|
|
1710
1710
|
});
|
|
1711
|
+
|
|
1712
|
+
test("Admins can set parent extensions", () => {
|
|
1713
|
+
const { group, node } = newGroupHighLevel();
|
|
1714
|
+
const parentGroup = node.createGroup();
|
|
1715
|
+
|
|
1716
|
+
group.set(`parent_${parentGroup.id}`, "extend", "trusting");
|
|
1717
|
+
expect(group.get(`parent_${parentGroup.id}`)).toEqual("extend");
|
|
1718
|
+
});
|
|
1719
|
+
|
|
1720
|
+
test("Writers, readers and invitees can not set parent extensions", () => {
|
|
1721
|
+
const { group, node } = newGroupHighLevel();
|
|
1722
|
+
const parentGroup = node.createGroup();
|
|
1723
|
+
|
|
1724
|
+
const writer = node.createAccount();
|
|
1725
|
+
const reader = node.createAccount();
|
|
1726
|
+
const adminInvite = node.createAccount();
|
|
1727
|
+
const writerInvite = node.createAccount();
|
|
1728
|
+
const readerInvite = node.createAccount();
|
|
1729
|
+
|
|
1730
|
+
group.addMember(writer, "writer");
|
|
1731
|
+
group.addMember(reader, "reader");
|
|
1732
|
+
group.addMember(adminInvite, "adminInvite");
|
|
1733
|
+
group.addMember(writerInvite, "writerInvite");
|
|
1734
|
+
group.addMember(readerInvite, "readerInvite");
|
|
1735
|
+
|
|
1736
|
+
const groupAsWriter = expectGroup(
|
|
1737
|
+
group.core
|
|
1738
|
+
.testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
|
|
1739
|
+
.getCurrentContent(),
|
|
1740
|
+
);
|
|
1741
|
+
|
|
1742
|
+
groupAsWriter.set(`parent_${parentGroup.id}`, "extend", "trusting");
|
|
1743
|
+
expect(groupAsWriter.get(`parent_${parentGroup.id}`)).toBeUndefined();
|
|
1744
|
+
|
|
1745
|
+
const groupAsReader = expectGroup(
|
|
1746
|
+
group.core
|
|
1747
|
+
.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
|
|
1748
|
+
.getCurrentContent(),
|
|
1749
|
+
);
|
|
1750
|
+
|
|
1751
|
+
groupAsReader.set(`parent_${parentGroup.id}`, "extend", "trusting");
|
|
1752
|
+
expect(groupAsReader.get(`parent_${parentGroup.id}`)).toBeUndefined();
|
|
1753
|
+
|
|
1754
|
+
const groupAsAdminInvite = expectGroup(
|
|
1755
|
+
group.core
|
|
1756
|
+
.testWithDifferentAccount(
|
|
1757
|
+
adminInvite,
|
|
1758
|
+
Crypto.newRandomSessionID(adminInvite.currentAgentID()._unsafeUnwrap()),
|
|
1759
|
+
)
|
|
1760
|
+
.getCurrentContent(),
|
|
1761
|
+
);
|
|
1762
|
+
|
|
1763
|
+
groupAsAdminInvite.set(`parent_${parentGroup.id}`, "extend", "trusting");
|
|
1764
|
+
expect(groupAsAdminInvite.get(`parent_${parentGroup.id}`)).toBeUndefined();
|
|
1765
|
+
|
|
1766
|
+
const groupAsWriterInvite = expectGroup(
|
|
1767
|
+
group.core
|
|
1768
|
+
.testWithDifferentAccount(
|
|
1769
|
+
writerInvite,
|
|
1770
|
+
Crypto.newRandomSessionID(
|
|
1771
|
+
writerInvite.currentAgentID()._unsafeUnwrap(),
|
|
1772
|
+
),
|
|
1773
|
+
)
|
|
1774
|
+
.getCurrentContent(),
|
|
1775
|
+
);
|
|
1776
|
+
|
|
1777
|
+
groupAsWriterInvite.set(`parent_${parentGroup.id}`, "extend", "trusting");
|
|
1778
|
+
expect(groupAsWriterInvite.get(`parent_${parentGroup.id}`)).toBeUndefined();
|
|
1779
|
+
|
|
1780
|
+
const groupAsReaderInvite = expectGroup(
|
|
1781
|
+
group.core
|
|
1782
|
+
.testWithDifferentAccount(
|
|
1783
|
+
readerInvite,
|
|
1784
|
+
Crypto.newRandomSessionID(
|
|
1785
|
+
readerInvite.currentAgentID()._unsafeUnwrap(),
|
|
1786
|
+
),
|
|
1787
|
+
)
|
|
1788
|
+
.getCurrentContent(),
|
|
1789
|
+
);
|
|
1790
|
+
|
|
1791
|
+
groupAsReaderInvite.set(`parent_${parentGroup.id}`, "extend", "trusting");
|
|
1792
|
+
expect(groupAsReaderInvite.get(`parent_${parentGroup.id}`)).toBeUndefined();
|
|
1793
|
+
});
|
|
1794
|
+
|
|
1795
|
+
test("Admins can set child extensions", () => {
|
|
1796
|
+
const { group, node } = newGroupHighLevel();
|
|
1797
|
+
const childGroup = node.createGroup();
|
|
1798
|
+
|
|
1799
|
+
group.set(`child_${childGroup.id}`, "extend", "trusting");
|
|
1800
|
+
expect(group.get(`child_${childGroup.id}`)).toEqual("extend");
|
|
1801
|
+
});
|
|
1802
|
+
|
|
1803
|
+
test("Writers, readers and invitees can not set child extensions", () => {
|
|
1804
|
+
const { group, node } = newGroupHighLevel();
|
|
1805
|
+
const childGroup = node.createGroup();
|
|
1806
|
+
|
|
1807
|
+
const writer = node.createAccount();
|
|
1808
|
+
const reader = node.createAccount();
|
|
1809
|
+
const adminInvite = node.createAccount();
|
|
1810
|
+
const writerInvite = node.createAccount();
|
|
1811
|
+
const readerInvite = node.createAccount();
|
|
1812
|
+
|
|
1813
|
+
group.addMember(writer, "writer");
|
|
1814
|
+
group.addMember(reader, "reader");
|
|
1815
|
+
group.addMember(adminInvite, "adminInvite");
|
|
1816
|
+
group.addMember(writerInvite, "writerInvite");
|
|
1817
|
+
group.addMember(readerInvite, "readerInvite");
|
|
1818
|
+
|
|
1819
|
+
const groupAsWriter = expectGroup(
|
|
1820
|
+
group.core
|
|
1821
|
+
.testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
|
|
1822
|
+
.getCurrentContent(),
|
|
1823
|
+
);
|
|
1824
|
+
|
|
1825
|
+
groupAsWriter.set(`child_${childGroup.id}`, "extend", "trusting");
|
|
1826
|
+
expect(groupAsWriter.get(`child_${childGroup.id}`)).toBeUndefined();
|
|
1827
|
+
|
|
1828
|
+
const groupAsReader = expectGroup(
|
|
1829
|
+
group.core
|
|
1830
|
+
.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
|
|
1831
|
+
.getCurrentContent(),
|
|
1832
|
+
);
|
|
1833
|
+
|
|
1834
|
+
groupAsReader.set(`child_${childGroup.id}`, "extend", "trusting");
|
|
1835
|
+
expect(groupAsReader.get(`child_${childGroup.id}`)).toBeUndefined();
|
|
1836
|
+
|
|
1837
|
+
const groupAsAdminInvite = expectGroup(
|
|
1838
|
+
group.core
|
|
1839
|
+
.testWithDifferentAccount(
|
|
1840
|
+
adminInvite,
|
|
1841
|
+
Crypto.newRandomSessionID(adminInvite.currentAgentID()._unsafeUnwrap()),
|
|
1842
|
+
)
|
|
1843
|
+
.getCurrentContent(),
|
|
1844
|
+
);
|
|
1845
|
+
|
|
1846
|
+
groupAsAdminInvite.set(`child_${childGroup.id}`, "extend", "trusting");
|
|
1847
|
+
expect(groupAsAdminInvite.get(`child_${childGroup.id}`)).toBeUndefined();
|
|
1848
|
+
|
|
1849
|
+
const groupAsWriterInvite = expectGroup(
|
|
1850
|
+
group.core
|
|
1851
|
+
.testWithDifferentAccount(
|
|
1852
|
+
writerInvite,
|
|
1853
|
+
Crypto.newRandomSessionID(
|
|
1854
|
+
writerInvite.currentAgentID()._unsafeUnwrap(),
|
|
1855
|
+
),
|
|
1856
|
+
)
|
|
1857
|
+
.getCurrentContent(),
|
|
1858
|
+
);
|
|
1859
|
+
|
|
1860
|
+
groupAsWriterInvite.set(`child_${childGroup.id}`, "extend", "trusting");
|
|
1861
|
+
expect(groupAsWriterInvite.get(`child_${childGroup.id}`)).toBeUndefined();
|
|
1862
|
+
|
|
1863
|
+
const groupAsReaderInvite = expectGroup(
|
|
1864
|
+
group.core
|
|
1865
|
+
.testWithDifferentAccount(
|
|
1866
|
+
readerInvite,
|
|
1867
|
+
Crypto.newRandomSessionID(
|
|
1868
|
+
readerInvite.currentAgentID()._unsafeUnwrap(),
|
|
1869
|
+
),
|
|
1870
|
+
)
|
|
1871
|
+
.getCurrentContent(),
|
|
1872
|
+
);
|
|
1873
|
+
|
|
1874
|
+
groupAsReaderInvite.set(`child_${childGroup.id}`, "extend", "trusting");
|
|
1875
|
+
expect(groupAsReaderInvite.get(`child_${childGroup.id}`)).toBeUndefined();
|
|
1876
|
+
});
|
|
1877
|
+
|
|
1878
|
+
test("Member roles are inherited by child groups (except invites)", () => {
|
|
1879
|
+
const { group, node, admin } = newGroupHighLevel();
|
|
1880
|
+
const parentGroup = node.createGroup();
|
|
1881
|
+
|
|
1882
|
+
group.set(`parent_${parentGroup.id}`, "extend", "trusting");
|
|
1883
|
+
|
|
1884
|
+
const writer = node.createAccount();
|
|
1885
|
+
const reader = node.createAccount();
|
|
1886
|
+
const adminInvite = node.createAccount();
|
|
1887
|
+
const writerInvite = node.createAccount();
|
|
1888
|
+
const readerInvite = node.createAccount();
|
|
1889
|
+
|
|
1890
|
+
parentGroup.addMember(writer, "writer");
|
|
1891
|
+
parentGroup.addMember(reader, "reader");
|
|
1892
|
+
parentGroup.addMember(adminInvite, "adminInvite");
|
|
1893
|
+
parentGroup.addMember(writerInvite, "writerInvite");
|
|
1894
|
+
parentGroup.addMember(readerInvite, "readerInvite");
|
|
1895
|
+
|
|
1896
|
+
expect(group.roleOfInternal(admin.id)).toEqual({
|
|
1897
|
+
role: "admin",
|
|
1898
|
+
via: undefined,
|
|
1899
|
+
});
|
|
1900
|
+
|
|
1901
|
+
expect(group.roleOfInternal(writer.id)).toEqual({
|
|
1902
|
+
role: "writer",
|
|
1903
|
+
via: parentGroup.id,
|
|
1904
|
+
});
|
|
1905
|
+
expect(group.roleOf(writer.id)).toEqual("writer");
|
|
1906
|
+
|
|
1907
|
+
expect(group.roleOfInternal(reader.id)).toEqual({
|
|
1908
|
+
role: "reader",
|
|
1909
|
+
via: parentGroup.id,
|
|
1910
|
+
});
|
|
1911
|
+
expect(group.roleOf(reader.id)).toEqual("reader");
|
|
1912
|
+
|
|
1913
|
+
expect(group.roleOfInternal(adminInvite.id)).toEqual(undefined);
|
|
1914
|
+
expect(group.roleOf(adminInvite.id)).toEqual(undefined);
|
|
1915
|
+
|
|
1916
|
+
expect(group.roleOfInternal(writerInvite.id)).toEqual(undefined);
|
|
1917
|
+
expect(group.roleOf(writerInvite.id)).toEqual(undefined);
|
|
1918
|
+
|
|
1919
|
+
expect(group.roleOfInternal(readerInvite.id)).toEqual(undefined);
|
|
1920
|
+
expect(group.roleOf(readerInvite.id)).toEqual(undefined);
|
|
1921
|
+
});
|
|
1922
|
+
|
|
1923
|
+
test("Member roles are inherited by grand-children groups (except invites)", () => {
|
|
1924
|
+
const { group, node, admin } = newGroupHighLevel();
|
|
1925
|
+
const parentGroup = node.createGroup();
|
|
1926
|
+
const grandParentGroup = node.createGroup();
|
|
1927
|
+
|
|
1928
|
+
group.set(`parent_${parentGroup.id}`, "extend", "trusting");
|
|
1929
|
+
parentGroup.set(`parent_${grandParentGroup.id}`, "extend", "trusting");
|
|
1930
|
+
|
|
1931
|
+
const writer = node.createAccount();
|
|
1932
|
+
const reader = node.createAccount();
|
|
1933
|
+
const adminInvite = node.createAccount();
|
|
1934
|
+
const writerInvite = node.createAccount();
|
|
1935
|
+
const readerInvite = node.createAccount();
|
|
1936
|
+
|
|
1937
|
+
grandParentGroup.addMember(writer, "writer");
|
|
1938
|
+
grandParentGroup.addMember(reader, "reader");
|
|
1939
|
+
grandParentGroup.addMember(adminInvite, "adminInvite");
|
|
1940
|
+
grandParentGroup.addMember(writerInvite, "writerInvite");
|
|
1941
|
+
grandParentGroup.addMember(readerInvite, "readerInvite");
|
|
1942
|
+
|
|
1943
|
+
expect(group.roleOfInternal(admin.id)).toEqual({
|
|
1944
|
+
role: "admin",
|
|
1945
|
+
via: undefined,
|
|
1946
|
+
});
|
|
1947
|
+
|
|
1948
|
+
expect(group.roleOfInternal(writer.id)).toEqual({
|
|
1949
|
+
role: "writer",
|
|
1950
|
+
via: parentGroup.id,
|
|
1951
|
+
});
|
|
1952
|
+
expect(group.roleOf(writer.id)).toEqual("writer");
|
|
1953
|
+
|
|
1954
|
+
expect(group.roleOfInternal(reader.id)).toEqual({
|
|
1955
|
+
role: "reader",
|
|
1956
|
+
via: parentGroup.id,
|
|
1957
|
+
});
|
|
1958
|
+
expect(group.roleOf(reader.id)).toEqual("reader");
|
|
1959
|
+
|
|
1960
|
+
expect(group.roleOfInternal(adminInvite.id)).toEqual(undefined);
|
|
1961
|
+
expect(group.roleOf(adminInvite.id)).toEqual(undefined);
|
|
1962
|
+
|
|
1963
|
+
expect(group.roleOfInternal(writerInvite.id)).toEqual(undefined);
|
|
1964
|
+
expect(group.roleOf(writerInvite.id)).toEqual(undefined);
|
|
1965
|
+
|
|
1966
|
+
expect(group.roleOfInternal(readerInvite.id)).toEqual(undefined);
|
|
1967
|
+
expect(group.roleOf(readerInvite.id)).toEqual(undefined);
|
|
1968
|
+
});
|
|
1969
|
+
|
|
1970
|
+
test("Admins can reveal parent read keys to child groups", () => {
|
|
1971
|
+
const { group, node } = newGroupHighLevel();
|
|
1972
|
+
const parentGroup = node.createGroup();
|
|
1973
|
+
|
|
1974
|
+
const parentReadKeyID = parentGroup.get("readKey");
|
|
1975
|
+
if (!parentReadKeyID) {
|
|
1976
|
+
throw new Error("Can't get parent group read key");
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
const readKeyID = group.get("readKey");
|
|
1980
|
+
if (!readKeyID) {
|
|
1981
|
+
throw new Error("Can't get group read key");
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1985
|
+
const encrypted = "fake_encrypted_key_secret" as any;
|
|
1986
|
+
|
|
1987
|
+
group.set(`${readKeyID}_for_${parentReadKeyID}`, encrypted, "trusting");
|
|
1988
|
+
expect(group.get(`${readKeyID}_for_${parentReadKeyID}`)).toEqual(encrypted);
|
|
1989
|
+
});
|
|
1990
|
+
|
|
1991
|
+
test("Writers, readers and invites can't reveal parent read keys to child groups", () => {
|
|
1992
|
+
const { group, node } = newGroupHighLevel();
|
|
1993
|
+
const parentGroup = node.createGroup();
|
|
1994
|
+
|
|
1995
|
+
const parentReadKeyID = parentGroup.get("readKey");
|
|
1996
|
+
if (!parentReadKeyID) {
|
|
1997
|
+
throw new Error("Can't get parent group read key");
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
const readKeyID = group.get("readKey");
|
|
2001
|
+
if (!readKeyID) {
|
|
2002
|
+
throw new Error("Can't get group read key");
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2006
|
+
const encrypted = "fake_encrypted_key_secret" as any;
|
|
2007
|
+
|
|
2008
|
+
const writer = node.createAccount();
|
|
2009
|
+
const reader = node.createAccount();
|
|
2010
|
+
const adminInvite = node.createAccount();
|
|
2011
|
+
const writerInvite = node.createAccount();
|
|
2012
|
+
const readerInvite = node.createAccount();
|
|
2013
|
+
|
|
2014
|
+
group.addMember(writer, "writer");
|
|
2015
|
+
group.addMember(reader, "reader");
|
|
2016
|
+
group.addMember(adminInvite, "adminInvite");
|
|
2017
|
+
group.addMember(writerInvite, "writerInvite");
|
|
2018
|
+
group.addMember(readerInvite, "readerInvite");
|
|
2019
|
+
|
|
2020
|
+
const groupAsWriter = expectGroup(
|
|
2021
|
+
group.core
|
|
2022
|
+
.testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
|
|
2023
|
+
.getCurrentContent(),
|
|
2024
|
+
);
|
|
2025
|
+
|
|
2026
|
+
groupAsWriter.set(
|
|
2027
|
+
`${readKeyID}_for_${parentReadKeyID}`,
|
|
2028
|
+
encrypted,
|
|
2029
|
+
"trusting",
|
|
2030
|
+
);
|
|
2031
|
+
expect(
|
|
2032
|
+
groupAsWriter.get(`${readKeyID}_for_${parentReadKeyID}`),
|
|
2033
|
+
).toBeUndefined();
|
|
2034
|
+
|
|
2035
|
+
const groupAsReader = expectGroup(
|
|
2036
|
+
group.core
|
|
2037
|
+
.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
|
|
2038
|
+
.getCurrentContent(),
|
|
2039
|
+
);
|
|
2040
|
+
|
|
2041
|
+
groupAsReader.set(
|
|
2042
|
+
`${readKeyID}_for_${parentReadKeyID}`,
|
|
2043
|
+
encrypted,
|
|
2044
|
+
"trusting",
|
|
2045
|
+
);
|
|
2046
|
+
expect(
|
|
2047
|
+
groupAsReader.get(`${readKeyID}_for_${parentReadKeyID}`),
|
|
2048
|
+
).toBeUndefined();
|
|
2049
|
+
|
|
2050
|
+
const groupAsAdminInvite = expectGroup(
|
|
2051
|
+
group.core
|
|
2052
|
+
.testWithDifferentAccount(
|
|
2053
|
+
adminInvite,
|
|
2054
|
+
Crypto.newRandomSessionID(adminInvite.currentAgentID()._unsafeUnwrap()),
|
|
2055
|
+
)
|
|
2056
|
+
.getCurrentContent(),
|
|
2057
|
+
);
|
|
2058
|
+
|
|
2059
|
+
groupAsAdminInvite.set(
|
|
2060
|
+
`${readKeyID}_for_${parentReadKeyID}`,
|
|
2061
|
+
encrypted,
|
|
2062
|
+
"trusting",
|
|
2063
|
+
);
|
|
2064
|
+
expect(
|
|
2065
|
+
groupAsAdminInvite.get(`${readKeyID}_for_${parentReadKeyID}`),
|
|
2066
|
+
).toBeUndefined();
|
|
2067
|
+
|
|
2068
|
+
const groupAsWriterInvite = expectGroup(
|
|
2069
|
+
group.core
|
|
2070
|
+
.testWithDifferentAccount(
|
|
2071
|
+
writerInvite,
|
|
2072
|
+
Crypto.newRandomSessionID(
|
|
2073
|
+
writerInvite.currentAgentID()._unsafeUnwrap(),
|
|
2074
|
+
),
|
|
2075
|
+
)
|
|
2076
|
+
.getCurrentContent(),
|
|
2077
|
+
);
|
|
2078
|
+
|
|
2079
|
+
groupAsWriterInvite.set(
|
|
2080
|
+
`${readKeyID}_for_${parentReadKeyID}`,
|
|
2081
|
+
encrypted,
|
|
2082
|
+
"trusting",
|
|
2083
|
+
);
|
|
2084
|
+
expect(
|
|
2085
|
+
groupAsWriterInvite.get(`${readKeyID}_for_${parentReadKeyID}`),
|
|
2086
|
+
).toBeUndefined();
|
|
2087
|
+
|
|
2088
|
+
const groupAsReaderInvite = expectGroup(
|
|
2089
|
+
group.core
|
|
2090
|
+
.testWithDifferentAccount(
|
|
2091
|
+
readerInvite,
|
|
2092
|
+
Crypto.newRandomSessionID(
|
|
2093
|
+
readerInvite.currentAgentID()._unsafeUnwrap(),
|
|
2094
|
+
),
|
|
2095
|
+
)
|
|
2096
|
+
.getCurrentContent(),
|
|
2097
|
+
);
|
|
2098
|
+
|
|
2099
|
+
groupAsReaderInvite.set(
|
|
2100
|
+
`${readKeyID}_for_${parentReadKeyID}`,
|
|
2101
|
+
encrypted,
|
|
2102
|
+
"trusting",
|
|
2103
|
+
);
|
|
2104
|
+
expect(
|
|
2105
|
+
groupAsReaderInvite.get(`${readKeyID}_for_${parentReadKeyID}`),
|
|
2106
|
+
).toBeUndefined();
|
|
2107
|
+
});
|
|
2108
|
+
|
|
2109
|
+
test("Writers and readers in a parent group can read from an object owned by a child group", () => {
|
|
2110
|
+
const { group, node } = newGroupHighLevel();
|
|
2111
|
+
const parentGroup = node.createGroup();
|
|
2112
|
+
|
|
2113
|
+
group.set(`parent_${parentGroup.id}`, "extend", "trusting");
|
|
2114
|
+
|
|
2115
|
+
const parentReadKeyID = parentGroup.get("readKey");
|
|
2116
|
+
const parentKey =
|
|
2117
|
+
parentReadKeyID && parentGroup.core.getReadKey(parentReadKeyID);
|
|
2118
|
+
if (!parentReadKeyID || !parentKey) {
|
|
2119
|
+
throw new Error("Can't get parent group read key");
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
const readKeyID = group.get("readKey");
|
|
2123
|
+
const readKey = readKeyID && group.core.getReadKey(readKeyID);
|
|
2124
|
+
if (!readKeyID || !readKey) {
|
|
2125
|
+
throw new Error("Can't get group read key");
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
const encrypted = node.crypto.encryptKeySecret({
|
|
2129
|
+
toEncrypt: {
|
|
2130
|
+
id: readKeyID,
|
|
2131
|
+
secret: readKey,
|
|
2132
|
+
},
|
|
2133
|
+
encrypting: {
|
|
2134
|
+
id: parentReadKeyID,
|
|
2135
|
+
secret: parentKey,
|
|
2136
|
+
},
|
|
2137
|
+
}).encrypted;
|
|
2138
|
+
|
|
2139
|
+
group.set(`${readKeyID}_for_${parentReadKeyID}`, encrypted, "trusting");
|
|
2140
|
+
|
|
2141
|
+
const writer = node.createAccount();
|
|
2142
|
+
const reader = node.createAccount();
|
|
2143
|
+
parentGroup.addMember(writer, "writer");
|
|
2144
|
+
parentGroup.addMember(reader, "reader");
|
|
2145
|
+
|
|
2146
|
+
const childObject = node.createCoValue({
|
|
2147
|
+
type: "comap",
|
|
2148
|
+
ruleset: { type: "ownedByGroup", group: group.id },
|
|
2149
|
+
meta: null,
|
|
2150
|
+
...Crypto.createdNowUnique(),
|
|
2151
|
+
});
|
|
2152
|
+
|
|
2153
|
+
const childContent = expectMap(childObject.getCurrentContent());
|
|
2154
|
+
|
|
2155
|
+
childContent.set("foo", "bar", "private");
|
|
2156
|
+
expect(childContent.get("foo")).toEqual("bar");
|
|
2157
|
+
|
|
2158
|
+
const childContentAsWriter = expectMap(
|
|
2159
|
+
childObject
|
|
2160
|
+
.testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
|
|
2161
|
+
.getCurrentContent(),
|
|
2162
|
+
);
|
|
2163
|
+
|
|
2164
|
+
expect(childContentAsWriter.get("foo")).toEqual("bar");
|
|
2165
|
+
|
|
2166
|
+
const childContentAsReader = expectMap(
|
|
2167
|
+
childObject
|
|
2168
|
+
.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
|
|
2169
|
+
.getCurrentContent(),
|
|
2170
|
+
);
|
|
2171
|
+
|
|
2172
|
+
expect(childContentAsReader.get("foo")).toEqual("bar");
|
|
2173
|
+
});
|
|
2174
|
+
|
|
2175
|
+
test("Writers in a parent group can write to an object owned by a child group", () => {
|
|
2176
|
+
const { group, node } = newGroupHighLevel();
|
|
2177
|
+
const parentGroup = node.createGroup();
|
|
2178
|
+
|
|
2179
|
+
group.set(`parent_${parentGroup.id}`, "extend", "trusting");
|
|
2180
|
+
|
|
2181
|
+
const parentReadKeyID = parentGroup.get("readKey");
|
|
2182
|
+
const parentKey =
|
|
2183
|
+
parentReadKeyID && parentGroup.core.getReadKey(parentReadKeyID);
|
|
2184
|
+
if (!parentReadKeyID || !parentKey) {
|
|
2185
|
+
throw new Error("Can't get parent group read key");
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
const readKeyID = group.get("readKey");
|
|
2189
|
+
const readKey = readKeyID && group.core.getReadKey(readKeyID);
|
|
2190
|
+
if (!readKeyID || !readKey) {
|
|
2191
|
+
throw new Error("Can't get group read key");
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
const encrypted = node.crypto.encryptKeySecret({
|
|
2195
|
+
toEncrypt: {
|
|
2196
|
+
id: readKeyID,
|
|
2197
|
+
secret: readKey,
|
|
2198
|
+
},
|
|
2199
|
+
encrypting: {
|
|
2200
|
+
id: parentReadKeyID,
|
|
2201
|
+
secret: parentKey,
|
|
2202
|
+
},
|
|
2203
|
+
}).encrypted;
|
|
2204
|
+
|
|
2205
|
+
group.set(`${readKeyID}_for_${parentReadKeyID}`, encrypted, "trusting");
|
|
2206
|
+
|
|
2207
|
+
const writer = node.createAccount();
|
|
2208
|
+
parentGroup.addMember(writer, "writer");
|
|
2209
|
+
|
|
2210
|
+
const childObject = node.createCoValue({
|
|
2211
|
+
type: "comap",
|
|
2212
|
+
ruleset: { type: "ownedByGroup", group: group.id },
|
|
2213
|
+
meta: null,
|
|
2214
|
+
...Crypto.createdNowUnique(),
|
|
2215
|
+
});
|
|
2216
|
+
|
|
2217
|
+
const childContentAsWriter = expectMap(
|
|
2218
|
+
childObject
|
|
2219
|
+
.testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
|
|
2220
|
+
.getCurrentContent(),
|
|
2221
|
+
);
|
|
2222
|
+
|
|
2223
|
+
childContentAsWriter.set("foo", "bar", "private");
|
|
2224
|
+
expect(childContentAsWriter.get("foo")).toEqual("bar");
|
|
2225
|
+
});
|
|
2226
|
+
|
|
2227
|
+
test("When rotating the key of a child group, the new child key is exposed to the parent group", () => {
|
|
2228
|
+
const { group, node } = newGroupHighLevel();
|
|
2229
|
+
const parentGroup = node.createGroup();
|
|
2230
|
+
|
|
2231
|
+
group.set(`parent_${parentGroup.id}`, "extend", "trusting");
|
|
2232
|
+
|
|
2233
|
+
const currentReadKeyID = group.get("readKey");
|
|
2234
|
+
if (!currentReadKeyID) {
|
|
2235
|
+
throw new Error("Can't get group read key");
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
group.rotateReadKey();
|
|
2239
|
+
|
|
2240
|
+
const newReadKeyID = group.get("readKey");
|
|
2241
|
+
if (!newReadKeyID) {
|
|
2242
|
+
throw new Error("Can't get new group read key");
|
|
2243
|
+
}
|
|
2244
|
+
expect(newReadKeyID).not.toEqual(currentReadKeyID);
|
|
2245
|
+
|
|
2246
|
+
const parentReadKeyID = parentGroup.get("readKey");
|
|
2247
|
+
if (!parentReadKeyID) {
|
|
2248
|
+
throw new Error("Can't get parent group read key");
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
console.log("Checking", `${newReadKeyID}_for_${parentReadKeyID}`);
|
|
2252
|
+
|
|
2253
|
+
expect(group.get(`${newReadKeyID}_for_${parentReadKeyID}`)).toBeDefined();
|
|
2254
|
+
});
|
|
2255
|
+
|
|
2256
|
+
test("When rotating the key of a parent group, the keys of all child groups are also rotated", () => {
|
|
2257
|
+
const { group, node } = newGroupHighLevel();
|
|
2258
|
+
const parentGroup = node.createGroup();
|
|
2259
|
+
|
|
2260
|
+
parentGroup.set(`child_${group.id}`, "extend", "trusting");
|
|
2261
|
+
group.set(`parent_${parentGroup.id}`, "extend", "trusting");
|
|
2262
|
+
|
|
2263
|
+
group.rotateReadKey();
|
|
2264
|
+
|
|
2265
|
+
const currentChildReadKeyID = group.get("readKey");
|
|
2266
|
+
if (!currentChildReadKeyID) {
|
|
2267
|
+
throw new Error("Can't get group read key");
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
console.log("child id", group.id);
|
|
2271
|
+
parentGroup.rotateReadKey();
|
|
2272
|
+
|
|
2273
|
+
const newChildReadKeyID = expectGroup(group.core.getCurrentContent()).get(
|
|
2274
|
+
"readKey",
|
|
2275
|
+
);
|
|
2276
|
+
if (!newChildReadKeyID) {
|
|
2277
|
+
throw new Error("Can't get new group read key");
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
expect(newChildReadKeyID).not.toEqual(currentChildReadKeyID);
|
|
2281
|
+
});
|
|
2282
|
+
|
|
2283
|
+
test("When rotating the key of a grand-parent group, the keys of all child and grand-child groups are also rotated", () => {
|
|
2284
|
+
const { group, node } = newGroupHighLevel();
|
|
2285
|
+
const grandParentGroup = node.createGroup();
|
|
2286
|
+
const parentGroup = node.createGroup();
|
|
2287
|
+
|
|
2288
|
+
grandParentGroup.set(`child_${parentGroup.id}`, "extend", "trusting");
|
|
2289
|
+
parentGroup.set(`child_${group.id}`, "extend", "trusting");
|
|
2290
|
+
parentGroup.set(`parent_${grandParentGroup.id}`, "extend", "trusting");
|
|
2291
|
+
group.set(`parent_${grandParentGroup.id}`, "extend", "trusting");
|
|
2292
|
+
|
|
2293
|
+
const currentGrandParentReadKeyID = grandParentGroup.get("readKey");
|
|
2294
|
+
if (!currentGrandParentReadKeyID) {
|
|
2295
|
+
throw new Error("Can't get grand-parent group read key");
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
const currentParentReadKeyID = parentGroup.get("readKey");
|
|
2299
|
+
if (!currentParentReadKeyID) {
|
|
2300
|
+
throw new Error("Can't get parent group read key");
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
const currentChildReadKeyID = group.get("readKey");
|
|
2304
|
+
if (!currentChildReadKeyID) {
|
|
2305
|
+
throw new Error("Can't get group read key");
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
grandParentGroup.rotateReadKey();
|
|
2309
|
+
|
|
2310
|
+
const newGrandParentReadKeyID = grandParentGroup.get("readKey");
|
|
2311
|
+
if (!newGrandParentReadKeyID) {
|
|
2312
|
+
throw new Error("Can't get new grand-parent group read key");
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
expect(newGrandParentReadKeyID).not.toEqual(currentGrandParentReadKeyID);
|
|
2316
|
+
|
|
2317
|
+
const newParentReadKeyID = expectGroup(
|
|
2318
|
+
parentGroup.core.getCurrentContent(),
|
|
2319
|
+
).get("readKey");
|
|
2320
|
+
if (!newParentReadKeyID) {
|
|
2321
|
+
throw new Error("Can't get new parent group read key");
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
expect(newParentReadKeyID).not.toEqual(currentParentReadKeyID);
|
|
2325
|
+
|
|
2326
|
+
const newChildReadKeyID = expectGroup(group.core.getCurrentContent()).get(
|
|
2327
|
+
"readKey",
|
|
2328
|
+
);
|
|
2329
|
+
if (!newChildReadKeyID) {
|
|
2330
|
+
throw new Error("Can't get new group read key");
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
expect(newChildReadKeyID).not.toEqual(currentChildReadKeyID);
|
|
2334
|
+
});
|
|
2335
|
+
|
|
2336
|
+
test("Calling extend on group sets up parent and child references and reveals child key to parent", () => {
|
|
2337
|
+
const { group, node } = newGroupHighLevel();
|
|
2338
|
+
const parentGroup = node.createGroup();
|
|
2339
|
+
|
|
2340
|
+
group.extend(parentGroup);
|
|
2341
|
+
|
|
2342
|
+
expect(group.get(`parent_${parentGroup.id}`)).toEqual("extend");
|
|
2343
|
+
expect(parentGroup.get(`child_${group.id}`)).toEqual("extend");
|
|
2344
|
+
|
|
2345
|
+
const parentReadKeyID = parentGroup.get("readKey");
|
|
2346
|
+
if (!parentReadKeyID) {
|
|
2347
|
+
throw new Error("Can't get parent group read key");
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
const childReadKeyID = group.get("readKey");
|
|
2351
|
+
if (!childReadKeyID) {
|
|
2352
|
+
throw new Error("Can't get group read key");
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
expect(group.get(`${childReadKeyID}_for_${parentReadKeyID}`)).toBeDefined();
|
|
2356
|
+
|
|
2357
|
+
const reader = node.createAccount();
|
|
2358
|
+
parentGroup.addMember(reader, "reader");
|
|
2359
|
+
|
|
2360
|
+
const childObject = node.createCoValue({
|
|
2361
|
+
type: "comap",
|
|
2362
|
+
ruleset: { type: "ownedByGroup", group: group.id },
|
|
2363
|
+
meta: null,
|
|
2364
|
+
...Crypto.createdNowUnique(),
|
|
2365
|
+
});
|
|
2366
|
+
const childMap = expectMap(childObject.getCurrentContent());
|
|
2367
|
+
|
|
2368
|
+
childMap.set("foo", "bar", "private");
|
|
2369
|
+
|
|
2370
|
+
const childContentAsReader = expectMap(
|
|
2371
|
+
childObject
|
|
2372
|
+
.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
|
|
2373
|
+
.getCurrentContent(),
|
|
2374
|
+
);
|
|
2375
|
+
|
|
2376
|
+
expect(childContentAsReader.get("foo")).toEqual("bar");
|
|
2377
|
+
});
|
|
2378
|
+
|
|
2379
|
+
test("Calling extend to create grand-child groups parent and child references and reveals child key to parent(s)", () => {
|
|
2380
|
+
const { group, node } = newGroupHighLevel();
|
|
2381
|
+
const parentGroup = node.createGroup();
|
|
2382
|
+
const grandParentGroup = node.createGroup();
|
|
2383
|
+
|
|
2384
|
+
group.extend(parentGroup);
|
|
2385
|
+
parentGroup.extend(grandParentGroup);
|
|
2386
|
+
|
|
2387
|
+
expect(group.get(`parent_${parentGroup.id}`)).toEqual("extend");
|
|
2388
|
+
expect(parentGroup.get(`parent_${grandParentGroup.id}`)).toEqual("extend");
|
|
2389
|
+
expect(parentGroup.get(`child_${group.id}`)).toEqual("extend");
|
|
2390
|
+
expect(grandParentGroup.get(`child_${parentGroup.id}`)).toEqual("extend");
|
|
2391
|
+
|
|
2392
|
+
const reader = node.createAccount();
|
|
2393
|
+
grandParentGroup.addMember(reader, "reader");
|
|
2394
|
+
|
|
2395
|
+
const childObject = node.createCoValue({
|
|
2396
|
+
type: "comap",
|
|
2397
|
+
ruleset: { type: "ownedByGroup", group: group.id },
|
|
2398
|
+
meta: null,
|
|
2399
|
+
...Crypto.createdNowUnique(),
|
|
2400
|
+
});
|
|
2401
|
+
const childMap = expectMap(childObject.getCurrentContent());
|
|
2402
|
+
|
|
2403
|
+
childMap.set("foo", "bar", "private");
|
|
2404
|
+
|
|
2405
|
+
const childContentAsReader = expectMap(
|
|
2406
|
+
childObject
|
|
2407
|
+
.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
|
|
2408
|
+
.getCurrentContent(),
|
|
2409
|
+
);
|
|
2410
|
+
|
|
2411
|
+
expect(childContentAsReader.get("foo")).toEqual("bar");
|
|
2412
|
+
});
|
|
2413
|
+
|
|
2414
|
+
test("High-level permissions work correctly when a group is extended", () => {
|
|
2415
|
+
const { group, node } = newGroupHighLevel();
|
|
2416
|
+
const parentGroup = node.createGroup();
|
|
2417
|
+
|
|
2418
|
+
group.extend(parentGroup);
|
|
2419
|
+
|
|
2420
|
+
const reader = node.createAccount();
|
|
2421
|
+
parentGroup.addMember(reader, "reader");
|
|
2422
|
+
|
|
2423
|
+
const mapCore = node.createCoValue({
|
|
2424
|
+
type: "comap",
|
|
2425
|
+
ruleset: { type: "ownedByGroup", group: group.id },
|
|
2426
|
+
meta: null,
|
|
2427
|
+
...Crypto.createdNowUnique(),
|
|
2428
|
+
});
|
|
2429
|
+
|
|
2430
|
+
const map = expectMap(mapCore.getCurrentContent());
|
|
2431
|
+
|
|
2432
|
+
map.set("foo", "bar", "private");
|
|
2433
|
+
|
|
2434
|
+
const mapAsReader = expectMap(
|
|
2435
|
+
mapCore
|
|
2436
|
+
.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
|
|
2437
|
+
.getCurrentContent(),
|
|
2438
|
+
);
|
|
2439
|
+
|
|
2440
|
+
expect(mapAsReader.get("foo")).toEqual("bar");
|
|
2441
|
+
|
|
2442
|
+
const groupKeyBeforeRemove = group.core.getCurrentReadKey().id;
|
|
2443
|
+
|
|
2444
|
+
parentGroup.removeMember(reader);
|
|
2445
|
+
|
|
2446
|
+
const groupKeyAfterRemove = group.core.getCurrentReadKey().id;
|
|
2447
|
+
expect(groupKeyAfterRemove).not.toEqual(groupKeyBeforeRemove);
|
|
2448
|
+
|
|
2449
|
+
map.set("foo", "baz", "private");
|
|
2450
|
+
|
|
2451
|
+
const mapAsReaderAfterRemove = expectMap(
|
|
2452
|
+
mapCore
|
|
2453
|
+
.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
|
|
2454
|
+
.getCurrentContent(),
|
|
2455
|
+
);
|
|
2456
|
+
|
|
2457
|
+
expect(mapAsReaderAfterRemove.get("foo")).not.toEqual("baz");
|
|
2458
|
+
});
|