Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 37 additions & 17 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package api
import (
"errors"
"fmt"
"encoding/json"
"io/ioutil"
"strings"

config "github.com/go-skynet/LocalAI/api/config"
Expand Down Expand Up @@ -144,30 +146,48 @@ func App(opts ...options.AppOption) (*fiber.App, error) {

// Auth middleware checking if API key is valid. If no API key is set, no auth is required.
auth := func(c *fiber.Ctx) error {
if len(options.ApiKeys) > 0 {
authHeader := c.Get("Authorization")
if authHeader == "" {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Authorization header missing"})
}
authHeaderParts := strings.Split(authHeader, " ")
if len(authHeaderParts) != 2 || authHeaderParts[0] != "Bearer" {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Invalid Authorization header format"})
}
if len(options.ApiKeys) == 0 {
return c.Next()
}

apiKey := authHeaderParts[1]
validApiKey := false
for _, key := range options.ApiKeys {
if apiKey == key {
validApiKey = true
}
// Check for api_keys.json file
fileContent, err := ioutil.ReadFile("api_keys.json")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lunamidori5 - Do we know what the performance impact of ioutil.ReadFile on every request will be? @mudler is more familiar with golang and would know if this is a concern or not - it might be worth some performance testing in the meantime.

Personally, I would be inclined to use something like https://pkg.go.dev/github.com/fsnotify/fsnotify to watch the file and keep options.ApiKeys up-to-date - that way, we aren't hitting the filesystem on every single request?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to keep it simple we can also just load the file once on start and pass the api keys down the line as a list

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mudler needs to be reloaded on request so we dont have to restart the program everytime is the goal, this way we can add keys as its running

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lunamidori5 - Do we know what the performance impact of ioutil.ReadFile on every request will be? @mudler is more familiar with golang and would know if this is a concern or not - it might be worth some performance testing in the meantime.

Personally, I would be inclined to use something like https://pkg.go.dev/github.com/fsnotify/fsnotify to watch the file and keep options.ApiKeys up-to-date - that way, we aren't hitting the filesystem on every single request?

I guess? I dont know much about GO...

if err == nil {
// Parse JSON content from the file
var fileKeys []string
err := json.Unmarshal(fileContent, &fileKeys)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"message": "Error parsing api_keys.json"})
}
if !validApiKey {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Invalid API key"})

// Add file keys to options.ApiKeys
options.ApiKeys = append(options.ApiKeys, fileKeys...)
}

authHeader := c.Get("Authorization")
if authHeader == "" {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Authorization header missing"})
}
authHeaderParts := strings.Split(authHeader, " ")
if len(authHeaderParts) != 2 || authHeaderParts[0] != "Bearer" {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Invalid Authorization header format"})
}

apiKey := authHeaderParts[1]
validApiKey := false
for _, key := range options.ApiKeys {
if apiKey == key {
validApiKey = true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we can move the c.Next() up here - no need to check further api keys if one passes the test - at least until we need to support per-endpoint api keys or anything weird.

}
}
if !validApiKey {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Invalid API key"})
}

return c.Next()
}


if options.CORS {
var c func(ctx *fiber.Ctx) error
if options.CORSAllowOrigins == "" {
Expand Down