Posted on

At work, some time ago I introduced a type wrapper for helping to prevent sensitive data from being accidentally leaked in logs or returned in APIs it shouldn't be.

data class Restricted<T : Any>(private val value: T) {
    override fun toString() = "Restricted(****)"

    fun unsafeGetRestricted() = value
}

While this doesn't actually prevent the data from being misused, it does make it easier to audit the usage, and helps data from being accidentally serialised.

I went even further and required an explanation of why the data was being accessed:

data class Restricted<T : Any>(private val value: T) {
    override fun toString() = "Restricted(****)"

    /**
     * Get the underlying restricted data.
     *
     * @param explanation Message justifying why accessing this restricted data is necessary and safe
     */
    fun unsafeGetRestricted(@Suppress("UNUSED_PARAMETER") explanation: String) = value
}

I don't know if the explanation really adds much value, but I like that the caller has to at least think a little when getting the underlying value.

We can then introduce extension functions for convenience, for example:

fun <A : Any, B : Any> Restricted<A>.unsafeMapRestricted(
    explanation: String,
    block: (A) -> B,
): Restricted<B> = Restricted(block(unsafeGetRestricted(explanation)))

Note that this is obviously somewhat dangerous, as the caller has to take care of how the underlying value is used in the block closure.

We can further refine this by introducing new types that wrap Restricted, for example:

data class AccessToken<T : Any>(private val restricted: Restricted<T>) {
    constructor(value: T) : this(Restricted(value))

    fun unsafeGetAccessToken(explanation: String) = restricted.unsafeGetRestricted(explanation)
}

At work, we introduced colour graded types like Red, Orange, and Yellow to match personal information grades. Of course, it goes without saying that this is sort of a last resort for protecting the personal data: the best option is not to collect it, to restrict access to it, and to avoid accessing it except when necessary.

Hopefully, this helps someone else who wants an extra layer of protection to mitigate accidental leaking of sensitive data.