Add CORS to api

This commit is contained in:
Kieran 2022-02-26 14:22:22 +00:00
parent 36d5db3f29
commit 67b5ef2b10
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
9 changed files with 51 additions and 32 deletions

View File

@ -75,7 +75,8 @@ public class AuthController : Controller
var claims = new List<Claim>()
{
new(ClaimTypes.NameIdentifier, user.Id.ToString()),
new(ClaimTypes.Expiration, DateTimeOffset.UtcNow.AddHours(6).ToUnixTimeSeconds().ToString())
new(JwtRegisteredClaimNames.Exp, DateTimeOffset.UtcNow.AddHours(6).ToUnixTimeSeconds().ToString()),
new(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString())
};
claims.AddRange(user.Roles.Select(a => new Claim(ClaimTypes.Role, a)));

View File

@ -1,7 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using Prometheus;
using VoidCat.Model;
using VoidCat.Services;
using VoidCat.Services.Abstractions;
namespace VoidCat.Controllers
@ -19,7 +17,7 @@ namespace VoidCat.Controllers
}
[HttpGet]
[ResponseCache(Location = ResponseCacheLocation.Client, Duration = 60)]
[ResponseCache(Location = ResponseCacheLocation.Any, Duration = 60)]
public async Task<GlobalStats> GetGlobalStats()
{
var bw = await _statsReporter.GetBandwidth();

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Newtonsoft.Json;
@ -8,6 +9,7 @@ using VoidCat.Services.Abstractions;
namespace VoidCat.Controllers
{
[EnableCors(CorsPolicy.Upload)]
[Route("upload")]
public class UploadController : Controller
{
@ -25,14 +27,6 @@ namespace VoidCat.Controllers
_paywallFactory = paywallFactory;
}
[HttpOptions]
public IActionResult UploadFileOptions()
{
// just return 200 status for pre-flight calls
// manging CORS headers is not managed inside void.cat
return Ok();
}
[HttpPost]
[DisableRequestSizeLimit]
[DisableFormValueModelBinding]

View File

@ -10,3 +10,9 @@ public static class Policies
{
public const string RequireAdmin = "RequireAdmin";
}
public static class CorsPolicy
{
public const string Default = "default";
public const string Upload = "upload";
}

View File

@ -15,6 +15,8 @@ namespace VoidCat.Model
public StrikeApiSettings? Strike { get; init; }
public SmtpSettings? Smtp { get; init; }
public List<Uri> CorsOrigins { get; init; } = new();
}
public sealed record TorSettings(Uri TorControl, string PrivateKey, string ControlPassword);

View File

@ -33,6 +33,24 @@ if (useRedis)
services.AddSingleton(cx.GetDatabase());
}
services.AddCors(opt =>
{
opt.AddPolicy(CorsPolicy.Default, p =>
{
p.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins(voidSettings.CorsOrigins.Select(a => a.OriginalString).ToArray());
});
opt.AddPolicy(CorsPolicy.Upload, p =>
{
p.AllowCredentials()
.AllowAnyMethod()
.WithHeaders("V-Content-Type", "V-Filename", "V-Digest", "Content-Type", "Authorization")
.WithOrigins(voidSettings.CorsOrigins.Select(a => a.OriginalString).ToArray());
});
});
services.AddRouting();
services.AddControllers().AddNewtonsoftJson((opt) =>
{
@ -112,6 +130,7 @@ app.UseStaticFiles();
#endif
app.UseRouting();
app.UseCors(CorsPolicy.Default);
app.UseAuthentication();
app.UseAuthorization();

View File

@ -1,6 +1,6 @@
import moment from "moment";
import {Link} from "react-router-dom";
import {useDispatch, useSelector} from "react-redux";
import {useDispatch} from "react-redux";
import {useEffect, useState} from "react";
import {FormatBytes} from "../Util";
import {useApi} from "../Api";
@ -8,9 +8,8 @@ import {logout} from "../LoginState";
import {PagedSortBy, PageSortOrder} from "../Const";
import {PageSelector} from "../PageSelector";
export function FileList(props) {
export function FileList() {
const {AdminApi} = useApi();
const auth = useSelector((state) => state.login.jwt);
const dispatch = useDispatch();
const [files, setFiles] = useState();
const [page, setPage] = useState(0);
@ -24,7 +23,7 @@ export function FileList(props) {
sortBy: PagedSortBy.Date,
sortOrder: PageSortOrder.Dsc
};
let req = await AdminApi.fileList(auth, pageReq);
let req = await AdminApi.fileList(pageReq);
if (req.ok) {
setFiles(await req.json());
} else if (req.status === 401) {
@ -37,7 +36,7 @@ export function FileList(props) {
async function deleteFile(e, id) {
e.target.disabled = true;
if (window.confirm(`Are you sure you want to delete: ${id}?`)) {
let req = await AdminApi.deleteFile(auth, id);
let req = await AdminApi.deleteFile(id);
if (req.ok) {
setFiles({
...files,

View File

@ -1,4 +1,4 @@
import {useDispatch, useSelector} from "react-redux";
import {useDispatch} from "react-redux";
import {useEffect, useState} from "react";
import {PagedSortBy, PageSortOrder} from "../Const";
import {useApi} from "../Api";
@ -8,7 +8,6 @@ import moment from "moment";
export function UserList() {
const {AdminApi} = useApi();
const auth = useSelector((state) => state.login.jwt);
const dispatch = useDispatch();
const [users, setUsers] = useState();
const [page, setPage] = useState(0);
@ -22,7 +21,7 @@ export function UserList() {
sortBy: PagedSortBy.Id,
sortOrder: PageSortOrder.Asc
};
let req = await AdminApi.userList(auth, pageReq);
let req = await AdminApi.userList(pageReq);
if (req.ok) {
setUsers(await req.json());
} else if (req.status === 401) {

View File

@ -4,12 +4,12 @@ import {ApiHost} from "./Const";
export function useApi() {
const auth = useSelector(state => state.login.jwt);
async function getJson(method, url, body) {
async function getJson(method, url, body, token) {
let headers = {
"Accept": "application/json"
};
if (auth) {
headers["Authorization"] = `Bearer ${auth}`;
if (token) {
headers["Authorization"] = `Bearer ${token}`;
}
if (body) {
headers["Content-Type"] = "application/json";
@ -18,20 +18,21 @@ export function useApi() {
return await fetch(`${ApiHost}${url}`, {
method,
headers,
mode: "cors",
body: body ? JSON.stringify(body) : undefined
});
}
return {
AdminApi: {
fileList: (pageReq) => getJson("POST", "/admin/file", pageReq),
deleteFile: (id) => getJson("DELETE", `/admin/file/${id}`),
userList: (pageReq) => getJson("POST", `/admin/user`, pageReq)
fileList: (pageReq) => getJson("POST", "/admin/file", pageReq, auth),
deleteFile: (id) => getJson("DELETE", `/admin/file/${id}`, undefined, auth),
userList: (pageReq) => getJson("POST", `/admin/user`, pageReq, auth)
},
Api: {
stats: () => getJson("GET", "/stats"),
fileInfo: (id) => getJson("GET", `/upload/${id}`),
setPaywallConfig: (id, cfg) => getJson("POST", `/upload/${id}/paywall`, cfg),
setPaywallConfig: (id, cfg) => getJson("POST", `/upload/${id}/paywall`, cfg, auth),
createOrder: (id) => getJson("GET", `/upload/${id}/paywall`),
getOrder: (file, order) => getJson("GET", `/upload/${file}/paywall/${order}`),
login: (username, password) => getJson("POST", `/auth/login`, {username, password}),