From 6cd5f1d041d09dbfe04882a8e8ef3df587214c0d Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Tue, 11 Feb 2025 04:45:05 +0000 Subject: [PATCH] Web Api found ... --- global.json | 5 +- .../new-templates/ViewGenerator/Create.cshtml | 145 ----------------- .../new-templates/ViewGenerator/Delete.cshtml | 81 ---------- .../ViewGenerator/Details.cshtml | 95 ----------- .../new-templates/ViewGenerator/Edit.cshtml | 149 ------------------ .../new-templates/ViewGenerator/List.cshtml | 115 -------------- scripts/make/Makefile | 78 --------- scripts/make/dnx.mk | 71 --------- scripts/make/msbuild.mk | 16 -- scripts/make/versioning.mk | 16 -- scripts/version.sh | 44 ------ src/Api/Api.csproj | 10 ++ src/Api/Program.cs | 77 +++++++++ src/Api/appsettings.json | 10 ++ src/Yavsc/Config.cs | 2 + src/Yavsc/Extensions/HostingExtensions.cs | 51 ++++-- src/Yavsc/Services/ProfileService.cs | 67 ++++---- .../Controllers/HomeController.cs | 25 ++- .../Properties/launchSettings.json | 8 +- src/sampleWebAsWebApiClient/Startup.cs | 4 +- .../Views/Home/Index.cshtml | 5 + .../sampleWebAsWebApiClient.csproj | 4 +- yavsc.sln | 7 + 23 files changed, 215 insertions(+), 870 deletions(-) delete mode 100644 scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Create.cshtml delete mode 100644 scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Delete.cshtml delete mode 100644 scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Details.cshtml delete mode 100644 scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Edit.cshtml delete mode 100644 scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/List.cshtml delete mode 100644 scripts/make/Makefile delete mode 100644 scripts/make/dnx.mk delete mode 100644 scripts/make/msbuild.mk delete mode 100644 scripts/make/versioning.mk delete mode 100755 scripts/version.sh create mode 100644 src/Api/Api.csproj create mode 100644 src/Api/Program.cs create mode 100644 src/Api/appsettings.json diff --git a/global.json b/global.json index 952b7d76..f2fb072c 100644 --- a/global.json +++ b/global.json @@ -1,12 +1,11 @@ { "projects": [ "src", - "scripts", - "tests" + "test" ], "sdk": { "runtime": "dotnet", - "architecture": "x64" + "version": "8.0.405" }, "packages": "packages" } diff --git a/scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Create.cshtml b/scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Create.cshtml deleted file mode 100644 index d212ba9f..00000000 --- a/scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Create.cshtml +++ /dev/null @@ -1,145 +0,0 @@ -@inherits Microsoft.Extensions.CodeGeneration.Templating.RazorTemplateBase -@using Microsoft.Extensions.CodeGeneration.EntityFramework -@@model @Model.ViewDataTypeName - -@{ - if (Model.IsPartialView) - { - } - else if (Model.IsLayoutPageSelected) - { -@:@@{ - @:ViewData["Title"] = @@Model.ViewName; - if (!string.IsNullOrEmpty(Model.LayoutPageFile)) - { - @:Layout = "@Model.LayoutPageFile"; - } -@:} -@: -@:

@@Model.ViewName

-@: - } - else - { -@:@@{ - @:Layout = "null"; -@:} -@: -@: -@: -@: -@: - @: - @:@Model.ViewName -@: -@: -@: - // PushIndent(" "); - } -@:
- @:
- @:

@@Model.ViewDataTypeShortName"]

- @:
- @:
- foreach (var property in Model.ModelMetadata.Properties) - { - if (property.Scaffold && !property.IsAutoGenerated && !property.IsReadOnly) - { - - // If the property is a primary key and Guid, then the Guid is generated in the controller. Hence, this propery is not displayed on the view. - if (property.IsPrimaryKey) - { - continue; - } - - if (property.IsForeignKey) - { - @:
- @: - @:
- @: - @:
- @:
- continue; - } - - bool isCheckbox = property.TypeName.Equals("System.Boolean"); - if (isCheckbox) - { - @:
- @:
- @:
- @: - @: - @:
- @:
- @:
- } - else if (property.IsEnum && !property.IsEnumFlags) - { - @:
- @: - @:
- @: - @: - @:
- @:
- } - else - { - @:
- @: - @:
- @: - @: - @:
- @:
- } - } - } -} -
-
- -
-
-
-
- -
- @Back to List -
- -@{ - if (Model.ReferenceScriptLibraries && (Model.IsLayoutPageSelected || Model.IsPartialView)) - { -@:@@section Scripts { - @: - @: - @: -@:} - } - // The following code closes the tag used in the case of a view using a layout page and the body and html tags in the case of a regular view page - if (!Model.IsPartialView && !Model.IsLayoutPageSelected) - { - if (Model.ReferenceScriptLibraries) - { -@:@@section Scripts { - @: - @: - @: -@:} - //ClearIndent(); - } -@: -@: - } -} -@functions -{ - // Do we need to use this in conjunction with the PrimaryKey check? - bool IsPropertyGuid(PropertyMetadata property) - { - return string.Equals("System.Guid", property.TypeName, StringComparison.OrdinalIgnoreCase); - } -} diff --git a/scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Delete.cshtml b/scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Delete.cshtml deleted file mode 100644 index 1c2dab35..00000000 --- a/scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Delete.cshtml +++ /dev/null @@ -1,81 +0,0 @@ -@inherits Microsoft.Extensions.CodeGeneration.Templating.RazorTemplateBase -@using Microsoft.Extensions.CodeGeneration.EntityFramework -@@model @Model.ViewDataTypeName - -@{ - if (Model.IsPartialView) - { - } - else if (Model.IsLayoutPageSelected) - { -@:@@{ - @:ViewData["Title"] = @@Model.ViewName; - if (!string.IsNullOrEmpty(Model.LayoutPageFile)) - { - @:Layout = "@Model.LayoutPageFile"; - } -@:} -@: -@:

@@Model.ViewName

-@: - } - else - { -@:@@{ - @:Layout = "null"; -@:} -@: -@: -@: -@: -@: - @: - @:@@@Model.ViewName -@: -@: -@: - // PushIndent(" "); - } -} -

@AreYourSureYouWantToDeleteThis

-
-

@@@Model.ViewDataTypeShortName

-
-
-@{ - foreach (var property in Model.ModelMetadata.Properties) - { - if (property.Scaffold && !property.IsPrimaryKey && !property.IsForeignKey) - { -
- @@Html.DisplayNameFor(model => model.@GetValueExpression(property)) -
-
- @@Html.DisplayFor(model => model.@GetValueExpression(property)) -
- } - } - @:
- @: - @:
- @:
- @: | - @:Back to List - @:
- @:
-@:
- if (!Model.IsPartialView && !Model.IsLayoutPageSelected) - { - //ClearIndent(); -@: -@: - } -} -@functions -{ - string GetValueExpression(PropertyMetadata property) - { - //Todo: Get the association for the property and use that. - return property.PropertyName; - } -} diff --git a/scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Details.cshtml b/scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Details.cshtml deleted file mode 100644 index 87e0bc5c..00000000 --- a/scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Details.cshtml +++ /dev/null @@ -1,95 +0,0 @@ -@inherits Microsoft.Extensions.CodeGeneration.Templating.RazorTemplateBase -@using Microsoft.Extensions.CodeGeneration.EntityFramework -@@model @Model.ViewDataTypeName - -@{ - if (Model.IsPartialView) - { - } - else if (Model.IsLayoutPageSelected) - { -@:@@{ - @:ViewData["Title"] = @@Model.ViewName; - if (!string.IsNullOrEmpty(Model.LayoutPageFile)) - { - @:Layout = "@Model.LayoutPageFile"; - } -@:} -@: -@:

@@Model.ViewName

-@: - } - else - { -@:@@{ - @:Layout = "null"; -@:} -@: -@: -@: -@: -@: - @: - @:@Model.ViewName -@: -@: -@: - // PushIndent(" "); - } -} -
-

@Model.ViewDataTypeShortName

-
-
-@{ - foreach (var property in Model.ModelMetadata.Properties) - { - if (property.Scaffold && !property.IsPrimaryKey && !property.IsForeignKey) - { -
- @@Html.DisplayNameFor(model => model.@GetValueExpression(property)) -
-
- @@Html.DisplayFor(model => model.@GetValueExpression(property)) -
- } - } -}
-
-

-@{ - string pkName = GetPrimaryKeyName(); - if (pkName != null) - { - @:@Edit | - @:Back to List - } - else - { - @:@@Html.ActionLink(@Edit, "Edit", new { /* id = Model.PrimaryKey */ }) | - @:Back to List - } -}

-@{ - if (!Model.IsPartialView && !Model.IsLayoutPageSelected) - { - //ClearIndent(); -@: -@: - } -} -@functions -{ - string GetPrimaryKeyName() - { - return (Model.ModelMetadata.PrimaryKeys != null && Model.ModelMetadata.PrimaryKeys.Length == 1) - ? Model.ModelMetadata.PrimaryKeys[0].PropertyName - : null; - } - - string GetValueExpression(PropertyMetadata property) - { - //Todo: Get the association for the property and use that. - return property.PropertyName; - } -} diff --git a/scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Edit.cshtml b/scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Edit.cshtml deleted file mode 100644 index 6437586f..00000000 --- a/scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/Edit.cshtml +++ /dev/null @@ -1,149 +0,0 @@ -@inherits Microsoft.Extensions.CodeGeneration.Templating.RazorTemplateBase -@using Microsoft.Extensions.CodeGeneration.EntityFramework -@@model @Model.ViewDataTypeName - -@{ - if (Model.IsPartialView) - { - } - else if (Model.IsLayoutPageSelected) - { -@:@@{ - @:ViewData["Title"] = @@Model.ViewName; - if (!string.IsNullOrEmpty(Model.LayoutPageFile)) - { - @:Layout = "@Model.LayoutPageFile"; - } -@:} -@: -@:

@@Model.ViewName

-@: - } - else - { -@:@@{ - @:Layout = "null"; -@:} -@: -@: -@: -@: -@: - @: - @:@@Model.ViewName -@: -@: -@: - // PushIndent(" "); - } -@:
- @:
- @:

@Model.ViewDataTypeShortName

- @:
- @:
- foreach (PropertyMetadata property in Model.ModelMetadata.Properties) - { - if (property.Scaffold) - { - if (property.IsPrimaryKey) - { - @: - continue; - } - if (property.IsReadOnly) - { - continue; - } - - if (property.IsForeignKey) - { - @:
- @: - @:
- @: - @: - @:
- @:
- continue; - } - - bool isCheckbox = property.TypeName.Equals("System.Boolean"); - if (isCheckbox) - { - @:
- @:
- @:
- @: - @: - @:
- @:
- @:
- } - else if (property.IsEnum && !property.IsEnumFlags) - { - @:
- @: - @:
- @: - @: - @:
- @:
- } - else - { - @:
- @: - @:
- @: - @: - @:
- @:
- } - } - } -} -
-
- -
-
-
-
- -
- Back to List -
- -@{ - if (Model.ReferenceScriptLibraries && (Model.IsLayoutPageSelected || Model.IsPartialView)) - { -@:@@section Scripts { - @: - @: - @: -@:} - } - // The following code closes the tag used in the case of a view using a layout page and the body and html tags in the case of a regular view page - if (!Model.IsPartialView && !Model.IsLayoutPageSelected) - { - if (Model.ReferenceScriptLibraries) - { -@:@@section Scripts { - @: - @: - @: -@:} - //ClearIndent(); - } -@: -@: - } -} -@functions -{ - string GetAssociationName(PropertyMetadata property) - { - //Todo: Implement properly. - return property.PropertyName; - } -} diff --git a/scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/List.cshtml b/scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/List.cshtml deleted file mode 100644 index a2e66ae2..00000000 --- a/scripts/configure/tools/mvc-code-generators/new-templates/ViewGenerator/List.cshtml +++ /dev/null @@ -1,115 +0,0 @@ -@inherits Microsoft.Extensions.CodeGeneration.Templating.RazorTemplateBase -@using Microsoft.Extensions.CodeGeneration.EntityFramework -@@model @GetEnumerableTypeExpression(Model.ViewDataTypeName) - -@{ - if (Model.IsPartialView) - { - } - else if (Model.IsLayoutPageSelected) - { -@:@@{ - @:ViewData["Title"] = @@Model.ViewName; - if (!string.IsNullOrEmpty(Model.LayoutPageFile)) - { - @:Layout = "@Model.LayoutPageFile"; - } -@:} -@: -@:

@@Model.ViewName

-@: - } - else - { -@:@@{ - @:Layout = "null"; -@:} -@: -@: -@: -@: -@: - @: - @:@@Model.ViewName -@: -@: - // PushIndent(" "); - } -@:

- @:Create New -@:

-@: - @: - IEnumerable properties = Model.ModelMetadata.Properties; - foreach (var property in properties) - { - if (property.Scaffold && !property.IsPrimaryKey && !property.IsForeignKey) - { - - } - } - @: - @: - @: -@:@@foreach (var item in Model) { - @: - foreach (PropertyMetadata property in properties) - { - if (property.Scaffold && !property.IsPrimaryKey && !property.IsForeignKey) - { - - } - } - - string pkName = GetPrimaryKeyName(); - if (pkName != null) - { - @: - } - else - { - - } - @: -@:} - -@:
- @@Html.DisplayNameFor(model => model.@GetValueExpression(property)) -
- @@Html.DisplayFor(modelItem => item.@GetValueExpression(property)) - - @:@Edit | - @:@Details | - @:@Delete - @: - @@Html.ActionLink("Edit", "Edit",new { /* id=item.PrimaryKey */ }) | - @@Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) | - @@Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ }) -
- if(!Model.IsPartialView && !Model.IsLayoutPageSelected) - { - //ClearIndent(); -@: -@: - } -} -@functions -{ - string GetPrimaryKeyName() - { - return (Model.ModelMetadata.PrimaryKeys != null && Model.ModelMetadata.PrimaryKeys.Length == 1) - ? Model.ModelMetadata.PrimaryKeys[0].PropertyName - : null; - } - - string GetValueExpression(PropertyMetadata property) - { - //Todo: Get the association for the property and use that. - return property.PropertyName; - } - - string GetEnumerableTypeExpression(string typeName) - { - return "IEnumerable<" + typeName + ">"; - } -} diff --git a/scripts/make/Makefile b/scripts/make/Makefile deleted file mode 100644 index 76584c73..00000000 --- a/scripts/make/Makefile +++ /dev/null @@ -1,78 +0,0 @@ -include versioning.mk - - -REPO_ROOT=../../../src - -SUBDIRS=Yavsc Yavsc.Server Yavsc.Abstract OAuth.AspNet.AuthServer OAuth.AspNet.Token cli test - -all: $(SUBDIRS) - -Yavsc.Abstract: - $(MAKE) -C $(REPO_ROOT)/Yavsc.Abstract VERSION=$(VERSION) - -OAuth.AspNet.Token: - $(MAKE) -C $(REPO_ROOT)/OAuth.AspNet.Token VERSION=$(VERSION) - -OAuth.AspNet.AuthServer: OAuth.AspNet.Token - $(MAKE) -C $(REPO_ROOT)/OAuth.AspNet.AuthServer VERSION=$(VERSION) - -Yavsc.Server: Yavsc.Abstract - $(MAKE) -C $(REPO_ROOT)/Yavsc.Server VERSION=$(VERSION) - -Yavsc: Yavsc.Server OAuth.AspNet.AuthServer OAuth.AspNet.Token - make -C $(REPO_ROOT)/Yavsc VERSION=$(VERSION) - -Yavsc-deploy-pkg: Yavsc - make -C $(REPO_ROOT)/Yavsc deploy-pkg - -Yavsc.Server-deploy-pkg: Yavsc.Server - make -C $(REPO_ROOT)/Yavsc.Server deploy-pkg - -Yavsc.Abstract-deploy-pkg: Yavsc.Abstract - make -C $(REPO_ROOT)/Yavsc.Abstract deploy-pkg - -cli-deploy-pkg: cli check - make -C $(REPO_ROOT)/cli deploy-pkg - -cli: Yavsc-deploy-pkg Yavsc.Server-deploy-pkg Yavsc.Abstract-deploy-pkg - make -C $(REPO_ROOT)/cli - -undoLocalYavscNugetDeploy: - rm -rf ../../../packages/Yavsc.Abstract - rm -rf ../../../packages/Yavsc.Server - rm -rf ../../../packages/Yavsc - rm -rf ~/.dnx/packages/Yavsc.Abstract - rm -rf ~/.dnx/packages/Yavsc.Server - rm -rf ~/.dnx/packages/Yavsc - -check: cli - make -C $(REPO_ROOT)/cli check - make -C $(REPO_ROOT)/test - -test: - make -C $(REPO_ROOT)/test - -pushInPre: - make -C $(REPO_ROOT)/Yavsc pushInPre - -pushInProd: - make -C $(REPO_ROOT)/Yavsc pushInProd - -deploy-pkgs: Yavsc-deploy-pkg Yavsc.Server-deploy-pkg Yavsc.Abstract-deploy-pkg cli-deploy-pkg - -memo: - vim ~/TODO.md - -rc-num: - @echo echo 1-alpha1 < $< ^ $^ @ $@ - -clean: - for subdir in $(SUBDIRS) ; do \ - make -C $(REPO_ROOT)/$${subdir} clean ; \ - done - -watch: - make -C $(REPO_ROOT)/Yavsc watch - -.PHONY: all $(SUBDIRS) - diff --git a/scripts/make/dnx.mk b/scripts/make/dnx.mk deleted file mode 100644 index 9a5a8a58..00000000 --- a/scripts/make/dnx.mk +++ /dev/null @@ -1,71 +0,0 @@ -# Common defs -# - -ifndef PRJNAME -PRJNAME := $(shell basename `pwd -P`) -endif -FRAMEWORK=dnx451 -ASPNET_ENV=Development -ASPNET_LOG_LEVEL=Debug -HOSTING=localhost -HOSTADMIN=root -FRAMEWORKALIAS=dnx451 -# nuget package destination, at generation time -BINTARGET=$(PRJNAME).dll -BINTARGETPATH=bin/$(CONFIGURATION)/$(FRAMEWORKALIAS)/$(BINTARGET) -PKGFILENAME=$(PRJNAME).$(VERSION).nupkg -dnu=dnu - -# OBS SUBDIRS=Yavsc.Server Yavsc.Abstract Yavsc cli -# - -# Git commit hash, in order to not publish some uncrontrolled code in production environment -# - -git_status := $(shell git status -s --porcelain |wc -l) - -all: $(BINTARGETPATH) - -fixSystemXML: - @# fixing package id reference case, to System.Xml, from package NJsonSchema.CodeGeneration.CSharp - @sed 's/System.XML/System.Xml/' project.lock.json > project.lock.json.new && mv project.lock.json.new project.lock.json - -restore: - touch project.json - $(dnu) restore --ignore-failed-sources - -project.lock.json: project.json - $(dnu) restore --ignore-failed-sources - -watch: project.lock.json - MONO_OPTIONS=--debug MONO_MANAGED_WATCHER=enabled ASPNET_ENV=$(ASPNET_ENV) ASPNET_LOG_LEVEL=$(ASPNET_LOG_LEVEL) dnx-watch web --configuration=$(CONFIGURATION) - -clean: - rm -rf bin obj - rm project.lock.json - -cleanoutput: - rm -rf bin/$(CONFIGURATION) - rm -rf bin/output - -$(BINTARGETPATH): project.lock.json rc-num.txt-check - $(dnu) build --configuration=$(CONFIGURATION) - -# Default target, from one level sub dirs - -bin/output: - $(dnu) publish - -bin/output/wwwroot/version: bin/output - @echo $(version) > bin/output/wwwroot/version - -pack: $(BINTARGETPATH) ../../version.txt - dnu pack --configuration $(CONFIGURATION) - -push: pack - @echo push to source: $(ISNSOURCE) - isn push -s $(ISNSOURCE) -k $(NUGETSOURCEAPIKEY) src/$(PRJNAME)/bin/$(CONFIGURATION)/$(PRJNAME).*.nupkg - -.PHONY: rc-num.txt-check - -# .DEFAULT_GOAL := $(BINTARGETPATH) diff --git a/scripts/make/msbuild.mk b/scripts/make/msbuild.mk deleted file mode 100644 index d6d260f8..00000000 --- a/scripts/make/msbuild.mk +++ /dev/null @@ -1,16 +0,0 @@ - - -MSBUILD=msbuild -MONO=mono -CONFIGURATION=Debug -BINTYPE=exe -PRJNAME := $(shell basename `pwd -P`) - -rc_num := $(shell cat ../../rc-num.txt) -VERSION=1.0.5-rc$(rc_num) - -BINTARGET=$(PRJNAME).$(BINTYPE) -BINTARGETPATH=bin/$(CONFIGURATION)/$(BINTARGET) -ISNSOURCE=$(HOME)/Nupkgs -PKGFILENAME=$(PRJNAME).$(VERSION).nupkg - diff --git a/scripts/make/versioning.mk b/scripts/make/versioning.mk deleted file mode 100644 index 2adc45d2..00000000 --- a/scripts/make/versioning.mk +++ /dev/null @@ -1,16 +0,0 @@ - -ifndef PRJNAME -PRJNAME := $(shell basename `pwd -P`) -endif -version := $(shell cat ../../version.txt) -MAKE=make -ISNSOURCE=$(HOME)/Nupkgs -VERSION=$(version) -CONFIGURATION=Debug - -version-check: -ifndef version - @echo no version number specification ... please, could you try and run 'echo 1.2.3 > ../../version.txt' ?. -else - @echo 'Got version number : $(version)' -endif diff --git a/scripts/version.sh b/scripts/version.sh deleted file mode 100755 index f6e09210..00000000 --- a/scripts/version.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -version="$1" -major=0 -minor=0 -build=0 - -# break down the version number into it's components -regex="([0-9]+).([0-9]+).([0-9]+)((-[A-Za-z]+)([0-9]+))?" -if [[ $version =~ $regex ]]; then - major="${BASH_REMATCH[1]}" - minor="${BASH_REMATCH[2]}" - build="${BASH_REMATCH[3]}" - patchtype="${BASH_REMATCH[5]}" - patchnum="${BASH_REMATCH[6]}" -fi - -# check paramater to see which number to increment -if [[ "$2" == "feature" ]]; then - minor=$(echo $minor + 1 | bc) - build=0 - patchtype= - patchnum= -elif [[ "$2" == "build" ]]; then - build=$(echo $build + 1 | bc) - patchtype= - patchnum= -elif [[ "$2" == "major" ]]; then - major=$(echo $major+1 | bc) - minor=0 - build=0 - patchtype= - patchnum= -elif [[ "$2" == "patch" ]]; then - patchnum=$(echo $patchnum + 1 | bc) -else - echo "usage: ./version.sh version_number [major/feature/build/patch]" >&2 - - exit -1 -fi - -# echo the new version number -echo "${major}.${minor}.${build}${patchtype}${patchnum}" - diff --git a/src/Api/Api.csproj b/src/Api/Api.csproj new file mode 100644 index 00000000..a04a493f --- /dev/null +++ b/src/Api/Api.csproj @@ -0,0 +1,10 @@ + + + net8.0 + enable + enable + + + + + diff --git a/src/Api/Program.cs b/src/Api/Program.cs new file mode 100644 index 00000000..9daf30a5 --- /dev/null +++ b/src/Api/Program.cs @@ -0,0 +1,77 @@ +/* + Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ + + Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. + + Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + Source code and license this software can be found + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. +*/ + +using Microsoft.AspNetCore.Mvc; + +internal class Program +{ + private static async Task Main(string[] args) + { + Console.Title = "API"; + + var builder = WebApplication.CreateBuilder(args); + + var services = builder.Services; + + // accepts any access token issued by identity server + // adds an authorization policy for scope 'api1' + services + .AddAuthorization(options => + { + options.AddPolicy("ApiScope", policy => + { + policy + .RequireAuthenticatedUser() + .RequireClaim("scope", "scope2"); + }); + }) + .AddCors(options => + { + // this defines a CORS policy called "default" + options.AddPolicy("default", policy => + { + policy.WithOrigins("https://localhost:5003") + .AllowAnyHeader() + .AllowAnyMethod(); + }); + }) + .AddControllers(); + + // accepts any access token issued by identity server + var authenticationBuilder = services.AddAuthentication() + .AddJwtBearer("Bearer", options => + { + options.IncludeErrorDetails = true; + options.Authority = "https://localhost:5001"; + options.TokenValidationParameters = + new() { ValidateAudience = false }; + }); + + using (var app = builder.Build()) + { + if (app.Environment.IsDevelopment()) + app.UseDeveloperExceptionPage(); + + app + .UseRouting() + .UseAuthentication() + .UseAuthorization() + .UseCors("default"); + + app.MapGet("/identity", (HttpContext context) => + new JsonResult(context?.User?.Claims.Select(c => new { c.Type, c.Value })) + ).RequireAuthorization("ApiScope"); + + await app.RunAsync(); + } + } +} diff --git a/src/Api/appsettings.json b/src/Api/appsettings.json new file mode 100644 index 00000000..d9d9a9bf --- /dev/null +++ b/src/Api/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Yavsc/Config.cs b/src/Yavsc/Config.cs index 05be8631..68d95685 100644 --- a/src/Yavsc/Config.cs +++ b/src/Yavsc/Config.cs @@ -77,10 +77,12 @@ public static class Config PostLogoutRedirectUris = { "https://localhost:5003/signout-callback-oidc" }, AllowOfflineAccess = true, + AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Email, + IdentityServerConstants.StandardScopes.OfflineAccess, "scope2" } }, }; diff --git a/src/Yavsc/Extensions/HostingExtensions.cs b/src/Yavsc/Extensions/HostingExtensions.cs index 9520711e..50087ac7 100644 --- a/src/Yavsc/Extensions/HostingExtensions.cs +++ b/src/Yavsc/Extensions/HostingExtensions.cs @@ -2,6 +2,7 @@ using System.Globalization; using System.Security.Cryptography.X509Certificates; using Google.Apis.Util.Store; using IdentityServer8; +using IdentityServer8.Hosting; using IdentityServer8.Services; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; @@ -187,8 +188,9 @@ internal static class HostingExtensions .AddInMemoryClients(Config.Clients) .AddInMemoryApiScopes(Config.ApiScopes) .AddAspNetIdentity() + .AddJwtBearerClientAuthentication() ; - services.AddScoped(); + //services.AddScoped(); if (builder.Environment.IsDevelopment()) { @@ -207,7 +209,15 @@ internal static class HostingExtensions // TODO .AddServerSideSessionStore() - var authenticationBuilder = services.AddAuthentication(); + + var authenticationBuilder = services.AddAuthentication() + .AddJwtBearer("Bearer", options => + { + options.IncludeErrorDetails=true; + options.Authority = "https://localhost:5001"; + options.TokenValidationParameters = + new() { ValidateAudience = false }; + }); authenticationBuilder.AddGoogle(options => { @@ -251,13 +261,7 @@ internal static class HostingExtensions }; }); - services.AddCors(options => - { - options.AddPolicy("CorsPolicy", builder => - { - _ = builder.WithOrigins("*"); - }); - }); + // Add the system clock service @@ -313,10 +317,10 @@ internal static class HostingExtensions services.AddAuthorization(options => { options.AddPolicy("ApiScope", policy => - { - policy.RequireAuthenticatedUser() - .RequireClaim("scope", "scope2"); - }); + { + policy.RequireAuthenticatedUser() + .RequireClaim("scope", "scope2"); + }); options.AddPolicy("Performer", policy => { policy @@ -334,11 +338,29 @@ internal static class HostingExtensions // options.AddPolicy("BuildingEntry", policy => policy.Requirements.Add(new OfficeEntryRequirement())); options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser()); options.AddPolicy("IsTheAuthor", policy => policy.Requirements.Add(new EditPermission())); + }) + .AddCors(options => + { + options.AddPolicy("CorsPolicy", builder => + { + _ = builder.WithOrigins("*") + .AllowAnyHeader() + .AllowAnyMethod(); + }); + + options.AddPolicy("default", policy => + { + policy.WithOrigins("https://localhost:5003") + .AllowAnyHeader() + .AllowAnyMethod(); + }); }); services.AddSingleton(); +// accepts any access token issued by identity server + return builder.Build(); } public static WebApplication ConfigurePipeline(this WebApplication app) @@ -357,9 +379,6 @@ internal static class HostingExtensions app.UseRouting(); app.UseIdentityServer(); app.UseAuthorization(); - app.MapGet("/api/me", (HttpContext context) => - new JsonResult(context?.User?.Claims.Select(c => new { c.Type, c.Value })) - ).RequireAuthorization("ApiScope"); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); diff --git a/src/Yavsc/Services/ProfileService.cs b/src/Yavsc/Services/ProfileService.cs index d58e982d..e6160744 100644 --- a/src/Yavsc/Services/ProfileService.cs +++ b/src/Yavsc/Services/ProfileService.cs @@ -8,11 +8,11 @@ using Yavsc.Models; namespace Yavsc.Services { - public class ProfileService : IProfileService + public class ProfileService : DefaultProfileService, IProfileService { private readonly UserManager _userManager; public ProfileService( - UserManager userManager) + UserManager userManager, ILogger logger) : base(logger) { _userManager = userManager; } @@ -21,50 +21,56 @@ namespace Yavsc.Services ProfileDataRequestContext context, ApplicationUser user) { + var requestedApiResources = context.RequestedResources.Resources.ApiResources.Select( + r => r.Name + ).ToArray(); + var requestedApiScopes = context.RequestedResources.Resources.ApiScopes.Select( + s => s.Name + ).ToArray(); - var allowedScopes = context.Client.AllowedScopes - .Where(s => s != JwtClaimTypes.Subject) + var requestedScopes = context.Client.AllowedScopes + .Where(s => s != JwtClaimTypes.Subject + && requestedApiScopes.Contains(s)) .ToList(); - if (allowedScopes.Contains("profile")) + + if (context.RequestedClaimTypes.Contains("profile")) + if (requestedScopes.Contains("profile")) { - allowedScopes.Remove("profile"); - allowedScopes.Add(JwtClaimTypes.Name); - allowedScopes.Add(JwtClaimTypes.FamilyName); - allowedScopes.Add(JwtClaimTypes.Email); - allowedScopes.Add(JwtClaimTypes.PreferredUserName); - allowedScopes.Add("http://schemas.microsoft.com/ws/2008/06/identity/claims/role"); + requestedScopes.Remove("profile"); + requestedScopes.Add(JwtClaimTypes.Name); + requestedScopes.Add(JwtClaimTypes.FamilyName); + requestedScopes.Add(JwtClaimTypes.Email); + requestedScopes.Add(JwtClaimTypes.PreferredUserName); + requestedScopes.Add(JwtClaimTypes.Role); } var claims = new List { new Claim(JwtClaimTypes.Subject,user.Id.ToString()), }; - - foreach (var subClaim in context.Subject.Claims) + if (requestedScopes.Contains(JwtClaimTypes.Name)|| + requestedScopes.Contains(JwtClaimTypes.FamilyName)) { - if (allowedScopes.Contains(subClaim.Type)) - claims.Add(subClaim); + claims.Add(new Claim(JwtClaimTypes.Name, user.FullName)); } - AddClaims(allowedScopes, claims, JwtClaimTypes.Email, user.Email); - AddClaims(allowedScopes, claims, JwtClaimTypes.PreferredUserName, user.FullName); - - foreach (var scope in context.Client.AllowedScopes) + if (requestedScopes.Contains(JwtClaimTypes.PreferredUserName) ) { - claims.Add(new Claim("scope", scope)); + claims.Add(new Claim(JwtClaimTypes.Name, user.UserName)); + } + if (requestedScopes.Contains(JwtClaimTypes.Email)) + claims.Add(new Claim(JwtClaimTypes.Email, user.Email)); + + if (requestedScopes.Contains(JwtClaimTypes.Role)) + { + var roles = await this._userManager.GetRolesAsync(user); + if (roles.Count()>0) + { + claims.Add(new Claim(JwtClaimTypes.Role,String.Join(" ",roles))); + } } - return claims; } - private static void AddClaims(List allowedScopes, List claims, - string claimType, string claimValue - ) - { - if (allowedScopes.Contains(claimType)) - if (!claims.Any(c => c.Type == claimType)) - claims.Add(new Claim(claimType, claimValue)); - } - public async Task GetProfileDataAsync(ProfileDataRequestContext context) { var subjectId = context.Subject.Claims.FirstOrDefault(c => c.Type == "sub").Value; @@ -72,7 +78,6 @@ namespace Yavsc.Services context.IssuedClaims = await GetClaimsFromUserAsync(context, user); } - public async Task IsActiveAsync(IsActiveContext context) { var subjectId = context.Subject.Claims.FirstOrDefault(c => c.Type == "sub").Value; diff --git a/src/sampleWebAsWebApiClient/Controllers/HomeController.cs b/src/sampleWebAsWebApiClient/Controllers/HomeController.cs index de6b72f3..8dcd1bab 100755 --- a/src/sampleWebAsWebApiClient/Controllers/HomeController.cs +++ b/src/sampleWebAsWebApiClient/Controllers/HomeController.cs @@ -54,14 +54,31 @@ namespace testOauthClient.Controllers public async Task GetUserInfo(CancellationToken cancellationToken) { var accessToken = await HttpContext.GetTokenAsync("access_token"); - var client = new HttpClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); + var client = new HttpClient(new HttpClientHandler(){ AllowAutoRedirect=false }); client.DefaultRequestHeaders.Add("Accept", "application/json"); - var content = await client.GetStringAsync("https://localhost:5001/api/account/me"); - var obj = JsonSerializer.Deserialize(content); + client.SetBearerToken(accessToken); + var content = await client.GetAsync("https://localhost:5001/api/account/me"); + content.EnsureSuccessStatusCode(); + var json = await content.Content.ReadAsStreamAsync(); + var obj = JsonSerializer.Deserialize(json); return View("UserInfo", obj.ToString()); } + [HttpPost] + public async Task GetApiCall(CancellationToken cancellationToken) + { + var accessToken = await HttpContext.GetTokenAsync("access_token"); + var client = new HttpClient(new HttpClientHandler(){ AllowAutoRedirect=false }); + client.DefaultRequestHeaders.Add("Accept", "application/json"); + client.SetBearerToken(accessToken); + var content = await client.GetAsync("https://localhost:6001/identity"); + content.EnsureSuccessStatusCode(); + var json = await content.Content.ReadAsStreamAsync(); + var obj = JsonSerializer.Deserialize(json); + return View("UserInfo", obj.ToString()); + } + + public IActionResult About() { ViewData["Message"] = "Your application description page."; diff --git a/src/sampleWebAsWebApiClient/Properties/launchSettings.json b/src/sampleWebAsWebApiClient/Properties/launchSettings.json index 9add1af2..e12a4be3 100644 --- a/src/sampleWebAsWebApiClient/Properties/launchSettings.json +++ b/src/sampleWebAsWebApiClient/Properties/launchSettings.json @@ -4,8 +4,8 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:16967", - "sslPort": 44387 + "applicationUrl": "http://localhost:5002", + "sslPort": 5003 } }, "profiles": { @@ -13,7 +13,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "applicationUrl": "http://localhost:5164", + "applicationUrl": "http://localhost:5002", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -22,7 +22,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "applicationUrl": "https://localhost:7055;http://localhost:5164", + "applicationUrl": "https://localhost:5003;http://localhost:5002", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/sampleWebAsWebApiClient/Startup.cs b/src/sampleWebAsWebApiClient/Startup.cs index 176c7ea6..161e5a20 100644 --- a/src/sampleWebAsWebApiClient/Startup.cs +++ b/src/sampleWebAsWebApiClient/Startup.cs @@ -23,7 +23,6 @@ public class Startup .AddOpenIdConnect("Yavsc", options => { options.Authority = "https://localhost:5001"; - options.ClientId = "mvc"; options.ClientSecret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0"; options.ResponseType = "code"; @@ -32,6 +31,8 @@ public class Startup options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("email"); + options.Scope.Add("offline_access"); + options.Scope.Add("scope2"); options.GetClaimsFromUserInfoEndpoint = true; options.SaveTokens = true; @@ -44,6 +45,7 @@ public class Startup NameClaimType = "name", RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" }; + }); } diff --git a/src/sampleWebAsWebApiClient/Views/Home/Index.cshtml b/src/sampleWebAsWebApiClient/Views/Home/Index.cshtml index ba2d5ffc..7899726a 100755 --- a/src/sampleWebAsWebApiClient/Views/Home/Index.cshtml +++ b/src/sampleWebAsWebApiClient/Views/Home/Index.cshtml @@ -21,6 +21,11 @@
+ +
+ +
+
diff --git a/src/sampleWebAsWebApiClient/sampleWebAsWebApiClient.csproj b/src/sampleWebAsWebApiClient/sampleWebAsWebApiClient.csproj index 0ca278ef..54e29a22 100644 --- a/src/sampleWebAsWebApiClient/sampleWebAsWebApiClient.csproj +++ b/src/sampleWebAsWebApiClient/sampleWebAsWebApiClient.csproj @@ -1,7 +1,8 @@ - + + @@ -10,6 +11,7 @@ PreserveNewest + net8.0 enable diff --git a/yavsc.sln b/yavsc.sln index 6aeb11cb..a110375d 100644 --- a/yavsc.sln +++ b/yavsc.sln @@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yavsc", "src\Yavsc\Yavsc.cs EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sampleWebAsWebApiClient", "src\sampleWebAsWebApiClient\sampleWebAsWebApiClient.csproj", "{38AA74FE-3932-49C3-A382-338E7C861BB1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api", "src\Api\Api.csproj", "{16CCC793-BF57-4E8B-9BB5-76283379BA3F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -40,11 +42,16 @@ Global {38AA74FE-3932-49C3-A382-338E7C861BB1}.Debug|Any CPU.Build.0 = Debug|Any CPU {38AA74FE-3932-49C3-A382-338E7C861BB1}.Release|Any CPU.ActiveCfg = Release|Any CPU {38AA74FE-3932-49C3-A382-338E7C861BB1}.Release|Any CPU.Build.0 = Release|Any CPU + {16CCC793-BF57-4E8B-9BB5-76283379BA3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {16CCC793-BF57-4E8B-9BB5-76283379BA3F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {16CCC793-BF57-4E8B-9BB5-76283379BA3F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {16CCC793-BF57-4E8B-9BB5-76283379BA3F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {5AFB6255-CF1B-4660-BB35-F24C8C75FECE} = {503DDD6B-BE10-4235-9EBD-E9B1FA6067DF} {830F5A71-0192-4288-9F4D-D7849D958970} = {503DDD6B-BE10-4235-9EBD-E9B1FA6067DF} {87DABC88-C38C-45DB-8F72-53B7C95ABC89} = {503DDD6B-BE10-4235-9EBD-E9B1FA6067DF} {38AA74FE-3932-49C3-A382-338E7C861BB1} = {503DDD6B-BE10-4235-9EBD-E9B1FA6067DF} + {16CCC793-BF57-4E8B-9BB5-76283379BA3F} = {503DDD6B-BE10-4235-9EBD-E9B1FA6067DF} EndGlobalSection EndGlobal