<commit_before>(ns gogolica.auth
  (:gen-class)
  (:require [clj-http.client :as http]
            [buddy.sign.jwt :as jwt]
            [clj-time.core :as time]
            [clojure.core.cache :as cache]
            [buddy.core.keys :as keys]
            [cheshire.core :refer [parse-string generate-string]]
            [clojure.string :as str]))


;;;; Key management

(def ^:dynamic *service-account* nil)

(defn set-key!
  "Updates the dynamic var *service-account* with the provided data"
  [new-key]
  (alter-var-root #'*service-account* (constantly new-key)))

(defn key-from-file
  "Given a file path containing the json service account key,
  read it, convert it to a clojure map, and save it in the *service-account* var"
  [path]
  (-> (slurp path)
      (parse-string true)
      (set-key!)
      (select-keys [:client_email :project_id])))


;;;; JWT and OAuth2

(def access-request-url
  "URL for requesting new OAuth2 tokens"
  "https://www.googleapis.com/oauth2/v4/token")

(def C
  "Cache for role:token so we don't have to get a new token for every request"
  (atom (cache/ttl-cache-factory {} :ttl 3500000))) ;; 3500 seconds

(defmacro with-cache
  "Given a key and an expression, will set the key in the cache to the result
  of evaluating it if that key is expired."
  [key body-expr]
  `(let [new-cache# (if (cache/has? @C ~key)
                      (cache/hit @C ~key)
                      (cache/miss @C ~key ((fn [] ~body-expr))))]
     (reset! C new-cache#)
     (get new-cache# ~key)))

(defn sign-jwt
  "Given a scope string, generates a new signed JWT claim"
  [scope]
  (let [privkey (keys/str->private-key (:private_key *service-account*))
        claim {:iss (:client_email *service-account*)
               :scope scope
               :aud access-request-url<commit_msg>Add private key loading from the standard environment var<commit_after>(ns gogolica.auth
  (:gen-class)
  (:require [clj-http.client :as http]
            [buddy.sign.jwt :as jwt]
            [clj-time.core :as time]
            [clojure.core.cache :as cache]
            [buddy.core.keys :as keys]
            [cheshire.core :refer [parse-string generate-string]]
            [clojure.string :as str]))


;;;; Key management

(def ^:dynamic *service-account* nil)

(defn set-key!
  "Updates the dynamic var *service-account* with the provided data"
  [new-key]
  (alter-var-root #'*service-account* (constantly new-key)))

(defn key-from-file
  "Given a file path containing the json service account key,
  read it, convert it to a clojure map, and save it in the *service-account* var"
  [path]
  (-> (slurp path)
      (parse-string true)
      (set-key!)
      (select-keys [:client_email :project_id])))

(defn read-application-credentials
  "Google specification for effortless auth on their services:
  https://developers.google.com/identity/protocols/application-default-credentials
  Supporting only reading from the standard environment variable for now;
  this function reads the path from the variable, and loads the service account key
  into the *service-account* var"
  []
  (try (key-from-file (System/getenv "GOOGLE_APPLICATION_CREDENTIALS"))
       (catch Exception e nil)))

(defn authenticated?
  "If we are authenticated, return the email of the loaded service account"
  []
  (:client_email *service-account*))

;;;; JWT and OAuth2

(def access-request-url
  "URL for requesting new OAuth2 tokens"
  "https://www.googleapis.com/oauth2/v4/token")

(def C
  "Cache for role:token so we don't have to get a new token for every request"
  (atom (cache/ttl-cache-factory {} :ttl 3500000))) ;; 3500 seconds

(defmacro with-cache
  "Given a key and an expression, will set the key in the cache to the result
  of evaluating it if that key is expired."
  [key body-expr]
  `(let [new-cache# (if (cache/has? @C ~key)
                      (cache/hit @C ~key)
                      (cache/miss @C ~key ((fn [] ~body-expr))))]
     (reset! C new-cache#)
     (get new-cache# ~key)))

(defn sign-jwt
  "Given a scope string, generates a new signed JWT claim"
  [scope]
  (let [privkey (keys/str->private-key (:private_key *service-account*))
        claim {:iss (:client_email *service-account*)
               :scope scope
               :aud access-request-url
{'fraction_non_alphanumeric': 0.12555529576806174, 'fraction_numerical': 0.007014262333411269, 'mean_word_length': 2.882032667876588, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 3, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4587425', 'n_tokens_mistral': 1435, 'n_tokens_neox': 1372, 'n_words': 456}