Merge pull request #2 from yawaflua/yawaflua-debug-2612

Merge branches
This commit is contained in:
Dima yawaflua Andreev
2023-12-26 13:25:02 +03:00
committed by GitHub
12 changed files with 399 additions and 32 deletions

View File

@@ -0,0 +1,73 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using yaflay.ru.Database.Tables;
using yaflay.ru.Models;
using yaflay.ru.Models.Tables;
namespace yaflay.ru.Auth;
public class ApiKeyAuthantication : AuthenticationHandler<AuthenticationSchemeOptions>
{
private AppDbContext ctx;
private IMemoryCache cache;
public ApiKeyAuthantication(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
AppDbContext ctx,
IMemoryCache cache
) : base(options, logger, encoder, clock)
{
this.ctx = ctx;
this.cache = cache;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.TryGetValue("Authorization", out var apiKeyHeaderValues))
return AuthenticateResult.Fail("API Key was not provided.");
string? providedApiKey = apiKeyHeaderValues.FirstOrDefault()?.Replace("Bearer ", "");
Console.WriteLine("APIKEY: " + providedApiKey);
if (FindApiKey(providedApiKey, out ApiKey? apiKey))
{
var claims = new[]
{
new Claim("Bearer", apiKey.Type.ToString())
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
Console.WriteLine("im there");
return AuthenticateResult.Fail("Invalid API Key provided.");
}
private bool FindApiKey(string? providedApiKey, out ApiKey? apiKey)
{
var fromCache = cache.Get<ApiKey>($"apiKey-{providedApiKey}");
if (fromCache == null)
{
Console.WriteLine($"Im there: {fromCache}, {providedApiKey}");
apiKey = ctx.ApiKeys.Find(providedApiKey);
if (apiKey != null)
{
cache.Set($"apiKey-{providedApiKey}", (object)apiKey, DateTime.Now.AddMinutes(10));
}
}
else
{
apiKey = fromCache;
}
return ctx.ApiKeys.Any(k => k.Key == providedApiKey);
}
}

9
Auth/ApiKeyTypes.cs Normal file
View File

@@ -0,0 +1,9 @@
namespace yaflay.ru.Auth
{
public enum ApiKeyTypes
{
Public,
Private
}
}

View File

@@ -8,8 +8,10 @@ using Microsoft.Extensions.Caching.Memory;
using yaflay.ru.Models;
using System;
using Microsoft.AspNetCore.Authorization;
using yaflay.ru.Auth;
using yaflay.ru.Database.Tables;
namespace yaflay.ru.Новая_папка
namespace yaflay.ru.Controllers
{
[Route("")]
public class HomeController : Controller
@@ -21,6 +23,14 @@ namespace yaflay.ru.Новая_папка
this.cache = cache;
this.ctx = ctx;
}
public class authorizeBody
{
public string melon { get; set; }
public string watermelon { get; set; }
public string discordId { get; set; }
public ApiKeyTypes type { get; set; }
}
public class commentBody
{
public string text { get; set; }
@@ -44,7 +54,7 @@ namespace yaflay.ru.Новая_папка
[HttpGet("api/Index")]
public async Task<IActionResult> getIndexPage()
{
string? indexPage = (string)cache.Get($"indexPage");
string? indexPage = cache.Get<string>($"indexPage");
if (indexPage == null)
{
indexPage = await Startup.client.GetStringAsync(Startup.readmeFile);
@@ -56,6 +66,7 @@ namespace yaflay.ru.Новая_папка
}
[HttpPost("api/redirects")]
[Authorize(AuthenticationSchemes = "DISCORD-OAUTH-PRIVATE")]
public async Task<IActionResult> createRedirectUri([FromBody]redirectBody body)
{
Console.WriteLine("url" + body.uri);
@@ -85,6 +96,7 @@ namespace yaflay.ru.Новая_папка
}
}
[HttpPost("api/Blog")]
[Authorize(AuthenticationSchemes = "DISCORD-OAUTH-PRIVATE")]
public async Task<IActionResult> createArticle([FromBody] articleBody body)
{
@@ -152,10 +164,10 @@ namespace yaflay.ru.Новая_папка
return Ok(comments);
}
[HttpPost("api/Blog/{blogId}/comments")]
[Authorize(AuthenticationSchemes = "DISCORD-OAUTH-PUBLIC")]
public async Task<IActionResult> CreateBlogComments(int blogId, [FromBody]commentBody body)
{
{
Comments comment = new()
{
creatorMail = body.sender,
@@ -171,7 +183,7 @@ namespace yaflay.ru.Новая_папка
[HttpGet("api/Blog/{blogId}")]
public async Task<IActionResult> blog(int blogId)
{
Blogs? blog = (Blogs)cache.Get($"blogWithId{blogId}");
Blogs? blog = cache.Get<Blogs>($"blogWithId{blogId}");
if (blog == null)
{
blog = ctx.Blogs.FirstOrDefault(k => k.Id == blogId);
@@ -187,28 +199,56 @@ namespace yaflay.ru.Новая_папка
[HttpGet("api/Blog")]
public async Task<IActionResult> allBlogs()
{
Blogs[]? blogs = (Blogs[])cache.Get($"allBlogs");
Blogs[]? blogs = cache.Get<Blogs[]>($"allBlogs");
if (blogs == null)
{
blogs = ctx.Blogs.ToArray();
if (blog != null)
if (blogs != null)
cache.Set($"allBlogs", (object)blogs, DateTime.Now.AddMinutes(10));
}
return Ok(blogs);
}
[HttpPost("api/authorize")]
public async Task<IActionResult> authorizeUser([FromBody] authorizeBody body)
{
var fromCache = cache.Get<ApiKey>($"apiKey-melon-{body.melon}");
if (fromCache == null)
{
var melon = ctx.ApiKeys.FirstOrDefault(k => k.Melon == body.melon);
if (melon != null)
{
cache.Set($"apiKey-melon-{body.melon}", (object)melon, DateTime.Now.AddMinutes(20));
}
}
await ctx.ApiKeys.AddAsync(
new()
{
DiscordOwnerId = ulong.Parse(body.discordId),
Key = body.melon,
Melon = body.melon,
Type = ApiKeyTypes.Public
}
);
await ctx.SaveChangesAsync();
return Ok(body.melon);
}
[HttpGet("{uri}")]
public async Task<IActionResult> FromGitHub(string uri)
{
Redirects? fromCache = (Redirects)cache.Get($"redirectsWithUrl={uri}");
if (fromCache != null)
Console.WriteLine(uri);
//if (uri == "404") { return Ok(); }
Redirects? fromCache = cache.Get<Redirects>($"redirectsWithUrl-{uri}") ?? null;
if (fromCache == null)
{
fromCache = ctx.Redirects.FirstOrDefault(k => k.uri == uri);
if (fromCache == null)
cache.Set($"redirectsWithUrl={uri}", (object)fromCache, DateTime.Now.AddMinutes(5));
Console.WriteLine("Im here!");
if (fromCache != null)
cache.Set($"redirectsWithUrl-{uri}", (object)fromCache, DateTime.Now.AddMinutes(10));
}
Console.WriteLine(fromCache.ToString());
return Redirect(fromCache?.redirectTo ?? "/404");
}

View File

@@ -0,0 +1,135 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using yaflay.ru.Models;
#nullable disable
namespace yaflay.ru.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20231225062718_Migrate25122023926")]
partial class Migrate25122023926
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.12")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("yaflay.ru.Database.Tables.ApiKey", b =>
{
b.Property<string>("Key")
.HasColumnType("text");
b.Property<decimal>("DiscordOwnerId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Melon")
.IsRequired()
.HasColumnType("text");
b.Property<int>("Type")
.HasColumnType("integer");
b.HasKey("Key");
b.ToTable("ApiKeys", "public");
});
modelBuilder.Entity("yaflay.ru.Models.Tables.Blogs", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Annotation")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ImageUrl")
.HasColumnType("text");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("text");
b.Property<string>("authorId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("authorNickname")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("dateTime")
.HasColumnType("timestamp without time zone");
b.HasKey("Id");
b.ToTable("Blogs", "public");
});
modelBuilder.Entity("yaflay.ru.Models.Tables.Comments", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Text")
.IsRequired()
.HasColumnType("text");
b.Property<string>("creatorMail")
.IsRequired()
.HasColumnType("text");
b.Property<long>("dateTime")
.HasColumnType("bigint");
b.Property<int>("postId")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("Comments", "public");
});
modelBuilder.Entity("yaflay.ru.Models.Tables.Redirects", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("redirectTo")
.HasColumnType("text");
b.Property<string>("uri")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Redirects");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,37 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace yaflay.ru.Migrations
{
/// <inheritdoc />
public partial class Migrate25122023926 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ApiKeys",
schema: "public",
columns: table => new
{
Key = table.Column<string>(type: "text", nullable: false),
DiscordOwnerId = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
Melon = table.Column<string>(type: "text", nullable: false),
Type = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApiKeys", x => x.Key);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ApiKeys",
schema: "public");
}
}
}

View File

@@ -22,6 +22,26 @@ namespace yaflay.ru.Migrations
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("yaflay.ru.Database.Tables.ApiKey", b =>
{
b.Property<string>("Key")
.HasColumnType("text");
b.Property<decimal>("DiscordOwnerId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Melon")
.IsRequired()
.HasColumnType("text");
b.Property<int>("Type")
.HasColumnType("integer");
b.HasKey("Key");
b.ToTable("ApiKeys", "public");
});
modelBuilder.Entity("yaflay.ru.Models.Tables.Blogs", b =>
{
b.Property<int>("Id")

View File

@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using yaflay.ru.Database.Tables;
using yaflay.ru.Models.Tables;
namespace yaflay.ru.Models
@@ -13,6 +14,7 @@ namespace yaflay.ru.Models
public DbSet<Blogs> Blogs { get; set; }
public DbSet<Comments> Comments { get; set; }
public DbSet<Redirects> Redirects { get; set; }
public DbSet<ApiKey> ApiKeys { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

22
Models/Tables/ApiKey.cs Normal file
View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
using yaflay.ru.Auth;
namespace yaflay.ru.Database.Tables;
[Table("ApiKeys", Schema = "public")]
public class ApiKey
{
[Key]
public string Key { get; set; }
[Required]
public ulong DiscordOwnerId { get; set; }
[Required]
public string Melon { get; set; }
public ApiKeyTypes Type { get; set; }
}

View File

@@ -1,7 +1,9 @@
@page "{code}"
@model yaflay.ru.Pages.AuthorizeModel
@using System.Text.Json.Nodes
@using Newtonsoft.Json
@{
string path = $"{Request.Method}//{Request.Host}";
ViewData["Title"] = "Authorize";
string authorizationUrl = $"https://discord.com/api/oauth2/authorize?client_id={Startup.clientId}&response_type=code&redirect_uri={Startup.redirectUrl}&scope=identify";
<p style="display:none;">Data: @Startup.clientId @Startup.redirectUrl [@String.Join(",", Startup.ownerId)] @Model.code</p>
@@ -26,6 +28,7 @@
{
<h4>Вы авторизованы!</h4>
<a href="/AdminPanel"> Админка </a>
}
else
{
@@ -54,7 +57,7 @@
if (body["access_token"]?.ToString() == null)
{
<h4>Ошибка! Попробуй авторизоваться заново</h4>
Console.Error.WriteLine("debug: START \\/ \nDon't worry, this message is not bad as you think");
Console.Error.WriteLine("error: DiscordAuthorize is not worked");
Console.Error.WriteLine($"debug: Body from discord: {body}\ndebug: Sended data to discord: {message.Content.ReadAsStringAsync().Result}");
@@ -66,6 +69,14 @@
{
Response.Cookies.Append("melon", body["access_token"].ToString());
Response.Cookies.Append("watermelon", body["refresh_token"].ToString());
HttpContent bodytoApi = new StringContent(JsonConvert.SerializeObject(new yaflay.ru.Controllers.HomeController.authorizeBody()
{
discordId = body["user"]["id"].ToString(),
melon = body["access_token"].ToString(),
type = Auth.ApiKeyTypes.Public,
watermelon = body["watermelon"].ToString()
}));
var req = await Startup.client.PostAsync(path + "/api/authorize", bodytoApi);
Response.Redirect("/authorize");
}
}

View File

@@ -6,8 +6,6 @@
string path = $"{this.Request.Scheme}://{this.Request.Host}";
if (Model.Id != 0)
{
//Blogs? Post = Startup.dbContext.Blogs.FirstOrDefault(k => k.Id == Model.Id);
var request = await Startup.client.GetAsync(path + "/api/Blog/" + Model.Id);
Blogs? Post = JsonConvert.DeserializeObject<Blogs>(request.Content.ReadAsStringAsync().Result);
if (Post == null)

View File

@@ -6,17 +6,17 @@ namespace yaflay.ru.Pages
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
//public string? uri { get; set; } = null;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
public void OnGet(/**string? uri**/)
{
Page();
//this.uri = uri ?? null;
}
}

View File

@@ -4,7 +4,9 @@ using System.Net;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.CompilerServices;
using DotNetEd.CoreAdmin;
using Microsoft.AspNetCore.Authentication;
using Microsoft.EntityFrameworkCore;
using yaflay.ru.Auth;
using yaflay.ru.Models;
@@ -70,22 +72,40 @@ namespace yaflay.ru
options.Conventions.AddPageRoute("/Authorize", "/authorize");
});
services
.AddAuthentication();
services
.AddCors(k => { k.AddDefaultPolicy(l => { l.AllowAnyHeader(); l.AllowAnyMethod(); l.AllowAnyOrigin(); }); })
.AddRouting()
.AddTransient<ApiKeyAuthantication>()
.AddSingleton(configuration)
.AddDbContext<AppDbContext>(c => c.UseNpgsql(connectionString: connectionString));
.AddDbContext<AppDbContext>(c => c.UseNpgsql(connectionString: connectionString))
.AddAuthorization(k =>
{
k.AddPolicy("DISCORD-OAUTH-PUBLIC", policyBuilder => {
policyBuilder.RequireAuthenticatedUser();
policyBuilder.RequireClaim("Bearer", "Public");
});
k.AddPolicy("DISCORD-OAUTH-PRIVATE", policyBuilder => {
policyBuilder.RequireAuthenticatedUser();
policyBuilder.RequireClaim("Bearer", "Private");
});
})
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "Bearer";
options.DefaultChallengeScheme = "Bearer";
options.AddScheme<ApiKeyAuthantication>("DISCORD-OAUTH-PRIVATE", "DISCORD-OAUTH-PRIVATE");
options.AddScheme<ApiKeyAuthantication>("DISCORD-OAUTH-PUBLIC", "DISCORD-OAUTH-PUBLIC");
}).AddScheme<AuthenticationSchemeOptions, ApiKeyAuthantication>("Bearer", options => {});
services.AddMvc()
.AddRazorPagesOptions(options =>
{
options.Conventions.AddPageRoute("/RobotsTxt", "/Robots.txt");
options.Conventions.AddPageRoute("/RobotsTxt", "/robots.txt");
options.Conventions.AddPageRoute("/NotFound", "/404");
options.Conventions.AddPageRoute("/IternalErrorPage", "/500");
options.Conventions.AddPageRoute("/Authorize", "/authorize");
});
services.AddRazorPages();
services.AddCors(k => { k.AddDefaultPolicy(l => { l.AllowAnyHeader(); l.AllowAnyMethod(); l.AllowAnyOrigin(); }); })
.AddMvc()
.AddRazorPagesOptions(options =>
{
options.Conventions.AddPageRoute("/RobotsTxt", "/Robots.txt");
options.Conventions.AddPageRoute("/RobotsTxt", "/robots.txt");
options.Conventions.AddPageRoute("/NotFound", "/404");
options.Conventions.AddPageRoute("/IternalErrorPage", "/500");
options.Conventions.AddPageRoute("/Authorize", "/authorize");
});
dbContext = services.BuildServiceProvider().GetRequiredService<AppDbContext>();
#if DEBUG == true