Compare commits
10 Commits
34a365960a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| db20e26d2e | |||
| 98f4aa97f3 | |||
| 1b33e18be5 | |||
| 173793caa3 | |||
| 8698e6b57b | |||
| 386d4f7434 | |||
| 82b1407489 | |||
| 5651cc1ab2 | |||
| 96ed6ae1e9 | |||
| d8c3f5ee67 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
|||||||
/.clj-kondo/
|
/.clj-kondo/
|
||||||
/**/.cpcache/
|
/.cpcache/
|
||||||
/.lsp/
|
/.lsp/
|
||||||
/target/
|
/target/
|
||||||
.nrepl-port
|
.nrepl-port
|
||||||
|
|||||||
@@ -152,7 +152,6 @@ clj-totp.sh import <alias> "<url>"
|
|||||||
- [x] Native compilation script corrections
|
- [x] Native compilation script corrections
|
||||||
|
|
||||||
### v2
|
### v2
|
||||||
- [x] Restructurate as a multiproject
|
|
||||||
- [ ] REST API
|
- [ ] REST API
|
||||||
- [ ] User management
|
- [ ] User management
|
||||||
- [ ] Robust BD backend (H2, datomic, or similar)
|
- [ ] Robust BD backend (H2, datomic, or similar)
|
||||||
@@ -177,14 +176,14 @@ The first step is to install Java JDK, version 11 or newer (version 21 recommend
|
|||||||
|
|
||||||
To execute manually the main function, simple use the `:run` alias:
|
To execute manually the main function, simple use the `:run` alias:
|
||||||
|
|
||||||
```bash
|
```clojure
|
||||||
clojure -M:run/cli <commands and parameters>
|
clojure -M:run <commands and parameters>
|
||||||
```
|
```
|
||||||
|
|
||||||
To build the uberjar:
|
To build the uberjar:
|
||||||
|
|
||||||
```bash
|
```clojure
|
||||||
clojure -T:build :uber/cli
|
clojure -T:build uber
|
||||||
```
|
```
|
||||||
|
|
||||||
There is a utility script to build a native executable using Graal VM. Please, edit the script and
|
There is a utility script to build a native executable using Graal VM. Please, edit the script and
|
||||||
|
|||||||
193
build.clj
193
build.clj
@@ -1,174 +1,37 @@
|
|||||||
(ns build
|
(ns build
|
||||||
(:refer-clojure :exclude [test])
|
(:require [clojure.tools.build.api :as b]))
|
||||||
(:require [clojure.tools.build.api :as b]
|
|
||||||
[clojure.java.io :as io]
|
|
||||||
[clojure.pprint :as pp]
|
|
||||||
[clojure.java.basis :as basis]))
|
|
||||||
|
|
||||||
(def lib-group "es.rcorral")
|
(def lib 'es.rcorral/clj-totp)
|
||||||
(def artifact-prefix "clj-totp")
|
(def version (format "1.2.%s" (b/git-count-revs nil)))
|
||||||
(def subprojs-base "projects")
|
(def target-dir "target")
|
||||||
(def curr-version (format "2.0.%s" (b/git-count-revs nil)))
|
(def class-dir (str target-dir "/classes"))
|
||||||
|
(def uber-file (format "target/%s-%s-standalone.jar" (name lib) version))
|
||||||
|
|
||||||
|
;; delay to defer side effects (artifact downloads)
|
||||||
|
(def basis (delay (b/create-basis {:project "deps.edn"})))
|
||||||
|
|
||||||
;; Builds artifact's full descriptor for each subproject
|
(defn clean [_]
|
||||||
(defn lib [subproj]
|
(b/delete {:path "target"}))
|
||||||
(symbol (str lib-group "/" artifact-prefix "-" subproj )))
|
|
||||||
|
|
||||||
|
(defn compile-java [_]
|
||||||
;; Basis for each subproject, using their own deps.edn
|
(b/javac {:src-dirs ["java"]
|
||||||
;; Injects :extra-deps from :build as additional dependencies
|
:class-dir class-dir
|
||||||
(defn basis [subproj]
|
|
||||||
(delay (b/create-basis {:project (str subprojs-base "/" subproj "/deps.edn")
|
|
||||||
;; Inject extra deps as deps
|
|
||||||
:extra {:deps (get-in (basis/initial-basis) [:aliases :build :extra-deps])}
|
|
||||||
})))
|
|
||||||
|
|
||||||
|
|
||||||
;; Show basis generated for a subproject
|
|
||||||
#_{:clojure-lsp/ignore [:clojure-lsp/unused-public-var]}
|
|
||||||
(defn show-basis [subproj]
|
|
||||||
(println (with-out-str
|
|
||||||
(pp/pprint
|
|
||||||
@(basis subproj)
|
|
||||||
;(basis/initial-basis)
|
|
||||||
))))
|
|
||||||
|
|
||||||
(comment
|
|
||||||
(pp/pprint (keys (basis/initial-basis)))
|
|
||||||
(pp/pprint (:deps (basis/initial-basis)))
|
|
||||||
(pp/pprint (:libs (basis/initial-basis)))
|
|
||||||
(pp/pprint (sort (keys (:aliases (basis/initial-basis)))))
|
|
||||||
(get-in (basis/initial-basis) [:aliases :build :extra-deps])
|
|
||||||
)
|
|
||||||
|
|
||||||
;; Target dir for each subproject
|
|
||||||
(defn target-dir [subproj]
|
|
||||||
(str "target/" subproj))
|
|
||||||
|
|
||||||
|
|
||||||
;; Path for compiled classes
|
|
||||||
(defn class-dir [subproj]
|
|
||||||
(str (target-dir subproj) "/" "classes"))
|
|
||||||
|
|
||||||
|
|
||||||
;; Jar file for each subproject. :uber type adds -standalone suffix
|
|
||||||
(defn jar-file [subproj version type]
|
|
||||||
(format "target/%s-%s-%s%s.jar" artifact-prefix subproj version
|
|
||||||
(if (= type :uber) "-standalone" "")))
|
|
||||||
|
|
||||||
|
|
||||||
;; Clean target dir for subproject
|
|
||||||
(defn clean [{:keys [subproj]}]
|
|
||||||
(b/delete {:path (target-dir subproj)})
|
|
||||||
(println "Project" subproj "cleaned"))
|
|
||||||
|
|
||||||
|
|
||||||
;; Compile java classes, only if java subdir exists
|
|
||||||
(defn compile-java [subproj]
|
|
||||||
(let [java-dir (str subprojs-base "/" subproj "/java")]
|
|
||||||
(when (.exists (io/file java-dir))
|
|
||||||
(println "Compiling java code for" subproj)
|
|
||||||
(b/javac {:src-dirs [java-dir]
|
|
||||||
:class-dir (class-dir subproj)
|
|
||||||
:basis @(basis subproj)
|
|
||||||
:javac-opts ["-source" "11" "--target" "11" "-proc:none"]}))))
|
|
||||||
|
|
||||||
|
|
||||||
;; Create a jar file
|
|
||||||
(defn jar
|
|
||||||
"Build a simple jar file, with no dependencies included."
|
|
||||||
[{:keys [subproj version]
|
|
||||||
:or {version curr-version}}]
|
|
||||||
(let [target-dir (target-dir subproj)
|
|
||||||
class-dir (class-dir subproj)
|
|
||||||
src-dir (str subprojs-base "/" subproj "/src")
|
|
||||||
resources-dir (str subprojs-base "/" subproj "/resources")
|
|
||||||
basis (basis subproj)
|
|
||||||
jar-file (jar-file subproj version :plain)]
|
|
||||||
;; Clean only class dir
|
|
||||||
(b/delete {:path class-dir})
|
|
||||||
;; Copy code
|
|
||||||
(b/copy-dir {:src-dirs [src-dir]
|
|
||||||
:target-dir class-dir})
|
|
||||||
;; Copy resources
|
|
||||||
(b/copy-dir {:src-dirs [resources-dir]
|
|
||||||
:target-dir target-dir})
|
|
||||||
;; Compile java code, if exists
|
|
||||||
(compile-java subproj)
|
|
||||||
;; Build jar
|
|
||||||
(b/jar {:class-dir class-dir
|
|
||||||
:basis @basis
|
:basis @basis
|
||||||
:jar-file jar-file
|
:javac-opts ["-source" "11" "--target" "11" "-proc:none"]}))
|
||||||
:lib (lib subproj)
|
|
||||||
:version version})
|
|
||||||
(println "Generated jar file:" jar-file)))
|
|
||||||
|
|
||||||
|
|
||||||
;; Create an uber jar, with all dependencies inside
|
|
||||||
#_{:clojure-lsp/ignore [:clojure-lsp/unused-public-var]}
|
#_{:clojure-lsp/ignore [:clojure-lsp/unused-public-var]}
|
||||||
(defn uber
|
(defn uber [_]
|
||||||
"Build a uberjar with all dependencies included"
|
(clean nil)
|
||||||
[{:keys [subproj version main-ns]
|
(b/copy-dir {:src-dirs ["src"]
|
||||||
:or {version curr-version}}]
|
:target-dir class-dir})
|
||||||
(let [target-dir (target-dir subproj)
|
(b/copy-file {:src "resources/clj-totp.sh"
|
||||||
basis (basis subproj)
|
:target "target/clj-totp.sh"})
|
||||||
class-dir (class-dir subproj)
|
(compile-java nil)
|
||||||
src-dir (str subprojs-base "/" subproj "/src")
|
(b/compile-clj {:basis @basis
|
||||||
resources-dir (str subprojs-base "/" subproj "/resources")
|
:ns-compile '[totp.app]
|
||||||
uber-file (jar-file subproj version :uber)]
|
:class-dir class-dir})
|
||||||
;(println "Using basis: ")(show-basis subproj)
|
(b/uber {:class-dir class-dir
|
||||||
(b/delete {:path class-dir})
|
:uber-file uber-file
|
||||||
(b/copy-dir {:src-dirs [src-dir]
|
:basis @basis
|
||||||
:target-dir class-dir})
|
:main 'totp.app}))
|
||||||
(b/copy-dir {:src-dirs [resources-dir]
|
|
||||||
:target-dir target-dir})
|
|
||||||
(compile-java subproj)
|
|
||||||
(b/compile-clj {:basis @basis
|
|
||||||
:src-dirs [src-dir] :class-dir class-dir})
|
|
||||||
(b/uber {:class-dir class-dir
|
|
||||||
:uber-file uber-file
|
|
||||||
:basis @basis
|
|
||||||
:main main-ns})
|
|
||||||
(println "Generated uberjar executable:" uber-file)))
|
|
||||||
|
|
||||||
|
|
||||||
;; Multimethod to get the name of all subdirs in a dir.
|
|
||||||
;; Accepts strings or files
|
|
||||||
(defmulti get-subdirs type)
|
|
||||||
|
|
||||||
(defmethod get-subdirs
|
|
||||||
java.lang.String [dir]
|
|
||||||
(get-subdirs (io/file dir)))
|
|
||||||
|
|
||||||
(defmethod get-subdirs
|
|
||||||
java.io.File [dir]
|
|
||||||
(if (.isDirectory dir)
|
|
||||||
(filter #(.isDirectory %) (.listFiles dir))
|
|
||||||
(println "Directory" subprojs-base "doesn't exists!")))
|
|
||||||
|
|
||||||
|
|
||||||
;; Get the name of all subdir in a given directory
|
|
||||||
(defn get-subdir-names
|
|
||||||
"Get a list projects in the 'projects' directory"
|
|
||||||
[dir-name]
|
|
||||||
(map #(.getName %) (get-subdirs dir-name)))
|
|
||||||
|
|
||||||
(comment
|
|
||||||
(get-subdirs "projects")
|
|
||||||
(get-subdirs (io/file "projects"))
|
|
||||||
(get-subdir-names "projects")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
;; Generate jar files for all projects
|
|
||||||
(defn jar-all
|
|
||||||
"Build jar files for all projects"
|
|
||||||
[& {:keys [version]
|
|
||||||
:or {version curr-version}}]
|
|
||||||
(dorun (map #(jar {:subproj % :version version}) (get-subdir-names subprojs-base))))
|
|
||||||
|
|
||||||
(comment
|
|
||||||
(jar-all )
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/env sh
|
#!/bin/env sh
|
||||||
|
|
||||||
protoc --java_out projects/core/java/protoc/ projects/core/resources/proto/otpauth-migration.proto
|
protoc --java_out java/protoc/ resources/proto/otpauth-migration.proto
|
||||||
#javac -cp resources/protobuf-java-3.25.8.jar -d target/classes/proto src/OtpauthMigration.java
|
#javac -cp resources/protobuf-java-3.25.8.jar -d target/classes/proto src/OtpauthMigration.java
|
||||||
|
|||||||
70
deps.edn
70
deps.edn
@@ -1,68 +1,24 @@
|
|||||||
{:paths ["src" "resources" "target/classes"]
|
{:paths ["src" "resources" "target/classes"]
|
||||||
:deps {org.clojure/clojure {:mvn/version "1.12.1"}
|
:deps {org.clojure/clojure {:mvn/version "1.12.1"}
|
||||||
;; Native image (GraalVM). Tutorial: https://shagunagrawal.me/posts/setup-clojure-with-graalvm-for-native-image/
|
io.github.clojure/tools.build {:mvn/version "0.10.10"}
|
||||||
com.github.clj-easy/graal-build-time {:mvn/version "1.0.5"}
|
mvxcvi/alphabase {:mvn/version "3.0.185"} ;; https://github.com/greglook/alphabase
|
||||||
;; Local subprojects
|
cli-matic/cli-matic {:mvn/version "0.5.4"} ;; https://github.com/l3nz/cli-matic
|
||||||
clj-totp/core {:local/root "projects/core"}
|
com.github.clj-easy/graal-build-time {:mvn/version "1.0.5"} ;; Tutorial: https://shagunagrawal.me/posts/setup-clojure-with-graalvm-for-native-image/
|
||||||
clj-totp/cli {:local/root "projects/cli"}
|
;; Protobuf for java
|
||||||
clj-totp/web {:local/root "projects/web"}
|
com.google.protobuf/protobuf-java {:mvn/version "3.25.8"}
|
||||||
clj-totp/gui {:local/root "projects/gui"}
|
;; Progress bar
|
||||||
|
com.github.pmonks/spinner {:mvn/version "2.0.284"}
|
||||||
}
|
}
|
||||||
|
|
||||||
:aliases {;; Execute the app.
|
:aliases {;; Execute the app
|
||||||
:run {:main-opts ["-m" "totp.app"]}
|
:run {:main-opts ["-m" "totp.app"]}
|
||||||
;:run {:exec-fn totp.app/-main}
|
|
||||||
|
|
||||||
;; Execute the app (prepared for more subprojects)
|
|
||||||
:run/cli {:main-opts ["-m" "totp.app"]}
|
|
||||||
|
|
||||||
:run/gui {:main-opts ["-m" "totp.gui"]}
|
|
||||||
|
|
||||||
;; Kaocha runner. You can use the 'kaocha' wrapper located in ~/bin/kaocha
|
;; Kaocha runner. You can use the 'kaocha' wrapper located in ~/bin/kaocha
|
||||||
;; Check test.edn for kaocha runner's config
|
:test {:extra-paths ["test"]
|
||||||
:test {:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}
|
:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
|
||||||
lambdaisland/kaocha-cloverage {:mvn/version "1.1.89"}}
|
|
||||||
:main-opts ["-m" "kaocha.runner"]}
|
:main-opts ["-m" "kaocha.runner"]}
|
||||||
|
|
||||||
;; Run with clj -T:build function-in-build
|
;; Run with clj -T:build function-in-build
|
||||||
:build {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
|
:build {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
|
||||||
;; Used by all compilations
|
:ns-default build}}}
|
||||||
:extra-deps {clj-totp/core {:local/root "projects/core"}}
|
|
||||||
:ns-default build}
|
|
||||||
|
|
||||||
;; Aliases for easy building
|
|
||||||
:build/core {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
|
|
||||||
:ns-default build
|
|
||||||
:exec-fn jar
|
|
||||||
:exec-args {:subproj "core"}}
|
|
||||||
|
|
||||||
:build/cli {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}
|
|
||||||
clj-totp/core {:local/root "projects/core"}}
|
|
||||||
:ns-default build
|
|
||||||
:exec-fn jar
|
|
||||||
:exec-args {:subproj "cli"}}
|
|
||||||
|
|
||||||
:build/web {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
|
|
||||||
:replace-deps {clj-totp/core {:local/root "projects/core"}}
|
|
||||||
:ns-default build
|
|
||||||
:exec-fn jar
|
|
||||||
:exec-args {:subproj "web"}}
|
|
||||||
|
|
||||||
:build/gui {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
|
|
||||||
:replace-deps {clj-totp/core {:local/root "projects/core"}}
|
|
||||||
:ns-default build
|
|
||||||
:exec-fn jar
|
|
||||||
:exec-args {:subproj "gui"}}
|
|
||||||
|
|
||||||
;; Build the three libraries
|
|
||||||
:build/all {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
|
|
||||||
:replace-deps {clj-totp/core {:local/root "projects/core"}}
|
|
||||||
:ns-default build
|
|
||||||
:exec-fn jar-all}
|
|
||||||
|
|
||||||
;; Build uber jar for CLI app
|
|
||||||
:uber/cli {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
|
|
||||||
:ns-default build
|
|
||||||
:exec-fn uber
|
|
||||||
:exec-args {:subproj "cli" :main-ns "totp.app"}}}}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
@startuml
|
|
||||||
' configuration
|
|
||||||
skinparam linetype ortho
|
|
||||||
|
|
||||||
entity "user" as user {
|
|
||||||
id: number
|
|
||||||
--
|
|
||||||
login: varchar(64)
|
|
||||||
passw: varchar(512)
|
|
||||||
active: shortint
|
|
||||||
desc: varchar(512)
|
|
||||||
config: varchar(512)
|
|
||||||
}
|
|
||||||
|
|
||||||
entity "app" as app {
|
|
||||||
id: number
|
|
||||||
--
|
|
||||||
name: varchar(32)
|
|
||||||
desc: varchar(512)
|
|
||||||
secret: varchar(512)
|
|
||||||
period: int
|
|
||||||
config: varchar(512)
|
|
||||||
}
|
|
||||||
|
|
||||||
entity "user_app" as user_app {
|
|
||||||
user_id: number
|
|
||||||
app_id: number
|
|
||||||
--
|
|
||||||
}
|
|
||||||
|
|
||||||
user ||--o{ user_app
|
|
||||||
app ||--o{ user_app
|
|
||||||
|
|
||||||
@enduml
|
|
||||||
BIN
doc/db.png
BIN
doc/db.png
Binary file not shown.
|
Before Width: | Height: | Size: 7.9 KiB |
@@ -4,7 +4,7 @@ NATIVE=~/.sdkman/candidates/java/21.0.2-graalce/bin/native-image
|
|||||||
BIN_FILE=totp
|
BIN_FILE=totp
|
||||||
|
|
||||||
echo "Creating uberjar"
|
echo "Creating uberjar"
|
||||||
clojure -T:build uber
|
#clojure -T:build uber
|
||||||
UBERJAR=$(realpath --relative-to=target target/clj-totp-*-standalone.jar)
|
UBERJAR=$(realpath --relative-to=target target/clj-totp-*-standalone.jar)
|
||||||
|
|
||||||
echo "Creating native image"
|
echo "Creating native image"
|
||||||
@@ -27,4 +27,4 @@ cp target/$BIN_FILE ~/bin
|
|||||||
echo "Copied to ~/bin/$BIN_FILE"
|
echo "Copied to ~/bin/$BIN_FILE"
|
||||||
|
|
||||||
echo "Compress executable for distribution"
|
echo "Compress executable for distribution"
|
||||||
xz -fv target/$BIN_FILE
|
xz target/$BIN_FILE
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
{:paths ["src" "resources" "target/classes"]
|
|
||||||
:deps {;clj-totp/core {:local/root "../core"}
|
|
||||||
org.clojure/clojure {:mvn/version "1.12.1"}
|
|
||||||
cli-matic/cli-matic {:mvn/version "0.5.4"} ;; https://github.com/l3nz/cli-matic
|
|
||||||
;; Progress bar
|
|
||||||
com.github.pmonks/spinner {:mvn/version "2.0.284"}}
|
|
||||||
|
|
||||||
:aliases {;; Execute the app
|
|
||||||
;:run {:main-opts ["-m" "totp.app"]}
|
|
||||||
|
|
||||||
;; Kaocha runner. You can use the 'kaocha' wrapper located in ~/bin/kaocha
|
|
||||||
:test {:extra-paths ["test"]
|
|
||||||
:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
|
|
||||||
:main-opts ["-m" "kaocha.runner"]}}}
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#kaocha/v1 {}
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
|
|||||||
-m
|
|
||||||
kaocha.runner
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{:paths ["src" "resources" "target/classes"]
|
|
||||||
:deps {org.clojure/clojure {:mvn/version "1.12.1"}
|
|
||||||
mvxcvi/alphabase {:mvn/version "3.0.185"} ;; https://github.com/greglook/alphabase
|
|
||||||
;; Protobuf for java
|
|
||||||
com.google.protobuf/protobuf-java {:mvn/version "3.25.8"}
|
|
||||||
}
|
|
||||||
|
|
||||||
:aliases {;; Kaocha runner. You can use the 'kaocha' wrapper located in ~/bin/kaocha
|
|
||||||
:test {:extra-paths ["test"]
|
|
||||||
:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
|
|
||||||
:main-opts ["-m" "kaocha.runner"]}}}
|
|
||||||
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
(ns totp.otp-import
|
|
||||||
(:require [alphabase.bytes :as b]
|
|
||||||
[alphabase.base16 :as hex]
|
|
||||||
[alphabase.base64 :as b64]
|
|
||||||
[alphabase.base2 :as b2]
|
|
||||||
[clojure.math :as m]
|
|
||||||
[clojure.string :as s]))
|
|
||||||
|
|
||||||
;; Original description of the export protocol uses Google's Protocol Buffers
|
|
||||||
;; https://protobuf.dev/
|
|
||||||
|
|
||||||
(comment
|
|
||||||
(let [encoded "CkkKEJ0M4MyHfITKCwCfqPIttjESFHJ1YmVuY2pAMThCMTY5RDVGRjAwGgRTTldMIAEoATACQhMzYjkxMDQxNzI3NzgzNDIzNDYyEAIYASAA"
|
|
||||||
decoded (b64/decode encoded)]
|
|
||||||
(print (hex/encode decoded))
|
|
||||||
(print (b/to-string decoded))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(defn to-fancy-bin
|
|
||||||
"Return a string with bits from number in a fancy manner"
|
|
||||||
[x]
|
|
||||||
(s/join " "
|
|
||||||
(map s/join
|
|
||||||
(partition 8 (b2/encode (byte-array (map #(b/to-byte %) x)))))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn to-fancy-hex
|
|
||||||
"Return a string with hex values from number in a fancy manner"
|
|
||||||
([x]
|
|
||||||
(to-fancy-hex x 2))
|
|
||||||
([x group-size]
|
|
||||||
(s/join " "
|
|
||||||
(map s/join
|
|
||||||
(partition group-size (hex/encode (byte-array (map #(b/to-byte %) x))))))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn len-bits
|
|
||||||
"How may blocks of n bits are needed to encode this number?
|
|
||||||
|
|
||||||
We use the following formula, for calculating the number of digits required
|
|
||||||
to encode the number n in the base b:
|
|
||||||
|
|
||||||
digits = ceil ( log_n ( x + 1 ) )
|
|
||||||
"
|
|
||||||
[x n]
|
|
||||||
(case x
|
|
||||||
nil 0 ;; nill is encoded with zero bytes
|
|
||||||
0 1 ;; One block to zero
|
|
||||||
9223372036854775807 (len-bits (dec x) n) ;; Beware the overflow!! it's best to lose some precision
|
|
||||||
(when (and (>= x 0) (some? n) (> n 0))
|
|
||||||
(long (m/ceil (/ (m/log (inc x)) (m/log (m/pow 2 n))))))))
|
|
||||||
|
|
||||||
(comment
|
|
||||||
(len-bits 513 8)
|
|
||||||
(len-bits Long/MAX_VALUE 8)
|
|
||||||
(len-bits (dec Long/MAX_VALUE) 8)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(defn len-bytes
|
|
||||||
"How may bytes are needed to encode this number?"
|
|
||||||
[x]
|
|
||||||
(len-bits x 8))
|
|
||||||
|
|
||||||
|
|
||||||
(defn integer>bytes
|
|
||||||
"Converts an integer to a byte array"
|
|
||||||
[x]
|
|
||||||
(when x (let [len (len-bytes x)
|
|
||||||
hex-len (* 2 len)]
|
|
||||||
(hex/decode (String/format (str "%0" hex-len "x") (into-array [x]))))))
|
|
||||||
|
|
||||||
(comment
|
|
||||||
(integer>bytes 513)
|
|
||||||
10r3
|
|
||||||
3r10
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(defn decimal-to-base
|
|
||||||
"Converts a decimal number to an arbitrary base. Each digit is encoded as an
|
|
||||||
integer with value between 0 and base-1.
|
|
||||||
|
|
||||||
For example, 10 in base 4 will be encoded as:
|
|
||||||
[2 0 2]
|
|
||||||
, 127 in base 126 will be:
|
|
||||||
[1 1]
|
|
||||||
and so on.
|
|
||||||
|
|
||||||
We will use the sucessive division method: do a integer division of the number
|
|
||||||
to the base until the quotient is zero, and take the reminders backwards. For
|
|
||||||
example, if we want to converto 127 to base 5:
|
|
||||||
|
|
||||||
127 / 5 = 25 rem 2
|
|
||||||
25 / 5 = 5 rem 0
|
|
||||||
5 / 5 = 1 rem 0
|
|
||||||
1 / 5 = 0 rem 1
|
|
||||||
|
|
||||||
so, 127 in base 5 is 1002.
|
|
||||||
"
|
|
||||||
([n base]
|
|
||||||
(decimal-to-base n base true))
|
|
||||||
([n base reverse?]
|
|
||||||
(if (or (nil? n) (nil? base) (zero? n) (zero? base)) ;; Allways [0] for base zero or number zero
|
|
||||||
[0]
|
|
||||||
(loop [acc []
|
|
||||||
x n]
|
|
||||||
(if (zero? x)
|
|
||||||
(vec (if reverse? (reverse acc) acc)) ;; When x is zero, we have finished
|
|
||||||
(recur
|
|
||||||
(conj acc (rem x base)) ;; Accumulate the remainder
|
|
||||||
(quot x base))))))) ;; Pass the quotient to the next step
|
|
||||||
|
|
||||||
(comment
|
|
||||||
(decimal-to-base 8 2)
|
|
||||||
2r1000
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(defn base-to-decimal
|
|
||||||
"Converts from an array with values in an arbitrary base into decimal values"
|
|
||||||
[n base]
|
|
||||||
(if (or (nil? n) (nil? base) (zero? (count n)))
|
|
||||||
0
|
|
||||||
(long (reduce-kv
|
|
||||||
(fn [acc k v]
|
|
||||||
(+ acc (* v (m/pow base k))))
|
|
||||||
0 (vec (reverse n))))))
|
|
||||||
|
|
||||||
(comment
|
|
||||||
(reduce-kv #(+ %1 (* %3 (m/pow 2 %2))) 0 [1 0 0 0]) ;; backwards!
|
|
||||||
|
|
||||||
(base-to-decimal [1 0 0 0] 2)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(defn int->varint
|
|
||||||
"Converts a integer value to a varint, that is encoded in 7 bits, where the first
|
|
||||||
bit is used to indicate if there are more bytes.
|
|
||||||
|
|
||||||
For example, the number 255 is usually encoded in a byte as follows:
|
|
||||||
11111111
|
|
||||||
but in a varint it will turn to:
|
|
||||||
10000001 01111111
|
|
||||||
|
|
||||||
First byte has the MSB set to 1, because there is another byte after it. The
|
|
||||||
second byte is the last one, so the MSB is set to 0.
|
|
||||||
|
|
||||||
The result is a byte array.
|
|
||||||
"
|
|
||||||
[x]
|
|
||||||
(let [b128 (decimal-to-base x 128 false)]
|
|
||||||
(byte-array (conj
|
|
||||||
(vec (map #(bit-or 2r10000000 %) (butlast b128)))
|
|
||||||
(bit-and 2r01111111 (peek b128))))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn varint->int
|
|
||||||
"Converts a varint to an integer. Each byte in varint uses the MSB as a continuation
|
|
||||||
bit: if it's value is 1, there are more bites, if it's 0 it's the last block.
|
|
||||||
|
|
||||||
For example, this varint with two bytes:
|
|
||||||
10000001 01111111
|
|
||||||
|
|
||||||
first byte's MSB has value 1, so there is another byte. The second one has a zero
|
|
||||||
in the MSB, so it's the last block. To calculate the final value, you must ignore
|
|
||||||
the MSB and concatenate both bytes:
|
|
||||||
110010110 00000001 -> original value
|
|
||||||
0010110 0000001 -> delete MSB
|
|
||||||
0000001 0010110 -> reverse bytes (little-endian to big endian)
|
|
||||||
00000010010110 -> concatenate
|
|
||||||
00000000 10010110 -> fill the bytes
|
|
||||||
"
|
|
||||||
[x]
|
|
||||||
(base-to-decimal (reverse (map #(bit-and 2r01111111 %) x)) 128))
|
|
||||||
|
|
||||||
(comment
|
|
||||||
;; 150
|
|
||||||
(to-fancy-bin [-106, 1])
|
|
||||||
(to-fancy-bin (map #(bit-and 2r01111111 %) [-106, 1]))
|
|
||||||
(to-fancy-bin (reverse (map #(bit-and 2r01111111 %) [-106, 1])))
|
|
||||||
(varint->int [-106, 1])
|
|
||||||
|
|
||||||
;; Long/MAX_VALUE
|
|
||||||
(to-fancy-bin (map #(bit-and 2r01111111 %) [-1, -1, -1, -1, -1, -1, -1, -1, 128]))
|
|
||||||
(varint->int [-2, -1, -1, -1, -1, -1, -1, -1, 127])
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(comment
|
|
||||||
(hex/decode (String/format "%08x" (into-array [1023])))
|
|
||||||
|
|
||||||
(to-fancy-bin [150])
|
|
||||||
|
|
||||||
(to-fancy-bin (int->varint 150))
|
|
||||||
(to-fancy-bin (int->varint 151))
|
|
||||||
|
|
||||||
(to-fancy-hex (int->varint 150))
|
|
||||||
(to-fancy-hex (int->varint 151) 4)
|
|
||||||
|
|
||||||
(varint->int [-106, 1])
|
|
||||||
(varint->int [-105, 1])
|
|
||||||
|
|
||||||
(to-fancy-bin (int->varint Long/MAX_VALUE))
|
|
||||||
(to-fancy-bin (int->varint (dec Long/MAX_VALUE)))
|
|
||||||
|
|
||||||
(to-fancy-hex (int->varint (dec Long/MAX_VALUE)))
|
|
||||||
|
|
||||||
(varint->int [-2, -1, -1, -1, -1, -1, -1, -1, 127])
|
|
||||||
|
|
||||||
(to-fancy-bin [2r10010110 2r00000001])
|
|
||||||
|
|
||||||
[2r10010110 2r00000001]
|
|
||||||
[2r0010110 2r0000001]
|
|
||||||
)
|
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
JAVA_EXECUTABLE=java
|
|
||||||
UBER_JAR=$(realpath clj-totp-*-standalone.jar)
|
|
||||||
OPTS="-Xms256m -Xmx256m -client -Dclojure.spec.skip-macros=true"
|
|
||||||
|
|
||||||
|
|
||||||
$JAVA_EXECUTABLE $OPTS -jar $UBER_JAR $@
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
(ns totp.otp-import-test
|
|
||||||
#_{:clj-kondo/ignore [:refer-all]}
|
|
||||||
(:require [clojure.test :refer :all]
|
|
||||||
[totp.otp-import :refer :all]
|
|
||||||
[alphabase.bytes :as b]
|
|
||||||
[alphabase.base16 :as hex]
|
|
||||||
[alphabase.base64 :as b64]
|
|
||||||
[alphabase.base32 :as b32])
|
|
||||||
(:import (java.util Arrays)))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest len-bits-test
|
|
||||||
(testing "Check required number of blocks to encode a number in n bits"
|
|
||||||
(is (nil? (len-bits 10 nil)))
|
|
||||||
(is (= 0 (len-bits nil 10)))
|
|
||||||
(is (= 1 (len-bits 1 2)))
|
|
||||||
(is (= 2 (len-bits 10 2)))
|
|
||||||
(is (= 2 (len-bits 15 2)))
|
|
||||||
(is (= 3 (len-bits 16 2)))
|
|
||||||
(is (= 1 (len-bits 255 8)))
|
|
||||||
(is (= 2 (len-bits 255 7)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(deftest len-bytes-test
|
|
||||||
(testing "Check required number of bytes to encode a number"
|
|
||||||
(is (= 0 (len-bytes nil)))
|
|
||||||
(is (= 1 (len-bytes 0)))
|
|
||||||
(is (= 1 (len-bytes 1)))
|
|
||||||
(is (= 1 (len-bytes 255)))
|
|
||||||
(is (= 2 (len-bytes 256)))
|
|
||||||
(is (= 2 (len-bytes 65535)))
|
|
||||||
(is (= 3 (len-bytes 65536)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(deftest integer>bytes-test
|
|
||||||
(testing "Convert several ints to a byte array"
|
|
||||||
(is (nil? (integer>bytes nil)))
|
|
||||||
(is (b/bytes= (b/init-bytes [0]) (integer>bytes 0)))
|
|
||||||
(is (b/bytes= (b/init-bytes [1]) (integer>bytes 1)))
|
|
||||||
(is (b/bytes= (b/init-bytes [-1]) (integer>bytes 255)))
|
|
||||||
(is (b/bytes= (b/init-bytes [1 0]) (integer>bytes 256)))
|
|
||||||
(is (b/bytes= (b/init-bytes [-1 -1]) (integer>bytes 65535)))
|
|
||||||
(is (b/bytes= (b/init-bytes [127 -1 -1 -1 -1 -1 -1 -1]) (integer>bytes Long/MAX_VALUE)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(deftest decimal-to-base-test
|
|
||||||
(testing "Convert from decimal base to an arbitrary base"
|
|
||||||
(is (= [0] (decimal-to-base 10 nil)))
|
|
||||||
(is (= [0] (decimal-to-base nil 10)))
|
|
||||||
(is (= [0] (decimal-to-base 0 2)))
|
|
||||||
(is (= [0] (decimal-to-base 2 0)))
|
|
||||||
(is (= [1 0 0 0] (decimal-to-base 8 2)))
|
|
||||||
(is (= [2 2] (decimal-to-base 8 3)))
|
|
||||||
(is (= [2 0] (decimal-to-base 8 4)))
|
|
||||||
(is (= [1 3] (decimal-to-base 8 5)))
|
|
||||||
(is (= [3 1] (decimal-to-base 8 5 false)))
|
|
||||||
(is (= [0] (decimal-to-base 0 5)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(deftest base-to-decimal-test
|
|
||||||
(testing "Convert from arbitrary base to decimal"
|
|
||||||
(is (zero? (base-to-decimal nil 2)))
|
|
||||||
(is (zero? (base-to-decimal [] 2)))
|
|
||||||
(is (zero? (base-to-decimal [1] nil)))
|
|
||||||
(is (= 8 (base-to-decimal [1 0 0 0] 2)))
|
|
||||||
(is (= 8 (base-to-decimal [2 2] 3)))
|
|
||||||
(is (= 8 (base-to-decimal [2 0] 4)))
|
|
||||||
(is (= 8 (base-to-decimal [1 3] 5)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(deftest decimal-base-decimal-test
|
|
||||||
(testing "Check if convert from decimal to a base and back preserves the original number"
|
|
||||||
(is (= 8 (base-to-decimal (decimal-to-base 8 2) 2)))
|
|
||||||
(is (= 127 (base-to-decimal (decimal-to-base 127 2) 2)))
|
|
||||||
(is (= 417 (base-to-decimal (decimal-to-base 417 13) 13)))
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(deftest int->varint-test
|
|
||||||
(testing "Convert from integer number (int, long, byte, etc) to varint"
|
|
||||||
(is (Arrays/equals (byte-array [0]) (int->varint nil)))
|
|
||||||
(is (Arrays/equals (byte-array [0]) (int->varint 0)))
|
|
||||||
(is (Arrays/equals (byte-array [2r10010110 2r00000001]) (int->varint 150)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(deftest int->varint-test
|
|
||||||
(testing "Convert from integer number (int, long, byte, etc) to varint"
|
|
||||||
(is (Arrays/equals (byte-array [0]) (int->varint nil)))
|
|
||||||
(is (Arrays/equals (byte-array [0]) (int->varint 0)))
|
|
||||||
(is (Arrays/equals (byte-array [2r10010110 2r00000001]) (int->varint 150)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(deftest varint->int-test
|
|
||||||
(testing "Convert from barint to long"
|
|
||||||
(is (= 0 (varint->int nil)))
|
|
||||||
(is (= 0 (varint->int [0])))
|
|
||||||
(is (= 150 (varint->int [-106, 1])))
|
|
||||||
(is (= 150 (varint->int [22 1])))
|
|
||||||
(is (= 151 (varint->int [-105, 1])))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(deftest int->varint->int-test
|
|
||||||
(testing "Convert from int to varint and back to int"
|
|
||||||
(is (= 150 (varint->int (int->varint 150))))
|
|
||||||
(is (= 151 (varint->int (int->varint 151))))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#kaocha/v1 {}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{:paths ["src" "target/classes"]
|
|
||||||
:deps {;;org.clojure/clojure {:mvn/version "1.12.1"}
|
|
||||||
cli-matic/cli-matic {:mvn/version "0.5.4"} ;; https://github.com/l3nz/cli-matic
|
|
||||||
;; GUI
|
|
||||||
seesaw/seesaw {:mvn/version "1.5.0"}}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
(ns totp.gui
|
|
||||||
#_{:clj-kondo/ignore [:refer-all]}
|
|
||||||
(:require [totp.core :refer :all]
|
|
||||||
[totp.data :refer :all]
|
|
||||||
[clojure.pprint :as pp]
|
|
||||||
[seesaw.core :refer :all]
|
|
||||||
[seesaw.mig :refer :all]
|
|
||||||
[seesaw.clipboard :as cp]
|
|
||||||
[seesaw.dev :refer :all])
|
|
||||||
(:import [java.util Date TimerTask Timer]))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defn content-test
|
|
||||||
[]
|
|
||||||
(let [choose (fn [e] (alert "I should open a file chooser"))]
|
|
||||||
(flow-panel
|
|
||||||
:items ["File" [:fill-h 5]
|
|
||||||
(text (System/getProperty "user.dir")) [:fill-h 5]
|
|
||||||
(action :handler choose :name "...")])))
|
|
||||||
|
|
||||||
|
|
||||||
(defn content-test2
|
|
||||||
[name category date comment]
|
|
||||||
(mig-panel
|
|
||||||
:constraints ["wrap 2"
|
|
||||||
"[shrink 0]20px[200, grow, fill]"
|
|
||||||
"[shrink 0]5px[]"]
|
|
||||||
:items [["name:"] [(text (or name ""))]
|
|
||||||
["category:"] [(text (or category ""))]
|
|
||||||
["date:"] [(text (or date ""))]
|
|
||||||
["comment:"] [(text (or comment ""))]]))
|
|
||||||
|
|
||||||
|
|
||||||
(defn copy-handler
|
|
||||||
"Copies TOTP from text with id field-id, to system clipboard"
|
|
||||||
[field-id e]
|
|
||||||
(let [b-name (str "#" field-id)
|
|
||||||
b-obj (select (to-root e) [(keyword b-name)])
|
|
||||||
b-text (config b-obj :text)]
|
|
||||||
(println "Copying text value: " b-text)
|
|
||||||
(cp/contents! b-text)
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
(defn make-otp-list
|
|
||||||
"Make panel with OTPs"
|
|
||||||
[]
|
|
||||||
(mig-panel
|
|
||||||
:constraints ["wrap 3"
|
|
||||||
"[shrink 0]20px[200, grow, fill]10px[shrink 0]"]
|
|
||||||
:items (let [apps (with-config (filter some? #_{:clj-kondo/ignore [:unresolved-symbol]} cfg))
|
|
||||||
]
|
|
||||||
(reduce (fn [acc a]
|
|
||||||
(let [{:keys [name secret algorithm digits period]} a
|
|
||||||
field-id (str "field-totp-" name)]
|
|
||||||
(-> acc
|
|
||||||
(conj [name])
|
|
||||||
(conj [(text :text (get-otp secret algorithm digits period)
|
|
||||||
:editable? false
|
|
||||||
:id field-id)])
|
|
||||||
(conj [(action :name "copy"
|
|
||||||
:handler (partial copy-handler field-id)
|
|
||||||
:command (str "cmd-" name))]))))
|
|
||||||
[] apps))))
|
|
||||||
|
|
||||||
(comment
|
|
||||||
(make-otp-list)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(defn make-time-bar
|
|
||||||
"Make the progress bar with the remaining time"
|
|
||||||
[init-val]
|
|
||||||
(border-panel
|
|
||||||
:hgap 5
|
|
||||||
:center (progress-bar :id "timer-bar"
|
|
||||||
:value init-val
|
|
||||||
:max 30)
|
|
||||||
:east (text :id "timer-text"
|
|
||||||
:text init-val
|
|
||||||
:editable? false
|
|
||||||
:columns 2
|
|
||||||
:halign :right)
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
(defn make-frame-content
|
|
||||||
[]
|
|
||||||
(border-panel :hgap 10 :vgap 10
|
|
||||||
:center (make-otp-list)
|
|
||||||
:north (make-time-bar (int(/ (calculate-offset-millis 30) 1000)))
|
|
||||||
;:south "SOUTH"
|
|
||||||
;:east "EAST"
|
|
||||||
;:west "WEST"
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
(defn make-frame
|
|
||||||
"Make main frame's content"
|
|
||||||
[]
|
|
||||||
(frame :title "TOTP",
|
|
||||||
:content (make-frame-content)
|
|
||||||
;:minimum-size [320 :by 240]
|
|
||||||
;;:on-close :exit
|
|
||||||
:on-close :dispose))
|
|
||||||
|
|
||||||
|
|
||||||
(defn update-totps
|
|
||||||
"Update all totps"
|
|
||||||
[root]
|
|
||||||
(let [apps (with-config (filter some? #_{:clj-kondo/ignore [:unresolved-symbol]} cfg))]
|
|
||||||
(doseq [app apps]
|
|
||||||
(let [{:keys [name secret algorithm digits period]} app
|
|
||||||
field-id (str "field-totp-" name)
|
|
||||||
field (select root [(keyword (str "#" field-id))])
|
|
||||||
current-otp (get-otp secret algorithm digits period)]
|
|
||||||
(println "Updating" field-id "with otp" current-otp)
|
|
||||||
(config! field :text current-otp)))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn update-progress
|
|
||||||
[root]
|
|
||||||
(let [time-bar (select root [:#timer-bar])
|
|
||||||
time-text (select root [:#timer-text])
|
|
||||||
offset (inc (int (/ (calculate-offset-millis 30) 1000)))]
|
|
||||||
(println "Updating at at" (System/currentTimeMillis))
|
|
||||||
(config! time-bar :value offset)
|
|
||||||
(config! time-text :text offset)
|
|
||||||
(when (= 1 offset)
|
|
||||||
(println "update TOTP")
|
|
||||||
(update-totps root))
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
(defn start-updater
|
|
||||||
[root]
|
|
||||||
(let [now (System/currentTimeMillis)
|
|
||||||
now-seconds (int (/ now 1000))
|
|
||||||
delay (- 1000 (- now (* 1000 now-seconds)))]
|
|
||||||
(. (new Timer) (scheduleAtFixedRate
|
|
||||||
(proxy [TimerTask] []
|
|
||||||
(run [] (update-progress root)))
|
|
||||||
delay 1000))
|
|
||||||
(println "Now" now "Delay" delay)
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
(defn -main [& args]
|
|
||||||
(native!)
|
|
||||||
(invoke-later
|
|
||||||
(-> (make-frame)
|
|
||||||
pack!
|
|
||||||
show!
|
|
||||||
start-updater))
|
|
||||||
(println "Gui started"))
|
|
||||||
|
|
||||||
|
|
||||||
(comment
|
|
||||||
;; This kills your REPL connection
|
|
||||||
(-main)
|
|
||||||
|
|
||||||
(show-options (frame))
|
|
||||||
(show-options (text))
|
|
||||||
|
|
||||||
)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{:paths ["src" "resources" "target/classes"]
|
|
||||||
:deps {;clj-totp/core {:local/root "../core"}
|
|
||||||
org.clojure/clojure {:mvn/version "1.12.1"}
|
|
||||||
;; For SQLite
|
|
||||||
com.github.seancorfield/next.jdbc {:mvn/version "1.3.1048"}
|
|
||||||
org.xerial/sqlite-jdbc {:mvn/version "3.50.3.0"}
|
|
||||||
;; For Datomic local
|
|
||||||
com.datomic/local {:mvn/version "1.0.291"};; https://docs.datomic.com/datomic-local.html
|
|
||||||
}
|
|
||||||
|
|
||||||
:aliases {;; Execute the app
|
|
||||||
;:run {:main-opts ["-m" "totp.app"]}
|
|
||||||
|
|
||||||
;; Kaocha runner. You can use the 'kaocha' wrapper located in ~/bin/kaocha
|
|
||||||
:test {:extra-paths ["test"]
|
|
||||||
:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
|
|
||||||
:main-opts ["-m" "kaocha.runner"]}}}
|
|
||||||
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
(ns totp.db.datomic
|
|
||||||
(:require [totp.data :as data]
|
|
||||||
[datomic.client.api :as d]))
|
|
||||||
|
|
||||||
|
|
||||||
(def cfg-path (data/join-path (System/getProperty "user.home") ".config" "totp"))
|
|
||||||
(def db-path (str cfg-path java.io.File/separator "data"))
|
|
||||||
|
|
||||||
(def cfg {:server-type :datomic-local
|
|
||||||
:system "local-data"
|
|
||||||
:storage-dir db-path})
|
|
||||||
|
|
||||||
(def client (d/client cfg))
|
|
||||||
|
|
||||||
;; Schema for our database
|
|
||||||
|
|
||||||
(def totp-schema [{:db/ident :app/name
|
|
||||||
:db/valueType :db.type/string
|
|
||||||
:db/cardinality :db.cardinality/one
|
|
||||||
:db/unique :db.unique/identity
|
|
||||||
:db/doc "Unique name of the application. Between 2 and 32 chars"
|
|
||||||
:db.attr/preds (fn [s] (<= 3 (count s) 15))}
|
|
||||||
{:db/ident :app/desc
|
|
||||||
:db/valueType :db.type/string
|
|
||||||
:db/cardinality :db.cardinality/one
|
|
||||||
:db/doc "(optional) Description of the application"}
|
|
||||||
{:db/ident :app/secret
|
|
||||||
:db/valueType :db.type/string
|
|
||||||
:db/cardinality :db.cardinality/one
|
|
||||||
:db/doc "Secret BASE32"}
|
|
||||||
{:db/ident :app/period
|
|
||||||
:db/valueType :db.type/long
|
|
||||||
:db/cardinality :db.cardinality/one
|
|
||||||
:db/doc "Time slice in seconds (30 by default)"}
|
|
||||||
{:db/ident :app/config
|
|
||||||
:db/valueType :db.type/string
|
|
||||||
:db/cardinality :db.cardinality/one
|
|
||||||
:db/doc "(Optional) Extra config"}
|
|
||||||
|
|
||||||
{:db/ident :user/login
|
|
||||||
:db/valueType :db.type/string
|
|
||||||
:db/cardinality :db.cardinality/one
|
|
||||||
:db/unique :db.unique/identity
|
|
||||||
:db/doc "Identifier for the user"}
|
|
||||||
{:db/ident :user/passwd
|
|
||||||
:db/valueType :db.type/string
|
|
||||||
:db/cardinality :db.cardinality/one
|
|
||||||
:db/doc "Password"}
|
|
||||||
{:db/ident :user/active
|
|
||||||
:db/valueType :db.type/boolean
|
|
||||||
:db/cardinality :db.cardinality/one
|
|
||||||
:db/doc "Is the user active?"}
|
|
||||||
{:db/ident :user/desc
|
|
||||||
:db/valueType :db.type/string
|
|
||||||
:db/cardinality :db.cardinality/one
|
|
||||||
:db/doc "(Optional) Description of the user"}
|
|
||||||
{:db/ident :user/config
|
|
||||||
:db/valueType :db.type/string
|
|
||||||
:db/cardinality :db.cardinality/one
|
|
||||||
:db/doc "(Optional) Extra config"}
|
|
||||||
{:db/ident :user/apps
|
|
||||||
:db/valueType :db.type/ref
|
|
||||||
:db/cardinality :db.cardinality/many
|
|
||||||
:db/doc "Applications for this user"}])
|
|
||||||
|
|
||||||
(defn init-db
|
|
||||||
[client]
|
|
||||||
(d/create-database client {:db-name "totp"}))
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
(ns totp.db.sqlite
|
|
||||||
(:require [next.jdbc :as jdbc]))
|
|
||||||
|
|
||||||
;; DB configuration
|
|
||||||
(def db {:dbname "totp-data.sqlite"
|
|
||||||
:dbtype "sqlite"})
|
|
||||||
|
|
||||||
;; DB parsed config
|
|
||||||
(def data-source (jdbc/get-datasource db))
|
|
||||||
|
|
||||||
(defn init-db
|
|
||||||
"Create an empty DB"
|
|
||||||
[]
|
|
||||||
(jdbc/execute! data-source ["
|
|
||||||
create table apps (
|
|
||||||
id int auto_increment primary key,
|
|
||||||
name varchar(32),
|
|
||||||
desc varchar(255)
|
|
||||||
)"]))
|
|
||||||
|
|
||||||
(comment
|
|
||||||
(init-db)
|
|
||||||
)
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#kaocha/v1 {}
|
|
||||||
Binary file not shown.
@@ -99,29 +99,3 @@
|
|||||||
(rem (int (m/pow 10 digits))))))))
|
(rem (int (m/pow 10 digits))))))))
|
||||||
([secret]
|
([secret]
|
||||||
(get-otp secret "sha1" 6 30)))
|
(get-otp secret "sha1" 6 30)))
|
||||||
|
|
||||||
|
|
||||||
(defn calculate-offset-millis
|
|
||||||
[period]
|
|
||||||
(let [step-millis (* 1000 period)
|
|
||||||
now (System/currentTimeMillis)]
|
|
||||||
(int (rem now step-millis))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn calculate-delay-millis
|
|
||||||
[period]
|
|
||||||
(let [step-millis (* 1000 period)]
|
|
||||||
(- step-millis (calculate-offset-millis period))))
|
|
||||||
|
|
||||||
|
|
||||||
(comment
|
|
||||||
(let [now (System/currentTimeMillis)
|
|
||||||
off (calculate-offset-millis 30)
|
|
||||||
delay (calculate-delay-millis 30)
|
|
||||||
]
|
|
||||||
(println "Now: " (int (/ now 1000)) "secs" now "millis")
|
|
||||||
(println "Offset:" (int (/ off 1000)) "secs" off "millis")
|
|
||||||
(println "Delay: " (int (/ delay 1000)) "secs" delay "millis")
|
|
||||||
(println "Total: " (+ (int (/ off 1000)) (int (/ delay 1000))) "secs" (+ delay off) "millis")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -89,16 +89,11 @@
|
|||||||
|
|
||||||
(defn list-apps
|
(defn list-apps
|
||||||
[cfg]
|
[cfg]
|
||||||
(->> cfg
|
(map :name
|
||||||
(filter #(contains? % :name))
|
(filter #(contains? % :name) cfg)))
|
||||||
(map :name)))
|
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(list-apps (load-config))
|
(list-apps (load-config)))
|
||||||
(with-config #_{:clj-kondo/ignore [:unresolved-symbol]}
|
|
||||||
(list-apps cfg))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
(defn get-app
|
(defn get-app
|
||||||
[cfg name]
|
[cfg name]
|
||||||
@@ -1,6 +1 @@
|
|||||||
#kaocha/v1
|
#kaocha/v1 {}
|
||||||
{:tests [{:test-paths ["projects/core/src" "projects/core/test"]}]
|
|
||||||
:plugins [:kaocha.plugin/cloverage]
|
|
||||||
:cloverage/opts {:src-ns-path ["projects/core/src" "projects/core/test"]
|
|
||||||
:ns-regex ["totp\\..*(?<!test)$"] ;; All starting with "totp" but not ending by "test"
|
|
||||||
}}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user