-
Notifications
You must be signed in to change notification settings - Fork 888
/
RealGpc.kt
98 lines (84 loc) · 3.2 KB
/
RealGpc.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/*
* Copyright (c) 2021 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.duckduckgo.privacy.config.impl.features.gpc
import androidx.annotation.VisibleForTesting
import com.duckduckgo.app.browser.UriString.Companion.sameOrSubdomain
import com.duckduckgo.app.privacy.db.UserAllowListRepository
import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.feature.toggles.api.FeatureToggle
import com.duckduckgo.privacy.config.api.Gpc
import com.duckduckgo.privacy.config.api.PrivacyFeatureName
import com.duckduckgo.privacy.config.api.UnprotectedTemporary
import com.duckduckgo.privacy.config.store.features.gpc.GpcRepository
import com.squareup.anvil.annotations.ContributesBinding
import dagger.SingleInstanceIn
import javax.inject.Inject
@ContributesBinding(AppScope::class)
@SingleInstanceIn(AppScope::class)
class RealGpc @Inject constructor(
private val featureToggle: FeatureToggle,
private val gpcRepository: GpcRepository,
private val unprotectedTemporary: UnprotectedTemporary,
private val userAllowListRepository: UserAllowListRepository,
) : Gpc {
override fun isEnabled(): Boolean {
return gpcRepository.isGpcEnabled()
}
override fun getHeaders(url: String): Map<String, String> {
return if (canGpcBeUsedByUrl(url)) {
mapOf(GPC_HEADER to GPC_HEADER_VALUE)
} else {
emptyMap()
}
}
override fun canUrlAddHeaders(
url: String,
existingHeaders: Map<String, String>,
): Boolean {
return if (canGpcBeUsedByUrl(url) && !containsGpcHeader(existingHeaders)) {
gpcRepository.headerEnabledSites.any { sameOrSubdomain(url, it.domain) }
} else {
false
}
}
override fun enableGpc() {
gpcRepository.enableGpc()
}
override fun disableGpc() {
gpcRepository.disableGpc()
}
@VisibleForTesting
fun canGpcBeUsedByUrl(url: String): Boolean {
return isFeatureEnabled() && isEnabled() && !isAnException(url)
}
private fun isFeatureEnabled(): Boolean {
return featureToggle.isFeatureEnabled(PrivacyFeatureName.GpcFeatureName.value)
}
private fun containsGpcHeader(headers: Map<String, String>): Boolean {
return headers.containsKey(GPC_HEADER)
}
@VisibleForTesting
fun isAnException(url: String): Boolean {
return matches(url) || unprotectedTemporary.isAnException(url) || userAllowListRepository.isUrlInUserAllowList(url)
}
private fun matches(url: String): Boolean {
return gpcRepository.exceptions.any { sameOrSubdomain(url, it.domain) }
}
companion object {
const val GPC_HEADER = "sec-gpc"
const val GPC_HEADER_VALUE = "1"
}
}