如何根据场景过滤对象的敏感数据服务器端
How to filter sensitive data server-side for object based on scenario
场景:
- Class
Example
,从 PFObject
继承而来。一些敏感属性 optional/can 为零。
- 用户A在客户端本地创建示例并直接保存到服务器(ACL只设置给该用户)
- 用户 B 应该只能看到该数据的一个子集。
我的做法:
- 创建云代码函数
getExample
供用户B调用
getExample
查询这些示例,删除结果中的所有敏感属性(或重新创建仅具有允许属性的对象)和 returns 那些过滤的对象。
问题:
- 告诉解析引擎 return 值实际上是
Example
类型并使其自动解析的最佳方法是什么? (通过 PFCloud.callFunction(inBackground: ...)
调用服务器)?
谢谢(另外,非常感谢任何架构建议)。
在 Swift 中解码后端 JSON 响应现在几乎是微不足道的,使用 Decodable
protocol and the JSONDecoder
class from the Foundation 库。
这是一个带有可选字段的示例:
import Foundation
let jsonString1 = """
{
"stringField" : "stringValue",
"intField" : 1,
"floatField" : 1.1
}
"""
let jsonString2 = """
{
"stringField" : "stringValue",
"intField" : 1,
"floatField" : 1.1,
"optionalField" : "optionalValue"
}
"""
struct Response: Decodable {
let stringField: String
let intField: Int
let floatField: Double
let optionalField: String?
}
let response1 = try! JSONDecoder().decode(Response.self, from: jsonString1.data(using: .utf8)!)
// Response(stringField: "stringValue", intField: 1, floatField: 1.1, optionalField: nil)
let response2 = try! JSONDecoder().decode(Response.self, from: jsonString2.data(using: .utf8)!)
// Response(stringField: "stringValue", intField: 1, floatField: 1.1, optionalField: Optional("optionalValue"))
再举一个嵌套对象的例子:
import Foundation
let jsonString = """
{
"stringField" : "stringValue",
"nestedObject" :
{
"intValue" : 2
}
}
"""
struct Response: Decodable {
struct NestedType: Decodable {
let intValue: Int
}
let stringField: String
let nestedObject: NestedType
}
let response = try! JSONDecoder().decode(Response.self, from: jsonString.data(using: .utf8)!)
// Response(stringField: "stringValue", nestedObject: __lldb_expr_10.Response.NestedType(intValue: 2))
带日期的例子:
import Foundation
let jsonString = """
{
"date" : "2021-02-04 09:38:33.000"
}
"""
struct Response: Decodable {
let date: Date
}
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let response = try! decoder.decode(Response.self, from: jsonString.data(using: .utf8)!)
// Response(date: 2021-02-04 09:38:33 +0000)
了解更多 here。
更新
经过讨论,很明显问题是要解析的对象是一个字典,其中包含无法在 JSON 中本地表示的对象(例如 Data
/NSData
)。在这种情况下,可以将它们转换成可表示的东西(例如,对于日期,它可以是格式化的 String
)。这是一个涉及 NSDate
和 JSONSerialization
的示例:
import Foundation
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
let jsonDict1 = ["stringField" : "stringValue"]
let jsonData1 = try JSONSerialization.data(withJSONObject: jsonDict1)
let date = NSDate()
let jsonDict2: [String : Any] = ["stringField" : "stringValue",
"optionalDate" : dateFormatter.string(from: date as Date)]
let jsonData2 = try JSONSerialization.data(withJSONObject: jsonDict2)
struct Response: Decodable {
let stringField: String
let optionalDate: Date?
}
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let response1 = try! decoder.decode(Response.self, from: jsonData1)
// Response(stringField: "stringValue", optionalDate: nil)
let response2 = try! decoder.decode(Response.self, from: jsonData2)
// Response(stringField: "stringValue", optionalDate: Optional(2022-02-21 13:48:48 +0000))
经过一番折腾,我找到了一个很好的解决方案,可以确保自动解析已编辑的 PFObject 子类。诀窍是将属性 __type
和 className
添加到 return 值,以便 objective-c Parse SDK 可以自动识别类型
云代码(简体):
Parse.Cloud.define("getExamples", async (request) => {
let query = new Parse.Query(ExampleClass);
query.limit(50);
let results = await query.find({useMasterKey:true});
let returnResults = [];
_.each(results, function(item) {
// make a JSON copy
let returnItem = item.toJSON();
// delete sensitive properties based on internal rules
if(thisSpecificUserShouldnotHaveAccess) {
delete returnItem["maybeSensitive"];
}
// Mark as valid PFObject (important!)
returnItem["__type"] = "Object";
returnItem["className"] = "Example";
returnResults.push(returnItem);
});
return returnResults;
});
Swift Class 代码(简体):
class Example: PFObject {
public static func parseClassName() -> String { "Example" }
@NSManaged public var title: String?
@NSManaged public var maybeSensitive: String? // will be nil based on server logic
}
Swift 检索代码(简体):
func callFunction<T: PFObject>(name: String, parameters: [String: Any]? = nil) async throws -> [T] {
return try await withCheckedThrowingContinuation { continuation in
PFCloud.callFunction(inBackground: name, withParameters: parameters) { (result, error) in
do {
if let error = error {
throw error
}
if let parsed = result as? [T] {
continuation.resume(returning: parsed)
return
}
if let parsed = result as? T {
continuation.resume(returning: [parsed])
return
}
throw Error.notAPFObject // defined somewhere else
} catch {
continuation.resume(with: .failure(error))
}
}
}
}
调用服务器:
let examples: [Example] = try await callFunction("getExamples")
场景:
- Class
Example
,从PFObject
继承而来。一些敏感属性 optional/can 为零。 - 用户A在客户端本地创建示例并直接保存到服务器(ACL只设置给该用户)
- 用户 B 应该只能看到该数据的一个子集。
我的做法:
- 创建云代码函数
getExample
供用户B调用 getExample
查询这些示例,删除结果中的所有敏感属性(或重新创建仅具有允许属性的对象)和 returns 那些过滤的对象。
问题:
- 告诉解析引擎 return 值实际上是
Example
类型并使其自动解析的最佳方法是什么? (通过PFCloud.callFunction(inBackground: ...)
调用服务器)?
谢谢(另外,非常感谢任何架构建议)。
在 Swift 中解码后端 JSON 响应现在几乎是微不足道的,使用 Decodable
protocol and the JSONDecoder
class from the Foundation 库。
这是一个带有可选字段的示例:
import Foundation
let jsonString1 = """
{
"stringField" : "stringValue",
"intField" : 1,
"floatField" : 1.1
}
"""
let jsonString2 = """
{
"stringField" : "stringValue",
"intField" : 1,
"floatField" : 1.1,
"optionalField" : "optionalValue"
}
"""
struct Response: Decodable {
let stringField: String
let intField: Int
let floatField: Double
let optionalField: String?
}
let response1 = try! JSONDecoder().decode(Response.self, from: jsonString1.data(using: .utf8)!)
// Response(stringField: "stringValue", intField: 1, floatField: 1.1, optionalField: nil)
let response2 = try! JSONDecoder().decode(Response.self, from: jsonString2.data(using: .utf8)!)
// Response(stringField: "stringValue", intField: 1, floatField: 1.1, optionalField: Optional("optionalValue"))
再举一个嵌套对象的例子:
import Foundation
let jsonString = """
{
"stringField" : "stringValue",
"nestedObject" :
{
"intValue" : 2
}
}
"""
struct Response: Decodable {
struct NestedType: Decodable {
let intValue: Int
}
let stringField: String
let nestedObject: NestedType
}
let response = try! JSONDecoder().decode(Response.self, from: jsonString.data(using: .utf8)!)
// Response(stringField: "stringValue", nestedObject: __lldb_expr_10.Response.NestedType(intValue: 2))
带日期的例子:
import Foundation
let jsonString = """
{
"date" : "2021-02-04 09:38:33.000"
}
"""
struct Response: Decodable {
let date: Date
}
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let response = try! decoder.decode(Response.self, from: jsonString.data(using: .utf8)!)
// Response(date: 2021-02-04 09:38:33 +0000)
了解更多 here。
更新
经过讨论,很明显问题是要解析的对象是一个字典,其中包含无法在 JSON 中本地表示的对象(例如 Data
/NSData
)。在这种情况下,可以将它们转换成可表示的东西(例如,对于日期,它可以是格式化的 String
)。这是一个涉及 NSDate
和 JSONSerialization
的示例:
import Foundation
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
let jsonDict1 = ["stringField" : "stringValue"]
let jsonData1 = try JSONSerialization.data(withJSONObject: jsonDict1)
let date = NSDate()
let jsonDict2: [String : Any] = ["stringField" : "stringValue",
"optionalDate" : dateFormatter.string(from: date as Date)]
let jsonData2 = try JSONSerialization.data(withJSONObject: jsonDict2)
struct Response: Decodable {
let stringField: String
let optionalDate: Date?
}
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let response1 = try! decoder.decode(Response.self, from: jsonData1)
// Response(stringField: "stringValue", optionalDate: nil)
let response2 = try! decoder.decode(Response.self, from: jsonData2)
// Response(stringField: "stringValue", optionalDate: Optional(2022-02-21 13:48:48 +0000))
经过一番折腾,我找到了一个很好的解决方案,可以确保自动解析已编辑的 PFObject 子类。诀窍是将属性 __type
和 className
添加到 return 值,以便 objective-c Parse SDK 可以自动识别类型
云代码(简体):
Parse.Cloud.define("getExamples", async (request) => {
let query = new Parse.Query(ExampleClass);
query.limit(50);
let results = await query.find({useMasterKey:true});
let returnResults = [];
_.each(results, function(item) {
// make a JSON copy
let returnItem = item.toJSON();
// delete sensitive properties based on internal rules
if(thisSpecificUserShouldnotHaveAccess) {
delete returnItem["maybeSensitive"];
}
// Mark as valid PFObject (important!)
returnItem["__type"] = "Object";
returnItem["className"] = "Example";
returnResults.push(returnItem);
});
return returnResults;
});
Swift Class 代码(简体):
class Example: PFObject {
public static func parseClassName() -> String { "Example" }
@NSManaged public var title: String?
@NSManaged public var maybeSensitive: String? // will be nil based on server logic
}
Swift 检索代码(简体):
func callFunction<T: PFObject>(name: String, parameters: [String: Any]? = nil) async throws -> [T] {
return try await withCheckedThrowingContinuation { continuation in
PFCloud.callFunction(inBackground: name, withParameters: parameters) { (result, error) in
do {
if let error = error {
throw error
}
if let parsed = result as? [T] {
continuation.resume(returning: parsed)
return
}
if let parsed = result as? T {
continuation.resume(returning: [parsed])
return
}
throw Error.notAPFObject // defined somewhere else
} catch {
continuation.resume(with: .failure(error))
}
}
}
}
调用服务器:
let examples: [Example] = try await callFunction("getExamples")