Smart Contract
CapabilityFilter
A.d8a7e05a7ac670c0.CapabilityFilter
1/// CapabilityFilter defines `Filter`, an interface to sit on top of a ChildAccount's capabilities. Requested
2/// capabilities will only return if the filter's `allowed` method returns true.
3///
4/// Along with the `Filter` interface are three implementations:
5/// - `DenylistFilter` - A filter which contains a mapping of denied Types
6/// - `AllowlistFilter` - A filter which contains a mapping of allowed Types
7/// - `AllowAllFilter` - A passthrough, all requested capabilities are allowed
8///
9access(all) contract CapabilityFilter {
10
11 /* --- Canonical Paths --- */
12 //
13 access(all) let StoragePath: StoragePath
14 access(all) let PublicPath: PublicPath
15
16 access(all) entitlement Add
17 access(all) entitlement Delete
18
19 /* --- Events --- */
20 //
21 access(all) event FilterUpdated(id: UInt64, filterType: Type, type: Type, active: Bool)
22
23 /// `Filter` is a simple interface with methods to determine if a Capability is allowed and retrieve details about
24 /// the Filter itself
25 ///
26 access(all) resource interface Filter {
27 access(all) view fun allowed(cap: Capability): Bool
28 access(all) view fun getDetails(): AnyStruct
29 }
30
31 /// `DenylistFilter` is a `Filter` which contains a mapping of denied Types
32 ///
33 access(all) resource DenylistFilter: Filter {
34
35 /// Represents the underlying types which should not ever be returned by a RestrictedChildAccount. The filter
36 /// will borrow a requested capability, and make sure that the type it gets back is not in the list of denied
37 /// types
38 access(self) let deniedTypes: {Type: Bool}
39
40 /// Adds a type to the mapping of denied types with a value of true
41 ///
42 /// @param type: The type to add to the denied types mapping
43 ///
44 access(Add) fun addType(_ type: Type) {
45 self.deniedTypes.insert(key: type, true)
46 emit FilterUpdated(id: self.uuid, filterType: self.getType(), type: type, active: true)
47 }
48
49 /// Removes a type from the mapping of denied types
50 ///
51 /// @param type: The type to remove from the denied types mapping
52 ///
53 access(Delete) fun removeType(_ type: Type) {
54 if let removed = self.deniedTypes.remove(key: type) {
55 emit FilterUpdated(id: self.uuid, filterType: self.getType(), type: type, active: false)
56 }
57 }
58
59 /// Removes all types from the mapping of denied types
60 ///
61 access(Delete) fun removeAllTypes() {
62 for type in self.deniedTypes.keys {
63 self.removeType(type)
64 }
65 }
66
67 /// Determines if a requested capability is allowed by this `Filter`
68 ///
69 /// @param cap: The capability to check
70 /// @return: true if the capability is allowed, false otherwise
71 ///
72 access(all) view fun allowed(cap: Capability): Bool {
73 if let item = cap.borrow<&AnyResource>() {
74 return !self.deniedTypes.containsKey(item.getType())
75 }
76
77 return false
78 }
79
80 /// Returns details about this filter
81 ///
82 /// @return A struct containing details about this filter including this Filter's Type indexed on the `type`
83 /// key as well as types denied indexed on the `deniedTypes` key
84 ///
85 access(all) view fun getDetails(): AnyStruct {
86 return {
87 "type": self.getType(),
88 "deniedTypes": self.deniedTypes.keys
89 }
90 }
91
92 init() {
93 self.deniedTypes = {}
94 }
95 }
96
97 /// `AllowlistFilter` is a `Filter` which contains a mapping of allowed Types
98 ///
99 access(all) resource AllowlistFilter: Filter {
100 // allowedTypes
101 // Represents the set of underlying types which are allowed to be
102 // returned by a RestrictedChildAccount. The filter will borrow
103 // a requested capability, and make sure that the type it gets back is
104 // in the list of allowed types
105 access(self) let allowedTypes: {Type: Bool}
106
107 /// Adds a type to the mapping of allowed types with a value of true
108 ///
109 /// @param type: The type to add to the allowed types mapping
110 ///
111 access(Add) fun addType(_ type: Type) {
112 self.allowedTypes.insert(key: type, true)
113 emit FilterUpdated(id: self.uuid, filterType: self.getType(), type: type, active: true)
114 }
115
116 /// Removes a type from the mapping of allowed types
117 ///
118 /// @param type: The type to remove from the denied types mapping
119 ///
120 access(Delete) fun removeType(_ type: Type) {
121 if let removed = self.allowedTypes.remove(key: type) {
122 emit FilterUpdated(id: self.uuid, filterType: self.getType(), type: type, active: false)
123 }
124 }
125
126 /// Removes all types from the mapping of denied types
127 ///
128 access(Delete) fun removeAllTypes() {
129 for type in self.allowedTypes.keys {
130 self.removeType(type)
131 }
132 }
133
134 /// Determines if a requested capability is allowed by this `Filter`
135 ///
136 /// @param cap: The capability to check
137 /// @return: true if the capability is allowed, false otherwise
138 ///
139 access(all) view fun allowed(cap: Capability): Bool {
140 if let item = cap.borrow<&AnyResource>() {
141 return self.allowedTypes.containsKey(item.getType())
142 }
143
144 return false
145 }
146
147 /// Returns details about this filter
148 ///
149 /// @return A struct containing details about this filter including this Filter's Type indexed on the `type`
150 /// key as well as types allowed indexed on the `allowedTypes` key
151 ///
152 access(all) view fun getDetails(): AnyStruct {
153 return {
154 "type": self.getType(),
155 "allowedTypes": self.allowedTypes.keys
156 }
157 }
158
159 init() {
160 self.allowedTypes = {}
161 }
162 }
163
164 /// AllowAllFilter is a passthrough, all requested capabilities are allowed
165 ///
166 access(all) resource AllowAllFilter: Filter {
167 /// Determines if a requested capability is allowed by this `Filter`
168 ///
169 /// @param cap: The capability to check
170 /// @return: true since this filter is a passthrough
171 ///
172 access(all) view fun allowed(cap: Capability): Bool {
173 return true
174 }
175
176 /// Returns details about this filter
177 ///
178 /// @return A struct containing details about this filter including this Filter's Type indexed on the `type`
179 /// key
180 ///
181 access(all) view fun getDetails(): AnyStruct {
182 return {
183 "type": self.getType()
184 }
185 }
186 }
187
188 /// Creates a new `Filter` of the given type
189 ///
190 /// @param t: The type of `Filter` to create
191 /// @return: A new instance of the given `Filter` type
192 ///
193 access(all) fun createFilter(_ t: Type): @{Filter} {
194 post {
195 result.getType() == t
196 }
197
198 switch t {
199 case Type<@AllowAllFilter>():
200 return <- create AllowAllFilter()
201 case Type<@AllowlistFilter>():
202 return <- create AllowlistFilter()
203 case Type<@DenylistFilter>():
204 return <- create DenylistFilter()
205 }
206
207 panic("unsupported type requested: ".concat(t.identifier))
208 }
209
210 init() {
211 let identifier = "CapabilityFilter_".concat(self.account.address.toString())
212
213 self.StoragePath = StoragePath(identifier: identifier)!
214 self.PublicPath = PublicPath(identifier: identifier)!
215 }
216}
217