From d332cd0d9617b46f1f73743c6560a1ea2bbaebaf Mon Sep 17 00:00:00 2001 From: vasich Date: Fri, 26 Jul 2024 17:17:39 +0700 Subject: [PATCH 1/3] OpenAuthImplementStarted --- .../API/OpenAuthEndpoints.cs | 159 ++++++++++++++++++ .../Yuna.Website.Server/GlobalSettings.json | 3 +- .../Infrastructure/Encrypter.cs | 8 + .../Infrastructure/Settings.cs | 3 + .../Yuna.Website.Server/Model/UserBinding.cs | 10 ++ .../Properties/launchSettings.json | 15 +- .../OpenAuthService/IOpenAuthService.cs | 11 ++ .../OpenAuthService/OpenAuthService.cs | 47 ++++++ Yuna.Website/Yuna.Website.Server/Starter.cs | 6 + .../UserBindings/IUserBindingsRepository.cs | 10 ++ .../UserBindings/UserBindingRepository.cs | 24 +++ .../yuna.website.client/src/services/Api.ts | 2 +- 12 files changed, 294 insertions(+), 4 deletions(-) create mode 100644 Yuna.Website/Yuna.Website.Server/API/OpenAuthEndpoints.cs create mode 100644 Yuna.Website/Yuna.Website.Server/Model/UserBinding.cs create mode 100644 Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/IOpenAuthService.cs create mode 100644 Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/OpenAuthService.cs create mode 100644 Yuna.Website/Yuna.Website.Server/Storage/Repositories/UserBindings/IUserBindingsRepository.cs create mode 100644 Yuna.Website/Yuna.Website.Server/Storage/Repositories/UserBindings/UserBindingRepository.cs diff --git a/Yuna.Website/Yuna.Website.Server/API/OpenAuthEndpoints.cs b/Yuna.Website/Yuna.Website.Server/API/OpenAuthEndpoints.cs new file mode 100644 index 0000000..a83950d --- /dev/null +++ b/Yuna.Website/Yuna.Website.Server/API/OpenAuthEndpoints.cs @@ -0,0 +1,159 @@ +using Microsoft.AspNetCore.Mvc; +using System.Text.Json.Serialization; +using Yuna.Website.Server.Infrastructure; +using Yuna.Website.Server.Services.OpenAuthService; + +namespace Yuna.Website.Server.API +{ + public class OpenAuthEndpoints + { + public void Define(WebApplication app) + { + app.MapGet("~/.well-known/openid-configuration", GetConfiguration); + + app.MapMethods("/v1.0", ["HEAD"], Ping); + + app.MapGet("api/oauth/login", LoginViaOauth); + } + + public class DiscoveryResponse + { + [JsonPropertyName("issuer")] + public string? Issuer { get; set; } + + [JsonPropertyName("authorization_endpoint")] + public string? AuthorizationEndpoint { get; set; } + + [JsonPropertyName("token_endpoint")] + public string? TokenEndpoint { get; set; } + + [JsonPropertyName("token_endpoint_auth_methods_supported")] + public IList? TokenEndpointAuthMethodsSupported { get; set; } + + [JsonPropertyName("token_endpoint_auth_signing_alg_values_supported")] + public IList? TokenEndpointAuthSigningAlgValuesSupported { get; set; } + + [JsonPropertyName("userinfo_endpoint")] + public string? UserinfoEndpoint { get; set; } + + [JsonPropertyName("check_session_iframe")] + public string? CheckSessionIframe { get; set; } + + [JsonPropertyName("end_session_endpoint")] + public string? EndSessionEndpoint { get; set; } + + [JsonPropertyName("jwks_uri")] + public string? JwksUri { get; set; } + + [JsonPropertyName("registration_endpoint")] + public string? RegistrationEndpoint { get; set; } + + [JsonPropertyName("scopes_supported")] + public IList? ScopesSupported { get; set; } + + [JsonPropertyName("response_types_supported")] + public IList? ResponseTypesSupported { get; set; } + + [JsonPropertyName("acr_values_supported")] + public IList? AcrValuesSupported { get; set; } + + [JsonPropertyName("subject_types_supported")] + public IList? SubjectTypesSupported { get; set; } + + [JsonPropertyName("userinfo_signing_alg_values_supported")] + public IList? UserinfoSigningAlgValuesSupported { get; set; } + + [JsonPropertyName("userinfo_encryption_alg_values_supported")] + public IList? UserinfoEncryptionAlgValuesSupported { get; set; } + + [JsonPropertyName("userinfo_encryption_enc_values_supported")] + public IList? UserinfoEncryptionEncValuesSupported { get; set; } + + [JsonPropertyName("id_token_signing_alg_values_supported")] + public IList? IdTokenSigningAlgValuesSupported { get; set; } + + [JsonPropertyName("id_token_encryption_alg_values_supported")] + public IList? IdTokenEncryptionAlgValuesSupported { get; set; } + + [JsonPropertyName("id_token_encryption_enc_values_supported")] + public IList? IdTokenEncryptionEncValuesSupported { get; set; } + + [JsonPropertyName("request_object_signing_alg_values_supported")] + public IList? RequestObjectSigningAlgValuesSupported { get; set; } + + [JsonPropertyName("display_values_supported")] + public IList? DisplayValuesSupported { get; set; } + + [JsonPropertyName("claim_types_supported")] + public IList? ClaimTypesSupported { get; set; } + + [JsonPropertyName("claims_supported")] + public IList? ClaimsSupported { get; set; } + + [JsonPropertyName("claims_parameter_supported")] + public bool? ClaimsParameterSupported { get; set; } + + [JsonPropertyName("service_documentation")] + public string? ServiceDocumentation { get; set; } + + [JsonPropertyName("ui_locales_supported")] + public IList? UiLocalesSupported { get; set; } + } + + public IResult GetConfiguration() + { + var response = new DiscoveryResponse() + { + Issuer = Settings.HttpsExternalUrl, + AuthorizationEndpoint = Settings.HttpsExternalUrl + "/makaka", + TokenEndpoint = Settings.HttpsExternalUrl + "/token", + TokenEndpointAuthMethodsSupported = ["client_secret_basic", "private_key_jwt"], + TokenEndpointAuthSigningAlgValuesSupported = ["RS256", "ES256"], + + AcrValuesSupported = ["urn:mace:incommon:iap:silver", "urn:mace:incommon:iap:bronze"], + ResponseTypesSupported = ["code", "code id_token", "id_token", "token id_token"], + SubjectTypesSupported = [], + + UserinfoEncryptionEncValuesSupported = ["A128CBC-HS256", "A128GCM"], + IdTokenSigningAlgValuesSupported = ["RS256", "ES256", "HS256"], + IdTokenEncryptionAlgValuesSupported = ["RSA1_5", "A128KW"], + IdTokenEncryptionEncValuesSupported = ["A128CBC-HS256", "A128GCM"], + + RequestObjectSigningAlgValuesSupported = ["none", "RS256", "ES256"], + DisplayValuesSupported = ["page", "popup"], + ClaimTypesSupported = ["normal", "distributed"], + + + ScopesSupported = [], + ClaimsSupported = [], + ClaimsParameterSupported = false, + ServiceDocumentation = null, + UiLocalesSupported = ["ru-RU"], + + }; + + return Results.Json(response); + } + + public IResult Ping() + { + return Results.Ok(); + } + + public IResult LoginViaOauth( + [FromQuery] string state, + [FromQuery] string redirect_uri, + [FromQuery]string response_type, + [FromQuery]string client_id, HttpContext context, + IOpenAuthService openAuthService) + { + + var host = context.Request.Host; + if (!openAuthService.ValidateLoginRequest(response_type, client_id, host.Value)) + return Results.Unauthorized(); + + //TODO LOGIN PAGE URL IN SETTINGS + return Results.Redirect($"https://localhost:5173/login?redirect_to={redirect_uri}"); + } + } +} diff --git a/Yuna.Website/Yuna.Website.Server/GlobalSettings.json b/Yuna.Website/Yuna.Website.Server/GlobalSettings.json index bf1f025..21855de 100644 --- a/Yuna.Website/Yuna.Website.Server/GlobalSettings.json +++ b/Yuna.Website/Yuna.Website.Server/GlobalSettings.json @@ -2,7 +2,8 @@ "AppVariables": { "AccessTokenLifeTimeMinutes": 1, "RefreshTokenLifeTimeDays": 365, - "ReferalCode": "#I_@m_g00d_guy_07_07_2024" + "ReferalCode": "#I_@m_g00d_guy_07_07_2024", + "HttpsExternalHost": "https://smarthome.vasich.keenetic.pro" }, "ConnectionStrings": { "BarcodeService": "http://localhost:7799/barcode", diff --git a/Yuna.Website/Yuna.Website.Server/Infrastructure/Encrypter.cs b/Yuna.Website/Yuna.Website.Server/Infrastructure/Encrypter.cs index d090709..abfb8b6 100644 --- a/Yuna.Website/Yuna.Website.Server/Infrastructure/Encrypter.cs +++ b/Yuna.Website/Yuna.Website.Server/Infrastructure/Encrypter.cs @@ -27,5 +27,13 @@ namespace Yuna.Website.Server.Infrastructure var hashedSalt = _tokenHasher.ComputeHash(bytesStr); return Convert.ToBase64String(hashedSalt); } + + public static string GenerateRandomString(int length) + { + const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + return new string(Enumerable.Repeat(chars, length) + .Select(s => s[random.Next(s.Length)]).ToArray()); + } } } diff --git a/Yuna.Website/Yuna.Website.Server/Infrastructure/Settings.cs b/Yuna.Website/Yuna.Website.Server/Infrastructure/Settings.cs index b6bae6e..36cb2e4 100644 --- a/Yuna.Website/Yuna.Website.Server/Infrastructure/Settings.cs +++ b/Yuna.Website/Yuna.Website.Server/Infrastructure/Settings.cs @@ -10,6 +10,8 @@ namespace Yuna.Website.Server.Infrastructure public static string ReferalCode { get; private set; } = null!; public static string DbConnectionStr { get; private set; } = null!; + public static string HttpsExternalUrl { get; private set; } = null!; + public static void Init() { var jsonText = File.ReadAllText("globalSettings.json"); @@ -50,6 +52,7 @@ namespace Yuna.Website.Server.Infrastructure private static void LoadAppVariables(JsonElement appVariablesStr) { + HttpsExternalUrl = appVariablesStr.GetProperty("HttpsExternalHost").GetString() ?? throw new Exception("no https exernal host"); ReferalCode = appVariablesStr.GetProperty("ReferalCode").GetString() ?? throw new Exception("No ref code"); AccessTokenLifeTime = TimeSpan.FromSeconds(appVariablesStr.GetProperty("AccessTokenLifeTimeMinutes").GetInt32()); RefreshTokenLifeTime = TimeSpan.FromDays(appVariablesStr.GetProperty("RefreshTokenLifeTimeDays").GetInt32()); diff --git a/Yuna.Website/Yuna.Website.Server/Model/UserBinding.cs b/Yuna.Website/Yuna.Website.Server/Model/UserBinding.cs new file mode 100644 index 0000000..70da23b --- /dev/null +++ b/Yuna.Website/Yuna.Website.Server/Model/UserBinding.cs @@ -0,0 +1,10 @@ +namespace Yuna.Website.Server.Model +{ + public class UserBinding + { + public long Id { get; set; } + public long UserId { get; set; } + public string Code { get; set; } = null!; + public string AccessToken { get; set; } = null!; + } +} diff --git a/Yuna.Website/Yuna.Website.Server/Properties/launchSettings.json b/Yuna.Website/Yuna.Website.Server/Properties/launchSettings.json index bcf7f19..095e15b 100644 --- a/Yuna.Website/Yuna.Website.Server/Properties/launchSettings.json +++ b/Yuna.Website/Yuna.Website.Server/Properties/launchSettings.json @@ -9,7 +9,7 @@ "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy" }, "dotnetRunMessages": true, - "applicationUrl": "http://localhost:5227" + "applicationUrl": "http://192.168.1.2:5227" }, "IIS Express": { "commandName": "IISExpress", @@ -28,6 +28,17 @@ "ASPNETCORE_HTTP_PORTS": "8080" }, "publishAllPorts": true + }, + "https": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://192.168.1.2:5227" } }, "$schema": "http://json.schemastore.org/launchsettings.json", @@ -39,4 +50,4 @@ "sslPort": 0 } } -} +} \ No newline at end of file diff --git a/Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/IOpenAuthService.cs b/Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/IOpenAuthService.cs new file mode 100644 index 0000000..3d705c6 --- /dev/null +++ b/Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/IOpenAuthService.cs @@ -0,0 +1,11 @@ +using Yuna.Website.Server.Model; + +namespace Yuna.Website.Server.Services.OpenAuthService +{ + public interface IOpenAuthService + { + public bool ValidateLoginRequest(string responseType, string clientId, string host); + public UserBinding CreateBinding(User user); + + } +} diff --git a/Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/OpenAuthService.cs b/Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/OpenAuthService.cs new file mode 100644 index 0000000..1b6a0ed --- /dev/null +++ b/Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/OpenAuthService.cs @@ -0,0 +1,47 @@ +using Yuna.Website.Server.Infrastructure; +using Yuna.Website.Server.Model; +using Yuna.Website.Server.Storage.Repositories.UserBindings; + +namespace Yuna.Website.Server.Services.OpenAuthService +{ + public class OpenAuthService : IOpenAuthService + { + private readonly IUserBindingsRepository _userBindingsRepository; + private readonly ILogger _logger; + public OpenAuthService(IUserBindingsRepository userBindingsRepository, ILogger logger) + { + _userBindingsRepository = userBindingsRepository; + _logger = logger; + } + + public UserBinding CreateBinding(User user) + { + //Надо переделать, чтобы код и токен точно были уникальные + var binding = new UserBinding() + { + AccessToken = Encrypter.GenerateRandomString(128), + Code = Encrypter.GenerateRandomString(50), + UserId = user.Id + }; + + _logger.LogInformation("Created OauthBinding:\n" + + "UserId: {0}\n" + + "AccessToken: {1}\n" + + "Code: {2}\n", user.Id, binding.Code, binding.AccessToken); + + return _userBindingsRepository.Create(binding); + } + + public bool ValidateLoginRequest(string responseType, string clientId, string host) + { + _logger.LogInformation("Host: {0}", host); + + var result = responseType.Equals("code") && clientId.Equals("vasich_yandex_server"); + + if (result) _logger.LogInformation("OauthRequest valid"); + else _logger.LogWarning("OauthRequest invalid"); + + return result; + } + } +} diff --git a/Yuna.Website/Yuna.Website.Server/Starter.cs b/Yuna.Website/Yuna.Website.Server/Starter.cs index c450dec..1eb9770 100644 --- a/Yuna.Website/Yuna.Website.Server/Starter.cs +++ b/Yuna.Website/Yuna.Website.Server/Starter.cs @@ -3,12 +3,14 @@ using Yuna.Website.Server.API; using Yuna.Website.Server.Infrastructure; using Yuna.Website.Server.Services.DeviceService; using Yuna.Website.Server.Services.DeviceSkillService; +using Yuna.Website.Server.Services.OpenAuthService; using Yuna.Website.Server.Services.TokenService; using Yuna.Website.Server.Services.UserService; using Yuna.Website.Server.Storage; using Yuna.Website.Server.Storage.Repositories.Device; using Yuna.Website.Server.Storage.Repositories.Prop; using Yuna.Website.Server.Storage.Repositories.User; +using Yuna.Website.Server.Storage.Repositories.UserBindings; namespace Yuna.Website.Server { @@ -60,6 +62,7 @@ namespace Yuna.Website.Server public static void DefineEndpoints(WebApplication app) { + new OpenAuthEndpoints().Define(app); new AuthEndpoints().Define(app); new SkillsEndpoints().Define(app); new DeviceEndpoints().Define(app); @@ -129,10 +132,13 @@ namespace Yuna.Website.Server builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); } public static void DefineAuth(WebApplicationBuilder builder) diff --git a/Yuna.Website/Yuna.Website.Server/Storage/Repositories/UserBindings/IUserBindingsRepository.cs b/Yuna.Website/Yuna.Website.Server/Storage/Repositories/UserBindings/IUserBindingsRepository.cs new file mode 100644 index 0000000..770b80f --- /dev/null +++ b/Yuna.Website/Yuna.Website.Server/Storage/Repositories/UserBindings/IUserBindingsRepository.cs @@ -0,0 +1,10 @@ +using Yuna.Website.Server.Model; + +namespace Yuna.Website.Server.Storage.Repositories.UserBindings +{ + public interface IUserBindingsRepository + { + public UserBinding? GetUserBinding(string code); + public UserBinding Create(UserBinding userBinding); + } +} diff --git a/Yuna.Website/Yuna.Website.Server/Storage/Repositories/UserBindings/UserBindingRepository.cs b/Yuna.Website/Yuna.Website.Server/Storage/Repositories/UserBindings/UserBindingRepository.cs new file mode 100644 index 0000000..59cc18a --- /dev/null +++ b/Yuna.Website/Yuna.Website.Server/Storage/Repositories/UserBindings/UserBindingRepository.cs @@ -0,0 +1,24 @@ +using Yuna.Website.Server.Model; + +namespace Yuna.Website.Server.Storage.Repositories.UserBindings +{ + public class UserBindingRepository : IUserBindingsRepository + { + private static List _inMemoryContext = new(); + + public UserBindingRepository() { } + + public UserBinding Create(UserBinding userBinding) + { + userBinding.Id = _inMemoryContext.Count; + _inMemoryContext.Add(userBinding); + return userBinding; + } + + public UserBinding? GetUserBinding(string code) + { + var binding = _inMemoryContext.FirstOrDefault(x => x.Code.Equals(code)); + return binding; + } + } +} diff --git a/Yuna.Website/yuna.website.client/src/services/Api.ts b/Yuna.Website/yuna.website.client/src/services/Api.ts index ef3d1f3..2dc89f1 100644 --- a/Yuna.Website/yuna.website.client/src/services/Api.ts +++ b/Yuna.Website/yuna.website.client/src/services/Api.ts @@ -1,7 +1,7 @@ import axios from "axios"; export const api = axios.create({ - baseURL: "http://localhost:5227/api/", + baseURL: "https://192.168.1.2:5227/api/", validateStatus: (status) => status < 500, withCredentials: true, headers: { 'Accept': 'application/json' } From 37146568a82aae0822679c76e34b245baba79233 Mon Sep 17 00:00:00 2001 From: vasich Date: Sat, 27 Jul 2024 12:21:46 +0700 Subject: [PATCH 2/3] finally implemented oauth 2.0 --- .../Yuna.Website.Server/API/AuthEndpoints.cs | 10 +- .../API/DTO/LoginRequest.cs | 13 ++ .../API/OpenAuthEndpoints.cs | 159 +++++++++++++++++- .../OpenAuthService/IOpenAuthService.cs | 2 +- .../OpenAuthService/OpenAuthService.cs | 5 + Yuna.Website/Yuna.Website.Server/Starter.cs | 15 +- .../src/pages/login/LoginPageService.ts | 21 ++- .../src/pages/login/LoginPageStore.ts | 48 ++++++ .../src/pages/login/types.ts | 9 +- .../yuna.website.client/src/services/Api.ts | 2 +- 10 files changed, 260 insertions(+), 24 deletions(-) create mode 100644 Yuna.Website/Yuna.Website.Server/API/DTO/LoginRequest.cs diff --git a/Yuna.Website/Yuna.Website.Server/API/AuthEndpoints.cs b/Yuna.Website/Yuna.Website.Server/API/AuthEndpoints.cs index 45486cf..b28f596 100644 --- a/Yuna.Website/Yuna.Website.Server/API/AuthEndpoints.cs +++ b/Yuna.Website/Yuna.Website.Server/API/AuthEndpoints.cs @@ -7,6 +7,7 @@ using System.Text.Json.Serialization; using Yuna.Website.Server.Services.TokenService; using Yuna.Website.Server.Model; using Yuna.Website.Server.Services.UserService; +using Yuna.Website.Server.API.DTO; namespace Yuna.Website.Server.API { @@ -27,14 +28,7 @@ namespace Yuna.Website.Server.API } - public class LoginRequest - { - [JsonPropertyName("password")] - public string RawPassword { get; set; } = null!; - - [JsonPropertyName("username")] - public string UserName { get; set; } = null!; - } + public async Task Login(HttpContext context, [FromBody] LoginRequest request, IUserService userService, ITokenService tokenService) { var userFromDb = await userService.GetByUsername(request.UserName); diff --git a/Yuna.Website/Yuna.Website.Server/API/DTO/LoginRequest.cs b/Yuna.Website/Yuna.Website.Server/API/DTO/LoginRequest.cs new file mode 100644 index 0000000..cb32c08 --- /dev/null +++ b/Yuna.Website/Yuna.Website.Server/API/DTO/LoginRequest.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace Yuna.Website.Server.API.DTO +{ + public class LoginRequest + { + [JsonPropertyName("password")] + public string RawPassword { get; set; } = null!; + + [JsonPropertyName("username")] + public string UserName { get; set; } = null!; + } +} diff --git a/Yuna.Website/Yuna.Website.Server/API/OpenAuthEndpoints.cs b/Yuna.Website/Yuna.Website.Server/API/OpenAuthEndpoints.cs index a83950d..ad3e0c1 100644 --- a/Yuna.Website/Yuna.Website.Server/API/OpenAuthEndpoints.cs +++ b/Yuna.Website/Yuna.Website.Server/API/OpenAuthEndpoints.cs @@ -1,7 +1,11 @@ using Microsoft.AspNetCore.Mvc; +using System.Text; using System.Text.Json.Serialization; +using Yuna.Website.Server.API.DTO; using Yuna.Website.Server.Infrastructure; using Yuna.Website.Server.Services.OpenAuthService; +using Yuna.Website.Server.Services.UserService; +using Yuna.Website.Server.Storage.Repositories.UserBindings; namespace Yuna.Website.Server.API { @@ -9,11 +13,20 @@ namespace Yuna.Website.Server.API { public void Define(WebApplication app) { - app.MapGet("~/.well-known/openid-configuration", GetConfiguration); + app.MapGet("~/.well-known/openid-configuration", GetConfiguration) + .WithTags("oauth"); - app.MapMethods("/v1.0", ["HEAD"], Ping); + app.MapMethods("/v1.0", ["HEAD"], Ping) + .WithTags("oauth"); - app.MapGet("api/oauth/login", LoginViaOauth); + app.MapGet("api/oauth/login", LoginViaOauth) + .WithTags("oauth"); + + app.MapPost("api/oauth/auth", AuthorizeViaOauth) + .WithTags("oauth"); + + app.MapPost("/api/oauth/token", GetTokenByCode) + .WithTags("oauth"); } public class DiscoveryResponse @@ -141,10 +154,11 @@ namespace Yuna.Website.Server.API } public IResult LoginViaOauth( - [FromQuery] string state, - [FromQuery] string redirect_uri, - [FromQuery]string response_type, - [FromQuery]string client_id, HttpContext context, + [FromQuery] string state, + [FromQuery] string redirect_uri, + [FromQuery] string response_type, + [FromQuery] string client_id, + HttpContext context, IOpenAuthService openAuthService) { @@ -153,7 +167,136 @@ namespace Yuna.Website.Server.API return Results.Unauthorized(); //TODO LOGIN PAGE URL IN SETTINGS - return Results.Redirect($"https://localhost:5173/login?redirect_to={redirect_uri}"); + return Results.Redirect($"https://localhost:5173/login?redirect_to={redirect_uri}&client_id={client_id}&state={state}"); + } + + + public class OauthLoginRequest : LoginRequest + { + [JsonPropertyName("state")] + public string State { get; set; } = null!; + + [JsonPropertyName("redirectUri")] + public string RedirectUri { get; set; } = null!; + } + public async Task AuthorizeViaOauth([FromBody] OauthLoginRequest request, IUserService userService, IOpenAuthService openAuthService, HttpContext context) + { + var user = await userService.GetByUsername(request.UserName); + + if (user is null) return Results.Problem(statusCode: 401, detail:"invalid user input data"); + + var inputHashedPassword = Encrypter.HashPassword(request.RawPassword, user.UserName); + + if (!inputHashedPassword.Equals(user.HashedPassword)) return Results.Problem(statusCode: 401, detail: "invalid login attempt"); + + var binding = openAuthService.CreateBinding(user); + + if(binding is null) return Results.Problem(statusCode: 401, detail: "Unable to create binding"); + + return Results.Ok(request.RedirectUri + $"?code={binding.Code}&state={request.State}"); + } + + + public class OauthTokenResponse + { + [JsonPropertyName("access_token")] + public string AccessToken { get; set; } = null!; + + [JsonPropertyName("token_type")] + public string TokenType => "Bearer"; + + [JsonPropertyName("expires_in")] + public int ExpiresIn => int.MaxValue; + } + + public class TokenRequest + { + public string Code { get; set; } + public string ClientId { get; set; } = null!; + + public string ClientSecret { get; set; } = null!; + + public string GrantType { get; set; } = null!; + + public string RedirectUri { get; set; } = null!; + } + + public async Task GetTokenByCode( + HttpContext context, + ILogger logger, + IOpenAuthService openAuthService) + { + var request = context.Request; + var sb = new StringBuilder(); + // Записываем метод и URL запроса + sb.AppendLine($"{request.Method} {request.Path}{request.QueryString}"); + + // Записываем заголовки + sb.AppendLine("Headers:"); + foreach (var header in request.Headers) + { + sb.AppendLine($"{header.Key}: {header.Value}"); + } + + // Записываем тело запроса + string bodyStr = ""; + sb.AppendLine("Body:"); + request.EnableBuffering(); // Для того чтобы можно было прочитать тело несколько раз + using (var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true)) + { + var body = await reader.ReadToEndAsync(); + sb.AppendLine(body); + bodyStr = body; + request.Body.Position = 0; // Возвращаем позицию в начале потока + } + + logger.LogInformation(sb.ToString()); + + + + var tokenRequest = ParseQueryStringToTokenRequest(bodyStr); + + + if (tokenRequest.GrantType != "authorization_code") + { + return Results.BadRequest(new { error = "unsupported_grant_type" }); + } + + if(!tokenRequest.ClientId.Equals("vasich_yandex_server")) + { + return Results.BadRequest(new { error = "client id invalid" }); + } + + if(!tokenRequest.ClientSecret.Equals("vasich_molodets_sdelal_oauth")) + { + return Results.BadRequest(new { error = "client secret invalid" }); + } + + var userBinding = openAuthService.GetByCode(tokenRequest.Code); + + if (userBinding is null) return Results.BadRequest(new { error = "Invalid binding" }); + + return Results.Ok(new OauthTokenResponse() + { + AccessToken = userBinding.AccessToken + }); + + } + + private static TokenRequest ParseQueryStringToTokenRequest(string queryString) + { + var queryParams = queryString.Split('&') + .Select(param => param.Split('=')) + .ToDictionary(pair => pair[0], pair => Uri.UnescapeDataString(pair[1])); + + return new TokenRequest + { + Code = queryParams["code"], + ClientId = queryParams["client_id"], + ClientSecret = queryParams["client_secret"], + GrantType = queryParams["grant_type"], + RedirectUri = queryParams["redirect_uri"] + }; } } } diff --git a/Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/IOpenAuthService.cs b/Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/IOpenAuthService.cs index 3d705c6..4af4ed4 100644 --- a/Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/IOpenAuthService.cs +++ b/Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/IOpenAuthService.cs @@ -6,6 +6,6 @@ namespace Yuna.Website.Server.Services.OpenAuthService { public bool ValidateLoginRequest(string responseType, string clientId, string host); public UserBinding CreateBinding(User user); - + public UserBinding? GetByCode(string code); } } diff --git a/Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/OpenAuthService.cs b/Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/OpenAuthService.cs index 1b6a0ed..f2dba29 100644 --- a/Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/OpenAuthService.cs +++ b/Yuna.Website/Yuna.Website.Server/Services/OpenAuthService/OpenAuthService.cs @@ -43,5 +43,10 @@ namespace Yuna.Website.Server.Services.OpenAuthService return result; } + + public UserBinding? GetByCode(string code) + { + return _userBindingsRepository.GetUserBinding(code); + } } } diff --git a/Yuna.Website/Yuna.Website.Server/Starter.cs b/Yuna.Website/Yuna.Website.Server/Starter.cs index 1eb9770..eb1cb86 100644 --- a/Yuna.Website/Yuna.Website.Server/Starter.cs +++ b/Yuna.Website/Yuna.Website.Server/Starter.cs @@ -191,8 +191,8 @@ namespace Yuna.Website.Server builder.Services.AddSwaggerGen(); builder.Services.AddCors(); - + //builder.Services.AddAutoMapper(typeof(ApplicationProfile)); } @@ -201,7 +201,14 @@ namespace Yuna.Website.Server { if (app.Environment.IsDevelopment()) { - app.UseCors(x => x.WithOrigins("https://localhost:5173", "http://localhost:5173").AllowAnyMethod().AllowAnyHeader().AllowCredentials()); + app.UseCors(x => + x.WithOrigins("https://localhost:5173", "http://localhost:5173", "https://192.168.1.2:5227") + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials() + .SetIsOriginAllowed(origin => true) // Разрешить все источники + .WithExposedHeaders("Location")); // Разрешить заголовок Location) + } else @@ -213,12 +220,12 @@ namespace Yuna.Website.Server app.UseDefaultFiles(); app.UseStaticFiles(); - + app.Use(async (context, next) => { var isLoggedIn = context.User?.Identity?.IsAuthenticated ?? false; - if(context.Request.Path.StartsWithSegments("/login") && isLoggedIn) + if (context.Request.Path.StartsWithSegments("/login") && isLoggedIn) { context.Response.Redirect("/"); return; diff --git a/Yuna.Website/yuna.website.client/src/pages/login/LoginPageService.ts b/Yuna.Website/yuna.website.client/src/pages/login/LoginPageService.ts index 0e6a432..07cdf69 100644 --- a/Yuna.Website/yuna.website.client/src/pages/login/LoginPageService.ts +++ b/Yuna.Website/yuna.website.client/src/pages/login/LoginPageService.ts @@ -1,7 +1,10 @@ import { api } from "../../services/Api"; -import { ILoginRequest, ILoginResult } from "./types"; +import { ILoginRequest, ILoginResult, IOauthLoginRequest } from "./types"; +interface IOauthLoginResult extends ILoginResult { + href: string | null; +} export class LoginPageService { public static async login(request: ILoginRequest): Promise { @@ -18,4 +21,20 @@ export class LoginPageService { return { loggedIn: false, isError: true }; } } + + + + public static async loginOauth(request: IOauthLoginRequest): Promise { + try { + const response = await api.post("/oauth/auth", request); + if (response.status === 200) return { loggedIn: true, isError: false, href: response.data }; + + return { loggedIn: false, isError: false, href: null }; + } + + catch (error) { + console.error("Error: ", error); + return { loggedIn: false, isError: true, href: null }; + } + } } diff --git a/Yuna.Website/yuna.website.client/src/pages/login/LoginPageStore.ts b/Yuna.Website/yuna.website.client/src/pages/login/LoginPageStore.ts index e590060..1884d84 100644 --- a/Yuna.Website/yuna.website.client/src/pages/login/LoginPageStore.ts +++ b/Yuna.Website/yuna.website.client/src/pages/login/LoginPageStore.ts @@ -2,6 +2,7 @@ import { makeAutoObservable } from "mobx"; import { LoginPageService } from "./LoginPageService"; import { createStandaloneToast } from "@chakra-ui/react"; import { error } from "../../utils/ToastHelper"; +import { IOauthQueryParams } from "./types"; @@ -11,6 +12,17 @@ export class LoginPageStore { password: string = ""; isLoading: boolean = false; + private getQueryParams(): IOauthQueryParams | null { + const url = window.location.href; + const urlObj = new URL(url); + const params = new URLSearchParams(urlObj.search); + + const redirectUri: string | null = params.get('redirect_to'); + const state: string | null = params.get('state'); + + if (state && redirectUri) return { redirectUri: redirectUri, state: state } + return null; + } setLogin(value: string) { this.login = value; @@ -22,7 +34,43 @@ export class LoginPageStore { } async handleLogin() { + const oauthParams = this.getQueryParams() + + if (oauthParams) await this.loginAsOauth(oauthParams); + else await this.loginSimpleWay() + } + + async loginAsOauth(oauthParams: IOauthQueryParams) { this.isLoading = true; + + const result = await LoginPageService + .loginOauth({ + password: this.password, + username: this.login, + redirectUri: oauthParams.redirectUri, + state: oauthParams.state + }); + this.isLoading = false; + + if (result?.isError) { + error("Ошибка", "Запрос не был выполнен") + return; + } + + if (result && !result.loggedIn) { + error("Неверный логин или пароль", "Проверьте еще раз") + return; + } + + window.location.replace(result.href!); + + } + + + async loginSimpleWay() { + + this.isLoading = true; + const result = await LoginPageService.login({ password: this.password, username: this.login }) this.isLoading = false; diff --git a/Yuna.Website/yuna.website.client/src/pages/login/types.ts b/Yuna.Website/yuna.website.client/src/pages/login/types.ts index 8cb6edf..d4ceff9 100644 --- a/Yuna.Website/yuna.website.client/src/pages/login/types.ts +++ b/Yuna.Website/yuna.website.client/src/pages/login/types.ts @@ -6,4 +6,11 @@ export interface ILoginResult { export interface ILoginRequest { password: string; username: string; -} \ No newline at end of file +} + +export interface IOauthQueryParams { + redirectUri: string; + state: string; +} + +export interface IOauthLoginRequest extends IOauthQueryParams, ILoginRequest { } \ No newline at end of file diff --git a/Yuna.Website/yuna.website.client/src/services/Api.ts b/Yuna.Website/yuna.website.client/src/services/Api.ts index 2dc89f1..c8cf293 100644 --- a/Yuna.Website/yuna.website.client/src/services/Api.ts +++ b/Yuna.Website/yuna.website.client/src/services/Api.ts @@ -4,7 +4,7 @@ export const api = axios.create({ baseURL: "https://192.168.1.2:5227/api/", validateStatus: (status) => status < 500, withCredentials: true, - headers: { 'Accept': 'application/json' } + headers: { 'Content-Type': 'application/json' } }); api.interceptors.response.use(response => { From b4538d322a7638591e22f844b74bcff35e658885 Mon Sep 17 00:00:00 2001 From: vasich Date: Sat, 27 Jul 2024 13:02:10 +0700 Subject: [PATCH 3/3] new gitea pipeline --- .gitea/workflows/dev_pipeline.yaml | 47 +++ .gitea/workflows/test.yaml | 19 -- Db/_localhost-2024_07_27_12_51_02-dump.sql | 371 +++++++++++++++++++++ 3 files changed, 418 insertions(+), 19 deletions(-) create mode 100644 .gitea/workflows/dev_pipeline.yaml delete mode 100644 .gitea/workflows/test.yaml create mode 100644 Db/_localhost-2024_07_27_12_51_02-dump.sql diff --git a/.gitea/workflows/dev_pipeline.yaml b/.gitea/workflows/dev_pipeline.yaml new file mode 100644 index 0000000..a74f527 --- /dev/null +++ b/.gitea/workflows/dev_pipeline.yaml @@ -0,0 +1,47 @@ +name: Dev testing pipeline +run-name: ${{ gitea.actor }} is testing out Yuna 🚀 +on: + push: + branches: + - dev + +jobs: + BuildAndTest: + runs-on: ubuntu-latest + steps: + - name: List Base info 👓 + run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event." + run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!" + run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}." + run: echo "${{ gitea.workspace }} is a workspace folder" + + - name: Check out repo code 📝 + uses: actions/checkout@v3 + + - name: Set up PostgreSQL 🐘 + uses: postgres-actions/setup-postgres@v1 + with: + postgres-version: '15' + postgres-user: 'developer' + postgres-password: 'magazinchik_forever' + postgres-port: '5432' + + - name: Init db 📜 + env: + PGPSSWORD: magazinchik_forever + run: | + psql -h localhost -U developer -p 5432 -f ${{ gitea.workspace }}/Db/_localhost-2024_07_27_12_51_02-dump.sql + + - name: Setup .NET 🛠️ + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '8.0.x' + + - name: Restore dependencies 🔪 + run: dotnet restore + + - name: Build debug app ☢️ + run: donet build -c Debug --no-restore + + - name: Run unit tests 💊 + run: dotnet test -c Debug --no-build \ No newline at end of file diff --git a/.gitea/workflows/test.yaml b/.gitea/workflows/test.yaml deleted file mode 100644 index 394c807..0000000 --- a/.gitea/workflows/test.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: Gitea Actions Demo -run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀 -on: [push] - -jobs: - Explore-Gitea-Actions: - runs-on: ubuntu-latest - steps: - - run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event." - - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!" - - run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}." - - name: Check out repository code - uses: actions/checkout@v4 - - run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner." - - run: echo "🖥️ The workflow is now ready to test your code on the runner." - - name: List files in the repository - run: | - ls ${{ gitea.workspace }} - - run: echo "🍏 This job's status is ${{ job.status }}." \ No newline at end of file diff --git a/Db/_localhost-2024_07_27_12_51_02-dump.sql b/Db/_localhost-2024_07_27_12_51_02-dump.sql new file mode 100644 index 0000000..8e9245c --- /dev/null +++ b/Db/_localhost-2024_07_27_12_51_02-dump.sql @@ -0,0 +1,371 @@ +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 15.2 +-- Dumped by pg_dump version 15.2 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Name: Yuna; Type: DATABASE; Schema: -; Owner: developer +-- + +CREATE DATABASE "Yuna" WITH TEMPLATE = template0 ENCODING = 'UTF8' LOCALE_PROVIDER = libc LOCALE = 'Russian_Russia.1251'; + + +ALTER DATABASE "Yuna" OWNER TO developer; + +\connect "Yuna" + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: Yuna_Devices; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public."Yuna_Devices" ( + "Id" bigint NOT NULL, + "Name" character varying(128) NOT NULL, + "Description" character varying(255), + "DeviceUrl" character varying(255), + "UserId" bigint NOT NULL, + "IsDeleted" boolean DEFAULT false NOT NULL +); + + +ALTER TABLE public."Yuna_Devices" OWNER TO postgres; + +-- +-- Name: Yuna_Devices_Id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public."Yuna_Devices_Id_seq" + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public."Yuna_Devices_Id_seq" OWNER TO postgres; + +-- +-- Name: Yuna_Devices_Id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public."Yuna_Devices_Id_seq" OWNED BY public."Yuna_Devices"."Id"; + + +-- +-- Name: Yuna_Props; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public."Yuna_Props" ( + "Id" bigint NOT NULL, + "Name" character varying(100) NOT NULL, + "MeasureName" character varying(255), + "JsonValueName" character varying(255), + "Type" smallint +); + + +ALTER TABLE public."Yuna_Props" OWNER TO postgres; + +-- +-- Name: Yuna_Props_Id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public."Yuna_Props_Id_seq" + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public."Yuna_Props_Id_seq" OWNER TO postgres; + +-- +-- Name: Yuna_Props_Id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public."Yuna_Props_Id_seq" OWNED BY public."Yuna_Props"."Id"; + + +-- +-- Name: Yuna_Props_In_Devices; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public."Yuna_Props_In_Devices" ( + "Id" bigint NOT NULL, + "PropId" bigint NOT NULL, + "DeviceId" bigint NOT NULL +); + + +ALTER TABLE public."Yuna_Props_In_Devices" OWNER TO postgres; + +-- +-- Name: Yuna_Props_In_Devices_Id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public."Yuna_Props_In_Devices_Id_seq" + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public."Yuna_Props_In_Devices_Id_seq" OWNER TO postgres; + +-- +-- Name: Yuna_Props_In_Devices_Id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public."Yuna_Props_In_Devices_Id_seq" OWNED BY public."Yuna_Props_In_Devices"."Id"; + + +-- +-- Name: Yuna_Users; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public."Yuna_Users" ( + "Id" bigint NOT NULL, + "Username" character varying(32) NOT NULL, + "HashedPassword" character varying(255) NOT NULL, + "IsAdmin" boolean NOT NULL +); + + +ALTER TABLE public."Yuna_Users" OWNER TO postgres; + +-- +-- Name: Yuna_Users_Id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public."Yuna_Users_Id_seq" + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public."Yuna_Users_Id_seq" OWNER TO postgres; + +-- +-- Name: Yuna_Users_Id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public."Yuna_Users_Id_seq" OWNED BY public."Yuna_Users"."Id"; + + +-- +-- Name: Yuna_Devices Id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public."Yuna_Devices" ALTER COLUMN "Id" SET DEFAULT nextval('public."Yuna_Devices_Id_seq"'::regclass); + + +-- +-- Name: Yuna_Props Id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public."Yuna_Props" ALTER COLUMN "Id" SET DEFAULT nextval('public."Yuna_Props_Id_seq"'::regclass); + + +-- +-- Name: Yuna_Props_In_Devices Id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public."Yuna_Props_In_Devices" ALTER COLUMN "Id" SET DEFAULT nextval('public."Yuna_Props_In_Devices_Id_seq"'::regclass); + + +-- +-- Name: Yuna_Users Id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public."Yuna_Users" ALTER COLUMN "Id" SET DEFAULT nextval('public."Yuna_Users_Id_seq"'::regclass); + + +-- +-- Data for Name: Yuna_Devices; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +INSERT INTO public."Yuna_Devices" ("Id", "Name", "Description", "DeviceUrl", "UserId", "IsDeleted") VALUES (9, 'Test Device', 'Test descr', 'sdfsdfsdfsdf', 8, false); +INSERT INTO public."Yuna_Devices" ("Id", "Name", "Description", "DeviceUrl", "UserId", "IsDeleted") VALUES (6, 'TenkiChan', 'Supa Weather', 'http://192.168.1.89/sensors', 8, false); +INSERT INTO public."Yuna_Devices" ("Id", "Name", "Description", "DeviceUrl", "UserId", "IsDeleted") VALUES (3, 'Test Device', 'Test Description', 'Test Urldfg', 1, true); +INSERT INTO public."Yuna_Devices" ("Id", "Name", "Description", "DeviceUrl", "UserId", "IsDeleted") VALUES (8, 'Test Devicedfgdfgdfgf', 'Test Description', 'Test Url11111111', 1, true); +INSERT INTO public."Yuna_Devices" ("Id", "Name", "Description", "DeviceUrl", "UserId", "IsDeleted") VALUES (11, 'string', 'string', 'ffffffffffdgghfghfghfgfghfgfghfg', 8, true); +INSERT INTO public."Yuna_Devices" ("Id", "Name", "Description", "DeviceUrl", "UserId", "IsDeleted") VALUES (12, 'Test Device', 'Test Description', 'Test Url', 1, true); +INSERT INTO public."Yuna_Devices" ("Id", "Name", "Description", "DeviceUrl", "UserId", "IsDeleted") VALUES (13, 'Test Device', 'Test Description', 'Test Url', 1, true); +INSERT INTO public."Yuna_Devices" ("Id", "Name", "Description", "DeviceUrl", "UserId", "IsDeleted") VALUES (10, 'string', 'string', 'fffdg', 8, true); +INSERT INTO public."Yuna_Devices" ("Id", "Name", "Description", "DeviceUrl", "UserId", "IsDeleted") VALUES (4, 'Test Device', 'Nyanyanyadfgdfgfdgfdg', 'Test Urldfgdfgdfgdf', 1, false); +INSERT INTO public."Yuna_Devices" ("Id", "Name", "Description", "DeviceUrl", "UserId", "IsDeleted") VALUES (1, 'strin111', 'strin', 'strindddd', 1, false); +INSERT INTO public."Yuna_Devices" ("Id", "Name", "Description", "DeviceUrl", "UserId", "IsDeleted") VALUES (14, 'Test Device', 'Test Description', 'Test Url', 1, false); + + +-- +-- Data for Name: Yuna_Props; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +INSERT INTO public."Yuna_Props" ("Id", "Name", "MeasureName", "JsonValueName", "Type") VALUES (1, 'test', 'test', 'test', 0); +INSERT INTO public."Yuna_Props" ("Id", "Name", "MeasureName", "JsonValueName", "Type") VALUES (2, 'test', 'test', 'test', 0); +INSERT INTO public."Yuna_Props" ("Id", "Name", "MeasureName", "JsonValueName", "Type") VALUES (3, 'Temp', 'C', 'string', 0); +INSERT INTO public."Yuna_Props" ("Id", "Name", "MeasureName", "JsonValueName", "Type") VALUES (4, 'string', 'string', 'string', 0); +INSERT INTO public."Yuna_Props" ("Id", "Name", "MeasureName", "JsonValueName", "Type") VALUES (5, 'strding', 'strding', 'strding', 0); +INSERT INTO public."Yuna_Props" ("Id", "Name", "MeasureName", "JsonValueName", "Type") VALUES (6, 'SupaTest', 'testing', 'sssss', 0); +INSERT INTO public."Yuna_Props" ("Id", "Name", "MeasureName", "JsonValueName", "Type") VALUES (7, 'test', 'test', 'test', 0); +INSERT INTO public."Yuna_Props" ("Id", "Name", "MeasureName", "JsonValueName", "Type") VALUES (8, 'Температура', '°C', 'temperature', 0); +INSERT INTO public."Yuna_Props" ("Id", "Name", "MeasureName", "JsonValueName", "Type") VALUES (9, 'Влажность', '%', 'humidity', 0); +INSERT INTO public."Yuna_Props" ("Id", "Name", "MeasureName", "JsonValueName", "Type") VALUES (10, 'Концентрация CO2', 'см³/м³', 'ppm', 0); +INSERT INTO public."Yuna_Props" ("Id", "Name", "MeasureName", "JsonValueName", "Type") VALUES (11, 'test', 'test', 'test', 0); + + +-- +-- Data for Name: Yuna_Props_In_Devices; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +INSERT INTO public."Yuna_Props_In_Devices" ("Id", "PropId", "DeviceId") VALUES (1, 1, 1); +INSERT INTO public."Yuna_Props_In_Devices" ("Id", "PropId", "DeviceId") VALUES (21, 4, 1); +INSERT INTO public."Yuna_Props_In_Devices" ("Id", "PropId", "DeviceId") VALUES (22, 5, 1); +INSERT INTO public."Yuna_Props_In_Devices" ("Id", "PropId", "DeviceId") VALUES (32, 6, 1); +INSERT INTO public."Yuna_Props_In_Devices" ("Id", "PropId", "DeviceId") VALUES (39, 3, 1); +INSERT INTO public."Yuna_Props_In_Devices" ("Id", "PropId", "DeviceId") VALUES (40, 8, 6); +INSERT INTO public."Yuna_Props_In_Devices" ("Id", "PropId", "DeviceId") VALUES (41, 9, 6); +INSERT INTO public."Yuna_Props_In_Devices" ("Id", "PropId", "DeviceId") VALUES (42, 10, 6); +INSERT INTO public."Yuna_Props_In_Devices" ("Id", "PropId", "DeviceId") VALUES (43, 1, 9); +INSERT INTO public."Yuna_Props_In_Devices" ("Id", "PropId", "DeviceId") VALUES (44, 2, 9); +INSERT INTO public."Yuna_Props_In_Devices" ("Id", "PropId", "DeviceId") VALUES (45, 3, 9); +INSERT INTO public."Yuna_Props_In_Devices" ("Id", "PropId", "DeviceId") VALUES (46, 4, 9); +INSERT INTO public."Yuna_Props_In_Devices" ("Id", "PropId", "DeviceId") VALUES (47, 5, 10); +INSERT INTO public."Yuna_Props_In_Devices" ("Id", "PropId", "DeviceId") VALUES (48, 6, 10); +INSERT INTO public."Yuna_Props_In_Devices" ("Id", "PropId", "DeviceId") VALUES (49, 5, 11); + + +-- +-- Data for Name: Yuna_Users; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +INSERT INTO public."Yuna_Users" ("Id", "Username", "HashedPassword", "IsAdmin") VALUES (1, 'asd1', '2', true); +INSERT INTO public."Yuna_Users" ("Id", "Username", "HashedPassword", "IsAdmin") VALUES (2, 'test', '123', false); +INSERT INTO public."Yuna_Users" ("Id", "Username", "HashedPassword", "IsAdmin") VALUES (3, 'test', '123', false); +INSERT INTO public."Yuna_Users" ("Id", "Username", "HashedPassword", "IsAdmin") VALUES (4, 'test', '123', false); +INSERT INTO public."Yuna_Users" ("Id", "Username", "HashedPassword", "IsAdmin") VALUES (5, 'test', '123', false); +INSERT INTO public."Yuna_Users" ("Id", "Username", "HashedPassword", "IsAdmin") VALUES (6, 'test', '123', false); +INSERT INTO public."Yuna_Users" ("Id", "Username", "HashedPassword", "IsAdmin") VALUES (8, 'test1', 'EbvfS/o9GSL7wkbnix+BzMpCLJqfWqeTgimj3u9CknA=', false); +INSERT INTO public."Yuna_Users" ("Id", "Username", "HashedPassword", "IsAdmin") VALUES (7, 'vasich', 'V64zSeugpX/I4ZSqot7K0NQBOGdMNYNclXButwoXasI=', true); +INSERT INTO public."Yuna_Users" ("Id", "Username", "HashedPassword", "IsAdmin") VALUES (9, 'test', '123', false); + + +-- +-- Name: Yuna_Devices_Id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public."Yuna_Devices_Id_seq"', 14, true); + + +-- +-- Name: Yuna_Props_Id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public."Yuna_Props_Id_seq"', 11, true); + + +-- +-- Name: Yuna_Props_In_Devices_Id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public."Yuna_Props_In_Devices_Id_seq"', 49, true); + + +-- +-- Name: Yuna_Users_Id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public."Yuna_Users_Id_seq"', 9, true); + + +-- +-- Name: Yuna_Devices Yuna_Devices_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public."Yuna_Devices" + ADD CONSTRAINT "Yuna_Devices_pkey" PRIMARY KEY ("Id"); + + +-- +-- Name: Yuna_Props_In_Devices Yuna_Props_In_Devices_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public."Yuna_Props_In_Devices" + ADD CONSTRAINT "Yuna_Props_In_Devices_pkey" PRIMARY KEY ("Id"); + + +-- +-- Name: Yuna_Props Yuna_Props_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public."Yuna_Props" + ADD CONSTRAINT "Yuna_Props_pkey" PRIMARY KEY ("Id"); + + +-- +-- Name: Yuna_Users_index_1; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX "Yuna_Users_index_1" ON public."Yuna_Users" USING btree ("Id"); + + +-- +-- Name: Yuna_Props_In_Devices Yuna_Props_In_Devices_relation_2; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public."Yuna_Props_In_Devices" + ADD CONSTRAINT "Yuna_Props_In_Devices_relation_2" FOREIGN KEY ("DeviceId") REFERENCES public."Yuna_Devices"("Id") ON DELETE CASCADE; + + +-- +-- Name: Yuna_Props_In_Devices Yuna_Props_In_Devices_relation_3; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public."Yuna_Props_In_Devices" + ADD CONSTRAINT "Yuna_Props_In_Devices_relation_3" FOREIGN KEY ("PropId") REFERENCES public."Yuna_Props"("Id") ON DELETE CASCADE; + + +-- +-- Name: Yuna_Devices fk_user_id; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public."Yuna_Devices" + ADD CONSTRAINT fk_user_id FOREIGN KEY ("UserId") REFERENCES public."Yuna_Users"("Id"); + + +-- +-- PostgreSQL database dump complete +-- +