├── .devcontainer
├── Dockerfile
├── devcontainer.json
└── docker-compose.yml
├── .github
├── dependabot.yml
└── workflows
│ ├── codeql-analysis.yml
│ ├── docker.yml
│ ├── linter.yml
│ └── tests.yml
├── .gitignore
├── CONTRIBUTING.md
├── Gatekeeper.LdapServerLibrary.Tests
├── Gatekeeper.LdapServerLibrary.Tests.csproj
├── Parser
│ └── RdnParserTest.cs
└── Session
│ └── LdapEventsTest.cs
├── Gatekeeper.LdapServerLibrary
├── Engine
│ ├── DecisionEngine.cs
│ ├── Handler
│ │ ├── BindRequestHandler.cs
│ │ ├── ExtendedRequestHandler.cs
│ │ ├── HandlerReply.cs
│ │ ├── IRequestHandler.cs
│ │ ├── SearchRequestHandler.cs
│ │ └── UnbindRequestHandler.cs
│ └── HandlerMapper.cs
├── Gatekeeper.LdapServerLibrary.csproj
├── ILogger.cs
├── LdapServer.cs
├── Models
│ ├── LdapMessage.cs
│ └── Operations
│ │ ├── IProtocolOp.cs
│ │ └── Response
│ │ ├── BindResponse.cs
│ │ ├── ExtendedOperationResponse.cs
│ │ ├── LdapResult.cs
│ │ ├── SearchResultDone.cs
│ │ ├── SearchResultEntry.cs
│ │ └── UnbindDummyResponse.cs
├── Network
│ ├── ClientSession.cs
│ ├── ConnectionManager.cs
│ └── NetworkListener.cs
├── Parser
│ ├── Encoder
│ │ ├── BindResponseEncoder.cs
│ │ ├── ExtendedOperationResponseEncoder.cs
│ │ ├── IApplicationEncoder.cs
│ │ ├── SearchResultDoneEncoder.cs
│ │ └── SearchResultEntryEncoder.cs
│ ├── OperationMapper.cs
│ ├── PacketParser.cs
│ └── RdnParser.cs
├── Session
│ ├── ClientContext.cs
│ ├── Events
│ │ ├── AuthenticationEvent.cs
│ │ ├── IAuthenticationEvent.cs
│ │ ├── ISearchEvent.cs
│ │ └── SearchEvent.cs
│ ├── LdapEvents.cs
│ └── Replies
│ │ └── SearchResultReply.cs
└── SingletonContainer.cs
├── LICENSE
├── README.md
├── Sample.Tests
├── Integration
│ ├── LdapSearchTests.cs
│ └── LdapServerFixture.cs
└── Sample.Tests.csproj
└── Sample
├── ConsoleLogger.cs
├── LdapEventListener.cs
├── Program.cs
├── README.md
├── Sample.csproj
├── SearchExpressionBuilder.cs
├── UserDatabase.cs
└── example_certificate.pfx
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/sdk:5.0
2 |
3 | LABEL org.opencontainers.image.source https://github.com/getgatekeeper/ldapserverlibrary
4 |
5 | RUN apt-get update
6 | RUN apt-get install -y ldap-utils netcat lsof zsh tcpdump vim
7 | RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
8 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "LdapServer",
3 | "dockerComposeFile": "docker-compose.yml",
4 | "service": "ldapserver",
5 | // Set *default* container specific settings.json values on container create.
6 | "settings": {},
7 | "runArgs": [
8 | "--privileged"
9 | ],
10 | // Add the IDs of extensions you want installed when the container is created.
11 | "extensions": [
12 | "ms-dotnettools.csharp",
13 | "ms-dotnettools.dotnet-interactive-vscode",
14 | "ms-azuretools.vscode-docker"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/.devcontainer/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | ldapserver:
4 | image: ghcr.io/getgatekeeper/ldapserver-dev:sha-583a956
5 | volumes:
6 | - .:/workspace:cached
7 | - /var/run/docker.sock:/var/run/docker-host.sock
8 | command: /bin/sh -c "while sleep 1000; do :; done"
9 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "daily"
7 | - package-ecosystem: nuget
8 | directory: "/Gatekeeper.LdapServerLibrary.Tests"
9 | schedule:
10 | interval: daily
11 | open-pull-requests-limit: 10
12 | - package-ecosystem: nuget
13 | directory: "/Sample.Tests"
14 | schedule:
15 | interval: daily
16 | open-pull-requests-limit: 10
17 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | # ******** NOTE ********
12 |
13 | name: "CodeQL"
14 |
15 | on:
16 | push:
17 | branches: [ main ]
18 | pull_request:
19 | # The branches below must be a subset of the branches above
20 | branches: [ main ]
21 | schedule:
22 | - cron: '27 10 * * 6'
23 |
24 | jobs:
25 | analyze:
26 | name: Analyze
27 | runs-on: ubuntu-latest
28 |
29 | strategy:
30 | fail-fast: false
31 | matrix:
32 | language: [ 'csharp' ]
33 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
34 | # Learn more:
35 | # https://docs.github.com/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
36 |
37 | steps:
38 | - name: Checkout repository
39 | uses: actions/checkout@v2
40 |
41 | # Initializes the CodeQL tools for scanning.
42 | - name: Initialize CodeQL
43 | uses: github/codeql-action/init@v1
44 | with:
45 | languages: ${{ matrix.language }}
46 | # If you wish to specify custom queries, you can do so here or in a config file.
47 | # By default, queries listed here will override any specified in a config file.
48 | # Prefix the list here with "+" to use these queries and those in the config file.
49 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
50 |
51 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
52 | # If this step fails, then you should remove it and run the build manually (see below)
53 | # - name: Autobuild
54 | # uses: github/codeql-action/autobuild@v1
55 |
56 | # ℹ️ Command-line programs to run using the OS shell.
57 | # 📚 https://git.io/JvXDl
58 |
59 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
60 | # and modify them (or add more) to build your code if your project
61 | # uses a compiled language
62 |
63 | #- run: |
64 | # make bootstrap
65 | # make release
66 |
67 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
68 | # and modify them (or add more) to build your code if your project
69 | # uses a compiled language
70 | - name: Setup .NET 5.0.100
71 | uses: actions/setup-dotnet@v1
72 | with:
73 | dotnet-version: 5.0.100
74 | - name: Build
75 | run: dotnet build --configuration Release Gatekeeper.LdapServerLibrary/Gatekeeper.LdapServerLibrary.csproj
76 |
77 | - name: Perform CodeQL Analysis
78 | uses: github/codeql-action/analyze@v1
79 |
--------------------------------------------------------------------------------
/.github/workflows/docker.yml:
--------------------------------------------------------------------------------
1 | name: Publish Docker images
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - '.devcontainer/Dockerfile'
9 | - '.github/workflows/docker.yml'
10 |
11 | jobs:
12 | build-dev:
13 | runs-on: ubuntu-latest
14 | steps:
15 | -
16 | name: Checkout
17 | uses: actions/checkout@v2
18 | -
19 | name: Docker meta
20 | id: docker_meta
21 | uses: crazy-max/ghaction-docker-meta@v1.11.0
22 | with:
23 | images: ghcr.io/getgatekeeper/ldapserver-dev
24 | tag-sha: true
25 | -
26 | name: Set up QEMU
27 | uses: docker/setup-qemu-action@v1
28 | -
29 | name: Set up Docker Buildx
30 | uses: docker/setup-buildx-action@v1
31 | -
32 | name: Login to GitHub Container Registry
33 | uses: docker/login-action@v1
34 | with:
35 | registry: ghcr.io
36 | username: lukasreschke
37 | password: ${{ secrets.CR_PAT }}
38 | -
39 | name: Build and push
40 | uses: docker/build-push-action@v2
41 | with:
42 | context: .
43 | file: .devcontainer/Dockerfile
44 | platforms: linux/amd64,linux/arm64
45 | push: true
46 | tags: ${{ steps.docker_meta.outputs.tags }}
47 | labels: ${{ steps.docker_meta.outputs.labels }}
48 |
--------------------------------------------------------------------------------
/.github/workflows/linter.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ###########################
3 | ###########################
4 | ## Linter GitHub Actions ##
5 | ###########################
6 | ###########################
7 | name: Lint Code Base
8 |
9 | #
10 | # Documentation:
11 | # https://help.github.com/en/articles/workflow-syntax-for-github-actions
12 | #
13 |
14 | #############################
15 | # Start the job on all push #
16 | #############################
17 | on:
18 | push:
19 | branches-ignore: [main]
20 | # Remove the line above to run when pushing to main
21 | pull_request:
22 | branches: [main]
23 |
24 | ###############
25 | # Set the Job #
26 | ###############
27 | jobs:
28 | build:
29 | # Name the Job
30 | name: Lint Code Base
31 | # Set the agent to run on
32 | runs-on: ubuntu-latest
33 |
34 | ##################
35 | # Load all steps #
36 | ##################
37 | steps:
38 | ##########################
39 | # Checkout the code base #
40 | ##########################
41 | - name: Checkout Code
42 | uses: actions/checkout@v2
43 | with:
44 | # Full git history is needed to get a proper list of changed files within `super-linter`
45 | fetch-depth: 0
46 |
47 | ################################
48 | # Run Linter against code base #
49 | ################################
50 | - name: Lint Code Base
51 | uses: github/super-linter@v3.15.1
52 | env:
53 | VALIDATE_ALL_CODEBASE: false
54 | DEFAULT_BRANCH: main
55 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
56 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Run tests
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Setup .NET Core
17 | uses: actions/setup-dotnet@v1
18 | with:
19 | dotnet-version: 5.0.100
20 | - name: Build Sample
21 | run: dotnet build --configuration Release Sample/Sample.csproj
22 | - name: Install ldapsearch
23 | run: sudo apt-get update && sudo apt-get -y install ldap-utils
24 | - name: Test Sample
25 | run: dotnet test Sample.Tests/ --collect:"XPlat Code Coverage" -r TestResults/
26 | - name: Test Gatekeeper.LdapServerLibrary.Tests
27 | run: dotnet test Gatekeeper.LdapServerLibrary.Tests/ --collect:"XPlat Code Coverage" -r TestResults/
28 | - uses: codecov/codecov-action@v1
29 | with:
30 | directory: TestResults/
31 | fail_ci_if_error: true
32 | verbose: true
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 | nupkg/
7 |
8 | # Visual Studio Code
9 | .vscode
10 |
11 | # Rider
12 | .idea
13 |
14 | # User-specific files
15 | *.suo
16 | *.user
17 | *.userosscache
18 | *.sln.docstates
19 |
20 | # Build results
21 | [Dd]ebug/
22 | [Dd]ebugPublic/
23 | [Rr]elease/
24 | [Rr]eleases/
25 | x64/
26 | x86/
27 | build/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Oo]ut/
32 | msbuild.log
33 | msbuild.err
34 | msbuild.wrn
35 |
36 | # Visual Studio 2015
37 | .vs/
38 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to the LDAP Server Library
2 |
3 | We generally appreciate any contributions to the LDAP Server Library. Please note, that whilst this aims to be a general-purpose library, this library was ultimately created to implement an LDAP Server in the [Gatekeeper application](https://github.com/GetGatekeeper/Server).
4 |
5 | ## Setup Dev Environment
6 |
7 | The repository includes a dev container that ships all the required dependencies to set this up. Either use [GitHub Codespaces](https://github.com/codespaces) or [Visual Studio Code Remote Containers](https://code.visualstudio.com/docs/remote/containers#_quick-start-open-a-git-repository-or-github-pr-in-an-isolated-container-volume).
8 |
9 | ## Using Wireshark to analyze the traffic
10 |
11 | The sample application provided in the "Sample" folder can easily be intercepted with tcpdump:
12 |
13 | ```bash
14 | cd Sample/ && dotnet run
15 | ldapsearch -w test -H ldap://localhost:3389 -b "dc=example,dc=com" -D "cn=Manager,dc=example,dc=com" "cn=test1"
16 | tcpdump -i lo -w output.dump port 3389
17 | ```
18 |
19 | Once done, download "output.dump" and open it in Wireshark. This will give you a good overview of the behaviour.
20 |
--------------------------------------------------------------------------------
/Gatekeeper.LdapServerLibrary.Tests/Gatekeeper.LdapServerLibrary.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net5.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 | all
17 |
18 |
19 | runtime; build; native; contentfiles; analyzers; buildtransitive
20 | all
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Gatekeeper.LdapServerLibrary.Tests/Parser/RdnParserTest.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Xunit;
3 | using Gatekeeper.LdapServerLibrary.Parser;
4 |
5 | namespace Gatekeeper.LdapServerLibrary.Tests.Parser
6 | {
7 | public class RdnParserTest
8 | {
9 | [Theory]
10 | [MemberData(nameof(GetData))]
11 | public void TestParseRdnString(string rdn, Dictionary> expected)
12 | {
13 | Assert.Equal(expected, RdnParser.ParseRdnString(rdn));
14 | }
15 |
16 | public static IEnumerable