<commit_before>(ns metabase.util.password
  (:require [cemerick.friend.credentials :as creds]
            [metabase.config :as config]))


(defn- count-occurrences
  "Return a map of the counts of each class of character for PASSWORD.

    (count-occurrences \"GoodPw!!\")
      -> {:total  8, :lower 4, :upper 2, :letter 6, :digit 0, :special 2}"
  [password]
  (loop [[^Character c & more] password, {:keys [total, lower, upper, letter, digit, special], :as counts} {:total 0, :lower 0, :upper 0, :letter 0, :digit 0, :special 0}]
    (if-not c counts
            (recur more (merge (update counts :total inc)
                               (cond
                                 (Character/isLowerCase c) {:lower   (inc lower), :letter (inc letter)}
                                 (Character/isUpperCase c) {:upper   (inc upper), :letter (inc letter)}
                                 (Character/isDigit     c) {:digit   (inc digit)}
                                 :else                     {:special (inc special)}))))))

(def ^:private ^:const complexity->char-type->min
  "Minimum counts of each class of character a password should have for a given password complexity level."
  {:weak   {:total   6} ; total here effectively means the same thing as a minimum password length
   :normal {:total   6
            :digit   1}
   :strong {:total   8
            :lower   2
            :upper   2
            :digit   1
            :special 1}})

(defn- password-has-char-counts?
  "Check that PASSWORD satisfies the minimum count requirements for each character class.

    (password-has-char-counts? {:total 6, :lower 1, :upper 1, :digit 1, :special 1} \"abc\")
      -> false
    (password-has-char-counts? {:total 6, :lower 1, :upper 1, :digit 1, :special 1} \"passworD1!\")
      -> true"
  [char-type->min password]
  {:pre [(map? char-type->min)
         (string? password)]}
  (let [occurances (count-occurrences password)]
    (boolean (loop [[[char-type min-count] & more] (seq char-type->min)]
               (if-not char-type
                 true
                 (when (>= (occurances char-type) min-count)
                   (recur more)))))))
<commit_msg>make code slightly prettier :sunglasses:<commit_after>(ns metabase.util.password
  (:require [cemerick.friend.credentials :as creds]
            [metabase.config :as config]))


(defn- count-occurrences
  "Return a map of the counts of each class of character for PASSWORD.

    (count-occurrences \"GoodPw!!\")
      -> {:total  8, :lower 4, :upper 2, :letter 6, :digit 0, :special 2}"
  [password]
  (loop [[^Character c & more] password, {:keys [total, lower, upper, letter, digit, special], :as counts} {:total 0, :lower 0, :upper 0, :letter 0, :digit 0, :special 0}]
    (if-not c counts
      (recur more (merge (update counts :total inc)
                         (cond
                           (Character/isLowerCase c) {:lower   (inc lower), :letter (inc letter)}
                           (Character/isUpperCase c) {:upper   (inc upper), :letter (inc letter)}
                           (Character/isDigit     c) {:digit   (inc digit)}
                           :else                     {:special (inc special)}))))))

(def ^:private ^:const complexity->char-type->min
  "Minimum counts of each class of character a password should have for a given password complexity level."
  {:weak   {:total   6} ; total here effectively means the same thing as a minimum password length
   :normal {:total   6
            :digit   1}
   :strong {:total   8
            :lower   2
            :upper   2
            :digit   1
            :special 1}})

(defn- password-has-char-counts?
  "Check that PASSWORD satisfies the minimum count requirements for each character class.

    (password-has-char-counts? {:total 6, :lower 1, :upper 1, :digit 1, :special 1} \"abc\")
      -> false
    (password-has-char-counts? {:total 6, :lower 1, :upper 1, :digit 1, :special 1} \"passworD1!\")
      -> true"
  [char-type->min password]
  {:pre [(map? char-type->min)
         (string? password)]}
  (let [occurances (count-occurrences password)]
    (boolean (loop [[[char-type min-count] & more] (seq char-type->min)]
               (if-not char-type true
                 (when (>= (occurances char-type) min-count)
                   (recur more)))))))

{'fraction_non_alphanumeric': 0.133086876155268, 'fraction_numerical': 0.01432532347504621, 'mean_word_length': 2.1188760806916425, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4601906', 'n_tokens_mistral': 1363, 'n_tokens_neox': 1253, 'n_words': 444}