@quenty/permissionprovider 14.43.0 → 14.44.1
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/CHANGELOG.md +10 -0
- package/package.json +13 -12
- package/src/Client/PermissionServiceClient.lua +5 -1
- package/src/Client/PermissionServiceClient.spec.lua +176 -0
- package/src/Client/Providers/PermissionProviderClient.lua +8 -2
- package/src/Client/Providers/PermissionProviderClient.spec.lua +186 -0
- package/src/Server/PermissionProviderUtils.spec.lua +118 -0
- package/src/Server/PermissionService.lua +6 -5
- package/src/Server/PermissionService.spec.lua +203 -0
- package/src/Server/Providers/BasePermissionProvider.lua +7 -6
- package/src/Server/Providers/BasePermissionProvider.spec.lua +136 -0
- package/src/Server/Providers/CreatorPermissionProvider.lua +6 -2
- package/src/Server/Providers/CreatorPermissionProvider.spec.lua +130 -0
- package/src/Server/Providers/GroupPermissionProvider.lua +17 -12
- package/src/Server/Providers/GroupPermissionProvider.spec.lua +180 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
Coverage for PermissionService's ServiceBag-driven lifecycle and permission queries, with a
|
|
4
|
+
single-user (creator) provider config and PlayerMock players.
|
|
5
|
+
|
|
6
|
+
The deny cases assume RunService:IsStudio() == false (true in the cloud test runner); in Studio
|
|
7
|
+
the service short-circuits every permission query to true.
|
|
8
|
+
|
|
9
|
+
@class PermissionService.spec.lua
|
|
10
|
+
]]
|
|
11
|
+
|
|
12
|
+
local require = require(script.Parent.loader).load(script)
|
|
13
|
+
|
|
14
|
+
local Jest = require("Jest")
|
|
15
|
+
local Maid = require("Maid")
|
|
16
|
+
local PermissionLevel = require("PermissionLevel")
|
|
17
|
+
local PermissionProviderUtils = require("PermissionProviderUtils")
|
|
18
|
+
local PermissionService = require("PermissionService")
|
|
19
|
+
local PlayerMock = require("PlayerMock")
|
|
20
|
+
local PromiseTestUtils = require("PromiseTestUtils")
|
|
21
|
+
local ServiceBag = require("ServiceBag")
|
|
22
|
+
|
|
23
|
+
local describe = Jest.Globals.describe
|
|
24
|
+
local expect = Jest.Globals.expect
|
|
25
|
+
local it = Jest.Globals.it
|
|
26
|
+
|
|
27
|
+
local CREATOR_USER_ID = 12345
|
|
28
|
+
|
|
29
|
+
local remoteNameCounter = 0
|
|
30
|
+
|
|
31
|
+
local function setup()
|
|
32
|
+
local maid = Maid.new()
|
|
33
|
+
local serviceBag = maid:Add(ServiceBag.new())
|
|
34
|
+
local permissionService: PermissionService.PermissionService = serviceBag:GetService(PermissionService) :: any
|
|
35
|
+
serviceBag:Init()
|
|
36
|
+
|
|
37
|
+
-- Unique remote function name so services from separate tests never share the global remote.
|
|
38
|
+
remoteNameCounter += 1
|
|
39
|
+
local remoteFunctionName = string.format("PermissionServiceSpecRemote%d", remoteNameCounter)
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
serviceBag = serviceBag,
|
|
43
|
+
permissionService = permissionService,
|
|
44
|
+
singleUserConfig = function(userId: number)
|
|
45
|
+
return PermissionProviderUtils.createSingleUserConfig({
|
|
46
|
+
userId = userId,
|
|
47
|
+
remoteFunctionName = remoteFunctionName,
|
|
48
|
+
})
|
|
49
|
+
end,
|
|
50
|
+
fakePlayer = function(userId: number): Player
|
|
51
|
+
return maid:Add(PlayerMock.new({ UserId = userId }))
|
|
52
|
+
end,
|
|
53
|
+
awaitBool = function(promise: any): boolean
|
|
54
|
+
expect(PromiseTestUtils.awaitSettled(promise, 5)).toEqual(true)
|
|
55
|
+
local ok, value = promise:Yield()
|
|
56
|
+
expect(ok).toEqual(true)
|
|
57
|
+
return value
|
|
58
|
+
end,
|
|
59
|
+
destroy = function(_self)
|
|
60
|
+
maid:DoCleaning()
|
|
61
|
+
end,
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe("PermissionService initialization", function()
|
|
66
|
+
it("should initialize and start with a configured provider", function()
|
|
67
|
+
local controller = setup()
|
|
68
|
+
controller.permissionService:SetProviderFromConfig(controller.singleUserConfig(CREATOR_USER_ID))
|
|
69
|
+
|
|
70
|
+
expect(function()
|
|
71
|
+
controller.serviceBag:Start()
|
|
72
|
+
end).never.toThrow()
|
|
73
|
+
|
|
74
|
+
controller:destroy()
|
|
75
|
+
end)
|
|
76
|
+
|
|
77
|
+
it("should start with a provider derived from the game when none is configured", function()
|
|
78
|
+
local controller = setup()
|
|
79
|
+
|
|
80
|
+
expect(function()
|
|
81
|
+
controller.serviceBag:Start()
|
|
82
|
+
end).never.toThrow()
|
|
83
|
+
|
|
84
|
+
controller:destroy()
|
|
85
|
+
end)
|
|
86
|
+
|
|
87
|
+
it("should resolve the permission provider after start", function()
|
|
88
|
+
local controller = setup()
|
|
89
|
+
controller.permissionService:SetProviderFromConfig(controller.singleUserConfig(CREATOR_USER_ID))
|
|
90
|
+
|
|
91
|
+
local promise = controller.permissionService:PromisePermissionProvider()
|
|
92
|
+
expect(promise:IsPending()).toEqual(true)
|
|
93
|
+
|
|
94
|
+
controller.serviceBag:Start()
|
|
95
|
+
|
|
96
|
+
expect(PromiseTestUtils.awaitSettled(promise, 5)).toEqual(true)
|
|
97
|
+
local ok, provider = promise:Yield()
|
|
98
|
+
expect(ok).toEqual(true)
|
|
99
|
+
expect((provider :: any).ClassName).toEqual("CreatorPermissionProvider")
|
|
100
|
+
|
|
101
|
+
controller:destroy()
|
|
102
|
+
end)
|
|
103
|
+
end)
|
|
104
|
+
|
|
105
|
+
describe("PermissionService.SetProviderFromConfig", function()
|
|
106
|
+
it("should reject a second provider config", function()
|
|
107
|
+
local controller = setup()
|
|
108
|
+
controller.permissionService:SetProviderFromConfig(controller.singleUserConfig(CREATOR_USER_ID))
|
|
109
|
+
|
|
110
|
+
expect(function()
|
|
111
|
+
controller.permissionService:SetProviderFromConfig(controller.singleUserConfig(CREATOR_USER_ID))
|
|
112
|
+
end).toThrow("Already have provider set")
|
|
113
|
+
|
|
114
|
+
controller:destroy()
|
|
115
|
+
end)
|
|
116
|
+
|
|
117
|
+
it("should reject an unknown config type", function()
|
|
118
|
+
local controller = setup()
|
|
119
|
+
|
|
120
|
+
expect(function()
|
|
121
|
+
controller.permissionService:SetProviderFromConfig({ type = "UnknownConfigType" } :: any)
|
|
122
|
+
end).toThrow("Bad provider")
|
|
123
|
+
|
|
124
|
+
controller:destroy()
|
|
125
|
+
end)
|
|
126
|
+
end)
|
|
127
|
+
|
|
128
|
+
describe("PermissionService permission queries", function()
|
|
129
|
+
it("should treat the configured user as an admin", function()
|
|
130
|
+
local controller = setup()
|
|
131
|
+
controller.permissionService:SetProviderFromConfig(controller.singleUserConfig(CREATOR_USER_ID))
|
|
132
|
+
controller.serviceBag:Start()
|
|
133
|
+
|
|
134
|
+
local player = controller.fakePlayer(CREATOR_USER_ID)
|
|
135
|
+
expect(controller.awaitBool(controller.permissionService:PromiseIsAdmin(player))).toEqual(true)
|
|
136
|
+
|
|
137
|
+
controller:destroy()
|
|
138
|
+
end)
|
|
139
|
+
|
|
140
|
+
it("should treat the configured user as a creator", function()
|
|
141
|
+
local controller = setup()
|
|
142
|
+
controller.permissionService:SetProviderFromConfig(controller.singleUserConfig(CREATOR_USER_ID))
|
|
143
|
+
controller.serviceBag:Start()
|
|
144
|
+
|
|
145
|
+
local player = controller.fakePlayer(CREATOR_USER_ID)
|
|
146
|
+
expect(controller.awaitBool(controller.permissionService:PromiseIsCreator(player))).toEqual(true)
|
|
147
|
+
|
|
148
|
+
controller:destroy()
|
|
149
|
+
end)
|
|
150
|
+
|
|
151
|
+
it("should deny another user admin permission", function()
|
|
152
|
+
local controller = setup()
|
|
153
|
+
controller.permissionService:SetProviderFromConfig(controller.singleUserConfig(CREATOR_USER_ID))
|
|
154
|
+
controller.serviceBag:Start()
|
|
155
|
+
|
|
156
|
+
local player = controller.fakePlayer(CREATOR_USER_ID + 1)
|
|
157
|
+
expect(controller.awaitBool(controller.permissionService:PromiseIsAdmin(player))).toEqual(false)
|
|
158
|
+
|
|
159
|
+
controller:destroy()
|
|
160
|
+
end)
|
|
161
|
+
|
|
162
|
+
it("should reject a non-player value", function()
|
|
163
|
+
local controller = setup()
|
|
164
|
+
controller.permissionService:SetProviderFromConfig(controller.singleUserConfig(CREATOR_USER_ID))
|
|
165
|
+
controller.serviceBag:Start()
|
|
166
|
+
|
|
167
|
+
expect(function()
|
|
168
|
+
controller.permissionService:PromiseIsAdmin(nil :: any)
|
|
169
|
+
end).toThrow("bad player")
|
|
170
|
+
|
|
171
|
+
controller:destroy()
|
|
172
|
+
end)
|
|
173
|
+
|
|
174
|
+
it("should reject an invalid permission level", function()
|
|
175
|
+
local controller = setup()
|
|
176
|
+
controller.permissionService:SetProviderFromConfig(controller.singleUserConfig(CREATOR_USER_ID))
|
|
177
|
+
controller.serviceBag:Start()
|
|
178
|
+
|
|
179
|
+
local player = controller.fakePlayer(CREATOR_USER_ID)
|
|
180
|
+
expect(function()
|
|
181
|
+
controller.permissionService:PromiseIsPermissionLevel(player, "not-a-level" :: any)
|
|
182
|
+
end).toThrow("Bad permissionLevel")
|
|
183
|
+
|
|
184
|
+
controller:destroy()
|
|
185
|
+
end)
|
|
186
|
+
end)
|
|
187
|
+
|
|
188
|
+
describe("PermissionService.ObservePermissionedPlayersBrio", function()
|
|
189
|
+
it("should subscribe and unsubscribe without error on an empty server", function()
|
|
190
|
+
local controller = setup()
|
|
191
|
+
controller.permissionService:SetProviderFromConfig(controller.singleUserConfig(CREATOR_USER_ID))
|
|
192
|
+
controller.serviceBag:Start()
|
|
193
|
+
|
|
194
|
+
expect(function()
|
|
195
|
+
local subscription = controller.permissionService
|
|
196
|
+
:ObservePermissionedPlayersBrio(PermissionLevel.ADMIN)
|
|
197
|
+
:Subscribe(function() end)
|
|
198
|
+
subscription:Destroy()
|
|
199
|
+
end).never.toThrow()
|
|
200
|
+
|
|
201
|
+
controller:destroy()
|
|
202
|
+
end)
|
|
203
|
+
end)
|
|
@@ -11,6 +11,7 @@ local BaseObject = require("BaseObject")
|
|
|
11
11
|
local GetRemoteFunction = require("GetRemoteFunction")
|
|
12
12
|
local PermissionLevel = require("PermissionLevel")
|
|
13
13
|
local PermissionProviderUtils = require("PermissionProviderUtils")
|
|
14
|
+
local PlayerMock = require("PlayerMock")
|
|
14
15
|
local Promise = require("Promise")
|
|
15
16
|
local Table = require("Table")
|
|
16
17
|
|
|
@@ -68,7 +69,7 @@ function BasePermissionProvider.PromiseIsPermissionLevel(
|
|
|
68
69
|
player: Player,
|
|
69
70
|
permissionLevel: PermissionLevel.PermissionLevel
|
|
70
71
|
): Promise.Promise<boolean>
|
|
71
|
-
assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad player")
|
|
72
|
+
assert((typeof(player) == "Instance" and player:IsA("Player")) or PlayerMock.isMock(player), "Bad player")
|
|
72
73
|
assert(PermissionLevel:IsValue(permissionLevel))
|
|
73
74
|
|
|
74
75
|
error("Not implemented")
|
|
@@ -85,7 +86,7 @@ function BasePermissionProvider.IsPermissionLevel(
|
|
|
85
86
|
player: Player,
|
|
86
87
|
permissionLevel: PermissionLevel.PermissionLevel
|
|
87
88
|
): boolean
|
|
88
|
-
assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad player")
|
|
89
|
+
assert((typeof(player) == "Instance" and player:IsA("Player")) or PlayerMock.isMock(player), "Bad player")
|
|
89
90
|
assert(PermissionLevel:IsValue(permissionLevel))
|
|
90
91
|
|
|
91
92
|
local promise = self:PromiseIsPermissionLevel(player, permissionLevel)
|
|
@@ -108,7 +109,7 @@ end
|
|
|
108
109
|
@return Promise<boolean>
|
|
109
110
|
]=]
|
|
110
111
|
function BasePermissionProvider.PromiseIsCreator(self: BasePermissionProvider, player: Player): Promise.Promise<boolean>
|
|
111
|
-
assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad player")
|
|
112
|
+
assert((typeof(player) == "Instance" and player:IsA("Player")) or PlayerMock.isMock(player), "Bad player")
|
|
112
113
|
|
|
113
114
|
return self:PromiseIsPermissionLevel(player, PermissionLevel.CREATOR)
|
|
114
115
|
end
|
|
@@ -119,7 +120,7 @@ end
|
|
|
119
120
|
@return Promise<boolean>
|
|
120
121
|
]=]
|
|
121
122
|
function BasePermissionProvider.PromiseIsAdmin(self: BasePermissionProvider, player: Player): Promise.Promise<boolean>
|
|
122
|
-
assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad player")
|
|
123
|
+
assert((typeof(player) == "Instance" and player:IsA("Player")) or PlayerMock.isMock(player), "Bad player")
|
|
123
124
|
|
|
124
125
|
return self:PromiseIsPermissionLevel(player, PermissionLevel.ADMIN)
|
|
125
126
|
end
|
|
@@ -135,7 +136,7 @@ end
|
|
|
135
136
|
@return boolean
|
|
136
137
|
]=]
|
|
137
138
|
function BasePermissionProvider.IsCreator(self: BasePermissionProvider, player: Player): boolean
|
|
138
|
-
assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad player")
|
|
139
|
+
assert((typeof(player) == "Instance" and player:IsA("Player")) or PlayerMock.isMock(player), "Bad player")
|
|
139
140
|
|
|
140
141
|
return self:IsPermissionLevel(player, PermissionLevel.CREATOR)
|
|
141
142
|
end
|
|
@@ -151,7 +152,7 @@ end
|
|
|
151
152
|
@return boolean
|
|
152
153
|
]=]
|
|
153
154
|
function BasePermissionProvider.IsAdmin(self: BasePermissionProvider, player: Player): boolean
|
|
154
|
-
assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad player")
|
|
155
|
+
assert((typeof(player) == "Instance" and player:IsA("Player")) or PlayerMock.isMock(player), "Bad player")
|
|
155
156
|
|
|
156
157
|
return self:IsPermissionLevel(player, PermissionLevel.ADMIN)
|
|
157
158
|
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class BasePermissionProvider.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local BasePermissionProvider = require("BasePermissionProvider")
|
|
9
|
+
local Jest = require("Jest")
|
|
10
|
+
local Maid = require("Maid")
|
|
11
|
+
local PermissionLevel = require("PermissionLevel")
|
|
12
|
+
local PermissionProviderUtils = require("PermissionProviderUtils")
|
|
13
|
+
local PlayerMock = require("PlayerMock")
|
|
14
|
+
local Promise = require("Promise")
|
|
15
|
+
|
|
16
|
+
local describe = Jest.Globals.describe
|
|
17
|
+
local expect = Jest.Globals.expect
|
|
18
|
+
local it = Jest.Globals.it
|
|
19
|
+
|
|
20
|
+
local function createStubProvider(maid: Maid.Maid, promiseFactory: () -> any)
|
|
21
|
+
local StubPermissionProvider = setmetatable({}, BasePermissionProvider)
|
|
22
|
+
StubPermissionProvider.ClassName = "StubPermissionProvider"
|
|
23
|
+
StubPermissionProvider.__index = StubPermissionProvider
|
|
24
|
+
|
|
25
|
+
local queriedLevels = {}
|
|
26
|
+
|
|
27
|
+
function StubPermissionProvider.PromiseIsPermissionLevel(_self: any, _player: any, permissionLevel: any)
|
|
28
|
+
table.insert(queriedLevels, permissionLevel)
|
|
29
|
+
return promiseFactory()
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
local config = PermissionProviderUtils.createSingleUserConfig({ userId = 12345 })
|
|
33
|
+
local provider = maid:Add(setmetatable(BasePermissionProvider.new(config) :: any, StubPermissionProvider))
|
|
34
|
+
|
|
35
|
+
return provider, queriedLevels
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe("BasePermissionProvider.new", function()
|
|
39
|
+
it("should reject a nil config", function()
|
|
40
|
+
expect(function()
|
|
41
|
+
BasePermissionProvider.new(nil :: any)
|
|
42
|
+
end).toThrow("Bad config")
|
|
43
|
+
end)
|
|
44
|
+
|
|
45
|
+
it("should reject a config without a remote function name", function()
|
|
46
|
+
expect(function()
|
|
47
|
+
BasePermissionProvider.new({} :: any)
|
|
48
|
+
end).toThrow("remoteFunctionName")
|
|
49
|
+
end)
|
|
50
|
+
end)
|
|
51
|
+
|
|
52
|
+
describe("BasePermissionProvider.PromiseIsPermissionLevel", function()
|
|
53
|
+
it("should error as not implemented on the base class", function()
|
|
54
|
+
local maid = Maid.new()
|
|
55
|
+
local provider =
|
|
56
|
+
maid:Add(BasePermissionProvider.new(PermissionProviderUtils.createSingleUserConfig({ userId = 12345 })))
|
|
57
|
+
local player = maid:Add(PlayerMock.new({ UserId = 12345 }))
|
|
58
|
+
|
|
59
|
+
expect(function()
|
|
60
|
+
provider:PromiseIsPermissionLevel(player, PermissionLevel.ADMIN)
|
|
61
|
+
end).toThrow("Not implemented")
|
|
62
|
+
|
|
63
|
+
maid:DoCleaning()
|
|
64
|
+
end)
|
|
65
|
+
|
|
66
|
+
it("should reject a non-player value", function()
|
|
67
|
+
local maid = Maid.new()
|
|
68
|
+
local provider =
|
|
69
|
+
maid:Add(BasePermissionProvider.new(PermissionProviderUtils.createSingleUserConfig({ userId = 12345 })))
|
|
70
|
+
|
|
71
|
+
expect(function()
|
|
72
|
+
provider:PromiseIsPermissionLevel(nil :: any, PermissionLevel.ADMIN)
|
|
73
|
+
end).toThrow("Bad player")
|
|
74
|
+
|
|
75
|
+
maid:DoCleaning()
|
|
76
|
+
end)
|
|
77
|
+
end)
|
|
78
|
+
|
|
79
|
+
describe("BasePermissionProvider.IsPermissionLevel", function()
|
|
80
|
+
it("should return false while the underlying promise is pending", function()
|
|
81
|
+
local maid = Maid.new()
|
|
82
|
+
local provider = createStubProvider(maid, function()
|
|
83
|
+
return maid:Add(Promise.new())
|
|
84
|
+
end)
|
|
85
|
+
local player = maid:Add(PlayerMock.new({ UserId = 12345 }))
|
|
86
|
+
|
|
87
|
+
expect(provider:IsPermissionLevel(player, PermissionLevel.ADMIN)).toEqual(false)
|
|
88
|
+
|
|
89
|
+
maid:DoCleaning()
|
|
90
|
+
end)
|
|
91
|
+
|
|
92
|
+
it("should return the resolved value when the promise settles synchronously", function()
|
|
93
|
+
local maid = Maid.new()
|
|
94
|
+
local provider = createStubProvider(maid, function()
|
|
95
|
+
return Promise.resolved(true)
|
|
96
|
+
end)
|
|
97
|
+
local player = maid:Add(PlayerMock.new({ UserId = 12345 }))
|
|
98
|
+
|
|
99
|
+
expect(provider:IsPermissionLevel(player, PermissionLevel.ADMIN)).toEqual(true)
|
|
100
|
+
expect(provider:IsCreator(player)).toEqual(true)
|
|
101
|
+
expect(provider:IsAdmin(player)).toEqual(true)
|
|
102
|
+
|
|
103
|
+
maid:DoCleaning()
|
|
104
|
+
end)
|
|
105
|
+
|
|
106
|
+
it("should return false when the underlying promise rejects", function()
|
|
107
|
+
local maid = Maid.new()
|
|
108
|
+
local provider = createStubProvider(maid, function()
|
|
109
|
+
local promise = Promise.rejected("simulated failure")
|
|
110
|
+
promise:Catch(function() end)
|
|
111
|
+
return promise
|
|
112
|
+
end)
|
|
113
|
+
local player = maid:Add(PlayerMock.new({ UserId = 12345 }))
|
|
114
|
+
|
|
115
|
+
expect(provider:IsPermissionLevel(player, PermissionLevel.ADMIN)).toEqual(false)
|
|
116
|
+
|
|
117
|
+
maid:DoCleaning()
|
|
118
|
+
end)
|
|
119
|
+
end)
|
|
120
|
+
|
|
121
|
+
describe("BasePermissionProvider promise wrappers", function()
|
|
122
|
+
it("should route PromiseIsAdmin and PromiseIsCreator through the matching level", function()
|
|
123
|
+
local maid = Maid.new()
|
|
124
|
+
local provider, queriedLevels = createStubProvider(maid, function()
|
|
125
|
+
return Promise.resolved(true)
|
|
126
|
+
end)
|
|
127
|
+
local player = maid:Add(PlayerMock.new({ UserId = 12345 }))
|
|
128
|
+
|
|
129
|
+
provider:PromiseIsAdmin(player)
|
|
130
|
+
provider:PromiseIsCreator(player)
|
|
131
|
+
|
|
132
|
+
expect(queriedLevels).toEqual({ PermissionLevel.ADMIN, PermissionLevel.CREATOR } :: { string })
|
|
133
|
+
|
|
134
|
+
maid:DoCleaning()
|
|
135
|
+
end)
|
|
136
|
+
end)
|
|
@@ -14,6 +14,7 @@ local BasePermissionProvider = require("BasePermissionProvider")
|
|
|
14
14
|
local PermissionLevel = require("PermissionLevel")
|
|
15
15
|
local PermissionProviderConstants = require("PermissionProviderConstants")
|
|
16
16
|
local PermissionProviderUtils = require("PermissionProviderUtils")
|
|
17
|
+
local PlayerMock = require("PlayerMock")
|
|
17
18
|
local Promise = require("Promise")
|
|
18
19
|
|
|
19
20
|
local CreatorPermissionProvider = setmetatable({}, BasePermissionProvider)
|
|
@@ -56,11 +57,14 @@ function CreatorPermissionProvider.PromiseIsPermissionLevel(
|
|
|
56
57
|
player: Player,
|
|
57
58
|
permissionLevel: PermissionLevel.PermissionLevel
|
|
58
59
|
): Promise.Promise<boolean>
|
|
59
|
-
assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad player")
|
|
60
|
+
assert((typeof(player) == "Instance" and player:IsA("Player")) or PlayerMock.isMock(player), "Bad player")
|
|
60
61
|
assert(PermissionLevel:IsValue(permissionLevel))
|
|
61
62
|
|
|
62
63
|
if permissionLevel == PermissionLevel.ADMIN or permissionLevel == PermissionLevel.CREATOR then
|
|
63
|
-
return Promise.resolved(
|
|
64
|
+
return Promise.resolved(
|
|
65
|
+
(if PlayerMock.isMock(player) then PlayerMock.read(player, "UserId") else player.UserId) == self._userId
|
|
66
|
+
or RunService:IsStudio()
|
|
67
|
+
)
|
|
64
68
|
else
|
|
65
69
|
error("Unknown permissionLevel")
|
|
66
70
|
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
Coverage for CreatorPermissionProvider: a single configured userId is both admin and creator,
|
|
4
|
+
everyone else is neither. Players are stood in by PlayerMock, whose seeded UserId the provider
|
|
5
|
+
reads through PlayerMock.read.
|
|
6
|
+
|
|
7
|
+
The negative cases assume RunService:IsStudio() == false (true in the cloud test runner); in
|
|
8
|
+
Studio the provider grants everyone permission.
|
|
9
|
+
|
|
10
|
+
@class CreatorPermissionProvider.spec.lua
|
|
11
|
+
]]
|
|
12
|
+
|
|
13
|
+
local require = require(script.Parent.loader).load(script)
|
|
14
|
+
|
|
15
|
+
local CreatorPermissionProvider = require("CreatorPermissionProvider")
|
|
16
|
+
local Jest = require("Jest")
|
|
17
|
+
local Maid = require("Maid")
|
|
18
|
+
local PermissionLevel = require("PermissionLevel")
|
|
19
|
+
local PermissionProviderUtils = require("PermissionProviderUtils")
|
|
20
|
+
local PlayerMock = require("PlayerMock")
|
|
21
|
+
local PromiseTestUtils = require("PromiseTestUtils")
|
|
22
|
+
|
|
23
|
+
local describe = Jest.Globals.describe
|
|
24
|
+
local expect = Jest.Globals.expect
|
|
25
|
+
local it = Jest.Globals.it
|
|
26
|
+
|
|
27
|
+
local CREATOR_USER_ID = 12345
|
|
28
|
+
|
|
29
|
+
local function setup()
|
|
30
|
+
local maid = Maid.new()
|
|
31
|
+
local provider = maid:Add(CreatorPermissionProvider.new(PermissionProviderUtils.createSingleUserConfig({
|
|
32
|
+
userId = CREATOR_USER_ID,
|
|
33
|
+
})))
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
provider = provider,
|
|
37
|
+
fakePlayer = function(userId: number): Player
|
|
38
|
+
return maid:Add(PlayerMock.new({ UserId = userId }))
|
|
39
|
+
end,
|
|
40
|
+
awaitBool = function(promise: any): boolean
|
|
41
|
+
expect(PromiseTestUtils.awaitSettled(promise, 5)).toEqual(true)
|
|
42
|
+
local ok, value = promise:Yield()
|
|
43
|
+
expect(ok).toEqual(true)
|
|
44
|
+
return value
|
|
45
|
+
end,
|
|
46
|
+
destroy = function(_self)
|
|
47
|
+
maid:DoCleaning()
|
|
48
|
+
end,
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe("CreatorPermissionProvider.new", function()
|
|
53
|
+
it("should reject a group rank config", function()
|
|
54
|
+
expect(function()
|
|
55
|
+
CreatorPermissionProvider.new(PermissionProviderUtils.createGroupRankConfig({
|
|
56
|
+
groupId = 12345,
|
|
57
|
+
minAdminRequiredRank = 250,
|
|
58
|
+
minCreatorRequiredRank = 254,
|
|
59
|
+
}) :: any)
|
|
60
|
+
end).toThrow("Bad configType")
|
|
61
|
+
end)
|
|
62
|
+
end)
|
|
63
|
+
|
|
64
|
+
describe("CreatorPermissionProvider.PromiseIsPermissionLevel", function()
|
|
65
|
+
it("should treat the configured user as an admin", function()
|
|
66
|
+
local controller = setup()
|
|
67
|
+
local player = controller.fakePlayer(CREATOR_USER_ID)
|
|
68
|
+
|
|
69
|
+
expect(controller.awaitBool(controller.provider:PromiseIsAdmin(player))).toEqual(true)
|
|
70
|
+
|
|
71
|
+
controller:destroy()
|
|
72
|
+
end)
|
|
73
|
+
|
|
74
|
+
it("should treat the configured user as a creator", function()
|
|
75
|
+
local controller = setup()
|
|
76
|
+
local player = controller.fakePlayer(CREATOR_USER_ID)
|
|
77
|
+
|
|
78
|
+
expect(controller.awaitBool(controller.provider:PromiseIsCreator(player))).toEqual(true)
|
|
79
|
+
|
|
80
|
+
controller:destroy()
|
|
81
|
+
end)
|
|
82
|
+
|
|
83
|
+
it("should deny another user admin permission", function()
|
|
84
|
+
local controller = setup()
|
|
85
|
+
local player = controller.fakePlayer(CREATOR_USER_ID + 1)
|
|
86
|
+
|
|
87
|
+
expect(controller.awaitBool(controller.provider:PromiseIsAdmin(player))).toEqual(false)
|
|
88
|
+
|
|
89
|
+
controller:destroy()
|
|
90
|
+
end)
|
|
91
|
+
|
|
92
|
+
it("should deny another user creator permission", function()
|
|
93
|
+
local controller = setup()
|
|
94
|
+
local player = controller.fakePlayer(CREATOR_USER_ID + 1)
|
|
95
|
+
|
|
96
|
+
expect(controller.awaitBool(controller.provider:PromiseIsCreator(player))).toEqual(false)
|
|
97
|
+
|
|
98
|
+
controller:destroy()
|
|
99
|
+
end)
|
|
100
|
+
|
|
101
|
+
it("should resolve synchronously for the IsAdmin wrapper", function()
|
|
102
|
+
local controller = setup()
|
|
103
|
+
local player = controller.fakePlayer(CREATOR_USER_ID)
|
|
104
|
+
|
|
105
|
+
expect(controller.provider:IsAdmin(player)).toEqual(true)
|
|
106
|
+
|
|
107
|
+
controller:destroy()
|
|
108
|
+
end)
|
|
109
|
+
|
|
110
|
+
it("should reject a non-player value", function()
|
|
111
|
+
local controller = setup()
|
|
112
|
+
|
|
113
|
+
expect(function()
|
|
114
|
+
controller.provider:PromiseIsPermissionLevel(nil :: any, PermissionLevel.ADMIN)
|
|
115
|
+
end).toThrow("Bad player")
|
|
116
|
+
|
|
117
|
+
controller:destroy()
|
|
118
|
+
end)
|
|
119
|
+
|
|
120
|
+
it("should reject an invalid permission level", function()
|
|
121
|
+
local controller = setup()
|
|
122
|
+
local player = controller.fakePlayer(CREATOR_USER_ID)
|
|
123
|
+
|
|
124
|
+
expect(function()
|
|
125
|
+
controller.provider:PromiseIsPermissionLevel(player, "not-a-level" :: any)
|
|
126
|
+
end).toThrow()
|
|
127
|
+
|
|
128
|
+
controller:destroy()
|
|
129
|
+
end)
|
|
130
|
+
end)
|
|
@@ -15,6 +15,7 @@ local GroupUtils = require("GroupUtils")
|
|
|
15
15
|
local PermissionLevel = require("PermissionLevel")
|
|
16
16
|
local PermissionProviderConstants = require("PermissionProviderConstants")
|
|
17
17
|
local PermissionProviderUtils = require("PermissionProviderUtils")
|
|
18
|
+
local PlayerMock = require("PlayerMock")
|
|
18
19
|
local Promise = require("Promise")
|
|
19
20
|
|
|
20
21
|
local GroupPermissionProvider = setmetatable({}, BasePermissionProvider)
|
|
@@ -62,7 +63,7 @@ function GroupPermissionProvider.Start(self: GroupPermissionProvider)
|
|
|
62
63
|
getmetatable(GroupPermissionProvider).Start(self)
|
|
63
64
|
|
|
64
65
|
self._maid:GiveTask(Players.PlayerRemoving:Connect(function(player: Player)
|
|
65
|
-
local userId = player.UserId
|
|
66
|
+
local userId = if PlayerMock.isMock(player) then PlayerMock.read(player, "UserId") else player.UserId
|
|
66
67
|
|
|
67
68
|
self._adminsCache[userId] = nil
|
|
68
69
|
self._creatorCache[userId] = nil
|
|
@@ -97,7 +98,7 @@ function GroupPermissionProvider.PromiseIsPermissionLevel(
|
|
|
97
98
|
player: Player,
|
|
98
99
|
permissionLevel: PermissionLevel.PermissionLevel
|
|
99
100
|
): Promise.Promise<boolean>
|
|
100
|
-
assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad player")
|
|
101
|
+
assert((typeof(player) == "Instance" and player:IsA("Player")) or PlayerMock.isMock(player), "Bad player")
|
|
101
102
|
assert(PermissionLevel:IsValue(permissionLevel))
|
|
102
103
|
|
|
103
104
|
if permissionLevel == PermissionLevel.ADMIN then
|
|
@@ -113,10 +114,11 @@ function GroupPermissionProvider._promiseIsCreator(
|
|
|
113
114
|
self: GroupPermissionProvider,
|
|
114
115
|
player: Player
|
|
115
116
|
): Promise.Promise<boolean>
|
|
116
|
-
assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad player")
|
|
117
|
+
assert((typeof(player) == "Instance" and player:IsA("Player")) or PlayerMock.isMock(player), "Bad player")
|
|
117
118
|
assert(player:IsDescendantOf(game), "Bad player")
|
|
118
119
|
|
|
119
|
-
if
|
|
120
|
+
local userId = if PlayerMock.isMock(player) then PlayerMock.read(player, "UserId") else player.UserId
|
|
121
|
+
if self._creatorCache[userId] then
|
|
120
122
|
return Promise.resolved(true)
|
|
121
123
|
end
|
|
122
124
|
|
|
@@ -131,12 +133,13 @@ function GroupPermissionProvider._promiseIsAdmin(
|
|
|
131
133
|
): Promise.Promise<boolean>
|
|
132
134
|
assert(player:IsDescendantOf(game))
|
|
133
135
|
|
|
136
|
+
local userId = if PlayerMock.isMock(player) then PlayerMock.read(player, "UserId") else player.UserId
|
|
134
137
|
-- really not saving much time.
|
|
135
|
-
if self._creatorCache[
|
|
138
|
+
if self._creatorCache[userId] then
|
|
136
139
|
return Promise.resolved(true)
|
|
137
140
|
end
|
|
138
141
|
|
|
139
|
-
if self._adminsCache[
|
|
142
|
+
if self._adminsCache[userId] then
|
|
140
143
|
return Promise.resolved(true)
|
|
141
144
|
end
|
|
142
145
|
|
|
@@ -148,13 +151,14 @@ end
|
|
|
148
151
|
function GroupPermissionProvider._handlePlayer(self: GroupPermissionProvider, player: Player): ()
|
|
149
152
|
assert(player, "Bad player")
|
|
150
153
|
|
|
154
|
+
local userId = if PlayerMock.isMock(player) then PlayerMock.read(player, "UserId") else player.UserId
|
|
151
155
|
self:_promiseRankInGroup(player):Then(function(rank)
|
|
152
156
|
if rank >= self._config.minAdminRequiredRank then
|
|
153
|
-
self._adminsCache[
|
|
157
|
+
self._adminsCache[userId] = true
|
|
154
158
|
end
|
|
155
159
|
|
|
156
160
|
if rank >= self._config.minCreatorRequiredRank then
|
|
157
|
-
self._creatorCache[
|
|
161
|
+
self._creatorCache[userId] = true
|
|
158
162
|
end
|
|
159
163
|
end)
|
|
160
164
|
end
|
|
@@ -165,12 +169,13 @@ function GroupPermissionProvider._promiseRankInGroup(
|
|
|
165
169
|
): Promise.Promise<number>
|
|
166
170
|
assert(typeof(player) == "Instance", "Bad player")
|
|
167
171
|
|
|
168
|
-
if
|
|
169
|
-
|
|
172
|
+
local userId = if PlayerMock.isMock(player) then PlayerMock.read(player, "UserId") else player.UserId
|
|
173
|
+
if self._promiseRankPromisesCache[userId] then
|
|
174
|
+
return self._promiseRankPromisesCache[userId]
|
|
170
175
|
end
|
|
171
176
|
|
|
172
|
-
self._promiseRankPromisesCache[
|
|
173
|
-
return self._promiseRankPromisesCache[
|
|
177
|
+
self._promiseRankPromisesCache[userId] = GroupUtils.promiseRankInGroup(player, self._config.groupId)
|
|
178
|
+
return self._promiseRankPromisesCache[userId]
|
|
174
179
|
end
|
|
175
180
|
|
|
176
181
|
return GroupPermissionProvider
|