mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 20:42:02 +00:00
Remove show_key endpoint
This commit is contained in:
@@ -5584,13 +5584,14 @@ class TestLighthouseConfigViewSet:
|
||||
assert "gpt-4o" in response.json()["data"]["available_models"]
|
||||
assert "gpt-4o-mini" in response.json()["data"]["available_models"]
|
||||
|
||||
def test_lighthouse_config_show_key(
|
||||
def test_lighthouse_config_get_key(
|
||||
self, authenticated_client, lighthouse_config_fixture, valid_config_payload
|
||||
):
|
||||
config_id = lighthouse_config_fixture.id
|
||||
expected_api_key = valid_config_payload["data"]["attributes"]["api_key"]
|
||||
response = authenticated_client.get(
|
||||
reverse("lighthouseconfig-show-key", kwargs={"pk": config_id})
|
||||
reverse("lighthouseconfig-detail", kwargs={"pk": config_id})
|
||||
+ "?fields[lighthouse-config]=api_key"
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["data"]["attributes"]["api_key"] == expected_api_key
|
||||
|
||||
@@ -2135,7 +2135,7 @@ class LighthouseConfigSerializer(RLSSerializer):
|
||||
Serializer for the LighthouseConfig model.
|
||||
"""
|
||||
|
||||
api_key = serializers.CharField(write_only=True, required=False)
|
||||
api_key = serializers.CharField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = LighthouseConfig
|
||||
@@ -2159,10 +2159,18 @@ class LighthouseConfigSerializer(RLSSerializer):
|
||||
}
|
||||
|
||||
def to_representation(self, instance):
|
||||
ret = super().to_representation(instance)
|
||||
# Add back the masked API key for display
|
||||
ret["api_key"] = "*" * len(instance.api_key) if instance.api_key else None
|
||||
return ret
|
||||
data = super().to_representation(instance)
|
||||
# Check if api_key is specifically requested in fields param
|
||||
fields_param = self.context.get("request", None) and self.context[
|
||||
"request"
|
||||
].query_params.get("fields[lighthouse-config]", "")
|
||||
if fields_param == "api_key":
|
||||
# Return decrypted key if specifically requested
|
||||
data["api_key"] = instance.api_key_decoded if instance.api_key else None
|
||||
else:
|
||||
# Return masked key for general requests
|
||||
data["api_key"] = "*" * len(instance.api_key) if instance.api_key else None
|
||||
return data
|
||||
|
||||
|
||||
class LighthouseConfigCreateSerializer(RLSSerializer, BaseWriteSerializer):
|
||||
|
||||
@@ -2848,38 +2848,3 @@ class LighthouseConfigViewSet(BaseRLSViewSet):
|
||||
status=status.HTTP_201_CREATED,
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
@action(detail=True, methods=["get"], url_path="show_key")
|
||||
def show_key(self, request, pk=None):
|
||||
"""
|
||||
Return the decrypted API key for the specified Lighthouse configuration.
|
||||
"""
|
||||
instance = self.get_object()
|
||||
|
||||
if not instance.api_key:
|
||||
return Response(
|
||||
{"detail": "API key is missing."},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
try:
|
||||
decrypted_key = instance.api_key_decoded
|
||||
if decrypted_key is None:
|
||||
return Response(
|
||||
{"detail": "API key is invalid or missing."},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
return Response(
|
||||
data={
|
||||
"type": "lighthouse-config",
|
||||
"attributes": {"api_key": decrypted_key},
|
||||
},
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
except Exception as e:
|
||||
return Response(
|
||||
{
|
||||
"detail": f"An unexpected error occurred while retrieving the API key. {str(e)}"
|
||||
},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
)
|
||||
|
||||
@@ -34,7 +34,9 @@ export const getAIKey = async (): Promise<string> => {
|
||||
return "";
|
||||
}
|
||||
|
||||
const url = new URL(`${apiBaseUrl}/lighthouse-config/${configId}/show_key`);
|
||||
const url = new URL(
|
||||
`${apiBaseUrl}/lighthouse-config/${configId}?fields[lighthouse-config]=api_key`,
|
||||
);
|
||||
const response = await fetch(url.toString(), {
|
||||
method: "GET",
|
||||
headers,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useChat } from "@ai-sdk/react";
|
||||
import { useRef, useEffect } from "react";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
import { MemoizedMarkdown } from "@/components/memoized-markdown";
|
||||
|
||||
@@ -60,11 +60,14 @@ export default function Chat() {
|
||||
const container = messagesContainerRef.current;
|
||||
const userMsg = latestUserMsgRef.current;
|
||||
const containerPadding = 16; // p-4 in Tailwind = 16px
|
||||
container.scrollTop = userMsg.offsetTop - container.offsetTop - containerPadding;
|
||||
container.scrollTop =
|
||||
userMsg.offsetTop - container.offsetTop - containerPadding;
|
||||
}
|
||||
}, [messages]);
|
||||
|
||||
const handleAutoResizeInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
const handleAutoResizeInputChange = (
|
||||
e: React.ChangeEvent<HTMLTextAreaElement>,
|
||||
) => {
|
||||
handleInputChange(e);
|
||||
const textarea = textareaRef.current;
|
||||
if (textarea) {
|
||||
@@ -104,13 +107,17 @@ export default function Chat() {
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex-1 space-y-4 overflow-y-auto p-4" ref={messagesContainerRef}>
|
||||
<div
|
||||
className="flex-1 space-y-4 overflow-y-auto p-4"
|
||||
ref={messagesContainerRef}
|
||||
>
|
||||
{messages.map((message, idx) => {
|
||||
const lastUserIdx = messages
|
||||
.map((m, i) => (m.role === "user" ? i : -1))
|
||||
.filter(i => i !== -1)
|
||||
.filter((i) => i !== -1)
|
||||
.pop();
|
||||
const isLatestUserMsg = message.role === "user" && lastUserIdx === idx;
|
||||
const isLatestUserMsg =
|
||||
message.role === "user" && lastUserIdx === idx;
|
||||
return (
|
||||
<div
|
||||
key={message.id}
|
||||
@@ -129,7 +136,10 @@ export default function Chat() {
|
||||
<div
|
||||
className={`prose dark:prose-invert ${message.role === "user" ? "dark:!text-black" : ""}`}
|
||||
>
|
||||
<MemoizedMarkdown id={message.id} content={message.content} />
|
||||
<MemoizedMarkdown
|
||||
id={message.id}
|
||||
content={message.content}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -156,7 +166,7 @@ export default function Chat() {
|
||||
onChange={handleAutoResizeInputChange}
|
||||
placeholder="Type your message..."
|
||||
rows={1}
|
||||
className="w-full flex-1 px-3 py-2 focus:outline-none resize-none overflow-hidden rounded-lg border bg-background"
|
||||
className="w-full flex-1 resize-none overflow-hidden rounded-lg border bg-background px-3 py-2 focus:outline-none"
|
||||
style={{ minHeight: "40px", maxHeight: "160px" }}
|
||||
onKeyDown={(e) => {
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
|
||||
@@ -168,7 +178,7 @@ export default function Chat() {
|
||||
<button
|
||||
type="submit"
|
||||
disabled={status === "submitted" || !input.trim()}
|
||||
className="rounded-lg bg-primary p-2 text-primary-foreground hover:bg-primary/90 disabled:opacity-50 flex-shrink-0 h-10 w-10 flex items-center justify-center"
|
||||
className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-primary p-2 text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
||||
>
|
||||
{status === "submitted" ? <span>■</span> : <span>➤</span>}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user