mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-20 08:02:50 +08:00
+semver: upgrade to net5.0 (#1390)
* breaking upgrade base build image to net5.0 * add make and build tools to image * fix code broken after net5.0 upgrade * fix warnings * fix tests and line endings * upgrade dotnet test and coverages packages * update circle build image * removed rafty and updated more packages * bring back develop * rename authorisation to authorization
This commit is contained in:
parent
c3a0cf1160
commit
b74a1197a2
@ -1,32 +1,38 @@
|
|||||||
version: 2.1
|
version: 2.1
|
||||||
orbs:
|
orbs:
|
||||||
queue: eddiewebb/queue@1.5.0
|
queue: eddiewebb/queue@1.5.0
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
docker:
|
docker:
|
||||||
- image: mijitt0m/ocelot-build:0.0.1
|
- image: mijitt0m/ocelot-build:0.0.3
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: make build
|
- run: make build
|
||||||
release:
|
release:
|
||||||
docker:
|
docker:
|
||||||
- image: mijitt0m/ocelot-build:0.0.1
|
- image: mijitt0m/ocelot-build:0.0.3
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: make release
|
- run: make release
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
master:
|
master:
|
||||||
jobs:
|
jobs:
|
||||||
- queue/block_workflow:
|
- queue/block_workflow:
|
||||||
time: "20"
|
time: "20"
|
||||||
only-on-branch: master
|
only-on-branch: master
|
||||||
- release:
|
- release:
|
||||||
requires:
|
requires:
|
||||||
- queue/block_workflow
|
- queue/block_workflow
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
only: master
|
only: master
|
||||||
|
develop:
|
||||||
|
jobs:
|
||||||
|
- build:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: develop
|
||||||
pr:
|
pr:
|
||||||
jobs:
|
jobs:
|
||||||
- build:
|
- build:
|
||||||
@ -34,3 +40,4 @@ workflows:
|
|||||||
branches:
|
branches:
|
||||||
ignore:
|
ignore:
|
||||||
- master
|
- master
|
||||||
|
- develop
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
*/*/bin
|
*/*/bin
|
||||||
*/*/obj
|
*/*/obj
|
||||||
|
artifacts/
|
||||||
|
TestResults/
|
45
Makefile
45
Makefile
@ -1,23 +1,24 @@
|
|||||||
NAME ?= ocelot
|
NAME ?= ocelot
|
||||||
|
|
||||||
build:
|
build:
|
||||||
./build.sh
|
./build.sh
|
||||||
|
|
||||||
build_and_run_tests:
|
build_and_run_tests:
|
||||||
./build.sh --target=RunTests
|
./build.sh --target=RunTests
|
||||||
|
|
||||||
release:
|
release:
|
||||||
./build.sh --target=Release
|
./build.sh --target=Release
|
||||||
|
|
||||||
run_acceptance_tests:
|
run_acceptance_tests:
|
||||||
./build.sh --target=RunAcceptanceTests
|
./build.sh --target=RunAcceptanceTests
|
||||||
|
|
||||||
run_benchmarks:
|
run_benchmarks:
|
||||||
./build.sh --target=RunBenchmarkTests
|
./build.sh --target=RunBenchmarkTests
|
||||||
|
|
||||||
run_unit_tests:
|
run_unit_tests:
|
||||||
./build.sh --target=RunUnitTests
|
./build.sh --target=RunUnitTests
|
||||||
|
|
||||||
release_notes:
|
release_notes:
|
||||||
./build.sh --target=ReleaseNotes
|
./build.sh --target=ReleaseNotes
|
||||||
|
|
||||||
|
|
@ -44,8 +44,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Eureka", "s
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Polly", "src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj", "{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Polly", "src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj", "{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Rafty", "src\Ocelot.Provider.Rafty\Ocelot.Provider.Rafty.csproj", "{AC153C67-EF18-47E6-A230-F0D3CF5F0A98}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.Butterfly", "src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj", "{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.Butterfly", "src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj", "{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Kubernetes", "src\Ocelot.Provider.Kubernetes\Ocelot.Provider.Kubernetes.csproj", "{72C8E528-B4F5-45CE-8A06-CD3787364856}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Kubernetes", "src\Ocelot.Provider.Kubernetes\Ocelot.Provider.Kubernetes.csproj", "{72C8E528-B4F5-45CE-8A06-CD3787364856}"
|
||||||
@ -138,10 +136,6 @@ Global
|
|||||||
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Release|Any CPU.Build.0 = Release|Any CPU
|
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@ -210,7 +204,6 @@ Global
|
|||||||
{02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
{02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
||||||
{9BBD3586-145C-4FA0-91C5-9ED58287D753} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
{9BBD3586-145C-4FA0-91C5-9ED58287D753} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
||||||
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
||||||
{AC153C67-EF18-47E6-A230-F0D3CF5F0A98} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
|
||||||
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
||||||
{72C8E528-B4F5-45CE-8A06-CD3787364856} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
{72C8E528-B4F5-45CE-8A06-CD3787364856} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
|
||||||
{ED0B3A09-112B-4BA4-82D6-11569BC7A99B} = {ED066001-BAF7-4117-9884-DF591A56347D}
|
{ED0B3A09-112B-4BA4-82D6-11569BC7A99B} = {ED066001-BAF7-4117-9884-DF591A56347D}
|
||||||
|
@ -30,7 +30,7 @@ A quick list of Ocelot's capabilities for more information see the [documentatio
|
|||||||
* Kubernetes
|
* Kubernetes
|
||||||
* WebSockets
|
* WebSockets
|
||||||
* Authentication
|
* Authentication
|
||||||
* Authorisation
|
* Authorization
|
||||||
* Rate Limiting
|
* Rate Limiting
|
||||||
* Caching
|
* Caching
|
||||||
* Retry policies / QoS
|
* Retry policies / QoS
|
||||||
|
1092
build.cake
1092
build.cake
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,16 @@
|
|||||||
# this is the dockerfile that create the ocelot build container
|
# this is the dockerfile that create the ocelot build container
|
||||||
# build with the docker-build.sh file in this folder
|
# build with the docker-build.sh file in this folder
|
||||||
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-bionic AS build
|
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-bionic AS build
|
||||||
|
|
||||||
RUN apt install gnupg ca-certificates
|
RUN apt install gnupg ca-certificates
|
||||||
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
|
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
|
||||||
RUN echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | tee /etc/apt/sources.list.d/mono-official-stable.list
|
RUN echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | tee /etc/apt/sources.list.d/mono-official-stable.list
|
||||||
RUN apt update
|
RUN apt update
|
||||||
RUN apt-get -y install mono-devel
|
RUN apt-get -y install mono-devel
|
||||||
|
|
||||||
|
RUN wget https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \
|
||||||
|
&& dpkg -i packages-microsoft-prod.deb \
|
||||||
|
&& apt-get update; \
|
||||||
|
apt-get install -y apt-transport-https && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y dotnet-sdk-5.0
|
@ -1,15 +1,15 @@
|
|||||||
# call from ocelot repo root with
|
# call from ocelot repo root with
|
||||||
# docker build --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.build .
|
# docker build --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.build .
|
||||||
FROM mijitt0m/ocelot-build:0.0.1
|
FROM mijitt0m/ocelot-build:0.0.3
|
||||||
|
|
||||||
ARG OCELOT_COVERALLS_TOKEN
|
ARG OCELOT_COVERALLS_TOKEN
|
||||||
|
|
||||||
ENV OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN
|
ENV OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
COPY ./. .
|
COPY ./. .
|
||||||
|
|
||||||
RUN chmod u+x build.sh
|
RUN chmod u+x build.sh
|
||||||
|
|
||||||
RUN make build
|
RUN make build
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
# call from ocelot repo root with
|
# call from ocelot repo root with
|
||||||
# docker build --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN --build-arg OCELOT_GITHUB_API_KEY=$OCELOT_GITHUB_API_KEY --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.release .
|
# docker build --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN --build-arg OCELOT_GITHUB_API_KEY=$OCELOT_GITHUB_API_KEY --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.release .
|
||||||
|
|
||||||
FROM mijitt0m/ocelot-build:0.0.1
|
FROM mijitt0m/ocelot-build:0.0.3
|
||||||
|
|
||||||
ARG OCELOT_COVERALLS_TOKEN
|
ARG OCELOT_COVERALLS_TOKEN
|
||||||
ARG OCELOT_NUTGET_API_KEY
|
ARG OCELOT_NUTGET_API_KEY
|
||||||
ARG OCELOT_GITHUB_API_KEY
|
ARG OCELOT_GITHUB_API_KEY
|
||||||
|
|
||||||
ENV OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN
|
ENV OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN
|
||||||
ENV OCELOT_NUTGET_API_KEY=$OCELOT_NUTGET_API_KEY
|
ENV OCELOT_NUTGET_API_KEY=$OCELOT_NUTGET_API_KEY
|
||||||
ENV OCELOT_GITHUB_API_KEY=$OCELOT_GITHUB_API_KEY
|
ENV OCELOT_GITHUB_API_KEY=$OCELOT_GITHUB_API_KEY
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
COPY ./. .
|
COPY ./. .
|
||||||
|
|
||||||
RUN chmod u+x build.sh
|
RUN chmod u+x build.sh
|
||||||
|
|
||||||
RUN make release
|
RUN make release
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# this script build the ocelot docker file
|
# this script build the ocelot docker file
|
||||||
docker build -t mijitt0m/ocelot-build -f Dockerfile.base .
|
version=0.0.3
|
||||||
echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin
|
docker build -t mijitt0m/ocelot-build -f Dockerfile.base .
|
||||||
docker tag mijitt0m/ocelot-build mijitt0m/ocelot-build:0.0.1
|
echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin
|
||||||
docker push mijitt0m/ocelot-build:latest
|
docker tag mijitt0m/ocelot-build mijitt0m/ocelot-build:$version
|
||||||
docker push mijitt0m/ocelot-build:0.0.1
|
docker push mijitt0m/ocelot-build:latest
|
||||||
|
docker push mijitt0m/ocelot-build:$version
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Release process
|
Release process
|
||||||
===============
|
===============
|
||||||
|
|
||||||
* The release process works best with GitHubFlow branching.
|
* The release process works best with Git Flow branching.
|
||||||
* Contributors can do whatever they want on PRs and merges to master will result in packages being released to GitHub and NuGet.
|
* Contributors can do whatever they want on PRs and merges to master will result in packages being released to GitHub and NuGet.
|
||||||
|
|
||||||
Ocelot uses the following process to accept work into the NuGet packages.
|
Ocelot uses the following process to accept work into the NuGet packages.
|
||||||
@ -10,7 +10,7 @@ Ocelot uses the following process to accept work into the NuGet packages.
|
|||||||
|
|
||||||
2. User creates a fork and branches from this (unless a member of core team, they can just create a branch on the main repo) e.g. feat/xxx, fix/xxx etc. It doesn't really matter what the xxx is. It might make sense to use the issue number and maybe a short description. I don't care as long as it has (feat, fix, refactor)/xxx :)
|
2. User creates a fork and branches from this (unless a member of core team, they can just create a branch on the main repo) e.g. feat/xxx, fix/xxx etc. It doesn't really matter what the xxx is. It might make sense to use the issue number and maybe a short description. I don't care as long as it has (feat, fix, refactor)/xxx :)
|
||||||
|
|
||||||
3. When the user is happy with their work they can create a pull request against master in GitHub with their changes. The user must follow the `SemVer <https://semver.org/>`_ support for this is provided by `GitVersion <https://gitversion.readthedocs.io/en/latest/>`_. So if you need to make breaking changes please make sure you use the correct commit message so GitVersion uses the correct semver tags. Do not manually tag the Ocelot repo this will break things.
|
3. When the user is happy with their work they can create a pull request against develop in GitHub with their changes. The user must follow the `SemVer <https://semver.org/>`_ support for this is provided by `GitVersion <https://gitversion.readthedocs.io/en/latest/>`_. So if you need to make breaking changes please make sure you use the correct commit message so GitVersion uses the correct semver tags. Do not manually tag the Ocelot repo this will break things.
|
||||||
|
|
||||||
4. The Ocelot team will review the PR and if all is good merge it, else they will suggest feedback that the user will need to act on. In order to speed up getting a PR the user should think about the following.
|
4. The Ocelot team will review the PR and if all is good merge it, else they will suggest feedback that the user will need to act on. In order to speed up getting a PR the user should think about the following.
|
||||||
- Have I covered all my changes with tests at unit and acceptance level?
|
- Have I covered all my changes with tests at unit and acceptance level?
|
||||||
@ -23,9 +23,11 @@ In order for a PR to be merged the following must have occured.
|
|||||||
- Build must not have slowed down dramatically.
|
- Build must not have slowed down dramatically.
|
||||||
- The main Ocelot package must not have taken on any non MS dependencies.
|
- The main Ocelot package must not have taken on any non MS dependencies.
|
||||||
|
|
||||||
5. After the PR is merged to master the release process will begin which builds the code, versions it, pushes artifacts to GitHub and NuGet packages to NuGet.
|
5. After the PR is merged to develop the Ocelot NuGet packages will not be updated until a release is created.
|
||||||
|
|
||||||
6. The final step is to go back to GitHub and close any issues that are now fixed. You should see something like this in`GitHub <https://github.com/ThreeMammals/Ocelot/releases/tag/13.0.0>`_ and this in `NuGet <https://www.nuget.org/packages/Ocelot/13.0.0>`_.
|
6. When enough work has been completed to justify a new release. Develop will be merged into master the release process will begin which builds the code, versions it, pushes artifacts to GitHub and NuGet packages to NuGet.
|
||||||
|
|
||||||
|
7. The final step is to go back to GitHub and close any issues that are now fixed. You should see something like this in`GitHub <https://github.com/ThreeMammals/Ocelot/releases/tag/13.0.0>`_ and this in `NuGet <https://www.nuget.org/packages/Ocelot/13.0.0>`_.
|
||||||
|
|
||||||
Notes
|
Notes
|
||||||
-----
|
-----
|
||||||
|
@ -12,19 +12,22 @@ This will bring down everything needed by the admin API.
|
|||||||
Providing your own IdentityServer
|
Providing your own IdentityServer
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
All you need to do to hook into your own IdentityServer is add the following to your ConfigureServices method.
|
All you need to do to hook into your own IdentityServer is add the following to your ConfigureServices method.
|
||||||
|
|
||||||
.. code-block:: csharp
|
.. code-block:: csharp
|
||||||
|
|
||||||
public virtual void ConfigureServices(IServiceCollection services)
|
public virtual void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
Action<IdentityServerAuthenticationOptions> options = o => {
|
Action<JwtBearerOptions> options = o =>
|
||||||
// o.Authority = ;
|
{
|
||||||
// o.ApiName = ;
|
o.Authority = identityServerRootUrl;
|
||||||
// etc....
|
o.RequireHttpsMetadata = false;
|
||||||
|
o.TokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateAudience = false,
|
||||||
};
|
};
|
||||||
|
// etc....
|
||||||
|
};
|
||||||
|
|
||||||
services
|
services
|
||||||
.AddOcelot()
|
.AddOcelot()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Authentication
|
Authentication
|
||||||
==============
|
==============
|
||||||
|
|
||||||
In order to authenticate Routes and subsequently use any of Ocelot's claims based features such as authorisation or modifying the request with values from the token. Users must register authentication services in their Startup.cs as usual but they provide a scheme (authentication provider key) with each registration e.g.
|
In order to authenticate Routes and subsequently use any of Ocelot's claims based features such as authorization or modifying the request with values from the token. Users must register authentication services in their Startup.cs as usual but they provide a scheme (authentication provider key) with each registration e.g.
|
||||||
|
|
||||||
.. code-block:: csharp
|
.. code-block:: csharp
|
||||||
|
|
||||||
@ -97,16 +97,14 @@ In order to use IdentityServer bearer tokens, register your IdentityServer servi
|
|||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
var authenticationProviderKey = "TestKey";
|
var authenticationProviderKey = "TestKey";
|
||||||
Action<IdentityServerAuthenticationOptions> options = o =>
|
Action<JwtBearerOptions> options = o =>
|
||||||
{
|
{
|
||||||
o.Authority = "https://whereyouridentityserverlives.com";
|
o.Authority = "https://whereyouridentityserverlives.com";
|
||||||
o.ApiName = "api";
|
// etc
|
||||||
o.SupportedTokens = SupportedTokens.Both;
|
|
||||||
o.ApiSecret = "secret";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
services.AddAuthentication()
|
services.AddAuthentication()
|
||||||
.AddIdentityServerAuthentication(authenticationProviderKey, options);
|
.AddJwtBearer(authenticationProviderKey, options);
|
||||||
|
|
||||||
services.AddOcelot();
|
services.AddOcelot();
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Authorisation
|
Authorization
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Ocelot supports claims based authorisation which is run post authentication. This means if you have a route you want to authorise you can add the following to you Route configuration.
|
Ocelot supports claims based authorization which is run post authentication. This means if you have a route you want to authorize you can add the following to you Route configuration.
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ Ocelot supports claims based authorisation which is run post authentication. Thi
|
|||||||
"UserType": "registered"
|
"UserType": "registered"
|
||||||
}
|
}
|
||||||
|
|
||||||
In this example when the authorisation middleware is called Ocelot will check to seeif the user has the claim type UserType and if the value of that claim is registered. If it isn't then the user will not be authorised and the response will be 403 forbidden.
|
In this example when the authorization middleware is called Ocelot will check to seeif the user has the claim type UserType and if the value of that claim is registered. If it isn't then the user will not be authorized and the response will be 403 forbidden.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -3,7 +3,7 @@ Claims Transformation
|
|||||||
|
|
||||||
Ocelot allows the user to access claims and transform them into headers, query string parameters, other claims and change downstream paths. This is only available once a user has been authenticated.
|
Ocelot allows the user to access claims and transform them into headers, query string parameters, other claims and change downstream paths. This is only available once a user has been authenticated.
|
||||||
|
|
||||||
After the user is authenticated we run the claims to claims transformation middleware. This allows the user to transform claims before the authorisation middleware is called. After the user is authorised first we call the claims to headers middleware, thenthe claims to query string parameters middleware, and Finally the claims to downstream pathmiddleware.
|
After the user is authenticated we run the claims to claims transformation middleware. This allows the user to transform claims before the authorization middleware is called. After the user is authorized first we call the claims to headers middleware, thenthe claims to query string parameters middleware, and Finally the claims to downstream pathmiddleware.
|
||||||
|
|
||||||
The syntax for performing the transforms is the same for each process. In the Route configuration a json dictionary is added with a specific name either AddClaimsToRequest, AddHeadersToRequest, AddQueriesToRequest, or ChangeDownstreamPathTemplate.
|
The syntax for performing the transforms is the same for each process. In the Route configuration a json dictionary is added with a specific name either AddClaimsToRequest, AddHeadersToRequest, AddQueriesToRequest, or ChangeDownstreamPathTemplate.
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ Http Error Status Codes
|
|||||||
|
|
||||||
Ocelot will return HTTP status error codes based on internal logic in certain siturations:
|
Ocelot will return HTTP status error codes based on internal logic in certain siturations:
|
||||||
- 401 if the authentication middleware runs and the user is not authenticated.
|
- 401 if the authentication middleware runs and the user is not authenticated.
|
||||||
- 403 if the authorisation middleware runs and the user is unauthenticated, claim value not authroised, scope not authorised, user doesnt have required claim or cannot find claim.
|
- 403 if the authorization middleware runs and the user is unauthenticated, claim value not authroised, scope not authorized, user doesnt have required claim or cannot find claim.
|
||||||
- 503 if the downstream request times out.
|
- 503 if the downstream request times out.
|
||||||
- 499 if the request is cancelled by the client.
|
- 499 if the request is cancelled by the client.
|
||||||
- 404 if unable to find a downstream route.
|
- 404 if unable to find a downstream route.
|
||||||
|
@ -31,9 +31,9 @@ The user can set functions against the following.
|
|||||||
|
|
||||||
* AuthenticationMiddleware - This overrides Ocelots authentication middleware.
|
* AuthenticationMiddleware - This overrides Ocelots authentication middleware.
|
||||||
|
|
||||||
* PreAuthorisationMiddleware - This allows the user to run pre authorisation logic and then call Ocelot's authorisation middleware.
|
* PreAuthorizationMiddleware - This allows the user to run pre authorization logic and then call Ocelot's authorization middleware.
|
||||||
|
|
||||||
* AuthorisationMiddleware - This overrides Ocelots authorisation middleware.
|
* AuthorizationMiddleware - This overrides Ocelots authorization middleware.
|
||||||
|
|
||||||
* PreQueryStringBuilderMiddleware - This allows the user to manipulate the query string on the http request before it is passed to Ocelots request creator.
|
* PreQueryStringBuilderMiddleware - This allows the user to manipulate the query string on the http request before it is passed to Ocelots request creator.
|
||||||
|
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
Raft (EXPERIMENTAL DO NOT USE IN PRODUCTION)
|
|
||||||
============================================
|
|
||||||
|
|
||||||
Ocelot has recently integrated `Rafty <https://github.com/ThreeMammals/Rafty>`_ which is an implementation of Raft that I have also been working on over the last year. This project is very experimental so please do not use this feature of Ocelot in production until I think it's OK.
|
|
||||||
|
|
||||||
Raft is a distributed concensus algorithm that allows a cluster of servers (Ocelots) to maintain local state without having a centralised database for storing state (e.g. SQL Server).
|
|
||||||
|
|
||||||
To get Raft support you must first install the Ocelot Rafty package.
|
|
||||||
|
|
||||||
``Install-Package Ocelot.Provider.Rafty``
|
|
||||||
|
|
||||||
Then you must make the following changes to your Startup.cs / Program.cs.
|
|
||||||
|
|
||||||
.. code-block:: csharp
|
|
||||||
|
|
||||||
public virtual void ConfigureServices(IServiceCollection services)
|
|
||||||
{
|
|
||||||
services
|
|
||||||
.AddOcelot()
|
|
||||||
.AddAdministration("/administration", "secret")
|
|
||||||
.AddRafty();
|
|
||||||
}
|
|
||||||
|
|
||||||
In addition to this you must add a file called peers.json to your main project and it will look as follows
|
|
||||||
|
|
||||||
.. code-block:: json
|
|
||||||
|
|
||||||
{
|
|
||||||
"Peers": [{
|
|
||||||
"HostAndPort": "http://localhost:5000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"HostAndPort": "http://localhost:5002"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"HostAndPort": "http://localhost:5003"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"HostAndPort": "http://localhost:5004"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"HostAndPort": "http://localhost:5001"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
Each instance of Ocelot must have it's address in the array so that they can communicate using Rafty.
|
|
||||||
|
|
||||||
Once you have made these configuration changes you must deploy and start each instance of Ocelot using the addresses in the peers.json file. The servers should then start communicating with each other! You can test if everything is working by posting a configuration update and checking it has replicated to all servers by getting their configuration.
|
|
@ -44,8 +44,8 @@ Below is an example of the logging when set at Debug level for a normal request.
|
|||||||
requestId: asdf, previousRequestId: no previous request id, message: downstream template is {downstreamRoute.Data.Route.DownstreamPath},
|
requestId: asdf, previousRequestId: no previous request id, message: downstream template is {downstreamRoute.Data.Route.DownstreamPath},
|
||||||
dbug: Ocelot.RateLimit.Middleware.ClientRateLimitMiddleware[0]
|
dbug: Ocelot.RateLimit.Middleware.ClientRateLimitMiddleware[0]
|
||||||
requestId: asdf, previousRequestId: no previous request id, message: EndpointRateLimiting is not enabled for Ocelot.Values.PathTemplate,
|
requestId: asdf, previousRequestId: no previous request id, message: EndpointRateLimiting is not enabled for Ocelot.Values.PathTemplate,
|
||||||
dbug: Ocelot.Authorisation.Middleware.AuthorisationMiddleware[0]
|
dbug: Ocelot.Authorization.Middleware.AuthorizationMiddleware[0]
|
||||||
requestId: 1234, previousRequestId: asdf, message: /posts/{postId} route does not require user to be authorised,
|
requestId: 1234, previousRequestId: asdf, message: /posts/{postId} route does not require user to be authorized,
|
||||||
dbug: Ocelot.DownstreamUrlCreator.Middleware.DownstreamUrlCreatorMiddleware[0]
|
dbug: Ocelot.DownstreamUrlCreator.Middleware.DownstreamUrlCreatorMiddleware[0]
|
||||||
requestId: 1234, previousRequestId: asdf, message: downstream url is {downstreamUrl.Data.Value},
|
requestId: 1234, previousRequestId: asdf, message: downstream url is {downstreamUrl.Data.Value},
|
||||||
dbug: Ocelot.Request.Middleware.HttpRequestBuilderMiddleware[0]
|
dbug: Ocelot.Request.Middleware.HttpRequestBuilderMiddleware[0]
|
||||||
|
@ -102,7 +102,7 @@ Unfortunately a lot of Ocelot's features are non websocket specific such as head
|
|||||||
9. Claims Transformation
|
9. Claims Transformation
|
||||||
10. Caching
|
10. Caching
|
||||||
11. Authentication - If anyone requests it we might be able to do something with basic authentication.
|
11. Authentication - If anyone requests it we might be able to do something with basic authentication.
|
||||||
12. Authorisation
|
12. Authorization
|
||||||
|
|
||||||
I'm not 100% sure what will happen with this feature when it get's into the wild so please make sure you test thoroughly!
|
I'm not 100% sure what will happen with this feature when it get's into the wild so please make sure you test thoroughly!
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
|
|||||||
features/servicefabric
|
features/servicefabric
|
||||||
features/kubernetes
|
features/kubernetes
|
||||||
features/authentication
|
features/authentication
|
||||||
features/authorisation
|
features/authorization
|
||||||
features/websockets
|
features/websockets
|
||||||
features/administration
|
features/administration
|
||||||
features/ratelimiting
|
features/ratelimiting
|
||||||
@ -41,7 +41,6 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
|
|||||||
features/middlewareinjection
|
features/middlewareinjection
|
||||||
features/loadbalancer
|
features/loadbalancer
|
||||||
features/delegatinghandlers
|
features/delegatinghandlers
|
||||||
features/raft
|
|
||||||
features/errorcodes
|
features/errorcodes
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="wwwroot\" />
|
<Folder Include="wwwroot\" />
|
||||||
@ -10,4 +10,4 @@
|
|||||||
<PackageReference Include="Ocelot.Administration" Version="14.0.9" />
|
<PackageReference Include="Ocelot.Administration" Version="14.0.9" />
|
||||||
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -17,7 +17,6 @@ COPY src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj src/Ocelot.C
|
|||||||
COPY src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj
|
COPY src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj
|
||||||
COPY src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj
|
COPY src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj
|
||||||
COPY src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj
|
COPY src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj
|
||||||
COPY src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj
|
|
||||||
COPY src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj
|
COPY src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj
|
||||||
COPY src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj
|
COPY src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj
|
||||||
COPY test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj
|
COPY test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="ocelot.json;appsettings.json">
|
<None Update="ocelot.json;appsettings.json">
|
||||||
@ -12,6 +12,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Ocelot" Version="14.0.9" />
|
<PackageReference Include="Ocelot" Version="14.0.9" />
|
||||||
<PackageReference Include="GraphQL" Version="2.4.0" />
|
<PackageReference Include="GraphQL" Version="3.1.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,133 +1,138 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore;
|
using Microsoft.AspNetCore;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using Ocelot.DependencyInjection;
|
using Ocelot.DependencyInjection;
|
||||||
using GraphQL.Types;
|
using GraphQL.Types;
|
||||||
using GraphQL;
|
using GraphQL;
|
||||||
using Ocelot.Requester;
|
using Ocelot.Requester;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace OcelotGraphQL
|
||||||
|
{
|
||||||
|
public class Hero
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Query
|
||||||
|
{
|
||||||
|
private readonly List<Hero> _heroes = new List<Hero>
|
||||||
|
{
|
||||||
|
new Hero { Id = 1, Name = "R2-D2" },
|
||||||
|
new Hero { Id = 2, Name = "Batman" },
|
||||||
|
new Hero { Id = 3, Name = "Wonder Woman" },
|
||||||
|
new Hero { Id = 4, Name = "Tom Pallister" }
|
||||||
|
};
|
||||||
|
|
||||||
|
[GraphQLMetadata("hero")]
|
||||||
|
public Hero GetHero(int id)
|
||||||
|
{
|
||||||
|
return _heroes.FirstOrDefault(x => x.Id == id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GraphQlDelegatingHandler : DelegatingHandler
|
||||||
|
{
|
||||||
|
//private readonly ISchema _schema;
|
||||||
|
private readonly IDocumentExecuter _executer;
|
||||||
|
private readonly IDocumentWriter _writer;
|
||||||
|
|
||||||
|
public GraphQlDelegatingHandler(IDocumentExecuter executer, IDocumentWriter writer)
|
||||||
|
{
|
||||||
|
_executer = executer;
|
||||||
|
_writer = writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
//try get query from body, could check http method :)
|
||||||
|
var query = await request.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
//if not body try query string, dont hack like this in real world..
|
||||||
|
if (query.Length == 0)
|
||||||
|
{
|
||||||
|
var decoded = WebUtility.UrlDecode(request.RequestUri.Query);
|
||||||
|
query = decoded.Replace("?query=", "");
|
||||||
|
}
|
||||||
|
|
||||||
namespace OcelotGraphQL
|
var result = await _executer.ExecuteAsync(_ =>
|
||||||
{
|
{
|
||||||
public class Hero
|
_.Query = query;
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Query
|
|
||||||
{
|
|
||||||
private readonly List<Hero> _heroes = new List<Hero>
|
|
||||||
{
|
|
||||||
new Hero { Id = 1, Name = "R2-D2" },
|
|
||||||
new Hero { Id = 2, Name = "Batman" },
|
|
||||||
new Hero { Id = 3, Name = "Wonder Woman" },
|
|
||||||
new Hero { Id = 4, Name = "Tom Pallister" }
|
|
||||||
};
|
|
||||||
|
|
||||||
[GraphQLMetadata("hero")]
|
|
||||||
public Hero GetHero(int id)
|
|
||||||
{
|
|
||||||
return _heroes.FirstOrDefault(x => x.Id == id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GraphQlDelegatingHandler : DelegatingHandler
|
|
||||||
{
|
|
||||||
private readonly ISchema _schema;
|
|
||||||
|
|
||||||
public GraphQlDelegatingHandler(ISchema schema)
|
|
||||||
{
|
|
||||||
_schema = schema;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
//try get query from body, could check http method :)
|
|
||||||
var query = await request.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
//if not body try query string, dont hack like this in real world..
|
|
||||||
if (query.Length == 0)
|
|
||||||
{
|
|
||||||
var decoded = WebUtility.UrlDecode(request.RequestUri.Query);
|
|
||||||
query = decoded.Replace("?query=", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = _schema.Execute(_ =>
|
|
||||||
{
|
|
||||||
_.Query = query;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//maybe check for errors and headers etc in real world?
|
var responseBody = await _writer.WriteToStringAsync(result);
|
||||||
var response = new HttpResponseMessage(HttpStatusCode.OK)
|
|
||||||
{
|
|
||||||
Content = new StringContent(result)
|
|
||||||
};
|
|
||||||
|
|
||||||
//ocelot will treat this like any other http request...
|
//maybe check for errors and headers etc in real world?
|
||||||
return response;
|
var response = new HttpResponseMessage(HttpStatusCode.OK)
|
||||||
}
|
{
|
||||||
}
|
Content = new StringContent(responseBody)
|
||||||
|
};
|
||||||
public class Program
|
|
||||||
{
|
//ocelot will treat this like any other http request...
|
||||||
public static void Main()
|
return response;
|
||||||
{
|
}
|
||||||
var schema = Schema.For(@"
|
}
|
||||||
type Hero {
|
|
||||||
id: Int
|
public class Program
|
||||||
name: String
|
{
|
||||||
}
|
public static void Main()
|
||||||
|
{
|
||||||
type Query {
|
var schema = Schema.For(@"
|
||||||
hero(id: Int): Hero
|
type Hero {
|
||||||
}
|
id: Int
|
||||||
", _ =>
|
name: String
|
||||||
{
|
}
|
||||||
_.Types.Include<Query>();
|
|
||||||
});
|
type Query {
|
||||||
|
hero(id: Int): Hero
|
||||||
new WebHostBuilder()
|
}
|
||||||
.UseKestrel()
|
", _ =>
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
{
|
||||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
_.Types.Include<Query>();
|
||||||
{
|
});
|
||||||
config
|
|
||||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
new WebHostBuilder()
|
||||||
.AddJsonFile("appsettings.json", true, true)
|
.UseKestrel()
|
||||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
.AddJsonFile("ocelot.json", false, false)
|
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||||
.AddEnvironmentVariables();
|
{
|
||||||
})
|
config
|
||||||
.ConfigureServices(s =>
|
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||||
{
|
.AddJsonFile("appsettings.json", true, true)
|
||||||
s.AddSingleton<ISchema>(schema);
|
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||||
s.AddOcelot()
|
.AddJsonFile("ocelot.json", false, false)
|
||||||
.AddDelegatingHandler<GraphQlDelegatingHandler>();
|
.AddEnvironmentVariables();
|
||||||
})
|
})
|
||||||
.ConfigureLogging((hostingContext, logging) =>
|
.ConfigureServices(s =>
|
||||||
{
|
{
|
||||||
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
|
s.AddSingleton<ISchema>(schema);
|
||||||
logging.AddConsole();
|
s.AddOcelot()
|
||||||
})
|
.AddDelegatingHandler<GraphQlDelegatingHandler>();
|
||||||
.UseIISIntegration()
|
})
|
||||||
.Configure(app =>
|
.ConfigureLogging((hostingContext, logging) =>
|
||||||
{
|
{
|
||||||
app.UseOcelot().Wait();
|
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
|
||||||
})
|
logging.AddConsole();
|
||||||
.Build()
|
})
|
||||||
.Run();
|
.UseIISIntegration()
|
||||||
}
|
.Configure(app =>
|
||||||
}
|
{
|
||||||
}
|
app.UseOcelot().Wait();
|
||||||
|
})
|
||||||
|
.Build()
|
||||||
|
.Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Ocelot using GraphQL example
|
# Ocelot using GraphQL example
|
||||||
|
|
||||||
Loads of people keep asking me if Ocelot will every support GraphQL, in my mind Ocelot and GraphQL are two different things that can work together.
|
Loads of people keep asking me if Ocelot will every support GraphQL, in my mind Ocelot and GraphQL are two different things that can work together.
|
||||||
I would not try and implement GraphQL in Ocelot instead I would either have Ocelot in front of GraphQL to handle things like authorisation / authentication or I would
|
I would not try and implement GraphQL in Ocelot instead I would either have Ocelot in front of GraphQL to handle things like authorization / authentication or I would
|
||||||
bring in the awesome [graphql-dotnet](https://github.com/graphql-dotnet/graphql-dotnet) library and use it in a [DelegatingHandler](http://ocelot.readthedocs.io/en/latest/features/delegatinghandlers.html). This way you could have Ocelot and GraphQL without the extra hop to GraphQL. This same is an example of how to do that.
|
bring in the awesome [graphql-dotnet](https://github.com/graphql-dotnet/graphql-dotnet) library and use it in a [DelegatingHandler](http://ocelot.readthedocs.io/en/latest/features/delegatinghandlers.html). This way you could have Ocelot and GraphQL without the extra hop to GraphQL. This same is an example of how to do that.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.9.10" />
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.8" />
|
||||||
<PackageReference Include="Ocelot" Version="14.0.9" />
|
<PackageReference Include="Ocelot" Version="14.0.9" />
|
||||||
<PackageReference Include="Ocelot.Provider.Kubernetes" Version="14.0.9" />
|
<PackageReference Include="Ocelot.Provider.Kubernetes" Version="14.0.9" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.9.10" />
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.8" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Jaeger" Version="0.3.7" />
|
<PackageReference Include="Jaeger" Version="0.4.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.3" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Description>Stateless Web Service for Stateful OcelotApplicationApiGateway App</Description>
|
<Description>Stateless Web Service for Stateful OcelotApplicationApiGateway App</Description>
|
||||||
<Authors> </Authors>
|
<Authors> </Authors>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<AssemblyName>OcelotApplicationApiGateway</AssemblyName>
|
<AssemblyName>OcelotApplicationApiGateway</AssemblyName>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<PackageId>OcelotApplicationApiGateway</PackageId>
|
<PackageId>OcelotApplicationApiGateway</PackageId>
|
||||||
@ -13,10 +13,10 @@
|
|||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.ServiceFabric" Version="6.1.456" />
|
<PackageReference Include="Microsoft.ServiceFabric" Version="7.2.434" />
|
||||||
<PackageReference Include="Microsoft.ServiceFabric.Services" Version="3.0.456" />
|
<PackageReference Include="Microsoft.ServiceFabric.Services" Version="4.2.434" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\..\src\Ocelot\Ocelot.csproj" />
|
<ProjectReference Include="..\..\..\..\src\Ocelot\Ocelot.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -3,16 +3,16 @@
|
|||||||
<Description>Stateless Service Application</Description>
|
<Description>Stateless Service Application</Description>
|
||||||
<Authors> </Authors>
|
<Authors> </Authors>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<AssemblyName>OcelotApplicationService</AssemblyName>
|
<AssemblyName>OcelotApplicationService</AssemblyName>
|
||||||
<PackageId>OcelotApplicationService</PackageId>
|
<PackageId>OcelotApplicationService</PackageId>
|
||||||
<PackageTargetFallback>$(PackageTargetFallback)</PackageTargetFallback>
|
<PackageTargetFallback>$(PackageTargetFallback)</PackageTargetFallback>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.ServiceFabric" Version="6.1.456" />
|
<PackageReference Include="Microsoft.ServiceFabric" Version="7.2.434" />
|
||||||
<PackageReference Include="Microsoft.ServiceFabric.Services" Version="3.0.456" />
|
<PackageReference Include="Microsoft.ServiceFabric.Services" Version="4.2.434" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.ServiceFabric.AspNetCore.Kestrel" Version="3.0.456" />
|
<PackageReference Include="Microsoft.ServiceFabric.AspNetCore.Kestrel" Version="4.2.434" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,39 +1,39 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<NoPackageAnalysis>true</NoPackageAnalysis>
|
<NoPackageAnalysis>true</NoPackageAnalysis>
|
||||||
<Description>Provides Ocelot extensions to use the administration API and IdentityService dependencies that come with it</Description>
|
<Description>Provides Ocelot extensions to use the administration API and IdentityService dependencies that come with it</Description>
|
||||||
<AssemblyTitle>Ocelot.Administration</AssemblyTitle>
|
<AssemblyTitle>Ocelot.Administration</AssemblyTitle>
|
||||||
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
||||||
<AssemblyName>Ocelot.Administration</AssemblyName>
|
<AssemblyName>Ocelot.Administration</AssemblyName>
|
||||||
<PackageId>Ocelot.Administration</PackageId>
|
<PackageId>Ocelot.Administration</PackageId>
|
||||||
<PackageTags>API Gateway;.NET core</PackageTags>
|
<PackageTags>API Gateway;.NET core</PackageTags>
|
||||||
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Administration</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Administration</PackageProjectUrl>
|
||||||
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Administration</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Administration</PackageProjectUrl>
|
||||||
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
|
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
|
||||||
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
|
||||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||||
<Authors>Tom Pallister</Authors>
|
<Authors>Tom Pallister</Authors>
|
||||||
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<DebugSymbols>True</DebugSymbols>
|
<DebugSymbols>True</DebugSymbols>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
|
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
|
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
|
||||||
<PackageReference Include="IdentityServer4" Version="3.1.1" />
|
<PackageReference Include="IdentityServer4" Version="4.1.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using Ocelot.DependencyInjection;
|
using Ocelot.DependencyInjection;
|
||||||
using IdentityServer4.AccessTokenValidation;
|
using IdentityServer4.AccessTokenValidation;
|
||||||
using IdentityServer4.Models;
|
using IdentityServer4.Models;
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
@ -10,6 +9,9 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
|
||||||
namespace Ocelot.Administration
|
namespace Ocelot.Administration
|
||||||
{
|
{
|
||||||
@ -18,6 +20,7 @@ namespace Ocelot.Administration
|
|||||||
public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder builder, string path, string secret)
|
public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder builder, string path, string secret)
|
||||||
{
|
{
|
||||||
var administrationPath = new AdministrationPath(path);
|
var administrationPath = new AdministrationPath(path);
|
||||||
|
|
||||||
builder.Services.AddSingleton<OcelotMiddlewareConfigurationDelegate>(IdentityServerMiddlewareConfigurationProvider.Get);
|
builder.Services.AddSingleton<OcelotMiddlewareConfigurationDelegate>(IdentityServerMiddlewareConfigurationProvider.Get);
|
||||||
|
|
||||||
//add identity server for admin area
|
//add identity server for admin area
|
||||||
@ -32,7 +35,7 @@ namespace Ocelot.Administration
|
|||||||
return new OcelotAdministrationBuilder(builder.Services, builder.Configuration);
|
return new OcelotAdministrationBuilder(builder.Services, builder.Configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder builder, string path, Action<IdentityServerAuthenticationOptions> configureOptions)
|
public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder builder, string path, Action<JwtBearerOptions> configureOptions)
|
||||||
{
|
{
|
||||||
var administrationPath = new AdministrationPath(path);
|
var administrationPath = new AdministrationPath(path);
|
||||||
builder.Services.AddSingleton<OcelotMiddlewareConfigurationDelegate>(IdentityServerMiddlewareConfigurationProvider.Get);
|
builder.Services.AddSingleton<OcelotMiddlewareConfigurationDelegate>(IdentityServerMiddlewareConfigurationProvider.Get);
|
||||||
@ -46,11 +49,11 @@ namespace Ocelot.Administration
|
|||||||
return new OcelotAdministrationBuilder(builder.Services, builder.Configuration);
|
return new OcelotAdministrationBuilder(builder.Services, builder.Configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddIdentityServer(Action<IdentityServerAuthenticationOptions> configOptions, IOcelotBuilder builder)
|
private static void AddIdentityServer(Action<JwtBearerOptions> configOptions, IOcelotBuilder builder)
|
||||||
{
|
{
|
||||||
builder.Services
|
builder.Services
|
||||||
.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
|
.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
|
||||||
.AddIdentityServerAuthentication(configOptions);
|
.AddJwtBearer("Bearer", configOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath, IOcelotBuilder builder, IConfiguration configuration)
|
private static void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath, IOcelotBuilder builder, IConfiguration configuration)
|
||||||
@ -60,7 +63,9 @@ namespace Ocelot.Administration
|
|||||||
.AddIdentityServer(o =>
|
.AddIdentityServer(o =>
|
||||||
{
|
{
|
||||||
o.IssuerUri = "Ocelot";
|
o.IssuerUri = "Ocelot";
|
||||||
|
o.EmitStaticAudienceClaim = true;
|
||||||
})
|
})
|
||||||
|
.AddInMemoryApiScopes(ApiScopes(identityServerConfiguration))
|
||||||
.AddInMemoryApiResources(Resources(identityServerConfiguration))
|
.AddInMemoryApiResources(Resources(identityServerConfiguration))
|
||||||
.AddInMemoryClients(Client(identityServerConfiguration));
|
.AddInMemoryClients(Client(identityServerConfiguration));
|
||||||
|
|
||||||
@ -68,14 +73,17 @@ namespace Ocelot.Administration
|
|||||||
var baseSchemeUrlAndPort = urlFinder.Find();
|
var baseSchemeUrlAndPort = urlFinder.Find();
|
||||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
|
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
|
||||||
|
|
||||||
builder.Services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
|
builder.Services
|
||||||
.AddIdentityServerAuthentication(o =>
|
.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
|
||||||
|
.AddJwtBearer("Bearer", options =>
|
||||||
{
|
{
|
||||||
o.Authority = baseSchemeUrlAndPort + adminPath.Path;
|
options.Authority = baseSchemeUrlAndPort + adminPath.Path;
|
||||||
o.ApiName = identityServerConfiguration.ApiName;
|
options.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
|
||||||
o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
|
|
||||||
o.SupportedTokens = SupportedTokens.Both;
|
options.TokenValidationParameters = new TokenValidationParameters
|
||||||
o.ApiSecret = identityServerConfiguration.ApiSecret;
|
{
|
||||||
|
ValidateAudience = false,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
//todo - refactor naming..
|
//todo - refactor naming..
|
||||||
@ -91,6 +99,11 @@ namespace Ocelot.Administration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<ApiScope> ApiScopes(IIdentityServerConfiguration identityServerConfiguration)
|
||||||
|
{
|
||||||
|
return identityServerConfiguration.AllowedScopes.Select(s => new ApiScope(s));
|
||||||
|
}
|
||||||
|
|
||||||
private static List<ApiResource> Resources(IIdentityServerConfiguration identityServerConfiguration)
|
private static List<ApiResource> Resources(IIdentityServerConfiguration identityServerConfiguration)
|
||||||
{
|
{
|
||||||
return new List<ApiResource>
|
return new List<ApiResource>
|
||||||
@ -101,9 +114,9 @@ namespace Ocelot.Administration
|
|||||||
{
|
{
|
||||||
new Secret
|
new Secret
|
||||||
{
|
{
|
||||||
Value = identityServerConfiguration.ApiSecret.Sha256()
|
Value = identityServerConfiguration.ApiSecret.Sha256(),
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -117,8 +130,8 @@ namespace Ocelot.Administration
|
|||||||
ClientId = identityServerConfiguration.ApiName,
|
ClientId = identityServerConfiguration.ApiName,
|
||||||
AllowedGrantTypes = GrantTypes.ClientCredentials,
|
AllowedGrantTypes = GrantTypes.ClientCredentials,
|
||||||
ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())},
|
ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())},
|
||||||
AllowedScopes = { identityServerConfiguration.ApiName }
|
AllowedScopes = identityServerConfiguration.AllowedScopes,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,39 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<NoPackageAnalysis>true</NoPackageAnalysis>
|
<NoPackageAnalysis>true</NoPackageAnalysis>
|
||||||
<Description>Provides Ocelot extensions to use CacheManager.Net</Description>
|
<Description>Provides Ocelot extensions to use CacheManager.Net</Description>
|
||||||
<AssemblyTitle>Ocelot.Cache.CacheManager</AssemblyTitle>
|
<AssemblyTitle>Ocelot.Cache.CacheManager</AssemblyTitle>
|
||||||
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
||||||
<AssemblyName>Ocelot.Cache.CacheManager</AssemblyName>
|
<AssemblyName>Ocelot.Cache.CacheManager</AssemblyName>
|
||||||
<PackageId>Ocelot.Cache.CacheManager</PackageId>
|
<PackageId>Ocelot.Cache.CacheManager</PackageId>
|
||||||
<PackageTags>API Gateway;.NET core</PackageTags>
|
<PackageTags>API Gateway;.NET core</PackageTags>
|
||||||
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Cache.CacheManager</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Cache.CacheManager</PackageProjectUrl>
|
||||||
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Cache.CacheManager</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Cache.CacheManager</PackageProjectUrl>
|
||||||
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
|
||||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||||
<Authors>Tom Pallister</Authors>
|
<Authors>Tom Pallister</Authors>
|
||||||
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<DebugSymbols>True</DebugSymbols>
|
<DebugSymbols>True</DebugSymbols>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
|
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="CacheManager.Core" Version="2.0.0-beta-1629" />
|
<PackageReference Include="CacheManager.Core" Version="2.0.0-beta-1629" />
|
||||||
<PackageReference Include="CacheManager.Microsoft.Extensions.Configuration" Version="2.0.0-beta-1629" />
|
<PackageReference Include="CacheManager.Microsoft.Extensions.Configuration" Version="2.0.0-beta-1629" />
|
||||||
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="2.0.0-beta-1629" />
|
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="2.0.0-beta-1629" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,38 +1,38 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<NoPackageAnalysis>true</NoPackageAnalysis>
|
<NoPackageAnalysis>true</NoPackageAnalysis>
|
||||||
<Description>Provides Ocelot extensions to use Consul</Description>
|
<Description>Provides Ocelot extensions to use Consul</Description>
|
||||||
<AssemblyTitle>Ocelot.Provider.Consul</AssemblyTitle>
|
<AssemblyTitle>Ocelot.Provider.Consul</AssemblyTitle>
|
||||||
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
||||||
<AssemblyName>Ocelot.Provider.Consul</AssemblyName>
|
<AssemblyName>Ocelot.Provider.Consul</AssemblyName>
|
||||||
<PackageId>Ocelot.Provider.Consul</PackageId>
|
<PackageId>Ocelot.Provider.Consul</PackageId>
|
||||||
<PackageTags>API Gateway;.NET core</PackageTags>
|
<PackageTags>API Gateway;.NET core</PackageTags>
|
||||||
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Consul</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Consul</PackageProjectUrl>
|
||||||
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Consul</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Consul</PackageProjectUrl>
|
||||||
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
|
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
|
||||||
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
|
||||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||||
<Authors>Tom Pallister</Authors>
|
<Authors>Tom Pallister</Authors>
|
||||||
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<DebugSymbols>True</DebugSymbols>
|
<DebugSymbols>True</DebugSymbols>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
|
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Consul" Version="0.7.2.6" />
|
<PackageReference Include="Consul" Version="1.6.1.1" />
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
namespace Ocelot.Provider.Eureka
|
namespace Ocelot.Provider.Eureka
|
||||||
{
|
{
|
||||||
using ServiceDiscovery.Providers;
|
using Ocelot.ServiceDiscovery.Providers;
|
||||||
using Steeltoe.Common.Discovery;
|
using Steeltoe.Discovery;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Values;
|
using Ocelot.Values;
|
||||||
|
|
||||||
public class Eureka : IServiceDiscoveryProvider
|
public class Eureka : IServiceDiscoveryProvider
|
||||||
{
|
{
|
||||||
private readonly IDiscoveryClient _client;
|
private readonly IDiscoveryClient _client;
|
||||||
private readonly string _serviceName;
|
private readonly string _serviceName;
|
||||||
|
|
||||||
public Eureka(string serviceName, IDiscoveryClient client)
|
public Eureka(string serviceName, IDiscoveryClient client)
|
||||||
{
|
{
|
||||||
_client = client;
|
_client = client;
|
||||||
_serviceName = serviceName;
|
_serviceName = serviceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<List<Service>> Get()
|
public Task<List<Service>> Get()
|
||||||
{
|
{
|
||||||
var services = new List<Service>();
|
var services = new List<Service>();
|
||||||
|
|
||||||
var instances = _client.GetInstances(_serviceName);
|
var instances = _client.GetInstances(_serviceName);
|
||||||
|
|
||||||
if (instances != null && instances.Any())
|
if (instances != null && instances.Any())
|
||||||
{
|
{
|
||||||
services.AddRange(instances.Select(i => new Service(i.ServiceId, new ServiceHostAndPort(i.Host, i.Port, i.Uri.Scheme), "", "", new List<string>())));
|
services.AddRange(instances.Select(i => new Service(i.ServiceId, new ServiceHostAndPort(i.Host, i.Port, i.Uri.Scheme), "", "", new List<string>())));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult(services);
|
return Task.FromResult(services);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
namespace Ocelot.Provider.Eureka
|
namespace Ocelot.Provider.Eureka
|
||||||
{
|
{
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using ServiceDiscovery;
|
using Ocelot.ServiceDiscovery;
|
||||||
using Steeltoe.Common.Discovery;
|
using Steeltoe.Discovery;
|
||||||
|
|
||||||
public static class EurekaProviderFactory
|
public static class EurekaProviderFactory
|
||||||
{
|
{
|
||||||
public static ServiceDiscoveryFinderDelegate Get = (provider, config, route) =>
|
public static ServiceDiscoveryFinderDelegate Get = (provider, config, route) =>
|
||||||
{
|
{
|
||||||
var client = provider.GetService<IDiscoveryClient>();
|
var client = provider.GetService<IDiscoveryClient>();
|
||||||
|
|
||||||
if (config.Type?.ToLower() == "eureka" && client != null)
|
if (config.Type?.ToLower() == "eureka" && client != null)
|
||||||
{
|
{
|
||||||
return new Eureka(route.ServiceName, client);
|
return new Eureka(route.ServiceName, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<NoPackageAnalysis>true</NoPackageAnalysis>
|
<NoPackageAnalysis>true</NoPackageAnalysis>
|
||||||
<Description>Provides Ocelot extensions to use Eureka</Description>
|
<Description>Provides Ocelot extensions to use Eureka</Description>
|
||||||
<AssemblyTitle>Ocelot.Provider.Eureka</AssemblyTitle>
|
<AssemblyTitle>Ocelot.Provider.Eureka</AssemblyTitle>
|
||||||
@ -27,7 +27,8 @@
|
|||||||
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
|
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Steeltoe.Discovery.ClientCore" Version="2.4.2" />
|
<PackageReference Include="Steeltoe.Discovery.ClientCore" Version="3.0.1" />
|
||||||
|
<PackageReference Include="Steeltoe.Discovery.Eureka" Version="3.0.1" />
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -1,43 +1,43 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<NoPackageAnalysis>true</NoPackageAnalysis>
|
<NoPackageAnalysis>true</NoPackageAnalysis>
|
||||||
<Product>Ocelot</Product>
|
<Product>Ocelot</Product>
|
||||||
<Description>Provides Ocelot extensions to use kubernetes</Description>
|
<Description>Provides Ocelot extensions to use kubernetes</Description>
|
||||||
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot</PackageProjectUrl>
|
||||||
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
|
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
|
||||||
<PackageReleaseNotes></PackageReleaseNotes>
|
<PackageReleaseNotes></PackageReleaseNotes>
|
||||||
<AssemblyName>Ocelot.Provider.Kubernetes</AssemblyName>
|
<AssemblyName>Ocelot.Provider.Kubernetes</AssemblyName>
|
||||||
<PackageId>Ocelot.Provider.Kubernetes</PackageId>
|
<PackageId>Ocelot.Provider.Kubernetes</PackageId>
|
||||||
<PackageTags>API Gateway;.NET core</PackageTags>
|
<PackageTags>API Gateway;.NET core</PackageTags>
|
||||||
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
|
||||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||||
<Version>0.0.0-dev</Version>
|
<Version>0.0.0-dev</Version>
|
||||||
<Authors>geffzhang</Authors>
|
<Authors>geffzhang</Authors>
|
||||||
<Company />
|
<Company />
|
||||||
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="IKubeApiClientFactory.cs" />
|
<Compile Remove="IKubeApiClientFactory.cs" />
|
||||||
<Compile Remove="KubeApiClientFactory.cs" />
|
<Compile Remove="KubeApiClientFactory.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="KubeClient" Version="2.3.11" />
|
<PackageReference Include="KubeClient" Version="2.3.15" />
|
||||||
<PackageReference Include="KubeClient.Extensions.DependencyInjection" Version="2.3.11" />
|
<PackageReference Include="KubeClient.Extensions.DependencyInjection" Version="2.3.15" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
|
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,38 +1,38 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<NoPackageAnalysis>true</NoPackageAnalysis>
|
<NoPackageAnalysis>true</NoPackageAnalysis>
|
||||||
<Description>Provides Ocelot extensions to use Polly.NET</Description>
|
<Description>Provides Ocelot extensions to use Polly.NET</Description>
|
||||||
<AssemblyTitle>Ocelot.Provider.Polly</AssemblyTitle>
|
<AssemblyTitle>Ocelot.Provider.Polly</AssemblyTitle>
|
||||||
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
||||||
<AssemblyName>Ocelot.Provider.Polly</AssemblyName>
|
<AssemblyName>Ocelot.Provider.Polly</AssemblyName>
|
||||||
<PackageId>Ocelot.Provider.Polly</PackageId>
|
<PackageId>Ocelot.Provider.Polly</PackageId>
|
||||||
<PackageTags>API Gateway;.NET core</PackageTags>
|
<PackageTags>API Gateway;.NET core</PackageTags>
|
||||||
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Polly</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Polly</PackageProjectUrl>
|
||||||
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Polly</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Polly</PackageProjectUrl>
|
||||||
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
|
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
|
||||||
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
|
||||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||||
<Authors>Tom Pallister</Authors>
|
<Authors>Tom Pallister</Authors>
|
||||||
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<DebugSymbols>True</DebugSymbols>
|
<DebugSymbols>True</DebugSymbols>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
|
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Polly" Version="7.2.0" />
|
<PackageReference Include="Polly" Version="7.2.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
namespace Ocelot.Provider.Rafty
|
|
||||||
{
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
internal class BearerToken
|
|
||||||
{
|
|
||||||
[JsonProperty("access_token")]
|
|
||||||
public string AccessToken { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("expires_in")]
|
|
||||||
public int ExpiresIn { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("token_type")]
|
|
||||||
public string TokenType { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
namespace Ocelot.Provider.Rafty
|
|
||||||
{
|
|
||||||
using global::Rafty.FiniteStateMachine;
|
|
||||||
|
|
||||||
public class FakeCommand : ICommand
|
|
||||||
{
|
|
||||||
public FakeCommand(string value)
|
|
||||||
{
|
|
||||||
this.Value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Value { get; private set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
namespace Ocelot.Provider.Rafty
|
|
||||||
{
|
|
||||||
public class FilePeer
|
|
||||||
{
|
|
||||||
public string HostAndPort { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
namespace Ocelot.Provider.Rafty
|
|
||||||
{
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
public class FilePeers
|
|
||||||
{
|
|
||||||
public FilePeers()
|
|
||||||
{
|
|
||||||
Peers = new List<FilePeer>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<FilePeer> Peers { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
namespace Ocelot.Provider.Rafty
|
|
||||||
{
|
|
||||||
using Administration;
|
|
||||||
using Configuration.Repository;
|
|
||||||
using global::Rafty.Concensus.Peers;
|
|
||||||
using global::Rafty.Infrastructure;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Middleware;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net.Http;
|
|
||||||
|
|
||||||
public class FilePeersProvider : IPeersProvider
|
|
||||||
{
|
|
||||||
private readonly IOptions<FilePeers> _options;
|
|
||||||
private readonly List<IPeer> _peers;
|
|
||||||
private IBaseUrlFinder _finder;
|
|
||||||
private IInternalConfigurationRepository _repo;
|
|
||||||
private IIdentityServerConfiguration _identityServerConfig;
|
|
||||||
|
|
||||||
public FilePeersProvider(IOptions<FilePeers> options, IBaseUrlFinder finder, IInternalConfigurationRepository repo, IIdentityServerConfiguration identityServerConfig)
|
|
||||||
{
|
|
||||||
_identityServerConfig = identityServerConfig;
|
|
||||||
_repo = repo;
|
|
||||||
_finder = finder;
|
|
||||||
_options = options;
|
|
||||||
_peers = new List<IPeer>();
|
|
||||||
|
|
||||||
var config = _repo.Get();
|
|
||||||
foreach (var item in _options.Value.Peers)
|
|
||||||
{
|
|
||||||
var httpClient = new HttpClient();
|
|
||||||
|
|
||||||
//todo what if this errors?
|
|
||||||
var httpPeer = new HttpPeer(item.HostAndPort, httpClient, _finder, config.Data, _identityServerConfig);
|
|
||||||
_peers.Add(httpPeer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<IPeer> Get()
|
|
||||||
{
|
|
||||||
return _peers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,130 +0,0 @@
|
|||||||
namespace Ocelot.Provider.Rafty
|
|
||||||
{
|
|
||||||
using Administration;
|
|
||||||
using Configuration;
|
|
||||||
using global::Rafty.Concensus.Messages;
|
|
||||||
using global::Rafty.Concensus.Peers;
|
|
||||||
using global::Rafty.FiniteStateMachine;
|
|
||||||
using global::Rafty.Infrastructure;
|
|
||||||
using Middleware;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
public class HttpPeer : IPeer
|
|
||||||
{
|
|
||||||
private readonly string _hostAndPort;
|
|
||||||
private readonly HttpClient _httpClient;
|
|
||||||
private readonly JsonSerializerSettings _jsonSerializerSettings;
|
|
||||||
private readonly string _baseSchemeUrlAndPort;
|
|
||||||
private BearerToken _token;
|
|
||||||
private readonly IInternalConfiguration _config;
|
|
||||||
private readonly IIdentityServerConfiguration _identityServerConfiguration;
|
|
||||||
|
|
||||||
public HttpPeer(string hostAndPort, HttpClient httpClient, IBaseUrlFinder finder, IInternalConfiguration config, IIdentityServerConfiguration identityServerConfiguration)
|
|
||||||
{
|
|
||||||
_identityServerConfiguration = identityServerConfiguration;
|
|
||||||
_config = config;
|
|
||||||
Id = hostAndPort;
|
|
||||||
_hostAndPort = hostAndPort;
|
|
||||||
_httpClient = httpClient;
|
|
||||||
_jsonSerializerSettings = new JsonSerializerSettings()
|
|
||||||
{
|
|
||||||
TypeNameHandling = TypeNameHandling.All
|
|
||||||
};
|
|
||||||
_baseSchemeUrlAndPort = finder.Find();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Id { get; }
|
|
||||||
|
|
||||||
public async Task<RequestVoteResponse> Request(RequestVote requestVote)
|
|
||||||
{
|
|
||||||
if (_token == null)
|
|
||||||
{
|
|
||||||
await SetToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
var json = JsonConvert.SerializeObject(requestVote, _jsonSerializerSettings);
|
|
||||||
var content = new StringContent(json);
|
|
||||||
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
|
|
||||||
var response = await _httpClient.PostAsync($"{_hostAndPort}/administration/raft/requestvote", content);
|
|
||||||
if (response.IsSuccessStatusCode)
|
|
||||||
{
|
|
||||||
return JsonConvert.DeserializeObject<RequestVoteResponse>(await response.Content.ReadAsStringAsync(), _jsonSerializerSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new RequestVoteResponse(false, requestVote.Term);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<AppendEntriesResponse> Request(AppendEntries appendEntries)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_token == null)
|
|
||||||
{
|
|
||||||
await SetToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
var json = JsonConvert.SerializeObject(appendEntries, _jsonSerializerSettings);
|
|
||||||
var content = new StringContent(json);
|
|
||||||
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
|
|
||||||
var response = await _httpClient.PostAsync($"{_hostAndPort}/administration/raft/appendEntries", content);
|
|
||||||
if (response.IsSuccessStatusCode)
|
|
||||||
{
|
|
||||||
return JsonConvert.DeserializeObject<AppendEntriesResponse>(await response.Content.ReadAsStringAsync(), _jsonSerializerSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AppendEntriesResponse(appendEntries.Term, false);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex);
|
|
||||||
return new AppendEntriesResponse(appendEntries.Term, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Response<T>> Request<T>(T command)
|
|
||||||
where T : ICommand
|
|
||||||
{
|
|
||||||
Console.WriteLine("SENDING REQUEST....");
|
|
||||||
if (_token == null)
|
|
||||||
{
|
|
||||||
await SetToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
var json = JsonConvert.SerializeObject(command, _jsonSerializerSettings);
|
|
||||||
var content = new StringContent(json);
|
|
||||||
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
|
|
||||||
var response = await _httpClient.PostAsync($"{_hostAndPort}/administration/raft/command", content);
|
|
||||||
if (response.IsSuccessStatusCode)
|
|
||||||
{
|
|
||||||
Console.WriteLine("REQUEST OK....");
|
|
||||||
var okResponse = JsonConvert.DeserializeObject<OkResponse<ICommand>>(await response.Content.ReadAsStringAsync(), _jsonSerializerSettings);
|
|
||||||
return new OkResponse<T>((T)okResponse.Command);
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("REQUEST NOT OK....");
|
|
||||||
return new ErrorResponse<T>(await response.Content.ReadAsStringAsync(), command);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SetToken()
|
|
||||||
{
|
|
||||||
var tokenUrl = $"{_baseSchemeUrlAndPort}{_config.AdministrationPath}/connect/token";
|
|
||||||
var formData = new List<KeyValuePair<string, string>>
|
|
||||||
{
|
|
||||||
new KeyValuePair<string, string>("client_id", _identityServerConfiguration.ApiName),
|
|
||||||
new KeyValuePair<string, string>("client_secret", _identityServerConfiguration.ApiSecret),
|
|
||||||
new KeyValuePair<string, string>("scope", _identityServerConfiguration.ApiName),
|
|
||||||
new KeyValuePair<string, string>("grant_type", "client_credentials")
|
|
||||||
};
|
|
||||||
var content = new FormUrlEncodedContent(formData);
|
|
||||||
var response = await _httpClient.PostAsync(tokenUrl, content);
|
|
||||||
var responseContent = await response.Content.ReadAsStringAsync();
|
|
||||||
response.EnsureSuccessStatusCode();
|
|
||||||
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
|
|
||||||
_httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(_token.TokenType, _token.AccessToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
|
||||||
<NoPackageAnalysis>true</NoPackageAnalysis>
|
|
||||||
<Description>Provides Ocelot extensions to use Rafty</Description>
|
|
||||||
<AssemblyTitle>Ocelot.Provider.Rafty</AssemblyTitle>
|
|
||||||
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
|
||||||
<AssemblyName>Ocelot.Provider.Rafty</AssemblyName>
|
|
||||||
<PackageId>Ocelot.Provider.Rafty</PackageId>
|
|
||||||
<PackageTags>API Gateway;.NET core</PackageTags>
|
|
||||||
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Rafty</PackageProjectUrl>
|
|
||||||
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Rafty</PackageProjectUrl>
|
|
||||||
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
|
|
||||||
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
|
|
||||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
|
||||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
|
||||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
|
||||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
|
||||||
<Authors>Tom Pallister</Authors>
|
|
||||||
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<DebugSymbols>True</DebugSymbols>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
|
|
||||||
<ProjectReference Include="..\Ocelot.Administration\Ocelot.Administration.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.Data.SQLite" Version="3.1.1" />
|
|
||||||
<PackageReference Include="Rafty" Version="0.4.4" />
|
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
@ -1,28 +0,0 @@
|
|||||||
namespace Ocelot.Provider.Rafty
|
|
||||||
{
|
|
||||||
using Configuration.Setter;
|
|
||||||
using DependencyInjection;
|
|
||||||
using global::Rafty.Concensus.Node;
|
|
||||||
using global::Rafty.FiniteStateMachine;
|
|
||||||
using global::Rafty.Infrastructure;
|
|
||||||
using global::Rafty.Log;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
||||||
|
|
||||||
public static class OcelotAdministrationBuilderExtensions
|
|
||||||
{
|
|
||||||
public static IOcelotAdministrationBuilder AddRafty(this IOcelotAdministrationBuilder builder)
|
|
||||||
{
|
|
||||||
var settings = new InMemorySettings(4000, 6000, 100, 10000);
|
|
||||||
builder.Services.RemoveAll<IFileConfigurationSetter>();
|
|
||||||
builder.Services.AddSingleton<IFileConfigurationSetter, RaftyFileConfigurationSetter>();
|
|
||||||
builder.Services.AddSingleton<ILog, SqlLiteLog>();
|
|
||||||
builder.Services.AddSingleton<IFiniteStateMachine, OcelotFiniteStateMachine>();
|
|
||||||
builder.Services.AddSingleton<ISettings>(settings);
|
|
||||||
builder.Services.AddSingleton<IPeersProvider, FilePeersProvider>();
|
|
||||||
builder.Services.AddSingleton<INode, Node>();
|
|
||||||
builder.Services.Configure<FilePeers>(builder.ConfigurationRoot);
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
namespace Ocelot.Provider.Rafty
|
|
||||||
{
|
|
||||||
using Configuration.Setter;
|
|
||||||
using global::Rafty.FiniteStateMachine;
|
|
||||||
using global::Rafty.Log;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
public class OcelotFiniteStateMachine : IFiniteStateMachine
|
|
||||||
{
|
|
||||||
private readonly IFileConfigurationSetter _setter;
|
|
||||||
|
|
||||||
public OcelotFiniteStateMachine(IFileConfigurationSetter setter)
|
|
||||||
{
|
|
||||||
_setter = setter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Handle(LogEntry log)
|
|
||||||
{
|
|
||||||
//todo - handle an error
|
|
||||||
//hack it to just cast as at the moment we know this is the only command :P
|
|
||||||
var hack = (UpdateFileConfiguration)log.CommandData;
|
|
||||||
await _setter.Set(hack.Configuration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
|
||||||
// set of attributes. Change these attribute values to modify the information
|
|
||||||
// associated with an assembly.
|
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("")]
|
|
||||||
[assembly: AssemblyProduct("Ocelot")]
|
|
||||||
[assembly: AssemblyTrademark("")]
|
|
||||||
|
|
||||||
// Setting ComVisible to false makes the types in this assembly not visible
|
|
||||||
// to COM components. If you need to access a type in this assembly from
|
|
||||||
// COM, set the ComVisible attribute to true on that type.
|
|
||||||
[assembly: ComVisible(false)]
|
|
||||||
|
|
||||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|
||||||
[assembly: Guid("d6df4206-0dba-41d8-884d-c3e08290fdbb")]
|
|
@ -1,96 +0,0 @@
|
|||||||
namespace Ocelot.Provider.Rafty
|
|
||||||
{
|
|
||||||
using global::Rafty.Concensus.Messages;
|
|
||||||
using global::Rafty.Concensus.Node;
|
|
||||||
using global::Rafty.FiniteStateMachine;
|
|
||||||
using Logging;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Middleware;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
[Authorize]
|
|
||||||
[Route("raft")]
|
|
||||||
public class RaftController : Controller
|
|
||||||
{
|
|
||||||
private readonly INode _node;
|
|
||||||
private readonly IOcelotLogger _logger;
|
|
||||||
private readonly string _baseSchemeUrlAndPort;
|
|
||||||
private readonly JsonSerializerSettings _jsonSerialiserSettings;
|
|
||||||
|
|
||||||
public RaftController(INode node, IOcelotLoggerFactory loggerFactory, IBaseUrlFinder finder)
|
|
||||||
{
|
|
||||||
_jsonSerialiserSettings = new JsonSerializerSettings
|
|
||||||
{
|
|
||||||
TypeNameHandling = TypeNameHandling.All
|
|
||||||
};
|
|
||||||
_baseSchemeUrlAndPort = finder.Find();
|
|
||||||
_logger = loggerFactory.CreateLogger<RaftController>();
|
|
||||||
_node = node;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Route("appendentries")]
|
|
||||||
public async Task<IActionResult> AppendEntries()
|
|
||||||
{
|
|
||||||
using (var reader = new StreamReader(HttpContext.Request.Body))
|
|
||||||
{
|
|
||||||
var json = await reader.ReadToEndAsync();
|
|
||||||
|
|
||||||
var appendEntries = JsonConvert.DeserializeObject<AppendEntries>(json, _jsonSerialiserSettings);
|
|
||||||
|
|
||||||
_logger.LogDebug($"{_baseSchemeUrlAndPort}/appendentries called, my state is {_node.State.GetType().FullName}");
|
|
||||||
|
|
||||||
var appendEntriesResponse = await _node.Handle(appendEntries);
|
|
||||||
|
|
||||||
return new OkObjectResult(appendEntriesResponse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Route("requestvote")]
|
|
||||||
public async Task<IActionResult> RequestVote()
|
|
||||||
{
|
|
||||||
using (var reader = new StreamReader(HttpContext.Request.Body))
|
|
||||||
{
|
|
||||||
var json = await reader.ReadToEndAsync();
|
|
||||||
|
|
||||||
var requestVote = JsonConvert.DeserializeObject<RequestVote>(json, _jsonSerialiserSettings);
|
|
||||||
|
|
||||||
_logger.LogDebug($"{_baseSchemeUrlAndPort}/requestvote called, my state is {_node.State.GetType().FullName}");
|
|
||||||
|
|
||||||
var requestVoteResponse = await _node.Handle(requestVote);
|
|
||||||
|
|
||||||
return new OkObjectResult(requestVoteResponse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Route("command")]
|
|
||||||
public async Task<IActionResult> Command()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var reader = new StreamReader(HttpContext.Request.Body))
|
|
||||||
{
|
|
||||||
var json = await reader.ReadToEndAsync();
|
|
||||||
|
|
||||||
var command = JsonConvert.DeserializeObject<ICommand>(json, _jsonSerialiserSettings);
|
|
||||||
|
|
||||||
_logger.LogDebug($"{_baseSchemeUrlAndPort}/command called, my state is {_node.State.GetType().FullName}");
|
|
||||||
|
|
||||||
var commandResponse = await _node.Accept(command);
|
|
||||||
|
|
||||||
json = JsonConvert.SerializeObject(commandResponse, _jsonSerialiserSettings);
|
|
||||||
|
|
||||||
return StatusCode(200, json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_logger.LogError($"THERE WAS A PROBLEM ON NODE {_node.State.CurrentState.Id}", e);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
namespace Ocelot.Provider.Rafty
|
|
||||||
{
|
|
||||||
using Configuration.File;
|
|
||||||
using Configuration.Setter;
|
|
||||||
using global::Rafty.Concensus.Node;
|
|
||||||
using global::Rafty.Infrastructure;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
public class RaftyFileConfigurationSetter : IFileConfigurationSetter
|
|
||||||
{
|
|
||||||
private readonly INode _node;
|
|
||||||
|
|
||||||
public RaftyFileConfigurationSetter(INode node)
|
|
||||||
{
|
|
||||||
_node = node;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Responses.Response> Set(FileConfiguration fileConfiguration)
|
|
||||||
{
|
|
||||||
var result = await _node.Accept(new UpdateFileConfiguration(fileConfiguration));
|
|
||||||
|
|
||||||
if (result.GetType() == typeof(ErrorResponse<UpdateFileConfiguration>))
|
|
||||||
{
|
|
||||||
return new Responses.ErrorResponse(new UnableToSaveAcceptCommand($"unable to save file configuration to state machine"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Responses.OkResponse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
namespace Ocelot.Provider.Rafty
|
|
||||||
{
|
|
||||||
using global::Rafty.Concensus.Node;
|
|
||||||
using global::Rafty.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Middleware;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
public static class RaftyMiddlewareConfigurationProvider
|
|
||||||
{
|
|
||||||
public static OcelotMiddlewareConfigurationDelegate Get = builder =>
|
|
||||||
{
|
|
||||||
if (UsingRafty(builder))
|
|
||||||
{
|
|
||||||
SetUpRafty(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
};
|
|
||||||
|
|
||||||
private static bool UsingRafty(IApplicationBuilder builder)
|
|
||||||
{
|
|
||||||
var node = builder.ApplicationServices.GetService<INode>();
|
|
||||||
if (node != null)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetUpRafty(IApplicationBuilder builder)
|
|
||||||
{
|
|
||||||
var applicationLifetime = builder.ApplicationServices.GetService<IApplicationLifetime>();
|
|
||||||
applicationLifetime.ApplicationStopping.Register(() => OnShutdown(builder));
|
|
||||||
var node = builder.ApplicationServices.GetService<INode>();
|
|
||||||
var nodeId = builder.ApplicationServices.GetService<NodeId>();
|
|
||||||
node.Start(nodeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnShutdown(IApplicationBuilder app)
|
|
||||||
{
|
|
||||||
var node = app.ApplicationServices.GetService<INode>();
|
|
||||||
node.Stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,334 +0,0 @@
|
|||||||
namespace Ocelot.Provider.Rafty
|
|
||||||
{
|
|
||||||
using global::Rafty.Infrastructure;
|
|
||||||
using global::Rafty.Log;
|
|
||||||
using Microsoft.Data.Sqlite;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
public class SqlLiteLog : ILog
|
|
||||||
{
|
|
||||||
private readonly string _path;
|
|
||||||
private readonly SemaphoreSlim _sempaphore = new SemaphoreSlim(1, 1);
|
|
||||||
private readonly ILogger _logger;
|
|
||||||
private readonly NodeId _nodeId;
|
|
||||||
|
|
||||||
public SqlLiteLog(NodeId nodeId, ILoggerFactory loggerFactory)
|
|
||||||
{
|
|
||||||
_logger = loggerFactory.CreateLogger<SqlLiteLog>();
|
|
||||||
_nodeId = nodeId;
|
|
||||||
_path = $"{nodeId.Id.Replace("/", "").Replace(":", "")}.db";
|
|
||||||
_sempaphore.Wait();
|
|
||||||
|
|
||||||
if (!File.Exists(_path))
|
|
||||||
{
|
|
||||||
var fs = File.Create(_path);
|
|
||||||
|
|
||||||
fs.Dispose();
|
|
||||||
|
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
|
||||||
{
|
|
||||||
connection.Open();
|
|
||||||
|
|
||||||
const string sql = @"create table logs (
|
|
||||||
id integer primary key,
|
|
||||||
data text not null
|
|
||||||
)";
|
|
||||||
|
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
|
||||||
{
|
|
||||||
var result = command.ExecuteNonQuery();
|
|
||||||
|
|
||||||
_logger.LogInformation(result == 0
|
|
||||||
? $"id: {_nodeId.Id} create database, result: {result}"
|
|
||||||
: $"id: {_nodeId.Id} did not create database., result: {result}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_sempaphore.Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> LastLogIndex()
|
|
||||||
{
|
|
||||||
_sempaphore.Wait();
|
|
||||||
var result = 1;
|
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
|
||||||
{
|
|
||||||
connection.Open();
|
|
||||||
var sql = @"select id from logs order by id desc limit 1";
|
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
|
||||||
{
|
|
||||||
var index = Convert.ToInt32(await command.ExecuteScalarAsync());
|
|
||||||
if (index > result)
|
|
||||||
{
|
|
||||||
result = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_sempaphore.Release();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<long> LastLogTerm()
|
|
||||||
{
|
|
||||||
_sempaphore.Wait();
|
|
||||||
long result = 0;
|
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
|
||||||
{
|
|
||||||
connection.Open();
|
|
||||||
var sql = @"select data from logs order by id desc limit 1";
|
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
|
||||||
{
|
|
||||||
var data = Convert.ToString(await command.ExecuteScalarAsync());
|
|
||||||
var jsonSerializerSettings = new JsonSerializerSettings()
|
|
||||||
{
|
|
||||||
TypeNameHandling = TypeNameHandling.All
|
|
||||||
};
|
|
||||||
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
|
||||||
if (log != null && log.Term > result)
|
|
||||||
{
|
|
||||||
result = log.Term;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_sempaphore.Release();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> Count()
|
|
||||||
{
|
|
||||||
_sempaphore.Wait();
|
|
||||||
var result = 0;
|
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
|
||||||
{
|
|
||||||
connection.Open();
|
|
||||||
var sql = @"select count(id) from logs";
|
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
|
||||||
{
|
|
||||||
var index = Convert.ToInt32(await command.ExecuteScalarAsync());
|
|
||||||
if (index > result)
|
|
||||||
{
|
|
||||||
result = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_sempaphore.Release();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> Apply(LogEntry log)
|
|
||||||
{
|
|
||||||
_sempaphore.Wait();
|
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
|
||||||
{
|
|
||||||
connection.Open();
|
|
||||||
var jsonSerializerSettings = new JsonSerializerSettings()
|
|
||||||
{
|
|
||||||
TypeNameHandling = TypeNameHandling.All
|
|
||||||
};
|
|
||||||
var data = JsonConvert.SerializeObject(log, jsonSerializerSettings);
|
|
||||||
|
|
||||||
//todo - sql injection dont copy this..
|
|
||||||
var sql = $"insert into logs (data) values ('{data}')";
|
|
||||||
_logger.LogInformation($"id: {_nodeId.Id}, sql: {sql}");
|
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
|
||||||
{
|
|
||||||
var result = await command.ExecuteNonQueryAsync();
|
|
||||||
_logger.LogInformation($"id: {_nodeId.Id}, insert log result: {result}");
|
|
||||||
}
|
|
||||||
|
|
||||||
sql = "select last_insert_rowid()";
|
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
|
||||||
{
|
|
||||||
var result = await command.ExecuteScalarAsync();
|
|
||||||
_logger.LogInformation($"id: {_nodeId.Id}, about to release semaphore");
|
|
||||||
_sempaphore.Release();
|
|
||||||
_logger.LogInformation($"id: {_nodeId.Id}, saved log to sqlite");
|
|
||||||
return Convert.ToInt32(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DeleteConflictsFromThisLog(int index, LogEntry logEntry)
|
|
||||||
{
|
|
||||||
_sempaphore.Wait();
|
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
|
||||||
{
|
|
||||||
connection.Open();
|
|
||||||
|
|
||||||
//todo - sql injection dont copy this..
|
|
||||||
var sql = $"select data from logs where id = {index};";
|
|
||||||
_logger.LogInformation($"id: {_nodeId.Id} sql: {sql}");
|
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
|
||||||
{
|
|
||||||
var data = Convert.ToString(await command.ExecuteScalarAsync());
|
|
||||||
var jsonSerializerSettings = new JsonSerializerSettings()
|
|
||||||
{
|
|
||||||
TypeNameHandling = TypeNameHandling.All
|
|
||||||
};
|
|
||||||
|
|
||||||
_logger.LogInformation($"id {_nodeId.Id} got log for index: {index}, data is {data} and new log term is {logEntry.Term}");
|
|
||||||
|
|
||||||
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
|
||||||
if (logEntry != null && log != null && logEntry.Term != log.Term)
|
|
||||||
{
|
|
||||||
//todo - sql injection dont copy this..
|
|
||||||
var deleteSql = $"delete from logs where id >= {index};";
|
|
||||||
_logger.LogInformation($"id: {_nodeId.Id} sql: {deleteSql}");
|
|
||||||
using (var deleteCommand = new SqliteCommand(deleteSql, connection))
|
|
||||||
{
|
|
||||||
var result = await deleteCommand.ExecuteNonQueryAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_sempaphore.Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> IsDuplicate(int index, LogEntry logEntry)
|
|
||||||
{
|
|
||||||
_sempaphore.Wait();
|
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
|
||||||
{
|
|
||||||
connection.Open();
|
|
||||||
|
|
||||||
//todo - sql injection dont copy this..
|
|
||||||
var sql = $"select data from logs where id = {index};";
|
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
|
||||||
{
|
|
||||||
var data = Convert.ToString(await command.ExecuteScalarAsync());
|
|
||||||
var jsonSerializerSettings = new JsonSerializerSettings()
|
|
||||||
{
|
|
||||||
TypeNameHandling = TypeNameHandling.All
|
|
||||||
};
|
|
||||||
|
|
||||||
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
|
||||||
|
|
||||||
if (logEntry != null && log != null && logEntry.Term == log.Term)
|
|
||||||
{
|
|
||||||
_sempaphore.Release();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_sempaphore.Release();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<LogEntry> Get(int index)
|
|
||||||
{
|
|
||||||
_sempaphore.Wait();
|
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
|
||||||
{
|
|
||||||
connection.Open();
|
|
||||||
|
|
||||||
//todo - sql injection dont copy this..
|
|
||||||
var sql = $"select data from logs where id = {index}";
|
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
|
||||||
{
|
|
||||||
var data = Convert.ToString(await command.ExecuteScalarAsync());
|
|
||||||
var jsonSerializerSettings = new JsonSerializerSettings()
|
|
||||||
{
|
|
||||||
TypeNameHandling = TypeNameHandling.All
|
|
||||||
};
|
|
||||||
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
|
||||||
_sempaphore.Release();
|
|
||||||
return log;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<(int index, LogEntry logEntry)>> GetFrom(int index)
|
|
||||||
{
|
|
||||||
_sempaphore.Wait();
|
|
||||||
var logsToReturn = new List<(int, LogEntry)>();
|
|
||||||
|
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
|
||||||
{
|
|
||||||
connection.Open();
|
|
||||||
|
|
||||||
//todo - sql injection dont copy this..
|
|
||||||
var sql = $"select id, data from logs where id >= {index}";
|
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
|
||||||
{
|
|
||||||
using (var reader = await command.ExecuteReaderAsync())
|
|
||||||
{
|
|
||||||
while (reader.Read())
|
|
||||||
{
|
|
||||||
var id = Convert.ToInt32(reader[0]);
|
|
||||||
var data = (string)reader[1];
|
|
||||||
var jsonSerializerSettings = new JsonSerializerSettings()
|
|
||||||
{
|
|
||||||
TypeNameHandling = TypeNameHandling.All
|
|
||||||
};
|
|
||||||
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
|
||||||
logsToReturn.Add((id, log));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_sempaphore.Release();
|
|
||||||
return logsToReturn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<long> GetTermAtIndex(int index)
|
|
||||||
{
|
|
||||||
_sempaphore.Wait();
|
|
||||||
long result = 0;
|
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
|
||||||
{
|
|
||||||
connection.Open();
|
|
||||||
|
|
||||||
//todo - sql injection dont copy this..
|
|
||||||
var sql = $"select data from logs where id = {index}";
|
|
||||||
using (var command = new SqliteCommand(sql, connection))
|
|
||||||
{
|
|
||||||
var data = Convert.ToString(await command.ExecuteScalarAsync());
|
|
||||||
var jsonSerializerSettings = new JsonSerializerSettings()
|
|
||||||
{
|
|
||||||
TypeNameHandling = TypeNameHandling.All
|
|
||||||
};
|
|
||||||
var log = JsonConvert.DeserializeObject<LogEntry>(data, jsonSerializerSettings);
|
|
||||||
if (log != null && log.Term > result)
|
|
||||||
{
|
|
||||||
result = log.Term;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_sempaphore.Release();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Remove(int indexOfCommand)
|
|
||||||
{
|
|
||||||
_sempaphore.Wait();
|
|
||||||
using (var connection = new SqliteConnection($"Data Source={_path};"))
|
|
||||||
{
|
|
||||||
connection.Open();
|
|
||||||
|
|
||||||
//todo - sql injection dont copy this..
|
|
||||||
var deleteSql = $"delete from logs where id >= {indexOfCommand};";
|
|
||||||
_logger.LogInformation($"id: {_nodeId.Id} Remove {deleteSql}");
|
|
||||||
using (var deleteCommand = new SqliteCommand(deleteSql, connection))
|
|
||||||
{
|
|
||||||
var result = await deleteCommand.ExecuteNonQueryAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_sempaphore.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
namespace Ocelot.Provider.Rafty
|
|
||||||
{
|
|
||||||
using Errors;
|
|
||||||
|
|
||||||
public class UnableToSaveAcceptCommand : Error
|
|
||||||
{
|
|
||||||
public UnableToSaveAcceptCommand(string message)
|
|
||||||
: base(message, OcelotErrorCode.UnknownError, 404)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
namespace Ocelot.Provider.Rafty
|
|
||||||
{
|
|
||||||
using Configuration.File;
|
|
||||||
using global::Rafty.FiniteStateMachine;
|
|
||||||
|
|
||||||
public class UpdateFileConfiguration : ICommand
|
|
||||||
{
|
|
||||||
public UpdateFileConfiguration(FileConfiguration configuration)
|
|
||||||
{
|
|
||||||
Configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileConfiguration Configuration { get; private set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<NoPackageAnalysis>true</NoPackageAnalysis>
|
<NoPackageAnalysis>true</NoPackageAnalysis>
|
||||||
<Description>This package provides methods to integrate Butterfly tracing with Ocelot.</Description>
|
<Description>This package provides methods to integrate Butterfly tracing with Ocelot.</Description>
|
||||||
<AssemblyTitle>Ocelot.Tracing.Butterfly</AssemblyTitle>
|
<AssemblyTitle>Ocelot.Tracing.Butterfly</AssemblyTitle>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<Version>0.0.0-dev</Version>
|
<Version>0.0.0-dev</Version>
|
||||||
<Authors>Kjell-Åke Gafvelin</Authors>
|
<Authors>Kjell-Åke Gafvelin</Authors>
|
||||||
<Description>This package provides OpenTracing support to Ocelot.</Description>
|
<Description>This package provides OpenTracing support to Ocelot.</Description>
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
namespace Ocelot.Authorisation
|
|
||||||
{
|
|
||||||
using Ocelot.Errors;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
public class ClaimValueNotAuthorisedError : Error
|
|
||||||
{
|
|
||||||
public ClaimValueNotAuthorisedError(string message)
|
|
||||||
: base(message, OcelotErrorCode.ClaimValueNotAuthorisedError, 403)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
namespace Ocelot.Authorisation.Middleware
|
|
||||||
{
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
|
|
||||||
public static class AuthorisationMiddlewareMiddlewareExtensions
|
|
||||||
{
|
|
||||||
public static IApplicationBuilder UseAuthorisationMiddleware(this IApplicationBuilder builder)
|
|
||||||
{
|
|
||||||
return builder.UseMiddleware<AuthorisationMiddleware>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
namespace Ocelot.Authorisation
|
|
||||||
{
|
|
||||||
using Ocelot.Errors;
|
|
||||||
|
|
||||||
public class ScopeNotAuthorisedError : Error
|
|
||||||
{
|
|
||||||
public ScopeNotAuthorisedError(string message)
|
|
||||||
: base(message, OcelotErrorCode.ScopeNotAuthorisedError, 403)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
13
src/Ocelot/Authorization/ClaimValueNotAuthorizedError.cs
Normal file
13
src/Ocelot/Authorization/ClaimValueNotAuthorizedError.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace Ocelot.Authorization
|
||||||
|
{
|
||||||
|
using Ocelot.Errors;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
public class ClaimValueNotAuthorizedError : Error
|
||||||
|
{
|
||||||
|
public ClaimValueNotAuthorizedError(string message)
|
||||||
|
: base(message, OcelotErrorCode.ClaimValueNotAuthorizedError, 403)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
namespace Ocelot.Authorisation
|
namespace Ocelot.Authorization
|
||||||
{
|
{
|
||||||
using Ocelot.Infrastructure.Claims.Parser;
|
using Ocelot.Infrastructure.Claims.Parser;
|
||||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||||
@ -8,16 +8,16 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
public class ClaimsAuthoriser : IClaimsAuthoriser
|
public class ClaimsAuthorizer : IClaimsAuthorizer
|
||||||
{
|
{
|
||||||
private readonly IClaimsParser _claimsParser;
|
private readonly IClaimsParser _claimsParser;
|
||||||
|
|
||||||
public ClaimsAuthoriser(IClaimsParser claimsParser)
|
public ClaimsAuthorizer(IClaimsParser claimsParser)
|
||||||
{
|
{
|
||||||
_claimsParser = claimsParser;
|
_claimsParser = claimsParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response<bool> Authorise(
|
public Response<bool> Authorize(
|
||||||
ClaimsPrincipal claimsPrincipal,
|
ClaimsPrincipal claimsPrincipal,
|
||||||
Dictionary<string, string> routeClaimsRequirement,
|
Dictionary<string, string> routeClaimsRequirement,
|
||||||
List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues
|
List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues
|
||||||
@ -45,10 +45,10 @@
|
|||||||
{
|
{
|
||||||
// match
|
// match
|
||||||
var actualValue = matchingPlaceholders[0].Value;
|
var actualValue = matchingPlaceholders[0].Value;
|
||||||
var authorised = values.Data.Contains(actualValue);
|
var authorized = values.Data.Contains(actualValue);
|
||||||
if (!authorised)
|
if (!authorized)
|
||||||
{
|
{
|
||||||
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
|
return new ErrorResponse<bool>(new ClaimValueNotAuthorizedError(
|
||||||
$"dynamic claim value for {variableName} of {string.Join(", ", values.Data)} is not the same as required value: {actualValue}"));
|
$"dynamic claim value for {variableName} of {string.Join(", ", values.Data)} is not the same as required value: {actualValue}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,12 +57,12 @@
|
|||||||
// config error
|
// config error
|
||||||
if (matchingPlaceholders.Length == 0)
|
if (matchingPlaceholders.Length == 0)
|
||||||
{
|
{
|
||||||
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
|
return new ErrorResponse<bool>(new ClaimValueNotAuthorizedError(
|
||||||
$"config error: requires variable claim value: {variableName} placeholders does not contain that variable: {string.Join(", ", urlPathPlaceholderNameAndValues.Select(p => p.Name))}"));
|
$"config error: requires variable claim value: {variableName} placeholders does not contain that variable: {string.Join(", ", urlPathPlaceholderNameAndValues.Select(p => p.Name))}"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
|
return new ErrorResponse<bool>(new ClaimValueNotAuthorizedError(
|
||||||
$"config error: requires variable claim value: {required.Value} but placeholders are ambiguous: {string.Join(", ", urlPathPlaceholderNameAndValues.Where(p => p.Name.Equals(variableName)).Select(p => p.Value))}"));
|
$"config error: requires variable claim value: {required.Value} but placeholders are ambiguous: {string.Join(", ", urlPathPlaceholderNameAndValues.Where(p => p.Name.Equals(variableName)).Select(p => p.Value))}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,10 +70,10 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// static claim
|
// static claim
|
||||||
var authorised = values.Data.Contains(required.Value);
|
var authorized = values.Data.Contains(required.Value);
|
||||||
if (!authorised)
|
if (!authorized)
|
||||||
{
|
{
|
||||||
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
|
return new ErrorResponse<bool>(new ClaimValueNotAuthorizedError(
|
||||||
$"claim value: {string.Join(", ", values.Data)} is not the same as required value: {required.Value} for type: {required.Key}"));
|
$"claim value: {string.Join(", ", values.Data)} is not the same as required value: {required.Value} for type: {required.Key}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,16 +2,16 @@
|
|||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
|
||||||
namespace Ocelot.Authorisation
|
namespace Ocelot.Authorization
|
||||||
{
|
{
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
public interface IClaimsAuthoriser
|
public interface IClaimsAuthorizer
|
||||||
{
|
{
|
||||||
Response<bool> Authorise(
|
Response<bool> Authorize(
|
||||||
ClaimsPrincipal claimsPrincipal,
|
ClaimsPrincipal claimsPrincipal,
|
||||||
Dictionary<string, string> routeClaimsRequirement,
|
Dictionary<string, string> routeClaimsRequirement,
|
||||||
List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues
|
List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,12 +1,12 @@
|
|||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
|
||||||
namespace Ocelot.Authorisation
|
namespace Ocelot.Authorization
|
||||||
{
|
{
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
public interface IScopesAuthoriser
|
public interface IScopesAuthorizer
|
||||||
{
|
{
|
||||||
Response<bool> Authorise(ClaimsPrincipal claimsPrincipal, List<string> routeAllowedScopes);
|
Response<bool> Authorize(ClaimsPrincipal claimsPrincipal, List<string> routeAllowedScopes);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
namespace Ocelot.Authorisation.Middleware
|
namespace Ocelot.Authorization.Middleware
|
||||||
{
|
{
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
@ -8,21 +8,21 @@
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
|
|
||||||
public class AuthorisationMiddleware : OcelotMiddleware
|
public class AuthorizationMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly RequestDelegate _next;
|
||||||
private readonly IClaimsAuthoriser _claimsAuthoriser;
|
private readonly IClaimsAuthorizer _claimsAuthorizer;
|
||||||
private readonly IScopesAuthoriser _scopesAuthoriser;
|
private readonly IScopesAuthorizer _scopesAuthorizer;
|
||||||
|
|
||||||
public AuthorisationMiddleware(RequestDelegate next,
|
public AuthorizationMiddleware(RequestDelegate next,
|
||||||
IClaimsAuthoriser claimsAuthoriser,
|
IClaimsAuthorizer claimsAuthorizer,
|
||||||
IScopesAuthoriser scopesAuthoriser,
|
IScopesAuthorizer scopesAuthorizer,
|
||||||
IOcelotLoggerFactory loggerFactory)
|
IOcelotLoggerFactory loggerFactory)
|
||||||
: base(loggerFactory.CreateLogger<AuthorisationMiddleware>())
|
: base(loggerFactory.CreateLogger<AuthorizationMiddleware>())
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_claimsAuthoriser = claimsAuthoriser;
|
_claimsAuthorizer = claimsAuthorizer;
|
||||||
_scopesAuthoriser = scopesAuthoriser;
|
_scopesAuthorizer = scopesAuthorizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext httpContext)
|
public async Task Invoke(HttpContext httpContext)
|
||||||
@ -33,65 +33,65 @@
|
|||||||
{
|
{
|
||||||
Logger.LogInformation("route is authenticated scopes must be checked");
|
Logger.LogInformation("route is authenticated scopes must be checked");
|
||||||
|
|
||||||
var authorised = _scopesAuthoriser.Authorise(httpContext.User, downstreamRoute.AuthenticationOptions.AllowedScopes);
|
var authorized = _scopesAuthorizer.Authorize(httpContext.User, downstreamRoute.AuthenticationOptions.AllowedScopes);
|
||||||
|
|
||||||
if (authorised.IsError)
|
if (authorized.IsError)
|
||||||
{
|
{
|
||||||
Logger.LogWarning("error authorising user scopes");
|
Logger.LogWarning("error authorizing user scopes");
|
||||||
|
|
||||||
httpContext.Items.UpsertErrors(authorised.Errors);
|
httpContext.Items.UpsertErrors(authorized.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsAuthorised(authorised))
|
if (IsAuthorized(authorized))
|
||||||
{
|
{
|
||||||
Logger.LogInformation("user scopes is authorised calling next authorisation checks");
|
Logger.LogInformation("user scopes is authorized calling next authorization checks");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.LogWarning("user scopes is not authorised setting pipeline error");
|
Logger.LogWarning("user scopes is not authorized setting pipeline error");
|
||||||
|
|
||||||
httpContext.Items.SetError(new UnauthorisedError(
|
httpContext.Items.SetError(new UnauthorizedError(
|
||||||
$"{httpContext.User.Identity.Name} unable to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}"));
|
$"{httpContext.User.Identity.Name} unable to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsOptionsHttpMethod(httpContext) && IsAuthorisedRoute(downstreamRoute))
|
if (!IsOptionsHttpMethod(httpContext) && IsAuthorizedRoute(downstreamRoute))
|
||||||
{
|
{
|
||||||
Logger.LogInformation("route is authorised");
|
Logger.LogInformation("route is authorized");
|
||||||
|
|
||||||
var authorised = _claimsAuthoriser.Authorise(httpContext.User, downstreamRoute.RouteClaimsRequirement, httpContext.Items.TemplatePlaceholderNameAndValues());
|
var authorized = _claimsAuthorizer.Authorize(httpContext.User, downstreamRoute.RouteClaimsRequirement, httpContext.Items.TemplatePlaceholderNameAndValues());
|
||||||
|
|
||||||
if (authorised.IsError)
|
if (authorized.IsError)
|
||||||
{
|
{
|
||||||
Logger.LogWarning($"Error whilst authorising {httpContext.User.Identity.Name}. Setting pipeline error");
|
Logger.LogWarning($"Error whilst authorizing {httpContext.User.Identity.Name}. Setting pipeline error");
|
||||||
|
|
||||||
httpContext.Items.UpsertErrors(authorised.Errors);
|
httpContext.Items.UpsertErrors(authorized.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsAuthorised(authorised))
|
if (IsAuthorized(authorized))
|
||||||
{
|
{
|
||||||
Logger.LogInformation($"{httpContext.User.Identity.Name} has succesfully been authorised for {downstreamRoute.UpstreamPathTemplate.OriginalValue}.");
|
Logger.LogInformation($"{httpContext.User.Identity.Name} has succesfully been authorized for {downstreamRoute.UpstreamPathTemplate.OriginalValue}.");
|
||||||
await _next.Invoke(httpContext);
|
await _next.Invoke(httpContext);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.LogWarning($"{httpContext.User.Identity.Name} is not authorised to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}. Setting pipeline error");
|
Logger.LogWarning($"{httpContext.User.Identity.Name} is not authorized to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}. Setting pipeline error");
|
||||||
|
|
||||||
httpContext.Items.SetError(new UnauthorisedError($"{httpContext.User.Identity.Name} is not authorised to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}"));
|
httpContext.Items.SetError(new UnauthorizedError($"{httpContext.User.Identity.Name} is not authorized to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.LogInformation($"{downstreamRoute.DownstreamPathTemplate.Value} route does not require user to be authorised");
|
Logger.LogInformation($"{downstreamRoute.DownstreamPathTemplate.Value} route does not require user to be authorized");
|
||||||
await _next.Invoke(httpContext);
|
await _next.Invoke(httpContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsAuthorised(Response<bool> authorised)
|
private static bool IsAuthorized(Response<bool> authorized)
|
||||||
{
|
{
|
||||||
return authorised.Data;
|
return authorized.Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsAuthenticatedRoute(DownstreamRoute route)
|
private static bool IsAuthenticatedRoute(DownstreamRoute route)
|
||||||
@ -99,9 +99,9 @@
|
|||||||
return route.IsAuthenticated;
|
return route.IsAuthenticated;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsAuthorisedRoute(DownstreamRoute route)
|
private static bool IsAuthorizedRoute(DownstreamRoute route)
|
||||||
{
|
{
|
||||||
return route.IsAuthorised;
|
return route.IsAuthorized;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsOptionsHttpMethod(HttpContext httpContext)
|
private static bool IsOptionsHttpMethod(HttpContext httpContext)
|
@ -0,0 +1,12 @@
|
|||||||
|
namespace Ocelot.Authorization.Middleware
|
||||||
|
{
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
|
||||||
|
public static class AuthorizationMiddlewareMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UseAuthorizationMiddleware(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<AuthorizationMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
src/Ocelot/Authorization/ScopeNotAuthorizedError.cs
Normal file
12
src/Ocelot/Authorization/ScopeNotAuthorizedError.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Ocelot.Authorization
|
||||||
|
{
|
||||||
|
using Ocelot.Errors;
|
||||||
|
|
||||||
|
public class ScopeNotAuthorizedError : Error
|
||||||
|
{
|
||||||
|
public ScopeNotAuthorizedError(string message)
|
||||||
|
: base(message, OcelotErrorCode.ScopeNotAuthorizedError, 403)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,47 +1,47 @@
|
|||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
|
||||||
namespace Ocelot.Authorisation
|
namespace Ocelot.Authorization
|
||||||
{
|
{
|
||||||
using Infrastructure.Claims.Parser;
|
using Infrastructure.Claims.Parser;
|
||||||
|
|
||||||
public class ScopesAuthoriser : IScopesAuthoriser
|
public class ScopesAuthorizer : IScopesAuthorizer
|
||||||
{
|
{
|
||||||
private readonly IClaimsParser _claimsParser;
|
private readonly IClaimsParser _claimsParser;
|
||||||
private readonly string _scope = "scope";
|
private readonly string _scope = "scope";
|
||||||
|
|
||||||
public ScopesAuthoriser(IClaimsParser claimsParser)
|
public ScopesAuthorizer(IClaimsParser claimsParser)
|
||||||
{
|
{
|
||||||
_claimsParser = claimsParser;
|
_claimsParser = claimsParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response<bool> Authorise(ClaimsPrincipal claimsPrincipal, List<string> routeAllowedScopes)
|
public Response<bool> Authorize(ClaimsPrincipal claimsPrincipal, List<string> routeAllowedScopes)
|
||||||
{
|
{
|
||||||
if (routeAllowedScopes == null || routeAllowedScopes.Count == 0)
|
if (routeAllowedScopes == null || routeAllowedScopes.Count == 0)
|
||||||
{
|
{
|
||||||
return new OkResponse<bool>(true);
|
return new OkResponse<bool>(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var values = _claimsParser.GetValuesByClaimType(claimsPrincipal.Claims, _scope);
|
var values = _claimsParser.GetValuesByClaimType(claimsPrincipal.Claims, _scope);
|
||||||
|
|
||||||
if (values.IsError)
|
if (values.IsError)
|
||||||
{
|
{
|
||||||
return new ErrorResponse<bool>(values.Errors);
|
return new ErrorResponse<bool>(values.Errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
var userScopes = values.Data;
|
var userScopes = values.Data;
|
||||||
|
|
||||||
var matchesScopes = routeAllowedScopes.Intersect(userScopes).ToList();
|
var matchesScopes = routeAllowedScopes.Intersect(userScopes).ToList();
|
||||||
|
|
||||||
if (matchesScopes.Count == 0)
|
if (matchesScopes.Count == 0)
|
||||||
{
|
{
|
||||||
return new ErrorResponse<bool>(
|
return new ErrorResponse<bool>(
|
||||||
new ScopeNotAuthorisedError($"no one user scope: '{string.Join(",", userScopes)}' match with some allowed scope: '{string.Join(",", routeAllowedScopes)}'"));
|
new ScopeNotAuthorizedError($"no one user scope: '{string.Join(",", userScopes)}' match with some allowed scope: '{string.Join(",", routeAllowedScopes)}'"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OkResponse<bool>(true);
|
return new OkResponse<bool>(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,10 +1,10 @@
|
|||||||
namespace Ocelot.Authorisation
|
namespace Ocelot.Authorization
|
||||||
{
|
{
|
||||||
using Ocelot.Errors;
|
using Ocelot.Errors;
|
||||||
|
|
||||||
public class UnauthorisedError : Error
|
public class UnauthorizedError : Error
|
||||||
{
|
{
|
||||||
public UnauthorisedError(string message)
|
public UnauthorizedError(string message)
|
||||||
: base(message, OcelotErrorCode.UnauthorizedError, 403)
|
: base(message, OcelotErrorCode.UnauthorizedError, 403)
|
||||||
{
|
{
|
||||||
}
|
}
|
@ -1,12 +1,12 @@
|
|||||||
namespace Ocelot.Authorisation
|
namespace Ocelot.Authorization
|
||||||
{
|
{
|
||||||
using Ocelot.Errors;
|
using Ocelot.Errors;
|
||||||
|
|
||||||
public class UserDoesNotHaveClaimError : Error
|
public class UserDoesNotHaveClaimError : Error
|
||||||
{
|
{
|
||||||
public UserDoesNotHaveClaimError(string message)
|
public UserDoesNotHaveClaimError(string message)
|
||||||
: base(message, OcelotErrorCode.UserDoesNotHaveClaimError, 403)
|
: base(message, OcelotErrorCode.UserDoesNotHaveClaimError, 403)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,7 +18,7 @@ namespace Ocelot.Configuration.Builder
|
|||||||
private List<ClaimToThing> _claimsToHeaders;
|
private List<ClaimToThing> _claimsToHeaders;
|
||||||
private List<ClaimToThing> _claimToClaims;
|
private List<ClaimToThing> _claimToClaims;
|
||||||
private Dictionary<string, string> _routeClaimRequirement;
|
private Dictionary<string, string> _routeClaimRequirement;
|
||||||
private bool _isAuthorised;
|
private bool _isAuthorized;
|
||||||
private List<ClaimToThing> _claimToQueries;
|
private List<ClaimToThing> _claimToQueries;
|
||||||
private List<ClaimToThing> _claimToDownstreamPath;
|
private List<ClaimToThing> _claimToDownstreamPath;
|
||||||
private string _requestIdHeaderKey;
|
private string _requestIdHeaderKey;
|
||||||
@ -101,9 +101,9 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownstreamRouteBuilder WithIsAuthorised(bool input)
|
public DownstreamRouteBuilder WithIsAuthorized(bool input)
|
||||||
{
|
{
|
||||||
_isAuthorised = input;
|
_isAuthorized = input;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +289,7 @@ namespace Ocelot.Configuration.Builder
|
|||||||
_claimToClaims,
|
_claimToClaims,
|
||||||
_claimToDownstreamPath,
|
_claimToDownstreamPath,
|
||||||
_isAuthenticated,
|
_isAuthenticated,
|
||||||
_isAuthorised,
|
_isAuthorized,
|
||||||
_authenticationOptions,
|
_authenticationOptions,
|
||||||
new DownstreamPathTemplate(_downstreamPathTemplate),
|
new DownstreamPathTemplate(_downstreamPathTemplate),
|
||||||
_loadBalancerKey,
|
_loadBalancerKey,
|
||||||
|
@ -3,7 +3,7 @@ namespace Ocelot.Configuration.Builder
|
|||||||
public class RouteOptionsBuilder
|
public class RouteOptionsBuilder
|
||||||
{
|
{
|
||||||
private bool _isAuthenticated;
|
private bool _isAuthenticated;
|
||||||
private bool _isAuthorised;
|
private bool _isAuthorized;
|
||||||
private bool _isCached;
|
private bool _isCached;
|
||||||
private bool _enableRateLimiting;
|
private bool _enableRateLimiting;
|
||||||
private bool _useServiceDiscovery;
|
private bool _useServiceDiscovery;
|
||||||
@ -20,9 +20,9 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RouteOptionsBuilder WithIsAuthorised(bool isAuthorised)
|
public RouteOptionsBuilder WithIsAuthorized(bool isAuthorized)
|
||||||
{
|
{
|
||||||
_isAuthorised = isAuthorised;
|
_isAuthorized = isAuthorized;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ namespace Ocelot.Configuration.Builder
|
|||||||
|
|
||||||
public RouteOptions Build()
|
public RouteOptions Build()
|
||||||
{
|
{
|
||||||
return new RouteOptions(_isAuthenticated, _isAuthorised, _isCached, _enableRateLimiting, _useServiceDiscovery);
|
return new RouteOptions(_isAuthenticated, _isAuthorized, _isCached, _enableRateLimiting, _useServiceDiscovery);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
namespace Ocelot.Configuration.Creator
|
namespace Ocelot.Configuration.Creator
|
||||||
{
|
{
|
||||||
using Ocelot.Configuration.Builder;
|
using Ocelot.Configuration.Builder;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
|
|
||||||
public class RouteOptionsCreator : IRouteOptionsCreator
|
public class RouteOptionsCreator : IRouteOptionsCreator
|
||||||
@ -8,14 +8,14 @@ namespace Ocelot.Configuration.Creator
|
|||||||
public RouteOptions Create(FileRoute fileRoute)
|
public RouteOptions Create(FileRoute fileRoute)
|
||||||
{
|
{
|
||||||
var isAuthenticated = IsAuthenticated(fileRoute);
|
var isAuthenticated = IsAuthenticated(fileRoute);
|
||||||
var isAuthorised = IsAuthorised(fileRoute);
|
var isAuthorized = IsAuthorized(fileRoute);
|
||||||
var isCached = IsCached(fileRoute);
|
var isCached = IsCached(fileRoute);
|
||||||
var enableRateLimiting = IsEnableRateLimiting(fileRoute);
|
var enableRateLimiting = IsEnableRateLimiting(fileRoute);
|
||||||
var useServiceDiscovery = !string.IsNullOrEmpty(fileRoute.ServiceName);
|
var useServiceDiscovery = !string.IsNullOrEmpty(fileRoute.ServiceName);
|
||||||
|
|
||||||
var options = new RouteOptionsBuilder()
|
var options = new RouteOptionsBuilder()
|
||||||
.WithIsAuthenticated(isAuthenticated)
|
.WithIsAuthenticated(isAuthenticated)
|
||||||
.WithIsAuthorised(isAuthorised)
|
.WithIsAuthorized(isAuthorized)
|
||||||
.WithIsCached(isCached)
|
.WithIsCached(isCached)
|
||||||
.WithRateLimiting(enableRateLimiting)
|
.WithRateLimiting(enableRateLimiting)
|
||||||
.WithUseServiceDiscovery(useServiceDiscovery)
|
.WithUseServiceDiscovery(useServiceDiscovery)
|
||||||
@ -34,7 +34,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
return !string.IsNullOrEmpty(fileRoute.AuthenticationOptions?.AuthenticationProviderKey);
|
return !string.IsNullOrEmpty(fileRoute.AuthenticationOptions?.AuthenticationProviderKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsAuthorised(FileRoute fileRoute)
|
private bool IsAuthorized(FileRoute fileRoute)
|
||||||
{
|
{
|
||||||
return fileRoute.RouteClaimsRequirement?.Count > 0;
|
return fileRoute.RouteClaimsRequirement?.Count > 0;
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
.WithClaimsToHeaders(claimsToHeaders)
|
.WithClaimsToHeaders(claimsToHeaders)
|
||||||
.WithClaimsToClaims(claimsToClaims)
|
.WithClaimsToClaims(claimsToClaims)
|
||||||
.WithRouteClaimsRequirement(fileRoute.RouteClaimsRequirement)
|
.WithRouteClaimsRequirement(fileRoute.RouteClaimsRequirement)
|
||||||
.WithIsAuthorised(fileRouteOptions.IsAuthorised)
|
.WithIsAuthorized(fileRouteOptions.IsAuthorized)
|
||||||
.WithClaimsToQueries(claimsToQueries)
|
.WithClaimsToQueries(claimsToQueries)
|
||||||
.WithClaimsToDownstreamPath(claimsToDownstreamPath)
|
.WithClaimsToDownstreamPath(claimsToDownstreamPath)
|
||||||
.WithRequestIdKey(requestIdKey)
|
.WithRequestIdKey(requestIdKey)
|
||||||
|
@ -31,7 +31,7 @@ namespace Ocelot.Configuration
|
|||||||
List<ClaimToThing> claimsToClaims,
|
List<ClaimToThing> claimsToClaims,
|
||||||
List<ClaimToThing> claimsToPath,
|
List<ClaimToThing> claimsToPath,
|
||||||
bool isAuthenticated,
|
bool isAuthenticated,
|
||||||
bool isAuthorised,
|
bool isAuthorized,
|
||||||
AuthenticationOptions authenticationOptions,
|
AuthenticationOptions authenticationOptions,
|
||||||
DownstreamPathTemplate downstreamPathTemplate,
|
DownstreamPathTemplate downstreamPathTemplate,
|
||||||
string loadBalancerKey,
|
string loadBalancerKey,
|
||||||
@ -69,7 +69,7 @@ namespace Ocelot.Configuration
|
|||||||
ClaimsToClaims = claimsToClaims ?? new List<ClaimToThing>();
|
ClaimsToClaims = claimsToClaims ?? new List<ClaimToThing>();
|
||||||
ClaimsToPath = claimsToPath ?? new List<ClaimToThing>();
|
ClaimsToPath = claimsToPath ?? new List<ClaimToThing>();
|
||||||
IsAuthenticated = isAuthenticated;
|
IsAuthenticated = isAuthenticated;
|
||||||
IsAuthorised = isAuthorised;
|
IsAuthorized = isAuthorized;
|
||||||
AuthenticationOptions = authenticationOptions;
|
AuthenticationOptions = authenticationOptions;
|
||||||
DownstreamPathTemplate = downstreamPathTemplate;
|
DownstreamPathTemplate = downstreamPathTemplate;
|
||||||
LoadBalancerKey = loadBalancerKey;
|
LoadBalancerKey = loadBalancerKey;
|
||||||
@ -102,7 +102,7 @@ namespace Ocelot.Configuration
|
|||||||
public List<ClaimToThing> ClaimsToClaims { get; }
|
public List<ClaimToThing> ClaimsToClaims { get; }
|
||||||
public List<ClaimToThing> ClaimsToPath { get; }
|
public List<ClaimToThing> ClaimsToPath { get; }
|
||||||
public bool IsAuthenticated { get; }
|
public bool IsAuthenticated { get; }
|
||||||
public bool IsAuthorised { get; }
|
public bool IsAuthorized { get; }
|
||||||
public AuthenticationOptions AuthenticationOptions { get; }
|
public AuthenticationOptions AuthenticationOptions { get; }
|
||||||
public DownstreamPathTemplate DownstreamPathTemplate { get; }
|
public DownstreamPathTemplate DownstreamPathTemplate { get; }
|
||||||
public string LoadBalancerKey { get; }
|
public string LoadBalancerKey { get; }
|
||||||
|
@ -2,17 +2,17 @@ namespace Ocelot.Configuration
|
|||||||
{
|
{
|
||||||
public class RouteOptions
|
public class RouteOptions
|
||||||
{
|
{
|
||||||
public RouteOptions(bool isAuthenticated, bool isAuthorised, bool isCached, bool isEnableRateLimiting, bool useServiceDiscovery)
|
public RouteOptions(bool isAuthenticated, bool isAuthorized, bool isCached, bool isEnableRateLimiting, bool useServiceDiscovery)
|
||||||
{
|
{
|
||||||
IsAuthenticated = isAuthenticated;
|
IsAuthenticated = isAuthenticated;
|
||||||
IsAuthorised = isAuthorised;
|
IsAuthorized = isAuthorized;
|
||||||
IsCached = isCached;
|
IsCached = isCached;
|
||||||
EnableRateLimiting = isEnableRateLimiting;
|
EnableRateLimiting = isEnableRateLimiting;
|
||||||
UseServiceDiscovery = useServiceDiscovery;
|
UseServiceDiscovery = useServiceDiscovery;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsAuthenticated { get; private set; }
|
public bool IsAuthenticated { get; private set; }
|
||||||
public bool IsAuthorised { get; private set; }
|
public bool IsAuthorized { get; private set; }
|
||||||
public bool IsCached { get; private set; }
|
public bool IsCached { get; private set; }
|
||||||
public bool EnableRateLimiting { get; private set; }
|
public bool EnableRateLimiting { get; private set; }
|
||||||
public bool UseServiceDiscovery { get; private set; }
|
public bool UseServiceDiscovery { get; private set; }
|
||||||
|
@ -5,7 +5,7 @@ namespace Ocelot.DependencyInjection
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Ocelot.Authorisation;
|
using Ocelot.Authorization;
|
||||||
using Ocelot.Cache;
|
using Ocelot.Cache;
|
||||||
using Ocelot.Claims;
|
using Ocelot.Claims;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
@ -96,8 +96,8 @@ namespace Ocelot.DependencyInjection
|
|||||||
Services.TryAddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
|
Services.TryAddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
|
||||||
Services.TryAddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
|
Services.TryAddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
|
||||||
Services.TryAddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
|
Services.TryAddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
|
||||||
Services.TryAddSingleton<IClaimsAuthoriser, ClaimsAuthoriser>();
|
Services.TryAddSingleton<IClaimsAuthorizer, ClaimsAuthorizer>();
|
||||||
Services.TryAddSingleton<IScopesAuthoriser, ScopesAuthoriser>();
|
Services.TryAddSingleton<IScopesAuthorizer, ScopesAuthorizer>();
|
||||||
Services.TryAddSingleton<IAddClaimsToRequest, AddClaimsToRequest>();
|
Services.TryAddSingleton<IAddClaimsToRequest, AddClaimsToRequest>();
|
||||||
Services.TryAddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
|
Services.TryAddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
|
||||||
Services.TryAddSingleton<IAddQueriesToRequest, AddQueriesToRequest>();
|
Services.TryAddSingleton<IAddQueriesToRequest, AddQueriesToRequest>();
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
NoInstructionsError = 11,
|
NoInstructionsError = 11,
|
||||||
InstructionNotForClaimsError = 12,
|
InstructionNotForClaimsError = 12,
|
||||||
UnauthorizedError = 13,
|
UnauthorizedError = 13,
|
||||||
ClaimValueNotAuthorisedError = 14,
|
ClaimValueNotAuthorizedError = 14,
|
||||||
ScopeNotAuthorisedError = 15,
|
ScopeNotAuthorizedError = 15,
|
||||||
UserDoesNotHaveClaimError = 16,
|
UserDoesNotHaveClaimError = 16,
|
||||||
DownstreamPathTemplateContainsSchemeError = 17,
|
DownstreamPathTemplateContainsSchemeError = 17,
|
||||||
DownstreamPathNullOrEmptyError = 18,
|
DownstreamPathNullOrEmptyError = 18,
|
||||||
|
@ -39,22 +39,22 @@
|
|||||||
public Func<HttpContext, Func<Task>, Task> AuthenticationMiddleware { get; set; }
|
public Func<HttpContext, Func<Task>, Task> AuthenticationMiddleware { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is to allow the user to run any extra authorisation before the Ocelot authentication
|
/// This is to allow the user to run any extra authorization before the Ocelot authentication
|
||||||
/// kicks in
|
/// kicks in
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>
|
/// <value>
|
||||||
/// <placeholder>This is to allow the user to run any extra authorisation before the Ocelot authentication
|
/// <placeholder>This is to allow the user to run any extra authorization before the Ocelot authentication
|
||||||
/// kicks in</placeholder>
|
/// kicks in</placeholder>
|
||||||
/// </value>
|
/// </value>
|
||||||
public Func<HttpContext, Func<Task>, Task> PreAuthorisationMiddleware { get; set; }
|
public Func<HttpContext, Func<Task>, Task> PreAuthorizationMiddleware { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This allows the user to completely override the ocelot authorisation middleware
|
/// This allows the user to completely override the ocelot authorization middleware
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>
|
/// <value>
|
||||||
/// <placeholder>This allows the user to completely override the ocelot authorisation middleware</placeholder>
|
/// <placeholder>This allows the user to completely override the ocelot authorization middleware</placeholder>
|
||||||
/// </value>
|
/// </value>
|
||||||
public Func<HttpContext, Func<Task>, Task> AuthorisationMiddleware { get; set; }
|
public Func<HttpContext, Func<Task>, Task> AuthorizationMiddleware { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This allows the user to implement there own query string manipulation logic
|
/// This allows the user to implement there own query string manipulation logic
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
using Ocelot.Responder.Middleware;
|
using Ocelot.Responder.Middleware;
|
||||||
using Ocelot.Security.Middleware;
|
using Ocelot.Security.Middleware;
|
||||||
using Ocelot.Authentication.Middleware;
|
using Ocelot.Authentication.Middleware;
|
||||||
using Ocelot.Authorisation.Middleware;
|
using Ocelot.Authorization.Middleware;
|
||||||
using Ocelot.Cache.Middleware;
|
using Ocelot.Cache.Middleware;
|
||||||
using Ocelot.Claims.Middleware;
|
using Ocelot.Claims.Middleware;
|
||||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
@ -102,23 +102,23 @@
|
|||||||
app.Use(pipelineConfiguration.AuthenticationMiddleware);
|
app.Use(pipelineConfiguration.AuthenticationMiddleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The next thing we do is look at any claims transforms in case this is important for authorisation
|
// The next thing we do is look at any claims transforms in case this is important for authorization
|
||||||
app.UseClaimsToClaimsMiddleware();
|
app.UseClaimsToClaimsMiddleware();
|
||||||
|
|
||||||
// Allow pre authorisation logic. The idea being people might want to run something custom before what is built in.
|
// Allow pre authorization logic. The idea being people might want to run something custom before what is built in.
|
||||||
app.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware);
|
app.UseIfNotNull(pipelineConfiguration.PreAuthorizationMiddleware);
|
||||||
|
|
||||||
// Now we have authenticated and done any claims transformation we
|
// Now we have authenticated and done any claims transformation we
|
||||||
// can authorise the request
|
// can authorize the request
|
||||||
// We allow the ocelot middleware to be overriden by whatever the
|
// We allow the ocelot middleware to be overriden by whatever the
|
||||||
// user wants
|
// user wants
|
||||||
if (pipelineConfiguration.AuthorisationMiddleware == null)
|
if (pipelineConfiguration.AuthorizationMiddleware == null)
|
||||||
{
|
{
|
||||||
app.UseAuthorisationMiddleware();
|
app.UseAuthorizationMiddleware();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
app.Use(pipelineConfiguration.AuthorisationMiddleware);
|
app.Use(pipelineConfiguration.AuthorizationMiddleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we can run the claims to headers transformation middleware
|
// Now we can run the claims to headers transformation middleware
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<NoPackageAnalysis>true</NoPackageAnalysis>
|
<NoPackageAnalysis>true</NoPackageAnalysis>
|
||||||
<Description>Ocelot is an API Gateway. The project is aimed at people using .NET running a micro services / service orientated architecture that need a unified point of entry into their system. In particular I want easy integration with IdentityServer reference and bearer tokens. reference tokens. Ocelot is a bunch of middlewares in a specific order. Ocelot manipulates the HttpRequest object into a state specified by its configuration until it reaches a request builder middleware where it creates a HttpRequestMessage object which is used to make a request to a downstream service. The middleware that makes the request is the last thing in the Ocelot pipeline. It does not call the next middleware. The response from the downstream service is stored in a per request scoped repository and retrived as the requests goes back up the Ocelot pipeline. There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client. That is basically it with a bunch of other features.</Description>
|
<Description>Ocelot is an API Gateway. The project is aimed at people using .NET running a micro services / service orientated architecture that need a unified point of entry into their system. In particular I want easy integration with IdentityServer reference and bearer tokens. reference tokens. Ocelot is a bunch of middlewares in a specific order. Ocelot manipulates the HttpRequest object into a state specified by its configuration until it reaches a request builder middleware where it creates a HttpRequestMessage object which is used to make a request to a downstream service. The middleware that makes the request is the last thing in the Ocelot pipeline. It does not call the next middleware. The response from the downstream service is stored in a per request scoped repository and retrived as the requests goes back up the Ocelot pipeline. There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client. That is basically it with a bunch of other features.</Description>
|
||||||
<AssemblyTitle>Ocelot</AssemblyTitle>
|
<AssemblyTitle>Ocelot</AssemblyTitle>
|
||||||
@ -24,12 +24,12 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentValidation" Version="8.6.2" />
|
<PackageReference Include="FluentValidation" Version="9.3.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="3.1.3" />
|
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="3.1.3">
|
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="3.1.10">
|
||||||
<NoWarn>NU1701</NoWarn>
|
<NoWarn>NU1701</NoWarn>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.3" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.0" />
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -14,8 +14,8 @@ namespace Ocelot.Responder
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (errors.Any(e => e.Code == OcelotErrorCode.UnauthorizedError
|
if (errors.Any(e => e.Code == OcelotErrorCode.UnauthorizedError
|
||||||
|| e.Code == OcelotErrorCode.ClaimValueNotAuthorisedError
|
|| e.Code == OcelotErrorCode.ClaimValueNotAuthorizedError
|
||||||
|| e.Code == OcelotErrorCode.ScopeNotAuthorisedError
|
|| e.Code == OcelotErrorCode.ScopeNotAuthorizedError
|
||||||
|| e.Code == OcelotErrorCode.UserDoesNotHaveClaimError
|
|| e.Code == OcelotErrorCode.UserDoesNotHaveClaimError
|
||||||
|| e.Code == OcelotErrorCode.CannotFindClaimError))
|
|| e.Code == OcelotErrorCode.CannotFindClaimError))
|
||||||
{
|
{
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
using Ocelot.Errors;
|
using Ocelot.Errors;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ocelot.Responses
|
namespace Ocelot.Responses
|
||||||
{
|
{
|
||||||
public abstract class Response
|
public abstract class Response
|
||||||
{
|
{
|
||||||
protected Response()
|
protected Response()
|
||||||
{
|
{
|
||||||
Errors = new List<Error>();
|
Errors = new List<Error>();
|
||||||
}
|
|
||||||
|
|
||||||
protected Response(List<Error> errors)
|
|
||||||
{
|
|
||||||
Errors = errors ?? new List<Error>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Error> Errors { get; }
|
protected Response(List<Error> errors)
|
||||||
|
{
|
||||||
public bool IsError => Errors.Count > 0;
|
Errors = errors ?? new List<Error>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Error> Errors { get; }
|
||||||
|
|
||||||
|
public bool IsError => Errors.Count > 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,119 +1,119 @@
|
|||||||
namespace Ocelot.AcceptanceTests
|
namespace Ocelot.AcceptanceTests
|
||||||
{
|
{
|
||||||
using IdentityServer4.AccessTokenValidation;
|
using IdentityServer4.AccessTokenValidation;
|
||||||
using IdentityServer4.Models;
|
using IdentityServer4.Models;
|
||||||
using IdentityServer4.Test;
|
using IdentityServer4.Test;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
public class AuthenticationTests : IDisposable
|
public class AuthenticationTests : IDisposable
|
||||||
{
|
{
|
||||||
private readonly Steps _steps;
|
private readonly Steps _steps;
|
||||||
private IWebHost _identityServerBuilder;
|
private IWebHost _identityServerBuilder;
|
||||||
private string _identityServerRootUrl;
|
private string _identityServerRootUrl;
|
||||||
private string _downstreamServicePath = "/";
|
private string _downstreamServicePath = "/";
|
||||||
private string _downstreamServiceHost = "localhost";
|
private string _downstreamServiceHost = "localhost";
|
||||||
private string _downstreamServiceScheme = "http";
|
private string _downstreamServiceScheme = "http";
|
||||||
private string _downstreamServiceUrl = "http://localhost:";
|
private string _downstreamServiceUrl = "http://localhost:";
|
||||||
private readonly Action<IdentityServerAuthenticationOptions> _options;
|
private readonly Action<IdentityServerAuthenticationOptions> _options;
|
||||||
private readonly ServiceHandler _serviceHandler;
|
private readonly ServiceHandler _serviceHandler;
|
||||||
|
|
||||||
public AuthenticationTests()
|
public AuthenticationTests()
|
||||||
{
|
{
|
||||||
_serviceHandler = new ServiceHandler();
|
_serviceHandler = new ServiceHandler();
|
||||||
_steps = new Steps();
|
_steps = new Steps();
|
||||||
var identityServerPort = RandomPortFinder.GetRandomPort();
|
var identityServerPort = RandomPortFinder.GetRandomPort();
|
||||||
_identityServerRootUrl = $"http://localhost:{identityServerPort}";
|
_identityServerRootUrl = $"http://localhost:{identityServerPort}";
|
||||||
_options = o =>
|
_options = o =>
|
||||||
{
|
{
|
||||||
o.Authority = _identityServerRootUrl;
|
o.Authority = _identityServerRootUrl;
|
||||||
o.ApiName = "api";
|
o.ApiName = "api";
|
||||||
o.RequireHttpsMetadata = false;
|
o.RequireHttpsMetadata = false;
|
||||||
o.SupportedTokens = SupportedTokens.Both;
|
o.SupportedTokens = SupportedTokens.Both;
|
||||||
o.ApiSecret = "secret";
|
o.ApiSecret = "secret";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_401_using_identity_server_access_token()
|
public void should_return_401_using_identity_server_access_token()
|
||||||
{
|
{
|
||||||
int port = RandomPortFinder.GetRandomPort();
|
int port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
Routes = new List<FileRoute>
|
Routes = new List<FileRoute>
|
||||||
{
|
{
|
||||||
new FileRoute
|
new FileRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = _downstreamServicePath,
|
DownstreamPathTemplate = _downstreamServicePath,
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
{
|
{
|
||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host =_downstreamServiceHost,
|
Host =_downstreamServiceHost,
|
||||||
Port = port,
|
Port = port,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
DownstreamScheme = _downstreamServiceScheme,
|
DownstreamScheme = _downstreamServiceScheme,
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = new List<string> { "Post" },
|
UpstreamHttpMethod = new List<string> { "Post" },
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
AuthenticationProviderKey = "Test"
|
AuthenticationProviderKey = "Test",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
|
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
|
||||||
.And(x => x.GivenThereIsAServiceRunningOn($"{_downstreamServiceUrl}{port}", 201, string.Empty))
|
.And(x => x.GivenThereIsAServiceRunningOn($"{_downstreamServiceUrl}{port}", 201, string.Empty))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
|
.And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
|
||||||
.And(x => _steps.GivenThePostHasContent("postContent"))
|
.And(x => _steps.GivenThePostHasContent("postContent"))
|
||||||
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_response_200_using_identity_server()
|
public void should_return_response_200_using_identity_server()
|
||||||
{
|
{
|
||||||
int port = RandomPortFinder.GetRandomPort();
|
int port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
Routes = new List<FileRoute>
|
Routes = new List<FileRoute>
|
||||||
{
|
{
|
||||||
new FileRoute
|
new FileRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = _downstreamServicePath,
|
DownstreamPathTemplate = _downstreamServicePath,
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
{
|
{
|
||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host =_downstreamServiceHost,
|
Host =_downstreamServiceHost,
|
||||||
Port = port,
|
Port = port,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
DownstreamScheme = _downstreamServiceScheme,
|
DownstreamScheme = _downstreamServiceScheme,
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = new List<string> { "Get" },
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
AuthenticationProviderKey = "Test"
|
AuthenticationProviderKey = "Test",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
|
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
|
||||||
@ -125,38 +125,38 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_response_401_using_identity_server_with_token_requested_for_other_api()
|
public void should_return_response_401_using_identity_server_with_token_requested_for_other_api()
|
||||||
{
|
{
|
||||||
int port = RandomPortFinder.GetRandomPort();
|
int port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
Routes = new List<FileRoute>
|
Routes = new List<FileRoute>
|
||||||
{
|
{
|
||||||
new FileRoute
|
new FileRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = _downstreamServicePath,
|
DownstreamPathTemplate = _downstreamServicePath,
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
{
|
{
|
||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host =_downstreamServiceHost,
|
Host =_downstreamServiceHost,
|
||||||
Port = port,
|
Port = port,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
DownstreamScheme = _downstreamServiceScheme,
|
DownstreamScheme = _downstreamServiceScheme,
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = new List<string> { "Get" },
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
AuthenticationProviderKey = "Test"
|
AuthenticationProviderKey = "Test",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
|
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
|
||||||
@ -167,38 +167,38 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
|
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_201_using_identity_server_access_token()
|
public void should_return_201_using_identity_server_access_token()
|
||||||
{
|
{
|
||||||
int port = RandomPortFinder.GetRandomPort();
|
int port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
Routes = new List<FileRoute>
|
Routes = new List<FileRoute>
|
||||||
{
|
{
|
||||||
new FileRoute
|
new FileRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = _downstreamServicePath,
|
DownstreamPathTemplate = _downstreamServicePath,
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
{
|
{
|
||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host =_downstreamServiceHost,
|
Host =_downstreamServiceHost,
|
||||||
Port = port,
|
Port = port,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
DownstreamScheme = _downstreamServiceScheme,
|
DownstreamScheme = _downstreamServiceScheme,
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = new List<string> { "Post" },
|
UpstreamHttpMethod = new List<string> { "Post" },
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
AuthenticationProviderKey = "Test"
|
AuthenticationProviderKey = "Test",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
|
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
|
||||||
@ -210,38 +210,38 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.And(x => _steps.GivenThePostHasContent("postContent"))
|
.And(x => _steps.GivenThePostHasContent("postContent"))
|
||||||
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_201_using_identity_server_reference_token()
|
public void should_return_201_using_identity_server_reference_token()
|
||||||
{
|
{
|
||||||
int port = RandomPortFinder.GetRandomPort();
|
int port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
Routes = new List<FileRoute>
|
Routes = new List<FileRoute>
|
||||||
{
|
{
|
||||||
new FileRoute
|
new FileRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = _downstreamServicePath,
|
DownstreamPathTemplate = _downstreamServicePath,
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
{
|
{
|
||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host =_downstreamServiceHost,
|
Host =_downstreamServiceHost,
|
||||||
Port = port,
|
Port = port,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
DownstreamScheme = _downstreamServiceScheme,
|
DownstreamScheme = _downstreamServiceScheme,
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = new List<string> { "Post" },
|
UpstreamHttpMethod = new List<string> { "Post" },
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
AuthenticationProviderKey = "Test"
|
AuthenticationProviderKey = "Test"
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Reference))
|
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Reference))
|
||||||
@ -253,126 +253,131 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.And(x => _steps.GivenThePostHasContent("postContent"))
|
.And(x => _steps.GivenThePostHasContent("postContent"))
|
||||||
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)
|
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)
|
||||||
{
|
{
|
||||||
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
|
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
|
||||||
{
|
{
|
||||||
context.Response.StatusCode = statusCode;
|
context.Response.StatusCode = statusCode;
|
||||||
await context.Response.WriteAsync(responseBody);
|
await context.Response.WriteAsync(responseBody);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenThereIsAnIdentityServerOn(string url, string apiName, string api2Name, AccessTokenType tokenType)
|
private void GivenThereIsAnIdentityServerOn(string url, string apiName, string api2Name, AccessTokenType tokenType)
|
||||||
{
|
{
|
||||||
_identityServerBuilder = new WebHostBuilder()
|
_identityServerBuilder = new WebHostBuilder()
|
||||||
.UseUrls(url)
|
.UseUrls(url)
|
||||||
.UseKestrel()
|
.UseKestrel()
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
.UseIISIntegration()
|
.UseIISIntegration()
|
||||||
.UseUrls(url)
|
.UseUrls(url)
|
||||||
.ConfigureServices(services =>
|
.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
services.AddLogging();
|
services.AddLogging();
|
||||||
services.AddIdentityServer()
|
services.AddIdentityServer()
|
||||||
.AddDeveloperSigningCredential()
|
.AddDeveloperSigningCredential()
|
||||||
.AddInMemoryApiResources(new List<ApiResource>
|
.AddInMemoryApiScopes(new List<ApiScope>
|
||||||
{
|
{
|
||||||
new ApiResource
|
new ApiScope(apiName, "test"),
|
||||||
{
|
new ApiScope(api2Name, "test"),
|
||||||
Name = apiName,
|
})
|
||||||
Description = "My API",
|
.AddInMemoryApiResources(new List<ApiResource>
|
||||||
Enabled = true,
|
{
|
||||||
DisplayName = "test",
|
new ApiResource
|
||||||
Scopes = new List<Scope>()
|
{
|
||||||
{
|
Name = apiName,
|
||||||
new Scope("api"),
|
Description = "My API",
|
||||||
new Scope("api.readOnly"),
|
Enabled = true,
|
||||||
new Scope("openid"),
|
DisplayName = "test",
|
||||||
new Scope("offline_access")
|
Scopes = new List<string>()
|
||||||
},
|
{
|
||||||
ApiSecrets = new List<Secret>()
|
"api",
|
||||||
{
|
"api.readOnly",
|
||||||
new Secret
|
"openid",
|
||||||
{
|
"offline_access",
|
||||||
Value = "secret".Sha256()
|
},
|
||||||
}
|
ApiSecrets = new List<Secret>()
|
||||||
},
|
{
|
||||||
UserClaims = new List<string>()
|
new Secret
|
||||||
{
|
{
|
||||||
"CustomerId", "LocationId"
|
Value = "secret".Sha256(),
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
new ApiResource
|
UserClaims = new List<string>()
|
||||||
{
|
{
|
||||||
Name = api2Name,
|
"CustomerId", "LocationId",
|
||||||
Description = "My second API",
|
},
|
||||||
Enabled = true,
|
},
|
||||||
DisplayName = "second test",
|
new ApiResource
|
||||||
Scopes = new List<Scope>()
|
{
|
||||||
{
|
Name = api2Name,
|
||||||
new Scope("api2"),
|
Description = "My second API",
|
||||||
new Scope("api2.readOnly"),
|
Enabled = true,
|
||||||
},
|
DisplayName = "second test",
|
||||||
ApiSecrets = new List<Secret>()
|
Scopes = new List<string>()
|
||||||
{
|
{
|
||||||
new Secret
|
"api2",
|
||||||
{
|
"api2.readOnly",
|
||||||
Value = "secret".Sha256()
|
},
|
||||||
}
|
ApiSecrets = new List<Secret>()
|
||||||
},
|
{
|
||||||
UserClaims = new List<string>()
|
new Secret
|
||||||
{
|
{
|
||||||
"CustomerId", "LocationId"
|
Value = "secret".Sha256(),
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
})
|
UserClaims = new List<string>()
|
||||||
.AddInMemoryClients(new List<Client>
|
{
|
||||||
{
|
"CustomerId", "LocationId",
|
||||||
new Client
|
},
|
||||||
{
|
},
|
||||||
ClientId = "client",
|
})
|
||||||
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
.AddInMemoryClients(new List<Client>
|
||||||
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
|
{
|
||||||
AllowedScopes = new List<string> { apiName, api2Name, "api.readOnly", "openid", "offline_access" },
|
new Client
|
||||||
AccessTokenType = tokenType,
|
{
|
||||||
Enabled = true,
|
ClientId = "client",
|
||||||
RequireClientSecret = false
|
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
||||||
}
|
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
|
||||||
})
|
AllowedScopes = new List<string> { apiName, api2Name, "api.readOnly", "openid", "offline_access" },
|
||||||
.AddTestUsers(new List<TestUser>
|
AccessTokenType = tokenType,
|
||||||
{
|
Enabled = true,
|
||||||
new TestUser
|
RequireClientSecret = false,
|
||||||
{
|
},
|
||||||
Username = "test",
|
})
|
||||||
Password = "test",
|
.AddTestUsers(new List<TestUser>
|
||||||
SubjectId = "registered|1231231",
|
{
|
||||||
Claims = new List<Claim>
|
new TestUser
|
||||||
{
|
{
|
||||||
new Claim("CustomerId", "123"),
|
Username = "test",
|
||||||
new Claim("LocationId", "321")
|
Password = "test",
|
||||||
}
|
SubjectId = "registered|1231231",
|
||||||
}
|
Claims = new List<Claim>
|
||||||
});
|
{
|
||||||
})
|
new Claim("CustomerId", "123"),
|
||||||
.Configure(app =>
|
new Claim("LocationId", "321"),
|
||||||
{
|
},
|
||||||
app.UseIdentityServer();
|
},
|
||||||
})
|
});
|
||||||
.Build();
|
})
|
||||||
|
.Configure(app =>
|
||||||
_identityServerBuilder.Start();
|
{
|
||||||
|
app.UseIdentityServer();
|
||||||
_steps.VerifyIdentiryServerStarted(url);
|
})
|
||||||
}
|
.Build();
|
||||||
|
|
||||||
public void Dispose()
|
_identityServerBuilder.Start();
|
||||||
{
|
|
||||||
_serviceHandler.Dispose();
|
_steps.VerifyIdentiryServerStarted(url);
|
||||||
_steps.Dispose();
|
}
|
||||||
_identityServerBuilder?.Dispose();
|
|
||||||
}
|
public void Dispose()
|
||||||
}
|
{
|
||||||
|
_serviceHandler.Dispose();
|
||||||
|
_steps.Dispose();
|
||||||
|
_identityServerBuilder?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,91 +1,91 @@
|
|||||||
namespace Ocelot.AcceptanceTests
|
namespace Ocelot.AcceptanceTests
|
||||||
{
|
{
|
||||||
using IdentityServer4.AccessTokenValidation;
|
using IdentityServer4.AccessTokenValidation;
|
||||||
using IdentityServer4.Models;
|
using IdentityServer4.Models;
|
||||||
using IdentityServer4.Test;
|
using IdentityServer4.Test;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
public class AuthorisationTests : IDisposable
|
public class AuthorizationTests : IDisposable
|
||||||
{
|
{
|
||||||
private IWebHost _identityServerBuilder;
|
private IWebHost _identityServerBuilder;
|
||||||
private readonly Steps _steps;
|
private readonly Steps _steps;
|
||||||
private readonly Action<IdentityServerAuthenticationOptions> _options;
|
private readonly Action<IdentityServerAuthenticationOptions> _options;
|
||||||
private string _identityServerRootUrl;
|
private string _identityServerRootUrl;
|
||||||
private readonly ServiceHandler _serviceHandler;
|
private readonly ServiceHandler _serviceHandler;
|
||||||
|
|
||||||
public AuthorisationTests()
|
public AuthorizationTests()
|
||||||
{
|
{
|
||||||
_serviceHandler = new ServiceHandler();
|
_serviceHandler = new ServiceHandler();
|
||||||
_steps = new Steps();
|
_steps = new Steps();
|
||||||
var identityServerPort = RandomPortFinder.GetRandomPort();
|
var identityServerPort = RandomPortFinder.GetRandomPort();
|
||||||
_identityServerRootUrl = $"http://localhost:{identityServerPort}";
|
_identityServerRootUrl = $"http://localhost:{identityServerPort}";
|
||||||
_options = o =>
|
_options = o =>
|
||||||
{
|
|
||||||
o.Authority = _identityServerRootUrl;
|
|
||||||
o.ApiName = "api";
|
|
||||||
o.RequireHttpsMetadata = false;
|
|
||||||
o.SupportedTokens = SupportedTokens.Both;
|
|
||||||
o.ApiSecret = "secret";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void should_return_response_200_authorising_route()
|
|
||||||
{
|
|
||||||
int port = RandomPortFinder.GetRandomPort();
|
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
|
||||||
{
|
{
|
||||||
Routes = new List<FileRoute>
|
o.Authority = _identityServerRootUrl;
|
||||||
{
|
o.ApiName = "api";
|
||||||
new FileRoute
|
o.RequireHttpsMetadata = false;
|
||||||
{
|
o.SupportedTokens = SupportedTokens.Both;
|
||||||
DownstreamPathTemplate = "/",
|
o.ApiSecret = "secret";
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
};
|
||||||
{
|
}
|
||||||
new FileHostAndPort
|
|
||||||
{
|
[Fact]
|
||||||
Host = "localhost",
|
public void should_return_response_200_authorizing_route()
|
||||||
Port = port,
|
{
|
||||||
}
|
int port = RandomPortFinder.GetRandomPort();
|
||||||
},
|
|
||||||
DownstreamScheme = "http",
|
var configuration = new FileConfiguration
|
||||||
UpstreamPathTemplate = "/",
|
{
|
||||||
UpstreamHttpMethod = new List<string> { "Get" },
|
Routes = new List<FileRoute>
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
{
|
||||||
{
|
new FileRoute
|
||||||
AuthenticationProviderKey = "Test"
|
{
|
||||||
},
|
DownstreamPathTemplate = "/",
|
||||||
AddHeadersToRequest =
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
{
|
{
|
||||||
{"CustomerId", "Claims[CustomerId] > value"},
|
new FileHostAndPort
|
||||||
{"LocationId", "Claims[LocationId] > value"},
|
{
|
||||||
{"UserType", "Claims[sub] > value[0] > |"},
|
Host = "localhost",
|
||||||
{"UserId", "Claims[sub] > value[1] > |"}
|
Port = port,
|
||||||
},
|
},
|
||||||
AddClaimsToRequest =
|
},
|
||||||
{
|
DownstreamScheme = "http",
|
||||||
{"CustomerId", "Claims[CustomerId] > value"},
|
UpstreamPathTemplate = "/",
|
||||||
{"UserType", "Claims[sub] > value[0] > |"},
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
{"UserId", "Claims[sub] > value[1] > |"}
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
},
|
{
|
||||||
RouteClaimsRequirement =
|
AuthenticationProviderKey = "Test",
|
||||||
{
|
},
|
||||||
{"UserType", "registered"}
|
AddHeadersToRequest =
|
||||||
}
|
{
|
||||||
}
|
{"CustomerId", "Claims[CustomerId] > value"},
|
||||||
}
|
{"LocationId", "Claims[LocationId] > value"},
|
||||||
|
{"UserType", "Claims[sub] > value[0] > |"},
|
||||||
|
{"UserId", "Claims[sub] > value[1] > |"},
|
||||||
|
},
|
||||||
|
AddClaimsToRequest =
|
||||||
|
{
|
||||||
|
{"CustomerId", "Claims[CustomerId] > value"},
|
||||||
|
{"UserType", "Claims[sub] > value[0] > |"},
|
||||||
|
{"UserId", "Claims[sub] > value[1] > |"},
|
||||||
|
},
|
||||||
|
RouteClaimsRequirement =
|
||||||
|
{
|
||||||
|
{"UserType", "registered"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt))
|
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt))
|
||||||
@ -97,54 +97,54 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_response_403_authorising_route()
|
public void should_return_response_403_authorizing_route()
|
||||||
{
|
{
|
||||||
int port = RandomPortFinder.GetRandomPort();
|
int port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
Routes = new List<FileRoute>
|
Routes = new List<FileRoute>
|
||||||
{
|
{
|
||||||
new FileRoute
|
new FileRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "/",
|
DownstreamPathTemplate = "/",
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
{
|
{
|
||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = port,
|
Port = port,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = new List<string> { "Get" },
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
AuthenticationProviderKey = "Test"
|
AuthenticationProviderKey = "Test",
|
||||||
},
|
},
|
||||||
AddHeadersToRequest =
|
AddHeadersToRequest =
|
||||||
{
|
{
|
||||||
{"CustomerId", "Claims[CustomerId] > value"},
|
{"CustomerId", "Claims[CustomerId] > value"},
|
||||||
{"LocationId", "Claims[LocationId] > value"},
|
{"LocationId", "Claims[LocationId] > value"},
|
||||||
{"UserType", "Claims[sub] > value[0] > |"},
|
{"UserType", "Claims[sub] > value[0] > |"},
|
||||||
{"UserId", "Claims[sub] > value[1] > |"}
|
{"UserId", "Claims[sub] > value[1] > |"},
|
||||||
},
|
},
|
||||||
AddClaimsToRequest =
|
AddClaimsToRequest =
|
||||||
{
|
{
|
||||||
{"CustomerId", "Claims[CustomerId] > value"},
|
{"CustomerId", "Claims[CustomerId] > value"},
|
||||||
{"UserId", "Claims[sub] > value[1] > |"}
|
{"UserId", "Claims[sub] > value[1] > |"},
|
||||||
},
|
},
|
||||||
RouteClaimsRequirement =
|
RouteClaimsRequirement =
|
||||||
{
|
{
|
||||||
{"UserType", "registered"}
|
{"UserType", "registered"},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt))
|
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt))
|
||||||
@ -155,39 +155,39 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
|
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_response_200_using_identity_server_with_allowed_scope()
|
public void should_return_response_200_using_identity_server_with_allowed_scope()
|
||||||
{
|
{
|
||||||
int port = RandomPortFinder.GetRandomPort();
|
int port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
Routes = new List<FileRoute>
|
Routes = new List<FileRoute>
|
||||||
{
|
{
|
||||||
new FileRoute
|
new FileRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "/",
|
DownstreamPathTemplate = "/",
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
{
|
{
|
||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = port,
|
Port = port,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = new List<string> { "Get" },
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
AuthenticationProviderKey = "Test",
|
AuthenticationProviderKey = "Test",
|
||||||
AllowedScopes = new List<string>{ "api", "api.readOnly", "openid", "offline_access" },
|
AllowedScopes = new List<string>{ "api", "api.readOnly", "openid", "offline_access" },
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt))
|
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt))
|
||||||
@ -198,39 +198,39 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
|
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_response_403_using_identity_server_with_scope_not_allowed()
|
public void should_return_response_403_using_identity_server_with_scope_not_allowed()
|
||||||
{
|
{
|
||||||
int port = RandomPortFinder.GetRandomPort();
|
int port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
Routes = new List<FileRoute>
|
Routes = new List<FileRoute>
|
||||||
{
|
{
|
||||||
new FileRoute
|
new FileRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "/",
|
DownstreamPathTemplate = "/",
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
{
|
{
|
||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = port,
|
Port = port,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = new List<string> { "Get" },
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
AuthenticationProviderKey = "Test",
|
AuthenticationProviderKey = "Test",
|
||||||
AllowedScopes = new List<string>{ "api", "openid", "offline_access" },
|
AllowedScopes = new List<string>{ "api", "openid", "offline_access" },
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt))
|
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt))
|
||||||
@ -241,57 +241,57 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
|
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_fix_issue_240()
|
public void should_fix_issue_240()
|
||||||
{
|
{
|
||||||
int port = RandomPortFinder.GetRandomPort();
|
int port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
Routes = new List<FileRoute>
|
Routes = new List<FileRoute>
|
||||||
{
|
{
|
||||||
new FileRoute
|
new FileRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "/",
|
DownstreamPathTemplate = "/",
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
{
|
{
|
||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = port,
|
Port = port,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = new List<string> { "Get" },
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
AuthenticationProviderKey = "Test"
|
AuthenticationProviderKey = "Test",
|
||||||
},
|
},
|
||||||
RouteClaimsRequirement =
|
RouteClaimsRequirement =
|
||||||
{
|
{
|
||||||
{"Role", "User"}
|
{"Role", "User"},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var users = new List<TestUser>
|
var users = new List<TestUser>
|
||||||
{
|
{
|
||||||
new TestUser
|
new TestUser
|
||||||
{
|
{
|
||||||
Username = "test",
|
Username = "test",
|
||||||
Password = "test",
|
Password = "test",
|
||||||
SubjectId = "registered|1231231",
|
SubjectId = "registered|1231231",
|
||||||
Claims = new List<Claim>
|
Claims = new List<Claim>
|
||||||
{
|
{
|
||||||
new Claim("Role", "AdminUser"),
|
new Claim("Role", "AdminUser"),
|
||||||
new Claim("Role", "User")
|
new Claim("Role", "User"),
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, users))
|
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, users))
|
||||||
@ -303,170 +303,181 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)
|
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)
|
||||||
{
|
{
|
||||||
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
|
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
|
||||||
{
|
{
|
||||||
context.Response.StatusCode = statusCode;
|
context.Response.StatusCode = statusCode;
|
||||||
await context.Response.WriteAsync(responseBody);
|
await context.Response.WriteAsync(responseBody);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType)
|
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType)
|
||||||
{
|
{
|
||||||
_identityServerBuilder = new WebHostBuilder()
|
_identityServerBuilder = new WebHostBuilder()
|
||||||
.UseUrls(url)
|
.UseUrls(url)
|
||||||
.UseKestrel()
|
.UseKestrel()
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
.UseIISIntegration()
|
.UseIISIntegration()
|
||||||
.UseUrls(url)
|
.UseUrls(url)
|
||||||
.ConfigureServices(services =>
|
.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
services.AddLogging();
|
services.AddLogging();
|
||||||
services.AddIdentityServer()
|
services.AddIdentityServer()
|
||||||
.AddDeveloperSigningCredential()
|
.AddDeveloperSigningCredential()
|
||||||
.AddInMemoryApiResources(new List<ApiResource>
|
.AddInMemoryApiScopes(new List<ApiScope>
|
||||||
{
|
{
|
||||||
new ApiResource
|
new ApiScope(apiName, "test"),
|
||||||
{
|
new ApiScope("openid", "test"),
|
||||||
Name = apiName,
|
new ApiScope("offline_access", "test"),
|
||||||
Description = "My API",
|
new ApiScope("api.readOnly", "test"),
|
||||||
Enabled = true,
|
})
|
||||||
DisplayName = "test",
|
.AddInMemoryApiResources(new List<ApiResource>
|
||||||
Scopes = new List<Scope>()
|
{
|
||||||
{
|
new ApiResource
|
||||||
new Scope("api"),
|
{
|
||||||
new Scope("api.readOnly"),
|
Name = apiName,
|
||||||
new Scope("openid"),
|
Description = "My API",
|
||||||
new Scope("offline_access")
|
Enabled = true,
|
||||||
},
|
DisplayName = "test",
|
||||||
ApiSecrets = new List<Secret>()
|
Scopes = new List<string>()
|
||||||
{
|
{
|
||||||
new Secret
|
"api",
|
||||||
{
|
"api.readOnly",
|
||||||
Value = "secret".Sha256()
|
"openid",
|
||||||
}
|
"offline_access",
|
||||||
},
|
},
|
||||||
UserClaims = new List<string>()
|
ApiSecrets = new List<Secret>()
|
||||||
{
|
{
|
||||||
"CustomerId", "LocationId", "UserType", "UserId"
|
new Secret
|
||||||
}
|
{
|
||||||
},
|
Value = "secret".Sha256(),
|
||||||
})
|
},
|
||||||
.AddInMemoryClients(new List<Client>
|
},
|
||||||
{
|
UserClaims = new List<string>()
|
||||||
new Client
|
{
|
||||||
{
|
"CustomerId", "LocationId", "UserType", "UserId",
|
||||||
ClientId = "client",
|
},
|
||||||
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
},
|
||||||
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
|
})
|
||||||
AllowedScopes = new List<string> { apiName, "api.readOnly", "openid", "offline_access" },
|
.AddInMemoryClients(new List<Client>
|
||||||
AccessTokenType = tokenType,
|
{
|
||||||
Enabled = true,
|
new Client
|
||||||
RequireClientSecret = false
|
{
|
||||||
}
|
ClientId = "client",
|
||||||
})
|
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
||||||
.AddTestUsers(new List<TestUser>
|
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
|
||||||
{
|
AllowedScopes = new List<string> { apiName, "api.readOnly", "openid", "offline_access" },
|
||||||
new TestUser
|
AccessTokenType = tokenType,
|
||||||
{
|
Enabled = true,
|
||||||
Username = "test",
|
RequireClientSecret = false,
|
||||||
Password = "test",
|
},
|
||||||
SubjectId = "registered|1231231",
|
})
|
||||||
Claims = new List<Claim>
|
.AddTestUsers(new List<TestUser>
|
||||||
{
|
{
|
||||||
new Claim("CustomerId", "123"),
|
new TestUser
|
||||||
new Claim("LocationId", "321")
|
{
|
||||||
}
|
Username = "test",
|
||||||
}
|
Password = "test",
|
||||||
});
|
SubjectId = "registered|1231231",
|
||||||
})
|
Claims = new List<Claim>
|
||||||
.Configure(app =>
|
{
|
||||||
{
|
new Claim("CustomerId", "123"),
|
||||||
app.UseIdentityServer();
|
new Claim("LocationId", "321"),
|
||||||
})
|
},
|
||||||
.Build();
|
},
|
||||||
|
});
|
||||||
_identityServerBuilder.Start();
|
})
|
||||||
|
.Configure(app =>
|
||||||
_steps.VerifyIdentiryServerStarted(url);
|
{
|
||||||
}
|
app.UseIdentityServer();
|
||||||
|
})
|
||||||
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, List<TestUser> users)
|
.Build();
|
||||||
{
|
|
||||||
_identityServerBuilder = new WebHostBuilder()
|
_identityServerBuilder.Start();
|
||||||
.UseUrls(url)
|
|
||||||
.UseKestrel()
|
_steps.VerifyIdentiryServerStarted(url);
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
}
|
||||||
.UseIISIntegration()
|
|
||||||
.UseUrls(url)
|
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, List<TestUser> users)
|
||||||
.ConfigureServices(services =>
|
{
|
||||||
{
|
_identityServerBuilder = new WebHostBuilder()
|
||||||
services.AddLogging();
|
.UseUrls(url)
|
||||||
services.AddIdentityServer()
|
.UseKestrel()
|
||||||
.AddDeveloperSigningCredential()
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
.AddInMemoryApiResources(new List<ApiResource>
|
.UseIISIntegration()
|
||||||
{
|
.UseUrls(url)
|
||||||
new ApiResource
|
.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
Name = apiName,
|
services.AddLogging();
|
||||||
Description = "My API",
|
services.AddIdentityServer()
|
||||||
Enabled = true,
|
.AddDeveloperSigningCredential()
|
||||||
DisplayName = "test",
|
.AddInMemoryApiScopes(new List<ApiScope>
|
||||||
Scopes = new List<Scope>()
|
{
|
||||||
{
|
new ApiScope(apiName, "test"),
|
||||||
new Scope("api"),
|
})
|
||||||
new Scope("api.readOnly"),
|
.AddInMemoryApiResources(new List<ApiResource>
|
||||||
new Scope("openid"),
|
{
|
||||||
new Scope("offline_access"),
|
new ApiResource
|
||||||
},
|
{
|
||||||
ApiSecrets = new List<Secret>()
|
Name = apiName,
|
||||||
{
|
Description = "My API",
|
||||||
new Secret
|
Enabled = true,
|
||||||
{
|
DisplayName = "test",
|
||||||
Value = "secret".Sha256()
|
Scopes = new List<string>()
|
||||||
}
|
{
|
||||||
},
|
"api",
|
||||||
UserClaims = new List<string>()
|
"api.readOnly",
|
||||||
{
|
"openid",
|
||||||
"CustomerId", "LocationId", "UserType", "UserId", "Role"
|
"offline_access",
|
||||||
}
|
},
|
||||||
},
|
ApiSecrets = new List<Secret>()
|
||||||
})
|
{
|
||||||
.AddInMemoryClients(new List<Client>
|
new Secret
|
||||||
{
|
{
|
||||||
new Client
|
Value = "secret".Sha256(),
|
||||||
{
|
},
|
||||||
ClientId = "client",
|
},
|
||||||
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
UserClaims = new List<string>()
|
||||||
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
|
{
|
||||||
AllowedScopes = new List<string> { apiName, "api.readOnly", "openid", "offline_access" },
|
"CustomerId", "LocationId", "UserType", "UserId", "Role",
|
||||||
AccessTokenType = tokenType,
|
},
|
||||||
Enabled = true,
|
},
|
||||||
RequireClientSecret = false,
|
})
|
||||||
}
|
.AddInMemoryClients(new List<Client>
|
||||||
})
|
{
|
||||||
.AddTestUsers(users);
|
new Client
|
||||||
})
|
{
|
||||||
.Configure(app =>
|
ClientId = "client",
|
||||||
{
|
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
||||||
app.UseIdentityServer();
|
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
|
||||||
})
|
AllowedScopes = new List<string> { apiName, "api.readOnly", "openid", "offline_access" },
|
||||||
.Build();
|
AccessTokenType = tokenType,
|
||||||
|
Enabled = true,
|
||||||
_identityServerBuilder.Start();
|
RequireClientSecret = false,
|
||||||
|
},
|
||||||
_steps.VerifyIdentiryServerStarted(url);
|
})
|
||||||
}
|
.AddTestUsers(users);
|
||||||
|
})
|
||||||
public void Dispose()
|
.Configure(app =>
|
||||||
{
|
{
|
||||||
_serviceHandler?.Dispose();
|
app.UseIdentityServer();
|
||||||
_steps.Dispose();
|
})
|
||||||
_identityServerBuilder?.Dispose();
|
.Build();
|
||||||
}
|
|
||||||
}
|
_identityServerBuilder.Start();
|
||||||
|
|
||||||
|
_steps.VerifyIdentiryServerStarted(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_serviceHandler?.Dispose();
|
||||||
|
_steps.Dispose();
|
||||||
|
_identityServerBuilder?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,11 +1,10 @@
|
|||||||
namespace Ocelot.AcceptanceTests
|
namespace Ocelot.AcceptanceTests
|
||||||
{
|
{
|
||||||
using Butterfly.Client.AspNetCore;
|
using Butterfly.Client.AspNetCore;
|
||||||
using Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Rafty.Infrastructure;
|
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,205 +1,217 @@
|
|||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Ocelot.AcceptanceTests
|
namespace Ocelot.AcceptanceTests
|
||||||
{
|
{
|
||||||
using IdentityServer4.AccessTokenValidation;
|
using IdentityServer4.AccessTokenValidation;
|
||||||
using IdentityServer4.Models;
|
using IdentityServer4.Models;
|
||||||
using IdentityServer4.Test;
|
using IdentityServer4.Test;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
|
|
||||||
public class ClaimsToDownstreamPathTests : IDisposable
|
public class ClaimsToDownstreamPathTests : IDisposable
|
||||||
{
|
{
|
||||||
private IWebHost _servicebuilder;
|
private IWebHost _servicebuilder;
|
||||||
private IWebHost _identityServerBuilder;
|
private IWebHost _identityServerBuilder;
|
||||||
private readonly Steps _steps;
|
private readonly Steps _steps;
|
||||||
private Action<IdentityServerAuthenticationOptions> _options;
|
private Action<IdentityServerAuthenticationOptions> _options;
|
||||||
private string _identityServerRootUrl;
|
private string _identityServerRootUrl;
|
||||||
private string _downstreamFinalPath;
|
private string _downstreamFinalPath;
|
||||||
|
|
||||||
public ClaimsToDownstreamPathTests()
|
public ClaimsToDownstreamPathTests()
|
||||||
{
|
{
|
||||||
var identityServerPort = RandomPortFinder.GetRandomPort();
|
var identityServerPort = RandomPortFinder.GetRandomPort();
|
||||||
_identityServerRootUrl = $"http://localhost:{identityServerPort}";
|
_identityServerRootUrl = $"http://localhost:{identityServerPort}";
|
||||||
_steps = new Steps();
|
_steps = new Steps();
|
||||||
_options = o =>
|
_options = o =>
|
||||||
{
|
{
|
||||||
o.Authority = _identityServerRootUrl;
|
o.Authority = _identityServerRootUrl;
|
||||||
o.ApiName = "api";
|
o.ApiName = "api";
|
||||||
o.RequireHttpsMetadata = false;
|
o.RequireHttpsMetadata = false;
|
||||||
o.SupportedTokens = SupportedTokens.Both;
|
o.SupportedTokens = SupportedTokens.Both;
|
||||||
o.ApiSecret = "secret";
|
o.ApiSecret = "secret";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_200_and_change_downstream_path()
|
public void should_return_200_and_change_downstream_path()
|
||||||
{
|
{
|
||||||
var user = new TestUser()
|
var user = new TestUser()
|
||||||
{
|
{
|
||||||
Username = "test",
|
Username = "test",
|
||||||
Password = "test",
|
Password = "test",
|
||||||
SubjectId = "registered|1231231",
|
SubjectId = "registered|1231231",
|
||||||
};
|
};
|
||||||
|
|
||||||
int port = RandomPortFinder.GetRandomPort();
|
int port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
Routes = new List<FileRoute>
|
Routes = new List<FileRoute>
|
||||||
{
|
{
|
||||||
new FileRoute
|
new FileRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "/users/{userId}",
|
DownstreamPathTemplate = "/users/{userId}",
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
{
|
{
|
||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = port,
|
Port = port,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
UpstreamPathTemplate = "/users",
|
UpstreamPathTemplate = "/users",
|
||||||
UpstreamHttpMethod = new List<string> { "Get" },
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
AuthenticationProviderKey = "Test",
|
AuthenticationProviderKey = "Test",
|
||||||
AllowedScopes = new List<string>
|
AllowedScopes = new List<string>
|
||||||
{
|
{
|
||||||
"openid", "offline_access", "api",
|
"openid", "offline_access", "api",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ChangeDownstreamPathTemplate =
|
ChangeDownstreamPathTemplate =
|
||||||
{
|
{
|
||||||
{"userId", "Claims[sub] > value[1] > |"},
|
{"userId", "Claims[sub] > value[1] > |"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user))
|
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user))
|
||||||
.And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200))
|
.And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200))
|
||||||
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
|
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
|
.And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
|
||||||
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
|
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/users"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/users"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("UserId: 1231231"))
|
.And(x => _steps.ThenTheResponseBodyShouldBe("UserId: 1231231"))
|
||||||
.And(x => _downstreamFinalPath.ShouldBe("/users/1231231"))
|
.And(x => ThenTheDownstreamPathIs("/users/1231231"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenThereIsAServiceRunningOn(string url, int statusCode)
|
private void ThenTheDownstreamPathIs(string path)
|
||||||
{
|
{
|
||||||
_servicebuilder = new WebHostBuilder()
|
_downstreamFinalPath.ShouldBe(path);
|
||||||
.UseUrls(url)
|
}
|
||||||
.UseKestrel()
|
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
private void GivenThereIsAServiceRunningOn(string url, int statusCode)
|
||||||
.UseIISIntegration()
|
{
|
||||||
.UseUrls(url)
|
_servicebuilder = new WebHostBuilder()
|
||||||
.Configure(app =>
|
.UseUrls(url)
|
||||||
{
|
.UseKestrel()
|
||||||
app.Run(async context =>
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
{
|
.UseIISIntegration()
|
||||||
_downstreamFinalPath = context.Request.Path.Value;
|
.UseUrls(url)
|
||||||
|
.Configure(app =>
|
||||||
string userId = _downstreamFinalPath.Replace("/users/", string.Empty);
|
{
|
||||||
|
app.Run(async context =>
|
||||||
var responseBody = $"UserId: {userId}";
|
{
|
||||||
context.Response.StatusCode = statusCode;
|
_downstreamFinalPath = context.Request.Path.Value;
|
||||||
await context.Response.WriteAsync(responseBody);
|
|
||||||
});
|
string userId = _downstreamFinalPath.Replace("/users/", string.Empty);
|
||||||
})
|
|
||||||
.Build();
|
var responseBody = $"UserId: {userId}";
|
||||||
|
context.Response.StatusCode = statusCode;
|
||||||
_servicebuilder.Start();
|
await context.Response.WriteAsync(responseBody);
|
||||||
}
|
});
|
||||||
|
})
|
||||||
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user)
|
.Build();
|
||||||
{
|
|
||||||
_identityServerBuilder = new WebHostBuilder()
|
_servicebuilder.Start();
|
||||||
.UseUrls(url)
|
}
|
||||||
.UseKestrel()
|
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user)
|
||||||
.UseIISIntegration()
|
{
|
||||||
.UseUrls(url)
|
_identityServerBuilder = new WebHostBuilder()
|
||||||
.ConfigureServices(services =>
|
.UseUrls(url)
|
||||||
{
|
.UseKestrel()
|
||||||
services.AddLogging();
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
services.AddIdentityServer()
|
.UseIISIntegration()
|
||||||
.AddDeveloperSigningCredential()
|
.UseUrls(url)
|
||||||
.AddInMemoryApiResources(new List<ApiResource>
|
.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
new ApiResource
|
services.AddLogging();
|
||||||
{
|
services.AddIdentityServer()
|
||||||
Name = apiName,
|
.AddDeveloperSigningCredential()
|
||||||
Description = "My API",
|
.AddInMemoryApiScopes(new List<ApiScope>
|
||||||
Enabled = true,
|
{
|
||||||
DisplayName = "test",
|
new ApiScope(apiName, "test"),
|
||||||
Scopes = new List<Scope>()
|
new ApiScope("openid", "test"),
|
||||||
{
|
new ApiScope("offline_access", "test"),
|
||||||
new Scope("api"),
|
new ApiScope("api.readOnly", "test"),
|
||||||
new Scope("openid"),
|
})
|
||||||
new Scope("offline_access")
|
.AddInMemoryApiResources(new List<ApiResource>
|
||||||
},
|
{
|
||||||
ApiSecrets = new List<Secret>()
|
new ApiResource
|
||||||
{
|
{
|
||||||
new Secret
|
Name = apiName,
|
||||||
{
|
Description = "My API",
|
||||||
Value = "secret".Sha256()
|
Enabled = true,
|
||||||
}
|
DisplayName = "test",
|
||||||
},
|
Scopes = new List<string>()
|
||||||
UserClaims = new List<string>()
|
{
|
||||||
{
|
"api",
|
||||||
"CustomerId", "LocationId", "UserType", "UserId"
|
"openid",
|
||||||
}
|
"offline_access",
|
||||||
}
|
},
|
||||||
})
|
ApiSecrets = new List<Secret>()
|
||||||
.AddInMemoryClients(new List<Client>
|
{
|
||||||
{
|
new Secret
|
||||||
new Client
|
{
|
||||||
{
|
Value = "secret".Sha256(),
|
||||||
ClientId = "client",
|
},
|
||||||
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
},
|
||||||
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
|
UserClaims = new List<string>()
|
||||||
AllowedScopes = new List<string> { apiName, "openid", "offline_access" },
|
{
|
||||||
AccessTokenType = tokenType,
|
"CustomerId", "LocationId", "UserType", "UserId",
|
||||||
Enabled = true,
|
},
|
||||||
RequireClientSecret = false
|
},
|
||||||
}
|
})
|
||||||
})
|
.AddInMemoryClients(new List<Client>
|
||||||
.AddTestUsers(new List<TestUser>
|
{
|
||||||
{
|
new Client
|
||||||
user
|
{
|
||||||
});
|
ClientId = "client",
|
||||||
})
|
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
||||||
.Configure(app =>
|
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
|
||||||
{
|
AllowedScopes = new List<string> { apiName, "openid", "offline_access" },
|
||||||
app.UseIdentityServer();
|
AccessTokenType = tokenType,
|
||||||
})
|
Enabled = true,
|
||||||
.Build();
|
RequireClientSecret = false,
|
||||||
|
},
|
||||||
_identityServerBuilder.Start();
|
})
|
||||||
|
.AddTestUsers(new List<TestUser>
|
||||||
_steps.VerifyIdentiryServerStarted(url);
|
{
|
||||||
}
|
user,
|
||||||
|
});
|
||||||
public void Dispose()
|
})
|
||||||
{
|
.Configure(app =>
|
||||||
_servicebuilder?.Dispose();
|
{
|
||||||
_steps.Dispose();
|
app.UseIdentityServer();
|
||||||
_identityServerBuilder?.Dispose();
|
})
|
||||||
}
|
.Build();
|
||||||
}
|
|
||||||
}
|
_identityServerBuilder.Start();
|
||||||
|
|
||||||
|
_steps.VerifyIdentiryServerStarted(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_servicebuilder?.Dispose();
|
||||||
|
_steps.Dispose();
|
||||||
|
_identityServerBuilder?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,101 +1,101 @@
|
|||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
||||||
|
|
||||||
namespace Ocelot.AcceptanceTests
|
namespace Ocelot.AcceptanceTests
|
||||||
{
|
{
|
||||||
using IdentityServer4.AccessTokenValidation;
|
using IdentityServer4.AccessTokenValidation;
|
||||||
using IdentityServer4.Models;
|
using IdentityServer4.Models;
|
||||||
using IdentityServer4.Test;
|
using IdentityServer4.Test;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
|
|
||||||
public class ClaimsToHeadersForwardingTests : IDisposable
|
public class ClaimsToHeadersForwardingTests : IDisposable
|
||||||
{
|
{
|
||||||
private IWebHost _identityServerBuilder;
|
private IWebHost _identityServerBuilder;
|
||||||
private readonly Steps _steps;
|
private readonly Steps _steps;
|
||||||
private Action<IdentityServerAuthenticationOptions> _options;
|
private Action<IdentityServerAuthenticationOptions> _options;
|
||||||
private string _identityServerRootUrl;
|
private string _identityServerRootUrl;
|
||||||
private readonly ServiceHandler _serviceHandler;
|
private readonly ServiceHandler _serviceHandler;
|
||||||
|
|
||||||
public ClaimsToHeadersForwardingTests()
|
public ClaimsToHeadersForwardingTests()
|
||||||
{
|
{
|
||||||
_serviceHandler = new ServiceHandler();
|
_serviceHandler = new ServiceHandler();
|
||||||
_steps = new Steps();
|
_steps = new Steps();
|
||||||
var identityServerPort = RandomPortFinder.GetRandomPort();
|
var identityServerPort = RandomPortFinder.GetRandomPort();
|
||||||
_identityServerRootUrl = $"http://localhost:{identityServerPort}";
|
_identityServerRootUrl = $"http://localhost:{identityServerPort}";
|
||||||
_options = o =>
|
_options = o =>
|
||||||
{
|
{
|
||||||
o.Authority = _identityServerRootUrl;
|
o.Authority = _identityServerRootUrl;
|
||||||
o.ApiName = "api";
|
o.ApiName = "api";
|
||||||
o.RequireHttpsMetadata = false;
|
o.RequireHttpsMetadata = false;
|
||||||
o.SupportedTokens = SupportedTokens.Both;
|
o.SupportedTokens = SupportedTokens.Both;
|
||||||
o.ApiSecret = "secret";
|
o.ApiSecret = "secret";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_response_200_and_foward_claim_as_header()
|
public void should_return_response_200_and_foward_claim_as_header()
|
||||||
{
|
{
|
||||||
var user = new TestUser()
|
var user = new TestUser()
|
||||||
{
|
{
|
||||||
Username = "test",
|
Username = "test",
|
||||||
Password = "test",
|
Password = "test",
|
||||||
SubjectId = "registered|1231231",
|
SubjectId = "registered|1231231",
|
||||||
Claims = new List<Claim>
|
Claims = new List<Claim>
|
||||||
{
|
{
|
||||||
new Claim("CustomerId", "123"),
|
new Claim("CustomerId", "123"),
|
||||||
new Claim("LocationId", "1")
|
new Claim("LocationId", "1"),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
int port = RandomPortFinder.GetRandomPort();
|
int port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
Routes = new List<FileRoute>
|
Routes = new List<FileRoute>
|
||||||
{
|
{
|
||||||
new FileRoute
|
new FileRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "/",
|
DownstreamPathTemplate = "/",
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
{
|
{
|
||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = port,
|
Port = port,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = new List<string> { "Get" },
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
AuthenticationProviderKey = "Test",
|
AuthenticationProviderKey = "Test",
|
||||||
AllowedScopes = new List<string>
|
AllowedScopes = new List<string>
|
||||||
{
|
{
|
||||||
"openid", "offline_access", "api"
|
"openid", "offline_access", "api",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AddHeadersToRequest =
|
AddHeadersToRequest =
|
||||||
{
|
{
|
||||||
{"CustomerId", "Claims[CustomerId] > value"},
|
{"CustomerId", "Claims[CustomerId] > value"},
|
||||||
{"LocationId", "Claims[LocationId] > value"},
|
{"LocationId", "Claims[LocationId] > value"},
|
||||||
{"UserType", "Claims[sub] > value[0] > |"},
|
{"UserType", "Claims[sub] > value[0] > |"},
|
||||||
{"UserId", "Claims[sub] > value[1] > |"}
|
{"UserId", "Claims[sub] > value[1] > |"},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user))
|
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user))
|
||||||
@ -107,98 +107,105 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231"))
|
.And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenThereIsAServiceRunningOn(string url, int statusCode)
|
private void GivenThereIsAServiceRunningOn(string url, int statusCode)
|
||||||
{
|
{
|
||||||
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
|
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
|
||||||
{
|
{
|
||||||
var customerId = context.Request.Headers.First(x => x.Key == "CustomerId").Value.First();
|
var customerId = context.Request.Headers.First(x => x.Key == "CustomerId").Value.First();
|
||||||
var locationId = context.Request.Headers.First(x => x.Key == "LocationId").Value.First();
|
var locationId = context.Request.Headers.First(x => x.Key == "LocationId").Value.First();
|
||||||
var userType = context.Request.Headers.First(x => x.Key == "UserType").Value.First();
|
var userType = context.Request.Headers.First(x => x.Key == "UserType").Value.First();
|
||||||
var userId = context.Request.Headers.First(x => x.Key == "UserId").Value.First();
|
var userId = context.Request.Headers.First(x => x.Key == "UserId").Value.First();
|
||||||
|
|
||||||
var responseBody = $"CustomerId: {customerId} LocationId: {locationId} UserType: {userType} UserId: {userId}";
|
var responseBody = $"CustomerId: {customerId} LocationId: {locationId} UserType: {userType} UserId: {userId}";
|
||||||
context.Response.StatusCode = statusCode;
|
context.Response.StatusCode = statusCode;
|
||||||
await context.Response.WriteAsync(responseBody);
|
await context.Response.WriteAsync(responseBody);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user)
|
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user)
|
||||||
{
|
{
|
||||||
_identityServerBuilder = new WebHostBuilder()
|
_identityServerBuilder = new WebHostBuilder()
|
||||||
.UseUrls(url)
|
.UseUrls(url)
|
||||||
.UseKestrel()
|
.UseKestrel()
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
.UseIISIntegration()
|
.UseIISIntegration()
|
||||||
.UseUrls(url)
|
.UseUrls(url)
|
||||||
.ConfigureServices(services =>
|
.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
services.AddLogging();
|
services.AddLogging();
|
||||||
services.AddIdentityServer()
|
services.AddIdentityServer()
|
||||||
.AddDeveloperSigningCredential()
|
.AddDeveloperSigningCredential()
|
||||||
.AddInMemoryApiResources(new List<ApiResource>
|
.AddInMemoryApiScopes(new List<ApiScope>
|
||||||
{
|
{
|
||||||
new ApiResource
|
new ApiScope(apiName, "test"),
|
||||||
{
|
new ApiScope("openid", "test"),
|
||||||
Name = apiName,
|
new ApiScope("offline_access", "test"),
|
||||||
Description = "My API",
|
new ApiScope("api.readOnly", "test"),
|
||||||
Enabled = true,
|
})
|
||||||
DisplayName = "test",
|
.AddInMemoryApiResources(new List<ApiResource>
|
||||||
Scopes = new List<Scope>()
|
{
|
||||||
{
|
new ApiResource
|
||||||
new Scope("api"),
|
{
|
||||||
new Scope("openid"),
|
Name = apiName,
|
||||||
new Scope("offline_access")
|
Description = "My API",
|
||||||
},
|
Enabled = true,
|
||||||
ApiSecrets = new List<Secret>()
|
DisplayName = "test",
|
||||||
{
|
Scopes = new List<string>()
|
||||||
new Secret
|
{
|
||||||
{
|
"api",
|
||||||
Value = "secret".Sha256()
|
"openid",
|
||||||
}
|
"offline_access",
|
||||||
},
|
},
|
||||||
UserClaims = new List<string>()
|
ApiSecrets = new List<Secret>()
|
||||||
{
|
{
|
||||||
"CustomerId", "LocationId", "UserType", "UserId"
|
new Secret
|
||||||
}
|
{
|
||||||
}
|
Value = "secret".Sha256(),
|
||||||
})
|
},
|
||||||
.AddInMemoryClients(new List<Client>
|
},
|
||||||
{
|
UserClaims = new List<string>()
|
||||||
new Client
|
{
|
||||||
{
|
"CustomerId", "LocationId", "UserType", "UserId",
|
||||||
ClientId = "client",
|
},
|
||||||
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
},
|
||||||
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
|
})
|
||||||
AllowedScopes = new List<string> { apiName, "openid", "offline_access" },
|
.AddInMemoryClients(new List<Client>
|
||||||
AccessTokenType = tokenType,
|
{
|
||||||
Enabled = true,
|
new Client
|
||||||
RequireClientSecret = false
|
{
|
||||||
}
|
ClientId = "client",
|
||||||
})
|
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
||||||
.AddTestUsers(new List<TestUser>
|
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
|
||||||
{
|
AllowedScopes = new List<string> { apiName, "openid", "offline_access" },
|
||||||
user
|
AccessTokenType = tokenType,
|
||||||
});
|
Enabled = true,
|
||||||
})
|
RequireClientSecret = false,
|
||||||
.Configure(app =>
|
},
|
||||||
{
|
})
|
||||||
app.UseIdentityServer();
|
.AddTestUsers(new List<TestUser>
|
||||||
})
|
{
|
||||||
.Build();
|
user,
|
||||||
|
});
|
||||||
_identityServerBuilder.Start();
|
})
|
||||||
|
.Configure(app =>
|
||||||
_steps.VerifyIdentiryServerStarted(url);
|
{
|
||||||
}
|
app.UseIdentityServer();
|
||||||
|
})
|
||||||
public void Dispose()
|
.Build();
|
||||||
{
|
|
||||||
_serviceHandler?.Dispose();
|
_identityServerBuilder.Start();
|
||||||
_steps.Dispose();
|
|
||||||
_identityServerBuilder?.Dispose();
|
_steps.VerifyIdentiryServerStarted(url);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_serviceHandler?.Dispose();
|
||||||
|
_steps.Dispose();
|
||||||
|
_identityServerBuilder?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,100 +1,99 @@
|
|||||||
using IdentityServer4.AccessTokenValidation;
|
namespace Ocelot.AcceptanceTests
|
||||||
using IdentityServer4.Models;
|
{
|
||||||
using Microsoft.AspNetCore.Builder;
|
using IdentityServer4.Test;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Shouldly;
|
||||||
using Microsoft.AspNetCore.Http;
|
using IdentityServer4.AccessTokenValidation;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using IdentityServer4.Models;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Ocelot.Configuration.File;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using System;
|
using Microsoft.AspNetCore.Http;
|
||||||
using System.Collections.Generic;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using System.IO;
|
using Microsoft.Extensions.Primitives;
|
||||||
using System.Net;
|
using Ocelot.Configuration.File;
|
||||||
using System.Security.Claims;
|
using System;
|
||||||
using TestStack.BDDfy;
|
using System.Collections.Generic;
|
||||||
using Xunit;
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
namespace Ocelot.AcceptanceTests
|
using System.Security.Claims;
|
||||||
{
|
using TestStack.BDDfy;
|
||||||
using IdentityServer4.Test;
|
using Xunit;
|
||||||
using Shouldly;
|
|
||||||
|
public class ClaimsToQueryStringForwardingTests : IDisposable
|
||||||
public class ClaimsToQueryStringForwardingTests : IDisposable
|
{
|
||||||
{
|
private IWebHost _servicebuilder;
|
||||||
private IWebHost _servicebuilder;
|
private IWebHost _identityServerBuilder;
|
||||||
private IWebHost _identityServerBuilder;
|
private readonly Steps _steps;
|
||||||
private readonly Steps _steps;
|
private Action<IdentityServerAuthenticationOptions> _options;
|
||||||
private Action<IdentityServerAuthenticationOptions> _options;
|
private string _identityServerRootUrl;
|
||||||
private string _identityServerRootUrl;
|
private string _downstreamQueryString;
|
||||||
private string _downstreamQueryString;
|
|
||||||
|
public ClaimsToQueryStringForwardingTests()
|
||||||
public ClaimsToQueryStringForwardingTests()
|
{
|
||||||
{
|
|
||||||
_steps = new Steps();
|
_steps = new Steps();
|
||||||
var identityServerPort = RandomPortFinder.GetRandomPort();
|
var identityServerPort = RandomPortFinder.GetRandomPort();
|
||||||
_identityServerRootUrl = $"http://localhost:{identityServerPort}";
|
_identityServerRootUrl = $"http://localhost:{identityServerPort}";
|
||||||
_options = o =>
|
_options = o =>
|
||||||
{
|
{
|
||||||
o.Authority = _identityServerRootUrl;
|
o.Authority = _identityServerRootUrl;
|
||||||
o.ApiName = "api";
|
o.ApiName = "api";
|
||||||
o.RequireHttpsMetadata = false;
|
o.RequireHttpsMetadata = false;
|
||||||
o.SupportedTokens = SupportedTokens.Both;
|
o.SupportedTokens = SupportedTokens.Both;
|
||||||
o.ApiSecret = "secret";
|
o.ApiSecret = "secret";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_response_200_and_foward_claim_as_query_string()
|
public void should_return_response_200_and_foward_claim_as_query_string()
|
||||||
{
|
{
|
||||||
var user = new TestUser()
|
var user = new TestUser()
|
||||||
{
|
{
|
||||||
Username = "test",
|
Username = "test",
|
||||||
Password = "test",
|
Password = "test",
|
||||||
SubjectId = "registered|1231231",
|
SubjectId = "registered|1231231",
|
||||||
Claims = new List<Claim>
|
Claims = new List<Claim>
|
||||||
{
|
{
|
||||||
new Claim("CustomerId", "123"),
|
new Claim("CustomerId", "123"),
|
||||||
new Claim("LocationId", "1")
|
new Claim("LocationId", "1"),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
int port = RandomPortFinder.GetRandomPort();
|
int port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
Routes = new List<FileRoute>
|
Routes = new List<FileRoute>
|
||||||
{
|
{
|
||||||
new FileRoute
|
new FileRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "/",
|
DownstreamPathTemplate = "/",
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
{
|
{
|
||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = port,
|
Port = port,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = new List<string> { "Get" },
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
AuthenticationProviderKey = "Test",
|
AuthenticationProviderKey = "Test",
|
||||||
AllowedScopes = new List<string>
|
AllowedScopes = new List<string>
|
||||||
{
|
{
|
||||||
"openid", "offline_access", "api"
|
"openid", "offline_access", "api",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AddQueriesToRequest =
|
AddQueriesToRequest =
|
||||||
{
|
{
|
||||||
{"CustomerId", "Claims[CustomerId] > value"},
|
{"CustomerId", "Claims[CustomerId] > value"},
|
||||||
{"LocationId", "Claims[LocationId] > value"},
|
{"LocationId", "Claims[LocationId] > value"},
|
||||||
{"UserType", "Claims[sub] > value[0] > |"},
|
{"UserType", "Claims[sub] > value[0] > |"},
|
||||||
{"UserId", "Claims[sub] > value[1] > |"}
|
{"UserId", "Claims[sub] > value[1] > |"},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user))
|
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user))
|
||||||
@ -106,61 +105,61 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231"))
|
.And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_response_200_and_foward_claim_as_query_string_and_preserve_original_string()
|
public void should_return_response_200_and_foward_claim_as_query_string_and_preserve_original_string()
|
||||||
{
|
{
|
||||||
var user = new TestUser()
|
var user = new TestUser()
|
||||||
{
|
{
|
||||||
Username = "test",
|
Username = "test",
|
||||||
Password = "test",
|
Password = "test",
|
||||||
SubjectId = "registered|1231231",
|
SubjectId = "registered|1231231",
|
||||||
Claims = new List<Claim>
|
Claims = new List<Claim>
|
||||||
{
|
{
|
||||||
new Claim("CustomerId", "123"),
|
new Claim("CustomerId", "123"),
|
||||||
new Claim("LocationId", "1")
|
new Claim("LocationId", "1"),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
int port = RandomPortFinder.GetRandomPort();
|
int port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
Routes = new List<FileRoute>
|
Routes = new List<FileRoute>
|
||||||
{
|
{
|
||||||
new FileRoute
|
new FileRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "/",
|
DownstreamPathTemplate = "/",
|
||||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
{
|
{
|
||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = port,
|
Port = port,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
DownstreamScheme = "http",
|
DownstreamScheme = "http",
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = new List<string> { "Get" },
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
AuthenticationProviderKey = "Test",
|
AuthenticationProviderKey = "Test",
|
||||||
AllowedScopes = new List<string>
|
AllowedScopes = new List<string>
|
||||||
{
|
{
|
||||||
"openid", "offline_access", "api"
|
"openid", "offline_access", "api",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AddQueriesToRequest =
|
AddQueriesToRequest =
|
||||||
{
|
{
|
||||||
{"CustomerId", "Claims[CustomerId] > value"},
|
{"CustomerId", "Claims[CustomerId] > value"},
|
||||||
{"LocationId", "Claims[LocationId] > value"},
|
{"LocationId", "Claims[LocationId] > value"},
|
||||||
{"UserType", "Claims[sub] > value[0] > |"},
|
{"UserType", "Claims[sub] > value[0] > |"},
|
||||||
{"UserId", "Claims[sub] > value[1] > |"}
|
{"UserId", "Claims[sub] > value[1] > |"},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user))
|
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user))
|
||||||
@ -172,120 +171,132 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/?test=1&test=2"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/?test=1&test=2"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231"))
|
.And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231"))
|
||||||
.And(_ => _downstreamQueryString.ShouldBe("?test=1&test=2&CustomerId=123&LocationId=1&UserId=1231231&UserType=registered"))
|
.And(_ => ThenTheQueryStringIs("?test=1&test=2&CustomerId=123&LocationId=1&UserId=1231231&UserType=registered"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenThereIsAServiceRunningOn(string url, int statusCode)
|
private void ThenTheQueryStringIs(string queryString)
|
||||||
{
|
{
|
||||||
_servicebuilder = new WebHostBuilder()
|
_downstreamQueryString.ShouldBe(queryString);
|
||||||
.UseUrls(url)
|
}
|
||||||
.UseKestrel()
|
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
private void GivenThereIsAServiceRunningOn(string url, int statusCode)
|
||||||
.UseIISIntegration()
|
{
|
||||||
.UseUrls(url)
|
_servicebuilder = new WebHostBuilder()
|
||||||
.Configure(app =>
|
.UseUrls(url)
|
||||||
{
|
.UseKestrel()
|
||||||
app.Run(async context =>
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
{
|
.UseIISIntegration()
|
||||||
_downstreamQueryString = context.Request.QueryString.Value;
|
.UseUrls(url)
|
||||||
|
.Configure(app =>
|
||||||
StringValues customerId;
|
{
|
||||||
context.Request.Query.TryGetValue("CustomerId", out customerId);
|
app.Run(async context =>
|
||||||
|
{
|
||||||
StringValues locationId;
|
_downstreamQueryString = context.Request.QueryString.Value;
|
||||||
context.Request.Query.TryGetValue("LocationId", out locationId);
|
|
||||||
|
StringValues customerId;
|
||||||
StringValues userType;
|
context.Request.Query.TryGetValue("CustomerId", out customerId);
|
||||||
context.Request.Query.TryGetValue("UserType", out userType);
|
|
||||||
|
StringValues locationId;
|
||||||
StringValues userId;
|
context.Request.Query.TryGetValue("LocationId", out locationId);
|
||||||
context.Request.Query.TryGetValue("UserId", out userId);
|
|
||||||
|
StringValues userType;
|
||||||
var responseBody = $"CustomerId: {customerId} LocationId: {locationId} UserType: {userType} UserId: {userId}";
|
context.Request.Query.TryGetValue("UserType", out userType);
|
||||||
context.Response.StatusCode = statusCode;
|
|
||||||
await context.Response.WriteAsync(responseBody);
|
StringValues userId;
|
||||||
});
|
context.Request.Query.TryGetValue("UserId", out userId);
|
||||||
})
|
|
||||||
.Build();
|
var responseBody = $"CustomerId: {customerId} LocationId: {locationId} UserType: {userType} UserId: {userId}";
|
||||||
|
context.Response.StatusCode = statusCode;
|
||||||
_servicebuilder.Start();
|
await context.Response.WriteAsync(responseBody);
|
||||||
}
|
});
|
||||||
|
})
|
||||||
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user)
|
.Build();
|
||||||
{
|
|
||||||
_identityServerBuilder = new WebHostBuilder()
|
_servicebuilder.Start();
|
||||||
.UseUrls(url)
|
}
|
||||||
.UseKestrel()
|
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user)
|
||||||
.UseIISIntegration()
|
{
|
||||||
.UseUrls(url)
|
_identityServerBuilder = new WebHostBuilder()
|
||||||
.ConfigureServices(services =>
|
.UseUrls(url)
|
||||||
{
|
.UseKestrel()
|
||||||
services.AddLogging();
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
services.AddIdentityServer()
|
.UseIISIntegration()
|
||||||
.AddDeveloperSigningCredential()
|
.UseUrls(url)
|
||||||
.AddInMemoryApiResources(new List<ApiResource>
|
.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
new ApiResource
|
services.AddLogging();
|
||||||
{
|
services.AddIdentityServer()
|
||||||
Name = apiName,
|
.AddDeveloperSigningCredential()
|
||||||
Description = "My API",
|
.AddInMemoryApiScopes(new List<ApiScope>
|
||||||
Enabled = true,
|
{
|
||||||
DisplayName = "test",
|
new ApiScope(apiName, "test"),
|
||||||
Scopes = new List<Scope>()
|
new ApiScope("openid", "test"),
|
||||||
{
|
new ApiScope("offline_access", "test"),
|
||||||
new Scope("api"),
|
new ApiScope("api.readOnly", "test"),
|
||||||
new Scope("openid"),
|
})
|
||||||
new Scope("offline_access")
|
.AddInMemoryApiResources(new List<ApiResource>
|
||||||
},
|
{
|
||||||
ApiSecrets = new List<Secret>()
|
new ApiResource
|
||||||
{
|
{
|
||||||
new Secret
|
Name = apiName,
|
||||||
{
|
Description = "My API",
|
||||||
Value = "secret".Sha256()
|
Enabled = true,
|
||||||
}
|
DisplayName = "test",
|
||||||
},
|
Scopes = new List<string>()
|
||||||
UserClaims = new List<string>()
|
{
|
||||||
{
|
"api",
|
||||||
"CustomerId", "LocationId", "UserType", "UserId"
|
"openid",
|
||||||
}
|
"offline_access",
|
||||||
}
|
},
|
||||||
})
|
ApiSecrets = new List<Secret>()
|
||||||
.AddInMemoryClients(new List<Client>
|
{
|
||||||
{
|
new Secret
|
||||||
new Client
|
{
|
||||||
{
|
Value = "secret".Sha256(),
|
||||||
ClientId = "client",
|
},
|
||||||
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
},
|
||||||
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
|
UserClaims = new List<string>()
|
||||||
AllowedScopes = new List<string> { apiName, "openid", "offline_access" },
|
{
|
||||||
AccessTokenType = tokenType,
|
"CustomerId", "LocationId", "UserType", "UserId",
|
||||||
Enabled = true,
|
},
|
||||||
RequireClientSecret = false
|
},
|
||||||
}
|
})
|
||||||
})
|
.AddInMemoryClients(new List<Client>
|
||||||
.AddTestUsers(new List<TestUser>
|
{
|
||||||
{
|
new Client
|
||||||
user
|
{
|
||||||
});
|
ClientId = "client",
|
||||||
})
|
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
||||||
.Configure(app =>
|
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
|
||||||
{
|
AllowedScopes = new List<string> { apiName, "openid", "offline_access" },
|
||||||
app.UseIdentityServer();
|
AccessTokenType = tokenType,
|
||||||
})
|
Enabled = true,
|
||||||
.Build();
|
RequireClientSecret = false,
|
||||||
|
},
|
||||||
_identityServerBuilder.Start();
|
})
|
||||||
|
.AddTestUsers(new List<TestUser>
|
||||||
_steps.VerifyIdentiryServerStarted(url);
|
{
|
||||||
}
|
user,
|
||||||
|
});
|
||||||
public void Dispose()
|
})
|
||||||
{
|
.Configure(app =>
|
||||||
_servicebuilder?.Dispose();
|
{
|
||||||
_steps.Dispose();
|
app.UseIdentityServer();
|
||||||
_identityServerBuilder?.Dispose();
|
})
|
||||||
}
|
.Build();
|
||||||
}
|
|
||||||
|
_identityServerBuilder.Start();
|
||||||
|
|
||||||
|
_steps.VerifyIdentiryServerStarted(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_servicebuilder?.Dispose();
|
||||||
|
_steps.Dispose();
|
||||||
|
_identityServerBuilder?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
private void ThenTheContentLengthShouldBeZero()
|
private void ThenTheContentLengthShouldBeZero()
|
||||||
{
|
{
|
||||||
_contentLength.ShouldBeEquivalentTo(0L);
|
_contentLength.ShouldBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThenTheContentLengthIs(int expected)
|
private void ThenTheContentLengthIs(int expected)
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
public class CustomMiddlewareTests : IDisposable
|
public class CustomMiddlewareTests : IDisposable
|
||||||
{
|
{
|
||||||
@ -32,13 +32,13 @@
|
|||||||
{
|
{
|
||||||
var configuration = new OcelotPipelineConfiguration
|
var configuration = new OcelotPipelineConfiguration
|
||||||
{
|
{
|
||||||
AuthorisationMiddleware = async (ctx, next) =>
|
AuthorizationMiddleware = async (ctx, next) =>
|
||||||
{
|
{
|
||||||
_counter++;
|
_counter++;
|
||||||
await next.Invoke();
|
await next.Invoke();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var fileConfiguration = new FileConfiguration
|
var fileConfiguration = new FileConfiguration
|
||||||
@ -73,17 +73,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_authorisation_middleware()
|
public void should_call_authorization_middleware()
|
||||||
{
|
{
|
||||||
var configuration = new OcelotPipelineConfiguration
|
var configuration = new OcelotPipelineConfiguration
|
||||||
{
|
{
|
||||||
AuthorisationMiddleware = async (ctx, next) =>
|
AuthorizationMiddleware = async (ctx, next) =>
|
||||||
{
|
{
|
||||||
_counter++;
|
_counter++;
|
||||||
await next.Invoke();
|
await next.Invoke();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var fileConfiguration = new FileConfiguration
|
var fileConfiguration = new FileConfiguration
|
||||||
@ -127,8 +127,8 @@
|
|||||||
_counter++;
|
_counter++;
|
||||||
await next.Invoke();
|
await next.Invoke();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var fileConfiguration = new FileConfiguration
|
var fileConfiguration = new FileConfiguration
|
||||||
@ -172,8 +172,8 @@
|
|||||||
_counter++;
|
_counter++;
|
||||||
await next.Invoke();
|
await next.Invoke();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var fileConfiguration = new FileConfiguration
|
var fileConfiguration = new FileConfiguration
|
||||||
@ -208,17 +208,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_pre_authorisation_middleware()
|
public void should_call_pre_authorization_middleware()
|
||||||
{
|
{
|
||||||
var configuration = new OcelotPipelineConfiguration
|
var configuration = new OcelotPipelineConfiguration
|
||||||
{
|
{
|
||||||
PreAuthorisationMiddleware = async (ctx, next) =>
|
PreAuthorizationMiddleware = async (ctx, next) =>
|
||||||
{
|
{
|
||||||
_counter++;
|
_counter++;
|
||||||
await next.Invoke();
|
await next.Invoke();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var fileConfiguration = new FileConfiguration
|
var fileConfiguration = new FileConfiguration
|
||||||
@ -262,8 +262,8 @@
|
|||||||
_counter++;
|
_counter++;
|
||||||
await next.Invoke();
|
await next.Invoke();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var fileConfiguration = new FileConfiguration
|
var fileConfiguration = new FileConfiguration
|
||||||
@ -295,8 +295,8 @@
|
|||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => x.ThenTheCounterIs(1))
|
.And(x => x.ThenTheCounterIs(1))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Skip = "This is just an example to show how you could hook into Ocelot pipeline with your own middleware. At the moment you must use Response.OnCompleted callback and cannot change the response :( I will see if this can be changed one day!")]
|
[Fact(Skip = "This is just an example to show how you could hook into Ocelot pipeline with your own middleware. At the moment you must use Response.OnCompleted callback and cannot change the response :( I will see if this can be changed one day!")]
|
||||||
public void should_fix_issue_237()
|
public void should_fix_issue_237()
|
||||||
{
|
{
|
||||||
@ -305,14 +305,14 @@
|
|||||||
var httpContext = (HttpContext)state;
|
var httpContext = (HttpContext)state;
|
||||||
|
|
||||||
if (httpContext.Response.StatusCode > 400)
|
if (httpContext.Response.StatusCode > 400)
|
||||||
{
|
{
|
||||||
Debug.WriteLine("COUNT CALLED");
|
Debug.WriteLine("COUNT CALLED");
|
||||||
Console.WriteLine("COUNT CALLED");
|
Console.WriteLine("COUNT CALLED");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var fileConfiguration = new FileConfiguration
|
var fileConfiguration = new FileConfiguration
|
||||||
@ -376,8 +376,8 @@
|
|||||||
public class FakeMiddleware
|
public class FakeMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly RequestDelegate _next;
|
||||||
private readonly Func<object, Task> _callback;
|
private readonly Func<object, Task> _callback;
|
||||||
|
|
||||||
public FakeMiddleware(RequestDelegate next, Func<object, Task> callback)
|
public FakeMiddleware(RequestDelegate next, Func<object, Task> callback)
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
@ -386,10 +386,10 @@
|
|||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(HttpContext context)
|
||||||
{
|
{
|
||||||
await _next(context);
|
await _next(context);
|
||||||
|
|
||||||
context.Response.OnCompleted(_callback, context);
|
context.Response.OnCompleted(_callback, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
namespace Ocelot.AcceptanceTests
|
namespace Ocelot.AcceptanceTests
|
||||||
{
|
{
|
||||||
using Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Steeltoe.Common.Discovery;
|
using Steeltoe.Common.Discovery;
|
||||||
@ -38,24 +38,24 @@
|
|||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
Routes = new List<FileRoute>
|
Routes = new List<FileRoute>
|
||||||
|
{
|
||||||
|
new FileRoute
|
||||||
{
|
{
|
||||||
new FileRoute
|
DownstreamPathTemplate = "/",
|
||||||
{
|
DownstreamScheme = "http",
|
||||||
DownstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
DownstreamScheme = "http",
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
UpstreamPathTemplate = "/",
|
ServiceName = serviceName,
|
||||||
UpstreamHttpMethod = new List<string> { "Get" },
|
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
|
||||||
ServiceName = serviceName,
|
|
||||||
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
GlobalConfiguration = new FileGlobalConfiguration()
|
GlobalConfiguration = new FileGlobalConfiguration()
|
||||||
{
|
{
|
||||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
|
||||||
{
|
{
|
||||||
Type = "Eureka"
|
Type = "Eureka",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Given(x => x.GivenEurekaProductServiceOneIsRunning(downstreamServiceOneUrl))
|
this.Given(x => x.GivenEurekaProductServiceOneIsRunning(downstreamServiceOneUrl))
|
||||||
@ -91,42 +91,42 @@
|
|||||||
{
|
{
|
||||||
name = serviceName,
|
name = serviceName,
|
||||||
instance = new List<Instance>
|
instance = new List<Instance>
|
||||||
|
{
|
||||||
|
new Instance
|
||||||
|
{
|
||||||
|
instanceId = $"{serviceInstance.Host}:{serviceInstance}",
|
||||||
|
hostName = serviceInstance.Host,
|
||||||
|
app = serviceName,
|
||||||
|
ipAddr = "127.0.0.1",
|
||||||
|
status = "UP",
|
||||||
|
overriddenstatus = "UNKNOWN",
|
||||||
|
port = new Port {value = serviceInstance.Port, enabled = "true"},
|
||||||
|
securePort = new SecurePort {value = serviceInstance.Port, enabled = "true"},
|
||||||
|
countryId = 1,
|
||||||
|
dataCenterInfo = new DataCenterInfo {value = "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo", name = "MyOwn"},
|
||||||
|
leaseInfo = new LeaseInfo
|
||||||
{
|
{
|
||||||
new Instance
|
renewalIntervalInSecs = 30,
|
||||||
{
|
durationInSecs = 90,
|
||||||
instanceId = $"{serviceInstance.Host}:{serviceInstance}",
|
registrationTimestamp = 1457714988223,
|
||||||
hostName = serviceInstance.Host,
|
lastRenewalTimestamp= 1457716158319,
|
||||||
app = serviceName,
|
evictionTimestamp = 0,
|
||||||
ipAddr = "127.0.0.1",
|
serviceUpTimestamp = 1457714988223,
|
||||||
status = "UP",
|
},
|
||||||
overriddenstatus = "UNKNOWN",
|
metadata = new Metadata
|
||||||
port = new Port {value = serviceInstance.Port, enabled = "true"},
|
{
|
||||||
securePort = new SecurePort {value = serviceInstance.Port, enabled = "true"},
|
value = "java.util.Collections$EmptyMap",
|
||||||
countryId = 1,
|
},
|
||||||
dataCenterInfo = new DataCenterInfo {value = "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo", name = "MyOwn"},
|
homePageUrl = $"{serviceInstance.Host}:{serviceInstance.Port}",
|
||||||
leaseInfo = new LeaseInfo
|
statusPageUrl = $"{serviceInstance.Host}:{serviceInstance.Port}",
|
||||||
{
|
healthCheckUrl = $"{serviceInstance.Host}:{serviceInstance.Port}",
|
||||||
renewalIntervalInSecs = 30,
|
vipAddress = serviceName,
|
||||||
durationInSecs = 90,
|
isCoordinatingDiscoveryServer = "false",
|
||||||
registrationTimestamp = 1457714988223,
|
lastUpdatedTimestamp = "1457714988223",
|
||||||
lastRenewalTimestamp= 1457716158319,
|
lastDirtyTimestamp = "1457714988172",
|
||||||
evictionTimestamp = 0,
|
actionType = "ADDED",
|
||||||
serviceUpTimestamp = 1457714988223
|
},
|
||||||
},
|
},
|
||||||
metadata = new Metadata
|
|
||||||
{
|
|
||||||
value = "java.util.Collections$EmptyMap"
|
|
||||||
},
|
|
||||||
homePageUrl = $"{serviceInstance.Host}:{serviceInstance.Port}",
|
|
||||||
statusPageUrl = $"{serviceInstance.Host}:{serviceInstance.Port}",
|
|
||||||
healthCheckUrl = $"{serviceInstance.Host}:{serviceInstance.Port}",
|
|
||||||
vipAddress = serviceName,
|
|
||||||
isCoordinatingDiscoveryServer = "false",
|
|
||||||
lastUpdatedTimestamp = "1457714988223",
|
|
||||||
lastDirtyTimestamp = "1457714988172",
|
|
||||||
actionType = "ADDED"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
apps.Add(a);
|
apps.Add(a);
|
||||||
@ -138,8 +138,8 @@
|
|||||||
{
|
{
|
||||||
application = apps,
|
application = apps,
|
||||||
apps__hashcode = "UP_1_",
|
apps__hashcode = "UP_1_",
|
||||||
versions__delta = "1"
|
versions__delta = "1",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var json = JsonConvert.SerializeObject(applications);
|
var json = JsonConvert.SerializeObject(applications);
|
||||||
|
@ -24,8 +24,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_transform_upstream_header()
|
public void should_transform_upstream_header()
|
||||||
{
|
{
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
@ -65,8 +65,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_transform_downstream_header()
|
public void should_transform_downstream_header()
|
||||||
{
|
{
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
@ -105,8 +105,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_fix_issue_190()
|
public void should_fix_issue_190()
|
||||||
{
|
{
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
@ -149,8 +149,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_fix_issue_205()
|
public void should_fix_issue_205()
|
||||||
{
|
{
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
@ -193,8 +193,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_fix_issue_417()
|
public void should_fix_issue_417()
|
||||||
{
|
{
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
@ -241,8 +241,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void request_should_reuse_cookies_with_cookie_container()
|
public void request_should_reuse_cookies_with_cookie_container()
|
||||||
{
|
{
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
@ -284,8 +284,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void request_should_have_own_cookies_no_cookie_container()
|
public void request_should_have_own_cookies_no_cookie_container()
|
||||||
{
|
{
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
@ -327,8 +327,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void issue_474_should_not_put_spaces_in_header()
|
public void issue_474_should_not_put_spaces_in_header()
|
||||||
{
|
{
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
@ -364,8 +364,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void issue_474_should_put_spaces_in_header()
|
public void issue_474_should_put_spaces_in_header()
|
||||||
{
|
{
|
||||||
var port = RandomPortFinder.GetRandomPort();
|
var port = RandomPortFinder.GetRandomPort();
|
||||||
|
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
@ -413,9 +413,18 @@ namespace Ocelot.AcceptanceTests
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.Request.Cookies.TryGetValue("test", out var cookieValue) || context.Request.Headers.TryGetValue("Set-Cookie", out var headerValue))
|
if (context.Request.Cookies.TryGetValue("test", out var cookieValue))
|
||||||
{
|
{
|
||||||
if (cookieValue == "0" || headerValue == "test=1; path=/")
|
if (cookieValue == "0")
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = statusCode;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.Request.Headers.TryGetValue("Set-Cookie", out var headerValue))
|
||||||
|
{
|
||||||
|
if (headerValue == "test=1; path=/")
|
||||||
{
|
{
|
||||||
context.Response.StatusCode = statusCode;
|
context.Response.StatusCode = statusCode;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
@ -62,7 +62,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
.And(x => cache.Count.ShouldBe(1))
|
.And(x => ThenTheCountShouldBe(cache, 1))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,10 +122,15 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
.And(x => cache.Count.ShouldBe(2))
|
.And(x => ThenTheCountShouldBe(cache, 2))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ThenTheCountShouldBe(FakeHttpClientCache cache, int count)
|
||||||
|
{
|
||||||
|
cache.Count.ShouldBe(count);
|
||||||
|
}
|
||||||
|
|
||||||
private void GivenThereIsAServiceRunningOn(string baseUrl, int statusCode, string responseBody)
|
private void GivenThereIsAServiceRunningOn(string baseUrl, int statusCode, string responseBody)
|
||||||
{
|
{
|
||||||
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, async context =>
|
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, async context =>
|
||||||
|
@ -1,77 +1,76 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<AssemblyName>Ocelot.AcceptanceTests</AssemblyName>
|
<AssemblyName>Ocelot.AcceptanceTests</AssemblyName>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<PackageId>Ocelot.AcceptanceTests</PackageId>
|
<PackageId>Ocelot.AcceptanceTests</PackageId>
|
||||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
||||||
<RuntimeIdentifiers>osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64</RuntimeIdentifiers>
|
||||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||||
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="appsettings.product.json">
|
<None Update="appsettings.product.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="appsettings.json">
|
<None Update="appsettings.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="idsrv3test.pfx">
|
<None Update="idsrv3test.pfx">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj" />
|
<ProjectReference Include="..\..\src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj" />
|
||||||
<ProjectReference Include="..\..\src\Ocelot.Tracing.OpenTracing\Ocelot.Tracing.OpenTracing.csproj" />
|
<ProjectReference Include="..\..\src\Ocelot.Tracing.OpenTracing\Ocelot.Tracing.OpenTracing.csproj" />
|
||||||
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
|
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
|
||||||
<ProjectReference Include="..\..\src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj" />
|
<ProjectReference Include="..\..\src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj" />
|
||||||
<ProjectReference Include="..\..\src\Ocelot.Provider.Consul\Ocelot.Provider.Consul.csproj" />
|
<ProjectReference Include="..\..\src\Ocelot.Provider.Consul\Ocelot.Provider.Consul.csproj" />
|
||||||
<ProjectReference Include="..\..\src\Ocelot.Provider.Eureka\Ocelot.Provider.Eureka.csproj" />
|
<ProjectReference Include="..\..\src\Ocelot.Provider.Eureka\Ocelot.Provider.Eureka.csproj" />
|
||||||
<ProjectReference Include="..\..\src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj" />
|
<ProjectReference Include="..\..\src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj" />
|
||||||
<ProjectReference Include="..\Ocelot.ManualTest\Ocelot.ManualTest.csproj" />
|
<ProjectReference Include="..\Ocelot.ManualTest\Ocelot.ManualTest.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.1.1" />
|
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
|
||||||
<PackageReference Include="Moq" Version="4.13.1" />
|
<PackageReference Include="Moq" Version="4.15.2" />
|
||||||
<PackageReference Include="OpenTracing" Version="0.12.1" />
|
<PackageReference Include="OpenTracing" Version="0.12.1" />
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="3.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.1" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
|
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
|
||||||
<PackageReference Include="Shouldly" Version="4.0.0-beta0002" />
|
<PackageReference Include="Shouldly" Version="4.0.1" />
|
||||||
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
|
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1" />
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
|
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
|
||||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
|
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
|
||||||
<PackageReference Include="IdentityServer4" Version="3.1.1" />
|
<PackageReference Include="IdentityServer4" Version="4.1.1" />
|
||||||
<PackageReference Include="Consul" Version="0.7.2.6" />
|
<PackageReference Include="Consul" Version="1.6.1.1" />
|
||||||
<PackageReference Include="Rafty" Version="0.4.4" />
|
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="2.0.0-beta-1629" />
|
||||||
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="2.0.0-beta-1629" />
|
<PackageReference Include="CacheManager.Serialization.Json" Version="2.0.0-beta-1629" />
|
||||||
<PackageReference Include="CacheManager.Serialization.Json" Version="2.0.0-beta-1629" />
|
<PackageReference Include="Steeltoe.Discovery.ClientCore" Version="3.0.1" />
|
||||||
<PackageReference Include="Steeltoe.Discovery.ClientCore" Version="2.4.2" />
|
</ItemGroup>
|
||||||
</ItemGroup>
|
<ItemGroup>
|
||||||
<ItemGroup>
|
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
||||||
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
</ItemGroup>
|
||||||
</ItemGroup>
|
</Project>
|
||||||
</Project>
|
|
||||||
|
@ -8,7 +8,6 @@ namespace Ocelot.AcceptanceTests
|
|||||||
using OpenTracing;
|
using OpenTracing;
|
||||||
using OpenTracing.Propagation;
|
using OpenTracing.Propagation;
|
||||||
using OpenTracing.Tag;
|
using OpenTracing.Tag;
|
||||||
using Rafty.Infrastructure;
|
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -17,6 +16,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
public class OpenTracingTests : IDisposable
|
public class OpenTracingTests : IDisposable
|
||||||
{
|
{
|
||||||
@ -513,4 +514,70 @@ namespace Ocelot.AcceptanceTests
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class Wait
|
||||||
|
{
|
||||||
|
public static Waiter WaitFor(int milliSeconds)
|
||||||
|
{
|
||||||
|
return new Waiter(milliSeconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Waiter
|
||||||
|
{
|
||||||
|
private readonly int _milliSeconds;
|
||||||
|
|
||||||
|
public Waiter(int milliSeconds)
|
||||||
|
{
|
||||||
|
_milliSeconds = milliSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Until(Func<bool> condition)
|
||||||
|
{
|
||||||
|
var stopwatch = Stopwatch.StartNew();
|
||||||
|
var passed = false;
|
||||||
|
while (stopwatch.ElapsedMilliseconds < _milliSeconds)
|
||||||
|
{
|
||||||
|
if (condition.Invoke())
|
||||||
|
{
|
||||||
|
passed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return passed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> Until(Func<Task<bool>> condition)
|
||||||
|
{
|
||||||
|
var stopwatch = Stopwatch.StartNew();
|
||||||
|
var passed = false;
|
||||||
|
while (stopwatch.ElapsedMilliseconds < _milliSeconds)
|
||||||
|
{
|
||||||
|
if (await condition.Invoke())
|
||||||
|
{
|
||||||
|
passed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return passed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Until<T>(Func<bool> condition)
|
||||||
|
{
|
||||||
|
var stopwatch = Stopwatch.StartNew();
|
||||||
|
var passed = false;
|
||||||
|
while (stopwatch.ElapsedMilliseconds < _milliSeconds)
|
||||||
|
{
|
||||||
|
if (condition.Invoke())
|
||||||
|
{
|
||||||
|
passed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return passed;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user