feat: add lastused field to api keys

This commit is contained in:
MiniDigger | Martin 2023-04-09 13:59:28 +02:00
parent 34d953b337
commit adf3bec630
10 changed files with 37 additions and 6 deletions

View File

@ -10,6 +10,6 @@ import org.springframework.stereotype.Repository;
@RegisterConstructorMapper(ApiKey.class)
public interface HangarApiKeysDAO {
@SqlQuery("SELECT created_at, name, token_identifier, raw_key_permissions::bigint permissions FROM api_keys WHERE owner_id = :userId ORDER BY created_at DESC")
@SqlQuery("SELECT created_at, name, token_identifier, raw_key_permissions::bigint permissions, last_used FROM api_keys WHERE owner_id = :userId ORDER BY created_at DESC")
List<ApiKey> getUserApiKeys(long userId);
}

View File

@ -13,7 +13,7 @@ import org.springframework.stereotype.Repository;
public interface ApiKeyDAO {
@Timestamped
@SqlUpdate("INSERT INTO api_keys (created_at, name, owner_id, token_identifier, token, raw_key_permissions) VALUES (:now, :name, :ownerId, :tokenIdentifier, :token, :permissions::bit(64))")
@SqlUpdate("INSERT INTO api_keys (created_at, name, owner_id, token_identifier, token, raw_key_permissions, last_used) VALUES (:now, :name, :ownerId, :tokenIdentifier, :token, :permissions::bit(64), :now)")
void insert(@BindBean ApiKeyTable apiKeyTable);
@SqlUpdate("DELETE FROM api_keys WHERE name = :keyName AND owner_id = :userId")
@ -27,4 +27,7 @@ public interface ApiKeyDAO {
@SqlQuery("SELECT *, raw_key_permissions::bigint permissions FROM api_keys WHERE owner_id = :userId AND token_identifier = :identifier")
ApiKeyTable findApiKey(long userId, String identifier);
@SqlUpdate("UPDATE api_keys SET last_used = :lastUsed WHERE id = :id")
void update(@BindBean ApiKeyTable apiKeyTable);
}

View File

@ -11,12 +11,14 @@ public class ApiKey extends Model {
private final String name;
private final String tokenIdentifier;
private final List<NamedPermission> permissions;
private final OffsetDateTime lastUsed;
public ApiKey(final OffsetDateTime createdAt, final String name, final String tokenIdentifier, final Permission permissions) {
public ApiKey(final OffsetDateTime createdAt, final String name, final String tokenIdentifier, final Permission permissions, final OffsetDateTime lastUsed) {
super(createdAt);
this.name = name;
this.tokenIdentifier = tokenIdentifier;
this.permissions = permissions.toNamed();
this.lastUsed = lastUsed;
}
public String getName() {
@ -30,4 +32,8 @@ public class ApiKey extends Model {
public List<NamedPermission> getPermissions() {
return this.permissions;
}
public OffsetDateTime getLastUsed() {
return this.lastUsed;
}
}

View File

@ -13,6 +13,7 @@ public class ApiKeyTable extends Table implements Named {
private final String tokenIdentifier;
private final String token;
private final Permission permissions;
private OffsetDateTime lastUsed = null;
public ApiKeyTable(final String name, final long ownerId, final String tokenIdentifier, final String token, final Permission permissions) {
this.name = name;
@ -23,13 +24,14 @@ public class ApiKeyTable extends Table implements Named {
}
@JdbiConstructor
public ApiKeyTable(final OffsetDateTime createdAt, final long id, final String name, final long ownerId, final String tokenIdentifier, final String token, final Permission permissions) {
public ApiKeyTable(final OffsetDateTime createdAt, final long id, final String name, final long ownerId, final String tokenIdentifier, final String token, final Permission permissions, final OffsetDateTime lastUsed) {
super(createdAt, id);
this.name = name;
this.ownerId = ownerId;
this.tokenIdentifier = tokenIdentifier;
this.token = token;
this.permissions = permissions;
this.lastUsed = lastUsed;
}
@Override
@ -53,6 +55,14 @@ public class ApiKeyTable extends Table implements Named {
return this.permissions;
}
public OffsetDateTime getLastUsed() {
return this.lastUsed;
}
public void setLastUsed(final OffsetDateTime lastUsed) {
this.lastUsed = lastUsed;
}
@Override
public String toString() {
return "ApiKeyTable{" +
@ -61,6 +71,7 @@ public class ApiKeyTable extends Table implements Named {
", tokenIdentifier='" + this.tokenIdentifier + '\'' +
", token='" + this.token + '\'' +
", permissions=" + this.permissions +
", lastUsed=" + this.lastUsed +
"} " + super.toString();
}
}

View File

@ -11,6 +11,7 @@ import io.papermc.hangar.model.db.auth.ApiKeyTable;
import io.papermc.hangar.components.auth.service.TokenService;
import io.papermc.hangar.util.CryptoUtils;
import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -45,6 +46,8 @@ public class APIAuthenticationService extends HangarComponent {
if (apiKeyTable == null) {
throw new HangarApiException("No valid API Key found");
}
apiKeyTable.setLastUsed(OffsetDateTime.now());
this.apiKeyDAO.update(apiKeyTable);
final UserTable userTable = this.userDAO.getUserTable(apiKeyTable.getOwnerId());
final int aal = this.credentialsService.getAal(userTable);

View File

@ -0,0 +1,2 @@
ALTER TABLE api_keys
ADD COLUMN last_used timestamptz

View File

@ -886,6 +886,7 @@
"created": "Copy the created key before creating another one or refreshing the page. Do not share this with anyone else.",
"keyIdentifier": "Identifier",
"permissions": "Permissions",
"lastUsed": "Last used",
"permissionRequired": "At least one permission needs to be set.",
"delete": "Delete",
"copy": "Click to copy key to clipboard",

View File

@ -11,7 +11,6 @@ import { handleRequestError } from "~/composables/useErrorHandling";
import InputCheckbox from "~/components/ui/InputCheckbox.vue";
import Table from "~/components/design/Table.vue";
import Alert from "~/components/design/Alert.vue";
import Card from "~/components/design/Card.vue";
import { useNotificationStore } from "~/store/notification";
import { maxLength, minLength, required } from "~/composables/useValidationHelpers";
import { validApiKeyName } from "~/composables/useHangarValidations";
@ -20,6 +19,7 @@ import { NamedPermission } from "~/types/enums";
import { definePageMeta } from "#imports";
import Tooltip from "~/components/design/Tooltip.vue";
import { useAuthStore } from "~/store/auth";
import PrettyTime from "~/components/design/PrettyTime.vue";
definePageMeta({
globalPermsRequired: ["EDIT_API_KEYS"],
@ -131,6 +131,9 @@ function copy(event: any) {
<th>
{{ i18n.t("apiKeys.permissions") }}
</th>
<th class="min-w-100px">
{{ i18n.t("apiKeys.lastUsed") }}
</th>
<th />
</tr>
</thead>
@ -139,6 +142,7 @@ function copy(event: any) {
<td>{{ key.name }}</td>
<td>{{ key.tokenIdentifier }}</td>
<td>{{ key.permissions.join(", ") }}</td>
<td><PrettyTime v-if="key.lastUsed" :time="key.lastUsed" long /></td>
<td>
<Button button-type="red" :loading="loadingDelete[key.name]" @click="deleteKey(key)"><IconMdiDelete /></Button>
</td>

View File

@ -1,4 +1,4 @@
import { NamedPermission, Platform, ProjectCategory, Prompt, RoleCategory } from "~/types/enums";
import { NamedPermission, Platform, ProjectCategory, Prompt } from "~/types/enums";
import { Color, FlagReason, IPlatform, IProjectCategory, IPrompt, IVisibility } from "hangar-internal";
import { IPermission, Role } from "hangar-api";

View File

@ -31,5 +31,6 @@ declare module "hangar-api" {
token?: string;
tokenIdentifier?: string;
permissions: NamedPermission[];
lastUsed?: string;
}
}