All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cloudinary.AuthToken.kt Maven / Gradle / Ivy

Go to download

Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. Upload images to the cloud. Automatically perform smart image resizing, cropping and conversion without installing any complex software. Integrate Facebook or Twitter profile image extraction in a snap, in any dimension and style to match your website’s graphics requirements. Images are seamlessly delivered through a fast CDN, and much much more. This Java library allows to easily integrate with Cloudinary in Kotlin applications.

The newest version!
package com.cloudinary

import com.cloudinary.util.cldHexStringToByteArray
import com.cloudinary.util.cldUrlEncode
import com.cloudinary.util.toHex
import java.nio.charset.Charset
import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException
import java.util.*
import java.util.regex.Pattern
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

const val KEY = "key"
const val IP = "ip"
const val ACL = "acl"
const val ACLS = "acls"
const val START_TIME = "start_time"
const val EXPIRATION = "expiration"
const val DURATION = "duration"

internal val NULL_AUTH_TOKEN = AuthToken(key = "", isNullToken = true)
private const val AUTH_TOKEN_NAME = "__cld_token__"
private val UNSAFE_URL_CHARS_PATTERN =
    Pattern.compile("[ \"#%&'/:;<=>?@\\[\\\\\\]^`{|}~]")!!

data class AuthToken(
    private val tokenName: String? = AUTH_TOKEN_NAME,
    private val key: String,
    val startTime: Long = 0,
    private val expiration: Long = 0,
    private val ip: String? = null,
    @Deprecated("This parameter will be removed in the next major version, use acls instead", replaceWith = ReplaceWith("acls"))
    val acl: String? = null,
    val duration: Long = 0,
    private val isNullToken: Boolean = false,
    val acls: ArrayList = arrayListOf()
) {

    constructor(params: Map<*, *>) : this(
        key = (params[KEY] ?: error("Must provide Auth Token key")).toString(),
        startTime = params[START_TIME]?.toString()?.toLong() ?: 0,
        expiration = params[EXPIRATION]?.toString()?.toLong() ?: 0,
        ip = params[IP]?.toString(),
        acl = params[ACL]?.toString(),
        duration = params[DURATION]?.toString()?.toLong() ?: 0,
        acls = (if (params[ACLS] != null)  params[ACLS] as ArrayList else arrayListOf())
    )

    /**
     * Generate a URL token for the given URL.
     *
     * @param url the URL to be authorized
     * @return a URL token
     */
    fun generate(url: String? = null): String {
        var expiration = expiration
        if (expiration == 0L) {
            expiration = if (duration > 0) {
                val start =
                    if (startTime > 0) startTime else Calendar.getInstance(TimeZone.getTimeZone("UTC")).timeInMillis / 1000L
                start + duration
            } else {
                throw IllegalArgumentException("Must provide either expiration or duration")
            }
        }
        val tokenParts = ArrayList()
        if (ip != null) {
            tokenParts.add("ip=$ip")
        }
        if (startTime > 0) {
            tokenParts.add("st=$startTime")
        }
        tokenParts.add("exp=$expiration")
        if (acl != null) {
            acls.add(acl)
        }
        if (acls.isNotEmpty()) {
            tokenParts.add("acl=" + escapeToLower(acls.joinToString("!")))
        }
        val toSign = ArrayList(tokenParts)
        if (url != null && acl == null) {
            toSign.add("url=" + escapeToLower(url))
        }
        val auth = digest(toSign.joinToString("~"))
        tokenParts.add("hmac=$auth")
        return tokenName + "=" + tokenParts.joinToString("~")
    }

    /**
     * Escape url using lowercase hex code
     *
     * @param url a url string
     * @return escaped url
     */
    private fun escapeToLower(url: String): String {
        return url.cldUrlEncode(UNSAFE_URL_CHARS_PATTERN, Charset.forName("UTF-8"))
    }

    private fun digest(message: String): String {
        val binKey: ByteArray = key.cldHexStringToByteArray()
        return try {
            val hmac = Mac.getInstance("HmacSHA256")
            val secret = SecretKeySpec(binKey, "HmacSHA256")
            hmac.init(secret)
            val bytes = message.toByteArray()
            hmac.doFinal(bytes).toHex().toLowerCase(Locale.US)
        } catch (e: NoSuchAlgorithmException) {
            throw RuntimeException("Cannot create authorization token.", e)
        } catch (e: InvalidKeyException) {
            throw RuntimeException("Cannot create authorization token.", e)
        }
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is AuthToken) return false

        if (tokenName != other.tokenName) return false
        if (key != other.key) return false
        if (startTime != other.startTime) return false
        if (expiration != other.expiration) return false
        if (ip != other.ip) return false
        if (acl != other.acl) return false
        if (duration != other.duration) return false
        if (isNullToken != other.isNullToken) return false

        return true
    }

    override fun hashCode(): Int {
        if (isNullToken) return 0

        var result = tokenName?.hashCode() ?: 0
        result = 31 * result + key.hashCode()
        result = 31 * result + startTime.hashCode()
        result = 31 * result + expiration.hashCode()
        result = 31 * result + (ip?.hashCode() ?: 0)
        result = 31 * result + (acl?.hashCode() ?: 0)
        result = 31 * result + duration.hashCode()
        return result
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy