1 /*
<lambda>null2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.libraries.pcc.chronicle.api.policy
18 
19 import com.android.libraries.pcc.chronicle.api.policy.annotation.Annotation
20 import com.android.libraries.pcc.chronicle.api.policy.capabilities.Capabilities
21 import com.android.libraries.pcc.chronicle.api.policy.capabilities.Capability
22 import com.android.libraries.pcc.chronicle.api.policy.contextrules.All
23 import com.android.libraries.pcc.chronicle.api.policy.contextrules.PolicyContextRule
24 
25 /** The name of a field within an entity. */
26 typealias FieldName = String
27 
28 /** Defines a data usage policy. See [PolicyProto] for the canonical definition of a policy. */
29 data class Policy(
30   val name: String,
31   val egressType: String,
32   val description: String = "",
33   val targets: List<PolicyTarget> = emptyList(),
34   val configs: Map<String, PolicyConfig> = emptyMap(),
35   val annotations: List<Annotation> = emptyList(),
36   val allowedContext: PolicyContextRule = All
37 ) {
38   /** All fields mentioned the policy (includes nested fields). */
39   val allFields: List<PolicyField> = collectAllFields()
40 
41   /** The set of all redaction labels mentioned in the policy. */
42   val allRedactionLabels: Set<String> = allFields.flatMap { it.redactedUsages.keys }.toSet()
43 
44   private fun collectAllFields(): List<PolicyField> {
45     fun getAllFields(field: PolicyField): List<PolicyField> {
46       return listOf(field) + field.subfields.flatMap { getAllFields(it) }
47     }
48     return targets.flatMap { target -> target.fields.flatMap { getAllFields(it) } }
49   }
50 }
51 
52 /** Target schema governed by a policy, see [PolicyTargetProto]. */
53 data class PolicyTarget(
54   val schemaName: String,
55   val maxAgeMs: Long = 0,
56   val retentions: List<PolicyRetention> = emptyList(),
57   val fields: List<PolicyField> = emptyList(),
58   val annotations: List<Annotation> = emptyList()
59 ) {
60 
toCapabilitiesnull61   fun toCapabilities(): List<Capabilities> {
62     return retentions.map {
63       val ranges = mutableListOf<Capability>()
64       ranges.add(
65         when (it.medium) {
66           StorageMedium.DISK -> Capability.Persistence.ON_DISK
67           StorageMedium.RAM -> Capability.Persistence.IN_MEMORY
68         }
69       )
70       if (it.encryptionRequired) {
71         ranges.add(Capability.Encryption(true))
72       }
73       ranges.add(Capability.Ttl.Minutes((maxAgeMs / Capability.Ttl.MILLIS_IN_MIN).toInt()))
74       Capabilities(ranges)
75     }
76   }
77 }
78 
79 /** Allowed usages for fields in a schema, see [PolicyFieldProto]. */
80 data class PolicyField(
81   /** List of field names leading from the [PolicyTarget] to this nested field. */
82   val fieldPath: List<FieldName>,
83   /** Valid usages of this field without redaction. */
84   val rawUsages: Set<UsageType> = emptySet(),
85   /** Valid usages of this field with redaction first. Maps from redaction label to usages. */
86   val redactedUsages: Map<String, Set<UsageType>> = emptyMap(),
87   val subfields: List<PolicyField> = emptyList(),
88   val annotations: List<Annotation> = emptyList()
89 ) {
90   init {
subfieldnull91     subfields.forEach { subfield ->
92       require(
93         fieldPath.size < subfield.fieldPath.size &&
94           subfield.fieldPath.subList(0, fieldPath.size) == fieldPath
95       ) {
96         "Subfield's field path must be nested inside parent's field path, " +
97           "but got parent: '$fieldPath', child: '${subfield.fieldPath}'."
98       }
99     }
100   }
101 }
102 
103 /** Retention options for storing data, see [PolicyRetentionProto]. */
104 data class PolicyRetention(val medium: StorageMedium, val encryptionRequired: Boolean = false)
105 
106 /**
107  * Config options specified by a policy, see [PolicyConfigProto]. These are arbitrary string
108  * key-value pairs set by the policy author. They have no direct affect on the policy itself.
109  */
110 typealias PolicyConfig = Map<String, String>
111 
112 /** Type of usage permitted of a field, see [PolicyFieldProto.UsageType]. */
113 enum class UsageType {
114   ANY,
115   EGRESS,
116   JOIN,
117   SANDBOX;
118 
119   val canEgress
120     get() = this == ANY || this == EGRESS
121 }
122 
123 /** Convenience method for checking if any usage in a set allows egress. */
canEgressnull124 fun Set<UsageType>.canEgress(): Boolean = any { it.canEgress }
125 
126 /** Target schema governed by a policy, see [PolicyRetentionProto.Medium]. */
127 enum class StorageMedium {
128   RAM,
129   DISK,
130 }
131