-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14489 from itisAliRH/api-key-Enhancement
API key enhancements
- Loading branch information
Showing
25 changed files
with
506 additions
and
191 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
<script setup> | ||
import { ref } from "vue"; | ||
import svc from "./model/service"; | ||
import APIKeyItem from "./APIKeyItem"; | ||
import { getGalaxyInstance } from "app"; | ||
import LoadingSpan from "components/LoadingSpan"; | ||
const apiKey = ref(null); | ||
const loading = ref(false); | ||
const errorMessage = ref(null); | ||
const createLoading = ref(false); | ||
const currentUserId = getGalaxyInstance().user.id; | ||
const getAPIKey = () => { | ||
loading.value = true; | ||
svc.getAPIKey(currentUserId) | ||
.then((result) => (apiKey.value = result[0])) | ||
.catch((err) => (errorMessage.value = err.message)) | ||
.finally(() => (loading.value = false)); | ||
}; | ||
const createNewAPIKey = () => { | ||
createLoading.value = true; | ||
svc.createNewAPIKey(currentUserId) | ||
.then(() => getAPIKey()) | ||
.catch((err) => (errorMessage.value = err.message)) | ||
.finally(() => (createLoading.value = false)); | ||
}; | ||
getAPIKey(); | ||
</script> | ||
|
||
<template> | ||
<section class="api-key d-flex flex-column"> | ||
<h2 v-localize>Manage API Key</h2> | ||
|
||
<span v-localize class="mb-2"> | ||
An API key will allow you to access via web API. Please note that this key acts as an alternate means to | ||
access your account and should be treated with the same care as your login password. | ||
</span> | ||
|
||
<b-alert :show="errorMessage" dismissible fade variant="warning" @dismissed="errorMessage = null"> | ||
{{ errorMessage }} | ||
</b-alert> | ||
|
||
<b-alert v-if="loading" class="m-2" show variant="info"> | ||
<LoadingSpan message="Loading API keys" /> | ||
</b-alert> | ||
|
||
<b-button | ||
v-else-if="!loading && !apiKey" | ||
:disabled="createLoading" | ||
class="create-button" | ||
variant="primary" | ||
@click.prevent="createNewAPIKey"> | ||
<icon v-if="!createLoading" icon="plus" /> | ||
<icon v-else icon="spinner" spin /> | ||
<span v-localize>Create a new key</span> | ||
</b-button> | ||
|
||
<div v-else-if="apiKey" class="mx-2"> | ||
<APIKeyItem :item="apiKey" @getAPIKey="getAPIKey" /> | ||
</div> | ||
</section> | ||
</template> | ||
|
||
<style scoped> | ||
.create-button { | ||
max-width: 10rem; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
<script setup> | ||
import { ref } from "vue"; | ||
import svc from "./model/service"; | ||
import { getGalaxyInstance } from "app"; | ||
import UtcDate from "components/UtcDate"; | ||
import CopyToClipboard from "components/CopyToClipboard"; | ||
defineProps({ | ||
item: { | ||
type: Object, | ||
required: true, | ||
}, | ||
}); | ||
const emit = defineEmits(["getAPIKey"]); | ||
const currentUserId = getGalaxyInstance().user.id; | ||
const modal = ref(null); | ||
const hover = ref(false); | ||
const errorMessage = ref(null); | ||
const toggleDeleteModal = () => { | ||
modal.value.toggle(); | ||
}; | ||
const deleteKey = () => { | ||
svc.deleteAPIKey(currentUserId) | ||
.then(() => emit("getAPIKey")) | ||
.catch((err) => (errorMessage.value = err.message)); | ||
}; | ||
</script> | ||
|
||
<template> | ||
<b-card title="Current API key"> | ||
<div class="d-flex justify-content-between w-100"> | ||
<div class="w-100"> | ||
<b-input-group | ||
class="w-100" | ||
@blur="hover = false" | ||
@focus="hover = true" | ||
@mouseover="hover = true" | ||
@mouseleave="hover = false"> | ||
<b-input-group-prepend> | ||
<b-input-group-text> | ||
<icon icon="key" /> | ||
</b-input-group-text> | ||
</b-input-group-prepend> | ||
|
||
<b-input | ||
:type="hover ? 'text' : 'password'" | ||
:value="item.key" | ||
disabled | ||
data-test-id="api-key-input" /> | ||
|
||
<b-input-group-append> | ||
<b-input-group-text> | ||
<copy-to-clipboard | ||
message="Key was copied to clipboard" | ||
:text="item.key" | ||
title="Copy key" /> | ||
</b-input-group-text> | ||
<b-button title="Delete api key" @click="toggleDeleteModal"> | ||
<icon icon="trash" /> | ||
</b-button> | ||
</b-input-group-append> | ||
</b-input-group> | ||
<span class="small text-black-50"> | ||
created on | ||
<UtcDate class="text-black-50 small" :date="item.create_time" mode="pretty" /> | ||
</span> | ||
</div> | ||
</div> | ||
|
||
<b-modal ref="modal" title="Delete API key" size="md" @ok="deleteKey"> | ||
<p v-localize>Are you sure you want to delete this key?</p> | ||
</b-modal> | ||
</b-card> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default as APIKey } from "./APIKey.vue"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import axios from "axios"; | ||
import { getRootFromIndexLink } from "onload"; | ||
|
||
const getUrl = (path) => getRootFromIndexLink() + path; | ||
|
||
export async function getAPIKey(userId) { | ||
const url = getUrl(`api/users/${userId}/api_key/detailed`); | ||
const response = await axios.get(url); | ||
if (response.status === 204) { | ||
return []; | ||
} | ||
if (response.status !== 200) { | ||
throw new Error("Unexpected response retrieving the API key."); | ||
} | ||
return [response.data]; | ||
} | ||
|
||
export async function createNewAPIKey(userId) { | ||
const url = getUrl(`api/users/${userId}/api_key`); | ||
const response = await axios.post(url); | ||
if (response.status !== 200) { | ||
throw new Error("Create API key failure."); | ||
} | ||
return response.data; | ||
} | ||
|
||
export async function deleteAPIKey(userId) { | ||
const url = getUrl(`api/users/${userId}/api_key`); | ||
const response = await axios.delete(url); | ||
if (response.status !== 204) { | ||
throw new Error("Delete API Key failure."); | ||
} | ||
} | ||
|
||
export default { | ||
getAPIKey, | ||
createNewAPIKey, | ||
deleteAPIKey, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,54 @@ | ||
from typing import ( | ||
Optional, | ||
TYPE_CHECKING, | ||
) | ||
|
||
from galaxy.model import User | ||
from galaxy.structured_app import BasicSharedApp | ||
|
||
if TYPE_CHECKING: | ||
from galaxy.model import APIKeys | ||
|
||
|
||
class ApiKeyManager: | ||
def __init__(self, app: BasicSharedApp): | ||
self.app = app | ||
|
||
def create_api_key(self, user) -> str: | ||
def get_api_key(self, user: User) -> Optional["APIKeys"]: | ||
sa_session = self.app.model.context | ||
api_key = ( | ||
sa_session.query(self.app.model.APIKeys) | ||
.filter_by(user_id=user.id, deleted=False) | ||
.order_by(self.app.model.APIKeys.create_time.desc()) | ||
.first() | ||
) | ||
return api_key | ||
|
||
def create_api_key(self, user: User) -> "APIKeys": | ||
guid = self.app.security.get_new_guid() | ||
new_key = self.app.model.APIKeys() | ||
new_key.user_id = user.id | ||
new_key.key = guid | ||
sa_session = self.app.model.context | ||
sa_session.add(new_key) | ||
sa_session.flush() | ||
return guid | ||
return new_key | ||
|
||
def get_or_create_api_key(self, user) -> str: | ||
def get_or_create_api_key(self, user: User) -> str: | ||
# Logic Galaxy has always used - but it would appear to have a race | ||
# condition. Worth fixing? Would kind of need a message queue to fix | ||
# in multiple process mode. | ||
if user.api_keys: | ||
key = user.api_keys[0].key | ||
else: | ||
key = self.create_api_key(user) | ||
api_key = self.get_api_key(user) | ||
key = api_key.key if api_key else self.create_api_key(user).key | ||
return key | ||
|
||
def delete_api_key(self, user: User) -> None: | ||
"""Marks the current user API key as deleted.""" | ||
sa_session = self.app.model.context | ||
# Before it was possible to create multiple API keys for the same user although they were not considered valid | ||
# So all non-deleted keys are marked as deleted for backward compatibility | ||
api_keys = sa_session.query(self.app.model.APIKeys).filter_by(user_id=user.id, deleted=False) | ||
for api_key in api_keys: | ||
api_key.deleted = True | ||
sa_session.add(api_key) | ||
sa_session.flush() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.