Skip to content

Commit

Permalink
tokens: save the last usage date
Browse files Browse the repository at this point in the history
Save the last usage date in the database when a token is used.
The "list tokens" API calls also returns this information.
  • Loading branch information
mcorbin committed Sep 6, 2020
1 parent a176002 commit 0293786
Show file tree
Hide file tree
Showing 11 changed files with 62 additions and 9 deletions.
1 change: 1 addition & 0 deletions resources/migrations/005-token-last-used.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE tokens ADD last_used_at timestamp;
2 changes: 2 additions & 0 deletions src/meuse/api/meuse/token.clj
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,15 @@
tokens (->> (public-token/by-user token-db user-name)
(map #(select-keys % [:tokens/id
:tokens/name
:tokens/last_used_at
:tokens/created_at
:tokens/expired_at]))
(map #(set/rename-keys
%
{:tokens/id :id
:tokens/name :name
:tokens/created_at :created-at
:tokens/last_used_at :last-used-at
:tokens/expired_at :expired-at})))]
{:status 200
:body {:tokens tokens}})))
Expand Down
1 change: 1 addition & 0 deletions src/meuse/auth/request.clj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
(if (auth-token/valid? token db-token)
(do
(log/info (log/req-ctx request) "user" (:users/name db-token) "authenticated")
(public-token/set-last-used token-db (:tokens/id db-token))
(assoc request
:auth
(-> (select-keys db-token [:roles/name
Expand Down
6 changes: 6 additions & 0 deletions src/meuse/db/actions/token.clj
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,9 @@
(throw (ex/ex-not-found (format "the token %s does not exist for the user %s"
token-name
user-name))))))

(defn set-last-used
"Set the last used value for a token."
[database token-id]
(jdbc/with-transaction [db-tx database]
(jdbc/execute! db-tx (token-queries/set-last-used token-id))))
3 changes: 3 additions & 0 deletions src/meuse/db/public/token.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
(by-user [this user-name])
(create [this token])
(delete [this user-name token-name])
(set-last-used [this token-id])
(get-token-user-role [this token]))

(defrecord TokenDB [database]
Expand All @@ -17,6 +18,8 @@
(token/create database token))
(delete [this user-name token-name]
(token/delete database user-name token-name))
(set-last-used [this token-id]
(token/set-last-used database token-id))
(get-token-user-role [this token]
(token/get-token-user-role database token)))

Expand Down
8 changes: 8 additions & 0 deletions src/meuse/db/queries/token.clj
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
:t.identifier
:t.token
:t.name
:t.last_used_at
:t.created_at
:t.expired_at
:t.user_id)
Expand Down Expand Up @@ -82,3 +83,10 @@
[:roles :r] [:= :u.role_id :r.id])
(h/where [:= :t.identifier identifier])
sql/format))

(defn set-last-used
[token-id]
(-> (h/update :tokens)
(h/sset {:last_used_at (new Timestamp (.getTime (new Date)))})
(h/where [:= :id token-id])
sql/format))
18 changes: 14 additions & 4 deletions test/meuse/api/meuse/token_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -168,24 +168,34 @@
db-token-2 (first (filter #(= (:name %) "token2") tokens))]
(is (uuid? (:id db-token-1)))
(is (inst? (:created-at db-token-1)))
(is (nil? (:last-used-at db-token-1)))
(is (inst? (:expired-at db-token-1)))
(is (= 4 (count (keys db-token-1))))
(is (= 5 (count (keys db-token-1))))
(is (uuid? (:id db-token-2)))
(is (inst? (:created-at db-token-2)))
(is (nil? (:last-used-at db-token-2)))
(is (inst? (:expired-at db-token-2)))
(is (= 4 (count (keys db-token-2))))))
(is (= 5 (count (keys db-token-2))))))
(testing "admin user can retrieve tokens for another user"
(is (= 2 (count tokens)))
(let [db-token-1 (first (filter #(= (:name %) "token1") tokens-admin))
db-token-2 (first (filter #(= (:name %) "token2") tokens-admin))]
(is (uuid? (:id db-token-1)))
(is (inst? (:created-at db-token-1)))
(is (inst? (:expired-at db-token-1)))
(is (= 4 (count (keys db-token-1))))
(is (= 5 (count (keys db-token-1))))
(is (uuid? (:id db-token-2)))
(is (inst? (:created-at db-token-2)))
(is (inst? (:expired-at db-token-2)))
(is (= 4 (count (keys db-token-2))))))
(is (= 5 (count (keys db-token-2))))))
(testing "last-used-at is returned"
(let [db-token-1 (first (filter #(= (:name %) "token1") tokens))]
(token-db/set-last-used database (:id db-token-1))
(let [tokens (get-in (meuse-api! (add-auth {:action :list-tokens}
"user2"
"tech"))
[:body :tokens])]
(is (inst? (:last-used-at (first (filter #(= (:name %) "token1") tokens))))))))
(testing "non-admin cannot retrieve the tokens for another user"
(is (thrown-with-msg?
ExceptionInfo
Expand Down
8 changes: 6 additions & 2 deletions test/meuse/auth/request_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@
:name "foo"})
user (public-user/by-name database "user2")
request {:headers {"authorization" token}}
result (check-user token-db request)]
result (check-user token-db request)
db-token (->> (public-token/by-user database "user2")
(filter #(= (:tokens/name %) "foo"))
(first))]
(is (= result (assoc request :auth {:user-name "user2"
:user-id (:users/id user)
:role-name "tech"}))))
:role-name "tech"})))
(is (inst? (:tokens/last_used_at db-token))))
(let [token (public-token/create database {:user "user1"
:validity 10
:name "foo"})
Expand Down
15 changes: 15 additions & 0 deletions test/meuse/db/actions/token_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,18 @@
(is (auth-token/valid? token db-token))
(is (= user-name (:users/name db-token)))
(is (= "tech" (:roles/name db-token))))))

(deftest set-last-used-test
(let [user-name "user2"
validity 10
token-name "mytoken"
token (token-db/create database {:user user-name
:validity validity
:name token-name})
db-token-fn (fn [] (->> (token-db/by-user database user-name)
(filter #(= (:tokens/name %) token-name))
first))
db-token (db-token-fn)]
(is (nil? (:tokens/last_used_at db-token)))
(token-db/set-last-used database (:tokens/id db-token))
(is (inst? (:tokens/last_used_at (db-token-fn))))))
6 changes: 4 additions & 2 deletions test/meuse/integration/integration_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,8 @@
(is (= "integration_token_user" (:name (first tokens))))
(is (string? (:created-at (first tokens))))
(is (string? (:expired-at (first tokens))))
(is (= 4 (count (keys (first tokens)))))))
(is (string? (:last-used-at (first tokens))))
(is (= 5 (count (keys (first tokens)))))))
(testing "list tokens: admin can list tokens for another user"
(let [response (client/get (str meuse-url "/api/v1/meuse/token?user=integration")
{:content-type :json
Expand All @@ -526,7 +527,8 @@
(is (= "integration_token_user" (:name (first tokens))))
(is (string? (:created-at (first tokens))))
(is (string? (:expired-at (first tokens))))
(is (= 4 (count (keys (first tokens)))))))
(is (string? (:last-used-at (first tokens))))
(is (= 5 (count (keys (first tokens)))))))
(testing "list tokens: no auth"
(test-http
{:status 403
Expand Down
3 changes: 2 additions & 1 deletion test/meuse/mocks/db.clj
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,13 @@
(search [this query-string] search))))

(defn token-mock
[{:keys [by-user create delete get-token-user-role]}]
[{:keys [by-user create delete get-token-user-role set-last-used]}]
(protocol/spy ITokenDB
(reify ITokenDB
(by-user [this user-name] by-user)
(create [this token] create)
(delete [this user-name token-name] delete)
(set-last-used [this token-id] set-last-used)
(get-token-user-role [this token] get-token-user-role))))

(defn user-mock
Expand Down

0 comments on commit 0293786

Please sign in to comment.