mirror of
https://github.com/vladmandic/automatic
synced 2026-09-20 01:31:13 +02:00
strong-enforce auth check on all api endpoints
Signed-off-by: Vladimir Mandic <mandic00@live.com>
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
- **sdnq**: simplify pre-quantization saved config
|
||||
- **attention**: refactor settings and improve handling of attention mechanisms
|
||||
- **lora**: separate fuse setting for native-vs-diffuser implementations
|
||||
- **auth**: strong-enforce auth check on all api endpoints
|
||||
- **Fixes**
|
||||
- hires strength save/load in metadata, thanks @awsr
|
||||
- fix imgi2img initial scale tab, thanks @awsr
|
||||
|
||||
+7
-2
@@ -30,10 +30,15 @@ async function main() {
|
||||
const headers = new Headers();
|
||||
const body = JSON.stringify(sd_options);
|
||||
headers.set('Content-Type', 'application/json');
|
||||
if (sd_username && sd_password) headers.set({ Authorization: `Basic ${btoa('sd_username:sd_password')}` });
|
||||
if (sd_username && sd_password) {
|
||||
// const credentials = btoa(`${sd_username}:${sd_password}`);
|
||||
const credentials = Buffer.from(`${sd_username}:${sd_password}`).toString('base64');
|
||||
headers.set('Authorization', `Basic ${credentials}`);
|
||||
}
|
||||
const res = await fetch(`${sd_url}/sdapi/v1/txt2img`, { method, headers, body });
|
||||
if (res.status !== 200) {
|
||||
console.log('Error', res.status);
|
||||
const err = await res.text();
|
||||
console.log('Error', res.status, res.statusText, err);
|
||||
} else {
|
||||
const json = await res.json();
|
||||
console.log('result:', json.info);
|
||||
|
||||
+6
-6
@@ -4,21 +4,21 @@ const loginCSS = `
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--background-fill-primary);
|
||||
color: var(--body-text-color-subdued);
|
||||
background: #222;
|
||||
color: #ddd;
|
||||
font-family: monospace;
|
||||
z-index: 100;
|
||||
`;
|
||||
|
||||
const loginHTML = `
|
||||
<div id="loginDiv" style="margin: 15% auto; max-width: 200px; padding: 2em; background: var(--background-fill-secondary);">
|
||||
<div id="loginDiv" style="margin: 15% auto; max-width: 200px; padding: 2em; background: #444; border-radius: 4px; filter: drop-shadow(2px 4px 6px black);">
|
||||
<h2>Login</h2>
|
||||
<label for="username" style="margin-top: 0.5em">Username</label>
|
||||
<input type="text" id="loginUsername" name="username" style="width: 92%; padding: 0.5em; margin-top: 0.5em">
|
||||
<input type="text" id="loginUsername" name="username" style="width: 92%; padding: 0.5em; margin-top: 0.5em; border-radius: 4px;">
|
||||
<label for="password" style="margin-top: 0.5em">Password</label>
|
||||
<input type="text" id="loginPassword" name="password" style="width: 92%; padding: 0.5em; margin-top: 0.5em">
|
||||
<input type="text" id="loginPassword" name="password" style="width: 92%; padding: 0.5em; margin-top: 0.5em; border-radius: 4px;">
|
||||
<div id="loginStatus" style="margin-top: 0.5em"></div>
|
||||
<button type="submit" style="width: 100%; padding: 0.5em; margin-top: 0.5em; background: var(--button-primary-background-fill); color: var(--button-primary-text-color); border: var(--button-primary-border-color);">Login</button>
|
||||
<button type="submit" style="width: 100%; padding: 0.5em; margin-top: 0.5em; background: #366; color: #ddd; border: none; border-radius: 4px; filter: drop-shadow(2px 4px 6px black);">Login</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
+13
-29
@@ -4,7 +4,7 @@ from secrets import compare_digest
|
||||
from fastapi import FastAPI, APIRouter, Depends, Request
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from fastapi.exceptions import HTTPException
|
||||
from modules import errors, shared, postprocessing
|
||||
from modules import errors, shared
|
||||
from modules.api import models, endpoints, script, helpers, server, generate, process, control, docs, gpu
|
||||
|
||||
|
||||
@@ -60,8 +60,8 @@ class Api:
|
||||
self.add_api_route("/sdapi/v1/txt2img", self.generate.post_text2img, methods=["POST"], response_model=models.ResTxt2Img)
|
||||
self.add_api_route("/sdapi/v1/img2img", self.generate.post_img2img, methods=["POST"], response_model=models.ResImg2Img)
|
||||
self.add_api_route("/sdapi/v1/control", self.control.post_control, methods=["POST"], response_model=control.ResControl)
|
||||
self.add_api_route("/sdapi/v1/extra-single-image", self.extras_single_image_api, methods=["POST"], response_model=models.ResProcessImage)
|
||||
self.add_api_route("/sdapi/v1/extra-batch-images", self.extras_batch_images_api, methods=["POST"], response_model=models.ResProcessBatch)
|
||||
self.add_api_route("/sdapi/v1/extra-single-image", self.process.extras_single_image_api, methods=["POST"], response_model=models.ResProcessImage)
|
||||
self.add_api_route("/sdapi/v1/extra-batch-images", self.process.extras_batch_images_api, methods=["POST"], response_model=models.ResProcessBatch)
|
||||
self.add_api_route("/sdapi/v1/preprocess", self.process.post_preprocess, methods=["POST"])
|
||||
self.add_api_route("/sdapi/v1/mask", self.process.post_mask, methods=["POST"])
|
||||
self.add_api_route("/sdapi/v1/detect", self.process.post_detect, methods=["POST"])
|
||||
@@ -117,17 +117,22 @@ class Api:
|
||||
from modules.civitai import api_civitai
|
||||
api_civitai.register_api()
|
||||
|
||||
|
||||
def add_api_route(self, path: str, endpoint, **kwargs):
|
||||
def add_api_route(self, path: str, fn, **kwargs):
|
||||
if self.credentials:
|
||||
deps = list(kwargs.get('dependencies', []))
|
||||
deps.append(Depends(self.auth))
|
||||
kwargs['dependencies'] = deps
|
||||
if shared.opts.subpath is not None and len(shared.opts.subpath) > 0:
|
||||
self.app.add_api_route(f'{shared.opts.subpath}{path}', endpoint, **kwargs)
|
||||
self.app.add_api_route(path, endpoint, **kwargs)
|
||||
self.app.add_api_route(f'{shared.opts.subpath}{path}', endpoint=fn, **kwargs)
|
||||
self.app.add_api_route(path, endpoint=fn, **kwargs)
|
||||
|
||||
def auth(self, credentials: HTTPBasicCredentials = Depends(HTTPBasic())):
|
||||
# this is only needed for api-only since otherwise auth is handled in gradio/routes.py
|
||||
if not self.credentials:
|
||||
return True
|
||||
if credentials.username in self.credentials:
|
||||
if compare_digest(credentials.password, self.credentials[credentials.username]):
|
||||
return True
|
||||
shared.log.error(f'API authentication: user="{credentials.username}" password="{credentials.password}"')
|
||||
raise HTTPException(status_code=401, detail="Unauthorized", headers={"WWW-Authenticate": "Basic"})
|
||||
|
||||
def get_session_start(self, req: Request, agent: Optional[str] = None):
|
||||
@@ -136,27 +141,6 @@ class Api:
|
||||
shared.log.info(f'Browser session: user={user} client={req.client.host} agent={agent}')
|
||||
return {}
|
||||
|
||||
def set_upscalers(self, req: dict):
|
||||
reqDict = vars(req)
|
||||
reqDict['extras_upscaler_1'] = reqDict.pop('upscaler_1', None)
|
||||
reqDict['extras_upscaler_2'] = reqDict.pop('upscaler_2', None)
|
||||
return reqDict
|
||||
|
||||
def extras_single_image_api(self, req: models.ReqProcessImage):
|
||||
reqDict = self.set_upscalers(req)
|
||||
reqDict['image'] = helpers.decode_base64_to_image(reqDict['image'])
|
||||
with self.queue_lock:
|
||||
result = postprocessing.run_extras(extras_mode=0, image_folder="", input_dir="", output_dir="", save_output=False, **reqDict)
|
||||
return models.ResProcessImage(image=helpers.encode_pil_to_base64(result[0][0]), html_info=result[1])
|
||||
|
||||
def extras_batch_images_api(self, req: models.ReqProcessBatch):
|
||||
reqDict = self.set_upscalers(req)
|
||||
image_list = reqDict.pop('imageList', [])
|
||||
image_folder = [helpers.decode_base64_to_image(x.data) for x in image_list]
|
||||
with self.queue_lock:
|
||||
result = postprocessing.run_extras(extras_mode=1, image_folder=image_folder, image="", input_dir="", output_dir="", save_output=False, **reqDict)
|
||||
return models.ResProcessBatch(images=list(map(helpers.encode_pil_to_base64, result[0])), html_info=result[1])
|
||||
|
||||
def launch(self):
|
||||
config = {
|
||||
"listen": shared.cmd_opts.listen,
|
||||
|
||||
+23
-2
@@ -4,8 +4,8 @@ from pydantic import BaseModel, Field # pylint: disable=no-name-in-module
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.exceptions import HTTPException
|
||||
from modules.api.helpers import decode_base64_to_image, encode_pil_to_base64
|
||||
from modules import errors, shared
|
||||
from modules.api import models
|
||||
from modules import errors, shared, postprocessing
|
||||
from modules.api import models, helpers
|
||||
|
||||
|
||||
processor = None # cached instance of processor
|
||||
@@ -175,3 +175,24 @@ class APIProcess():
|
||||
raise HTTPException(status_code=400, detail="prompt enhancement: invalid type")
|
||||
res = models.ResPromptEnhance(prompt=prompt, seed=seed)
|
||||
return res
|
||||
|
||||
def set_upscalers(self, req: dict):
|
||||
reqDict = vars(req)
|
||||
reqDict['extras_upscaler_1'] = reqDict.pop('upscaler_1', None)
|
||||
reqDict['extras_upscaler_2'] = reqDict.pop('upscaler_2', None)
|
||||
return reqDict
|
||||
|
||||
def extras_single_image_api(self, req: models.ReqProcessImage):
|
||||
reqDict = self.set_upscalers(req)
|
||||
reqDict['image'] = helpers.decode_base64_to_image(reqDict['image'])
|
||||
with self.queue_lock:
|
||||
result = postprocessing.run_extras(extras_mode=0, image_folder="", input_dir="", output_dir="", save_output=False, **reqDict)
|
||||
return models.ResProcessImage(image=helpers.encode_pil_to_base64(result[0][0]), html_info=result[1])
|
||||
|
||||
def extras_batch_images_api(self, req: models.ReqProcessBatch):
|
||||
reqDict = self.set_upscalers(req)
|
||||
image_list = reqDict.pop('imageList', [])
|
||||
image_folder = [helpers.decode_base64_to_image(x.data) for x in image_list]
|
||||
with self.queue_lock:
|
||||
result = postprocessing.run_extras(extras_mode=1, image_folder=image_folder, image="", input_dir="", output_dir="", save_output=False, **reqDict)
|
||||
return models.ResProcessBatch(images=list(map(helpers.encode_pil_to_base64, result[0])), html_info=result[1])
|
||||
|
||||
Reference in New Issue
Block a user