Compare commits
199 Commits
Author | SHA1 | Date | |
---|---|---|---|
6199e395fe | |||
9a938ac3ea | |||
ad018b5469 | |||
59c8ed95c5 | |||
4c8bd1c81f | |||
ded1f74285 | |||
b6d1124d4e | |||
be40178234 | |||
acc6598da1 | |||
c1ba6df90d | |||
76306b9a1b | |||
332aa279a8 | |||
0f9bb07d21 | |||
b92157a987 | |||
6991983743 | |||
cb06f3dcb6 | |||
eda3680997 | |||
6cef2dfa99 | |||
3b6ee582dd | |||
1f753c4dc6 | |||
c02f390f64 | |||
c8fffd6d81 | |||
cb783da34b | |||
12da43828e | |||
74bd996f7a | |||
ef355e8193 | |||
18f9f1a9b3 | |||
8529b1fd18 | |||
290ea279b9 | |||
00fb3e9312 | |||
652cdee034 | |||
6d900d0964 | |||
9d1d969113 | |||
c8b5b2303a | |||
7382394783 | |||
8b72933be2 | |||
c907180882 | |||
36a985554d | |||
9207d4e698 | |||
c3f9e80b78 | |||
a4c05002a1 | |||
9e90820bfd | |||
eafae02c7b | |||
f01f88b3bd | |||
20a2fede08 | |||
|
5b046a95eb | ||
409b0301dc | |||
02f3152ffd | |||
a62e7ddeca | |||
4fd51abcb1 | |||
d6f833f6eb | |||
2accc63091 | |||
dcb0269fc5 | |||
c36383b0fc | |||
a1a9b8ba76 | |||
e9cb41e5e4 | |||
30b749dc75 | |||
f043c74ae6 | |||
61202d3617 | |||
dd17873905 | |||
a46a61249e | |||
f90706d140 | |||
583ec23f99 | |||
7458001f5a | |||
|
08678ce929 | ||
a99770951d | |||
7d19b89637 | |||
c0c9a5576e | |||
35596ec867 | |||
e0fb057c84 | |||
4767eb3a4f | |||
ee556582d2 | |||
64d1a09db4 | |||
933ab44b99 | |||
|
491f04cd5d | ||
|
4f9accbadf | ||
|
92e5afd614 | ||
|
8ec1d2b1fe | ||
|
815cd49868 | ||
|
651fb9f3bf | ||
|
cb55a660e5 | ||
|
66409c6688 | ||
|
637a0cd0f4 | ||
|
482d9670e3 | ||
|
6751c9dd81 | ||
|
413f1ec065 | ||
|
210793e814 | ||
|
daeea9a1f6 | ||
|
e0185345c8 | ||
|
58e171aea1 | ||
|
d0242ce3d8 | ||
|
181a57586c | ||
|
10a2d2161b | ||
|
dabca67fc8 | ||
|
a80a2725bc | ||
|
2ccc1d3b83 | ||
|
88eb35a567 | ||
|
0b867abbac | ||
|
48131c1b4e | ||
|
851010d6d5 | ||
|
2a099e2dc4 | ||
|
4ef074fac4 | ||
|
b043e3bc93 | ||
|
902330e734 | ||
|
cec8775f8e | ||
|
d20a17d3fe | ||
|
db61e0d1ce | ||
|
c549d28340 | ||
|
1ebdffa358 | ||
|
15ff508429 | ||
|
f323be3d7f | ||
|
130ae005a3 | ||
|
e89b4d3287 | ||
|
7a195bb844 | ||
|
6620b9147e | ||
|
de4fa04d3b | ||
|
1e3e4b2196 | ||
|
f3282bed38 | ||
|
f8717480fd | ||
|
4c90ce11f2 | ||
|
ed7087c802 | ||
|
fc7ab06358 | ||
|
a4924f7453 | ||
|
0011184a3f | ||
|
aa5be41b06 | ||
|
bdfd68c3b6 | ||
|
fb437b037e | ||
|
34d4f20222 | ||
|
0b65747110 | ||
|
4661aa2114 | ||
|
0a57b2f80e | ||
|
0fe1758572 | ||
|
4eb3737d31 | ||
|
cbdc0dacd6 | ||
|
8f4ced96f9 | ||
|
722fefad1c | ||
|
c87571f6b7 | ||
|
cb9c0cce3e | ||
|
0b10c30c79 | ||
|
c22c98c463 | ||
|
25cbb26e1d | ||
|
08c21fe7ca | ||
|
1bd14ddf68 | ||
|
95411c05e1 | ||
|
7b1f6b5132 | ||
|
3c0ca27477 | ||
|
511ead3ec6 | ||
|
e37b498de1 | ||
|
29d7d4b2f7 | ||
|
c494078550 | ||
|
73b7795bc0 | ||
|
c1af05f403 | ||
|
49ef60f26b | ||
|
aa11e318ec | ||
|
f3aad72b57 | ||
|
6a55d808a2 | ||
|
aec5053f55 | ||
|
4484129a41 | ||
|
2e43cf95b9 | ||
|
7569423f11 | ||
|
5c0f787fbf | ||
|
6c85d61525 | ||
|
884cce1475 | ||
|
53e005c1f4 | ||
|
733a10a1c5 | ||
|
708bc5ed83 | ||
|
e46579b34e | ||
|
4fefe2aa8c | ||
|
ee9034f24c | ||
|
61af45e872 | ||
|
dd6e9583a2 | ||
|
665dbc2690 | ||
|
599d55700d | ||
|
726190e5b8 | ||
|
0b8375fd2a | ||
|
fa88250f0e | ||
|
0491916d90 | ||
|
c004a291d7 | ||
|
54191d7ffb | ||
|
9e283f369f | ||
|
19114385fc | ||
|
7701e98bcc | ||
|
d4c2fb52ab | ||
|
7cbdd0c8db | ||
|
afec12d163 | ||
|
a4a602b6ec | ||
|
e1952cddb6 | ||
|
ee415fe8c6 | ||
|
eca5c4ea9f | ||
|
a01752a68c | ||
|
ba90f86e7b | ||
|
6135f94a51 | ||
|
f7c4bd311a | ||
|
c736ecd8c2 | ||
|
d2a800878f | ||
|
5a7edc892f | ||
|
d258398b68 | ||
|
b082cf1742 | ||
|
e673df3ba8 |
10
.dockerignore
Normal file
10
.dockerignore
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
.dockerignore
|
||||||
|
.editorconfig
|
||||||
|
.env.testing
|
||||||
|
.git*
|
||||||
|
docker/
|
||||||
|
package.json
|
||||||
|
package-lock.json
|
||||||
|
phpunit.xml
|
||||||
|
webpack.mix.js
|
||||||
|
yarn.lock
|
18
.editorconfig
Normal file
18
.editorconfig
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = tab
|
||||||
|
insert_final_newline = false
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.{yml,yaml}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[docker-compose.yml]
|
||||||
|
indent_size = 4
|
51
.env.example
Normal file
51
.env.example
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
APP_NAME=Laravel
|
||||||
|
APP_ENV=production
|
||||||
|
APP_KEY=
|
||||||
|
APP_DEBUG=true
|
||||||
|
APP_URL=http://localhost
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
|
||||||
|
DB_CONNECTION=mysql
|
||||||
|
DB_HOST=127.0.0.1
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=laravel
|
||||||
|
DB_USERNAME=root
|
||||||
|
DB_PASSWORD=
|
||||||
|
|
||||||
|
BROADCAST_DRIVER=log
|
||||||
|
CACHE_DRIVER=file
|
||||||
|
QUEUE_CONNECTION=sync
|
||||||
|
SESSION_DRIVER=file
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PASSWORD=null
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
MAIL_DRIVER=smtp
|
||||||
|
MAIL_HOST=smtp.mailtrap.io
|
||||||
|
MAIL_PORT=2525
|
||||||
|
MAIL_USERNAME=null
|
||||||
|
MAIL_PASSWORD=null
|
||||||
|
MAIL_ENCRYPTION=null
|
||||||
|
MAIL_FROM_ADDRESS=null
|
||||||
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID=
|
||||||
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
AWS_DEFAULT_REGION=us-east-1
|
||||||
|
AWS_BUCKET=
|
||||||
|
|
||||||
|
PUSHER_APP_ID=
|
||||||
|
PUSHER_APP_KEY=
|
||||||
|
PUSHER_APP_SECRET=
|
||||||
|
PUSHER_APP_CLUSTER=mt1
|
||||||
|
|
||||||
|
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||||
|
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||||
|
|
||||||
|
LDAP_HOST=
|
||||||
|
LDAP_BASE_DN=
|
||||||
|
LDAP_USERNAME=
|
||||||
|
LDAP_PASSWORD=
|
51
.env.testing
Normal file
51
.env.testing
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
APP_NAME=Laravel
|
||||||
|
APP_ENV=dev
|
||||||
|
APP_KEY=base64:KvIecx8zoy6RjcbJM8s98ZKs9IDGUHFVqBRn3Awfmso=
|
||||||
|
APP_DEBUG=true
|
||||||
|
APP_URL=http://localhost
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
|
||||||
|
DB_CONNECTION=mysql
|
||||||
|
DB_HOST=127.0.0.1
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=homestead
|
||||||
|
DB_USERNAME=homestead
|
||||||
|
DB_PASSWORD=secret
|
||||||
|
|
||||||
|
BROADCAST_DRIVER=log
|
||||||
|
CACHE_DRIVER=file
|
||||||
|
QUEUE_CONNECTION=sync
|
||||||
|
SESSION_DRIVER=file
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PASSWORD=null
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
MAIL_DRIVER=smtp
|
||||||
|
MAIL_HOST=smtp.mailtrap.io
|
||||||
|
MAIL_PORT=2525
|
||||||
|
MAIL_USERNAME=null
|
||||||
|
MAIL_PASSWORD=null
|
||||||
|
MAIL_ENCRYPTION=null
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID=
|
||||||
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
AWS_DEFAULT_REGION=us-east-1
|
||||||
|
AWS_BUCKET=
|
||||||
|
|
||||||
|
PUSHER_APP_ID=
|
||||||
|
PUSHER_APP_KEY=
|
||||||
|
PUSHER_APP_SECRET=
|
||||||
|
PUSHER_APP_CLUSTER=mt1
|
||||||
|
|
||||||
|
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||||
|
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||||
|
|
||||||
|
LDAP_HOST=test_ldap
|
||||||
|
LDAP_PORT=389
|
||||||
|
LDAP_BASE_DN="dc=Test"
|
||||||
|
LDAP_USERNAME="cn=admin,dc=Test"
|
||||||
|
LDAP_PASSWORD="test"
|
||||||
|
LDAP_CACHE=false
|
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
* text=auto
|
||||||
|
*.css linguist-vendored
|
||||||
|
*.scss linguist-vendored
|
||||||
|
*.js linguist-vendored
|
||||||
|
CHANGELOG.md export-ignore
|
13
.github/FUNDING.yml
vendored
Normal file
13
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: [leenooks]
|
||||||
|
patreon: # Replace with a single Patreon username
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||||
|
custom: ['https://www.buymeacoffee.com/dege']
|
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem. Also include any logs or backtrace information
|
||||||
|
|
||||||
|
**LDAP Server details (please complete the following information):**
|
||||||
|
- OS: [e.g. iOS]
|
||||||
|
- Server Name [e.g. OpenLDAP, Windows AD,...]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
**Smartphone (please complete the following information):**
|
||||||
|
- Device: [e.g. iPhone6]
|
||||||
|
- OS: [e.g. iOS8.1]
|
||||||
|
- Browser [e.g. stock browser, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
22
.gitignore
vendored
22
.gitignore
vendored
@ -1,3 +1,19 @@
|
|||||||
config/config.php
|
/node_modules
|
||||||
queries/custom_*
|
/public/hot
|
||||||
templates/*/custom_*
|
/public/storage
|
||||||
|
/storage/*.key
|
||||||
|
/vendor
|
||||||
|
.env
|
||||||
|
.env.backup
|
||||||
|
.phpunit.result.cache
|
||||||
|
Homestead.json
|
||||||
|
Homestead.yaml
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
public/css/app.css
|
||||||
|
public/js/app.js
|
||||||
|
public/js/vendor.js
|
||||||
|
public/js/manifest.js
|
||||||
|
public/fonts/vendor
|
||||||
|
public/images/vendor
|
||||||
|
public/mix-manifest.json
|
||||||
|
38
.gitlab-ci.yml
Normal file
38
.gitlab-ci.yml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
stages:
|
||||||
|
- test
|
||||||
|
- build
|
||||||
|
- build-manifest
|
||||||
|
|
||||||
|
variables:
|
||||||
|
DOCKER_HOST: tcp://docker:2375
|
||||||
|
VERSION: latest
|
||||||
|
VERSIONARCH: ${VERSION}-${ARCH}
|
||||||
|
|
||||||
|
# This folder is cached between builds
|
||||||
|
# http://docs.gitlab.com/ce/ci/yaml/README.html#cache
|
||||||
|
cache:
|
||||||
|
key: ${CI_COMMIT_REF_SLUG}
|
||||||
|
paths:
|
||||||
|
- public/css/app.css
|
||||||
|
- public/js/app.js
|
||||||
|
- public/js/manifest.js
|
||||||
|
- public/js/vendor.js
|
||||||
|
- public/*/vendor/
|
||||||
|
- node_modules/
|
||||||
|
- vendor/
|
||||||
|
|
||||||
|
image: docker:latest
|
||||||
|
services:
|
||||||
|
- docker:dind
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- docker info && docker version
|
||||||
|
- echo "$CI_JOB_TOKEN" | docker login -u "$CI_REGISTRY_USER" "$CI_REGISTRY" --password-stdin
|
||||||
|
- if [ -n "$GITHUB_TOKEN" ]; then cat $GITHUB_TOKEN |base64 -d > auth.json; fi
|
||||||
|
|
||||||
|
include:
|
||||||
|
- .gitlab-test.yml
|
||||||
|
- .gitlab-docker-x86_64.yml
|
||||||
|
- .gitlab-docker-armv7l.yml
|
||||||
|
- .gitlab-docker-arm64.yml
|
||||||
|
- .gitlab-docker-manifest.yml
|
18
.gitlab-docker-arm64.yml
Normal file
18
.gitlab-docker-arm64.yml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
arm64:build:
|
||||||
|
variables:
|
||||||
|
ARCH: arm64
|
||||||
|
|
||||||
|
stage: build
|
||||||
|
|
||||||
|
script:
|
||||||
|
- if [ -f init ]; then chmod 500 init; fi
|
||||||
|
- echo -n ${CI_COMMIT_SHORT_SHA} > VERSION
|
||||||
|
- rm -rf node_modules database/seeds database/schema database/factories/*
|
||||||
|
- docker build -f docker/Dockerfile -t ${CI_REGISTRY_IMAGE}:${VERSIONARCH} .
|
||||||
|
- docker push ${CI_REGISTRY_IMAGE}:${VERSIONARCH}
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
- arm64
|
||||||
|
only:
|
||||||
|
- master
|
18
.gitlab-docker-armv7l.yml
Normal file
18
.gitlab-docker-armv7l.yml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
armv7l:build:
|
||||||
|
variables:
|
||||||
|
ARCH: armv7l
|
||||||
|
|
||||||
|
stage: build
|
||||||
|
|
||||||
|
script:
|
||||||
|
- if [ -f init ]; then chmod 500 init; fi
|
||||||
|
- echo -n ${CI_COMMIT_SHORT_SHA} > VERSION
|
||||||
|
- rm -rf node_modules database/seeds database/schema database/factories/*
|
||||||
|
- docker build -f docker/Dockerfile -t ${CI_REGISTRY_IMAGE}:${VERSIONARCH} .
|
||||||
|
- docker push ${CI_REGISTRY_IMAGE}:${VERSIONARCH}
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
- armv7l
|
||||||
|
only:
|
||||||
|
- master
|
10
.gitlab-docker-manifest.yml
Normal file
10
.gitlab-docker-manifest.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
x86_64:build-manifest:
|
||||||
|
stage: build-manifest
|
||||||
|
script:
|
||||||
|
- docker manifest create ${CI_REGISTRY_IMAGE}:${VERSION} ${CI_REGISTRY_IMAGE}:${VERSION}-x86_64 ${CI_REGISTRY_IMAGE}:${VERSION}-armv7l ${CI_REGISTRY_IMAGE}:${VERSION}-arm64
|
||||||
|
- docker manifest push --purge ${CI_REGISTRY_IMAGE}:${VERSION}
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
- x86_64
|
||||||
|
only:
|
||||||
|
- master
|
18
.gitlab-docker-x86_64.yml
Normal file
18
.gitlab-docker-x86_64.yml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
x86_64:build:
|
||||||
|
variables:
|
||||||
|
ARCH: x86_64
|
||||||
|
|
||||||
|
stage: build
|
||||||
|
|
||||||
|
script:
|
||||||
|
- if [ -f init ]; then chmod 500 init; fi
|
||||||
|
- echo -n ${CI_COMMIT_SHORT_SHA} > VERSION
|
||||||
|
- rm -rf node_modules database/seeds database/schema database/factories/*
|
||||||
|
- docker build -f docker/Dockerfile -t ${CI_REGISTRY_IMAGE}:${VERSIONARCH} .
|
||||||
|
- docker push ${CI_REGISTRY_IMAGE}:${VERSIONARCH}
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
- x86_64
|
||||||
|
only:
|
||||||
|
- master
|
52
.gitlab-test.yml
Normal file
52
.gitlab-test.yml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
test:
|
||||||
|
image: ${CI_REGISTRY}/leenooks/php:8.3-fpm-ldap-test
|
||||||
|
|
||||||
|
stage: test
|
||||||
|
|
||||||
|
# NOTE: This service is dependant on project file configuration, which is not there if the cache was deleted
|
||||||
|
# resulting in the testing to fail on the first run.
|
||||||
|
services:
|
||||||
|
- name: osixia/openldap:latest
|
||||||
|
alias: test_ldap
|
||||||
|
command: ["--loglevel","debug"]
|
||||||
|
|
||||||
|
variables:
|
||||||
|
LDAP_SEED_INTERNAL_LDIF_PATH: "${CI_PROJECT_DIR}/tests/server/openldap/data"
|
||||||
|
LDAP_SEED_INTERNAL_SCHEMA_PATH: "${CI_PROJECT_DIR}/tests/server/openldap/schema"
|
||||||
|
LDAP_BASE_DN: "dc=Test"
|
||||||
|
LDAP_DOMAIN: "Test"
|
||||||
|
LDAP_ADMIN_PASSWORD: test
|
||||||
|
#CI_DEBUG_SERVICES: "true"
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- php
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- mv .env.testing .env
|
||||||
|
|
||||||
|
# Install npm and dependancies
|
||||||
|
- apk add --no-cache npm
|
||||||
|
- npm i
|
||||||
|
- npm run prod
|
||||||
|
|
||||||
|
# Install Composer and project dependencies.
|
||||||
|
- mkdir -p ${COMPOSER_HOME}
|
||||||
|
- if [ -n "$GITHUB_TOKEN" ]; then cat $GITHUB_TOKEN |base64 -d > ${COMPOSER_HOME}/auth.json; fi
|
||||||
|
- composer install
|
||||||
|
|
||||||
|
# Generate an application key. Re-cache.
|
||||||
|
- php artisan key:generate
|
||||||
|
|
||||||
|
script:
|
||||||
|
# Sleep if we need to, in case we want to jump in and see what is going on during the test
|
||||||
|
- if [ -n "$DEBUG_PAUSE" ]; then echo "Pausing for $DEBUG_PAUSE seconds, so you can jump into the containers"; sleep $DEBUG_PAUSE; fi
|
||||||
|
# run laravel tests
|
||||||
|
- XDEBUG_MODE=coverage php vendor/bin/phpunit --coverage-text --colors=never
|
||||||
|
|
||||||
|
# run frontend tests
|
||||||
|
# if you have any task for testing frontend
|
||||||
|
# set it in your package.json script
|
||||||
|
# comment this out if you don't have a frontend test
|
||||||
|
# npm test
|
23
INSTALL
23
INSTALL
@ -1,23 +0,0 @@
|
|||||||
For install instructions in non-English languages, see the wiki:
|
|
||||||
http://phpldapadmin.sourceforge.net
|
|
||||||
|
|
||||||
* Requirements
|
|
||||||
|
|
||||||
phpLDAPadmin requires the following:
|
|
||||||
a. A web server (Apache, IIS, etc).
|
|
||||||
b. PHP 5.0.0 or newer (with LDAP support)
|
|
||||||
|
|
||||||
* To install
|
|
||||||
|
|
||||||
1. Unpack the archive (if you're reading this, you already did that).
|
|
||||||
2. Put the resulting 'phpldapadmin' directory somewhere in your webroot.
|
|
||||||
3. Copy 'config.php.example' to 'config.php' and edit to taste (this is in the config/ directory).
|
|
||||||
4. Then, point your browser to the phpldapadmin directory.
|
|
||||||
|
|
||||||
* For additional help
|
|
||||||
|
|
||||||
See the wiki:
|
|
||||||
http://phpldapadmin.sourceforge.net
|
|
||||||
|
|
||||||
Join our mailing list:
|
|
||||||
https://lists.sourceforge.net/lists/listinfo/phpldapadmin-devel
|
|
50
README.md
Normal file
50
README.md
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# phpLDAPadmin
|
||||||
|
phpLDAPadmin is a web based LDAP data management tool for system administrators. It is commonly known and referred by many as "PLA".
|
||||||
|
|
||||||
|
PLA is designed to be compliant with LDAP RFCs, enabling it to be used with any LDAP server.
|
||||||
|
If you come across an LDAP server, where PLA exhibits problems, please open an issue with full details of the problem so that we can have it fixed.
|
||||||
|
|
||||||
|
For up to date information on PLA, please head to the [wiki](https://github.com/leenooks/phpLDAPadmin/wiki).
|
||||||
|
|
||||||
|
> **NOTE**: GIT **master** is currently in active development, and as such functionality may be missing, broken or not working as expected.
|
||||||
|
>
|
||||||
|
> If you are after a working version of PLA, please use one of the tagged releases.
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
If you havent seen PLA in action, you can head here to the [demo](https://demo.phpldapadmin.org) site.
|
||||||
|
|
||||||
|
## Running the docker image
|
||||||
|
PLA v2 is available via docker for preview. (PLA v2 is still under heavy development.)
|
||||||
|
|
||||||
|
The container is the same one used for the demo site - but you'll be able to point it to your local LDAP server and see how things work.
|
||||||
|
|
||||||
|
Take a look at the [Docker Container](https://github.com/leenooks/phpLDAPadmin/wiki/Docker-Container) page for more details.
|
||||||
|
|
||||||
|
> Please let me know if you have any troubles with the container image, eg: usage of the container itself, or usage when it is pointing to your LDAP server.
|
||||||
|
>
|
||||||
|
> Open an issue (details below) with enough information for me to be able to recreate the problem. An `LDIF` will be invaluable if it is not handling data correctly.
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
The best place to get help with PLA (new and old) is on [Stack Overflow](https://stackoverflow.com/tags/phpldapadmin/info).
|
||||||
|
|
||||||
|
## Found a bug?
|
||||||
|
If you have found a bug, and can provide detailed instructions so that it can be reproduced, please open an [issue](https://github.com/leenooks/phpLDAPadmin/issues) and provide those details.
|
||||||
|
|
||||||
|
Before opening a ticket, please check to see if it hasnt already been reported, and if it has, please provide any additional information that will help it be fixed.
|
||||||
|
|
||||||
|
*TIP*: Issues opened with:
|
||||||
|
|
||||||
|
* details enabling the problem to be reproduced,
|
||||||
|
* including (if appropriate) an LDIF with the data that exhibits the problem,
|
||||||
|
* a patch (or a git pull request) to fix the problem
|
||||||
|
|
||||||
|
will be looked at first :)
|
||||||
|
|
||||||
|
## THANK YOU
|
||||||
|
Over the years, many, many, many people have supported PLA with either their time, their coding or with financial donations.
|
||||||
|
I have tried to send an email to acknowledge each contribution, and if you havent seen anything personally from me, I am sorry, but please know that I do appreciate all the help I get, in whatever form it is provided.
|
||||||
|
|
||||||
|
Again, Thank You.
|
||||||
|
|
||||||
|
## License
|
||||||
|
[LICENSE](LICENSE)
|
3
app/.gitignore
vendored
Normal file
3
app/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
q*
|
||||||
|
!public/
|
||||||
|
!.gitignore
|
@ -1,33 +1,50 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
|
||||||
* Classes and functions for the template engine.
|
namespace App\Classes\LDAP;
|
||||||
*
|
|
||||||
* @author The phpLDAPadmin development team
|
use Illuminate\Contracts\View\View;
|
||||||
* @package phpLDAPadmin
|
use Illuminate\Support\Collection;
|
||||||
*/
|
|
||||||
|
use App\Classes\LDAP\Schema\AttributeType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an attribute of a template.
|
* Represents an attribute of an LDAP Object
|
||||||
*
|
|
||||||
* @package phpLDAPadmin
|
|
||||||
* @subpackage Templates
|
|
||||||
*/
|
*/
|
||||||
class Attribute {
|
class Attribute implements \Countable, \ArrayAccess
|
||||||
# Attribute Name
|
{
|
||||||
public $name;
|
// Attribute Name
|
||||||
|
protected string $name;
|
||||||
|
|
||||||
|
protected ?AttributeType $schema = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
# Source of this attribute definition
|
# Source of this attribute definition
|
||||||
protected $source;
|
protected $source;
|
||||||
|
*/
|
||||||
|
|
||||||
# Current and Old Values
|
// Current and Old Values
|
||||||
protected $oldvalues = array();
|
protected Collection $values;
|
||||||
protected $values = array();
|
|
||||||
|
|
||||||
# MIN/MAX number of values
|
// Is this attribute an internal attribute
|
||||||
protected $min_value_count = -1;
|
protected bool $is_internal = FALSE;
|
||||||
protected $max_value_count = -1;
|
|
||||||
|
|
||||||
# Is the attribute internal
|
// Is this attribute the RDN?
|
||||||
protected $internal = false;
|
protected bool $is_rdn = FALSE;
|
||||||
|
|
||||||
|
// Objectclasses that require this attribute
|
||||||
|
protected Collection $required_by;
|
||||||
|
|
||||||
|
// MIN/MAX number of values
|
||||||
|
protected int $min_values_count = 0;
|
||||||
|
protected int $max_values_count = 0;
|
||||||
|
|
||||||
|
// RFC3866 Language Tags
|
||||||
|
protected Collection $lang_tags;
|
||||||
|
|
||||||
|
// The old values for this attribute - helps with isDirty() to determine if there is an update pending
|
||||||
|
protected Collection $oldValues;
|
||||||
|
|
||||||
|
/*
|
||||||
# Has the attribute been modified
|
# Has the attribute been modified
|
||||||
protected $modified = false;
|
protected $modified = false;
|
||||||
# Is the attribute being deleted because of an object class removal
|
# Is the attribute being deleted because of an object class removal
|
||||||
@ -69,7 +86,6 @@ class Attribute {
|
|||||||
public $page = 1;
|
public $page = 1;
|
||||||
public $order = 255;
|
public $order = 255;
|
||||||
public $ordersort = 255;
|
public $ordersort = 255;
|
||||||
public $rdn = false;
|
|
||||||
|
|
||||||
# Schema Aliases for this attribute (stored in lowercase)
|
# Schema Aliases for this attribute (stored in lowercase)
|
||||||
protected $aliases = array();
|
protected $aliases = array();
|
||||||
@ -77,37 +93,21 @@ class Attribute {
|
|||||||
# Configuration for automatically generated values
|
# Configuration for automatically generated values
|
||||||
protected $autovalue = array();
|
protected $autovalue = array();
|
||||||
protected $postvalue = array();
|
protected $postvalue = array();
|
||||||
|
*/
|
||||||
|
|
||||||
public function __construct($name,$values,$server_id,$source=null) {
|
public function __construct(string $name,array $values)
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
{
|
||||||
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
$this->name = $name;
|
||||||
|
$this->values = collect($values);
|
||||||
|
$this->lang_tags = collect();
|
||||||
|
$this->required_by = collect();
|
||||||
|
$this->oldValues = collect($values);
|
||||||
|
|
||||||
$server = $_SESSION[APPCONFIG]->getServer($server_id);
|
// No need to load our schema for internal attributes
|
||||||
|
if (! $this->is_internal)
|
||||||
$sattr = $server->getSchemaAttribute($name);
|
$this->schema = (new Server)->schema('attributetypes',$name);
|
||||||
if ($sattr) {
|
|
||||||
$this->name = $sattr->getName(false);
|
|
||||||
$this->setLDAPdetails($sattr);
|
|
||||||
|
|
||||||
} else
|
|
||||||
$this->name = $name;
|
|
||||||
|
|
||||||
$this->source = $source;
|
|
||||||
|
|
||||||
# XML attributes are shown by default
|
|
||||||
switch ($source) {
|
|
||||||
case 'XML': $this->show();
|
|
||||||
$this->setXML($values);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (! isset($values['values']))
|
|
||||||
debug_dump_backtrace('no index "values"',1);
|
|
||||||
|
|
||||||
$this->initValue($values['values']);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/*
|
||||||
# Should this attribute be hidden
|
# Should this attribute be hidden
|
||||||
if ($server->isAttrHidden($this->name))
|
if ($server->isAttrHidden($this->name))
|
||||||
$this->forcehide = true;
|
$this->forcehide = true;
|
||||||
@ -119,6 +119,176 @@ class Attribute {
|
|||||||
# Should this attribute value be unique
|
# Should this attribute value be unique
|
||||||
if ($server->isAttrUnique($this->name))
|
if ($server->isAttrUnique($this->name))
|
||||||
$this->unique = true;
|
$this->unique = true;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get(string $key): mixed
|
||||||
|
{
|
||||||
|
return match ($key) {
|
||||||
|
// List all the attributes
|
||||||
|
'attributes' => $this->attributes(),
|
||||||
|
// Can this attribute have more values
|
||||||
|
'can_addvalues' => $this->schema && (! $this->schema->is_single_value) && ((! $this->max_values_count) || ($this->values->count() < $this->max_values_count)),
|
||||||
|
// Schema attribute description
|
||||||
|
'description' => $this->schema ? $this->schema->{$key} : NULL,
|
||||||
|
// Attribute hints
|
||||||
|
'hints' => $this->hints(),
|
||||||
|
// Can this attribute be edited
|
||||||
|
'is_editable' => $this->schema ? $this->schema->{$key} : NULL,
|
||||||
|
// Is this an internal attribute
|
||||||
|
'is_internal' => isset($this->{$key}) && $this->{$key},
|
||||||
|
// Is this attribute the RDN
|
||||||
|
'is_rdn' => $this->is_rdn,
|
||||||
|
// We prefer the name as per the schema if it exists
|
||||||
|
'name' => $this->schema ? $this->schema->{$key} : $this->{$key},
|
||||||
|
// Attribute name in lower case
|
||||||
|
'name_lc' => strtolower($this->name),
|
||||||
|
// Old Values
|
||||||
|
'old_values' => $this->oldValues,
|
||||||
|
// Attribute values
|
||||||
|
'values' => $this->values,
|
||||||
|
|
||||||
|
default => throw new \Exception('Unknown key:' . $key),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set(string $key,mixed $values): void
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'value':
|
||||||
|
$this->values = collect($values);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addValue(string $value): void
|
||||||
|
{
|
||||||
|
$this->values->push($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function count(): int
|
||||||
|
{
|
||||||
|
return $this->values->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetExists(mixed $offset): bool
|
||||||
|
{
|
||||||
|
return ! is_null($this->values->get($offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetGet(mixed $offset): mixed
|
||||||
|
{
|
||||||
|
return $this->values->get($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetSet(mixed $offset, mixed $value): void
|
||||||
|
{
|
||||||
|
// We cannot set new values using array syntax
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetUnset(mixed $offset): void
|
||||||
|
{
|
||||||
|
// We cannot clear values using array syntax
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the hints about this attribute, ie: RDN, Required, etc
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function hints(): array
|
||||||
|
{
|
||||||
|
$result = collect();
|
||||||
|
|
||||||
|
// Is this Attribute an RDN
|
||||||
|
if ($this->is_rdn)
|
||||||
|
$result->put(__('rdn'),__('This attribute is required for the RDN'));
|
||||||
|
|
||||||
|
// If this attribute name is an alias for the schema attribute name
|
||||||
|
// @todo
|
||||||
|
|
||||||
|
// objectClasses requiring this attribute
|
||||||
|
// eg: $result->put('required','Required by objectClasses: a,b');
|
||||||
|
if ($this->required_by->count())
|
||||||
|
$result->put(__('required'),sprintf('%s: %s',__('Required Attribute by ObjectClass(es)'),$this->required_by->join(',')));
|
||||||
|
|
||||||
|
// This attribute has language tags
|
||||||
|
if ($this->lang_tags->count())
|
||||||
|
$result->put(__('language tags'),sprintf('%s: %d',__('This Attribute has Language Tags'),$this->lang_tags->count()));
|
||||||
|
|
||||||
|
return $result->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this attribute has changes
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isDirty(): bool
|
||||||
|
{
|
||||||
|
if ($this->oldValues->count() !== $this->values->count())
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return $this->values->diff($this->oldValues)->count() !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function oldValues(array $array): void
|
||||||
|
{
|
||||||
|
$this->oldValues = collect($array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the attribute value
|
||||||
|
*
|
||||||
|
* @param bool $edit
|
||||||
|
* @param bool $old
|
||||||
|
* @param bool $new
|
||||||
|
* @return View
|
||||||
|
*/
|
||||||
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
|
{
|
||||||
|
return view('components.attribute')
|
||||||
|
->with('o',$this)
|
||||||
|
->with('edit',$edit)
|
||||||
|
->with('old',$old)
|
||||||
|
->with('new',$new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the objectclasses that require this attribute
|
||||||
|
*
|
||||||
|
* @param Collection $oc
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function required_by(Collection $oc): Collection
|
||||||
|
{
|
||||||
|
return $this->required_by = ($this->schema
|
||||||
|
? $oc->intersect($this->schema->required_by_object_classes)
|
||||||
|
: collect());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this attribute has RFC3866 Language Tags, this will enable those values to be captured
|
||||||
|
*
|
||||||
|
* @param string $tag
|
||||||
|
* @param array $value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setLangTag(string $tag,array $value): void
|
||||||
|
{
|
||||||
|
$this->lang_tags->put($tag,$value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRDN(): void
|
||||||
|
{
|
||||||
|
$this->is_rdn = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -127,7 +297,7 @@ class Attribute {
|
|||||||
* @param boolean $lower - Return the attribute in normal or lower case (default lower)
|
* @param boolean $lower - Return the attribute in normal or lower case (default lower)
|
||||||
* @param boolean $real - Return the real attribute name (with ;binary, or just the name)
|
* @param boolean $real - Return the real attribute name (with ;binary, or just the name)
|
||||||
* @return string Attribute name
|
* @return string Attribute name
|
||||||
*/
|
*
|
||||||
public function getName($lower=true,$real=false) {
|
public function getName($lower=true,$real=false) {
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||||||
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs,$this->name);
|
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs,$this->name);
|
||||||
@ -168,7 +338,7 @@ class Attribute {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Autovalue is called after the attribute is initialised, and thus the values from the ldap server will be set.
|
* Autovalue is called after the attribute is initialised, and thus the values from the ldap server will be set.
|
||||||
*/
|
*
|
||||||
public function autoValue($new_val) {
|
public function autoValue($new_val) {
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||||||
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||||||
@ -258,64 +428,6 @@ class Attribute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getValue($i) {
|
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
|
||||||
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
|
||||||
|
|
||||||
if (isset($this->values[$i]))
|
|
||||||
return $this->values[$i];
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getOldValue($i) {
|
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
|
||||||
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
|
||||||
|
|
||||||
if (isset($this->oldvalues[$i]))
|
|
||||||
return $this->oldvalues[$i];
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMinValueCount() {
|
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
|
||||||
debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->min_value_count);
|
|
||||||
|
|
||||||
return $this->min_value_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setMinValueCount($min) {
|
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
|
||||||
debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs);
|
|
||||||
|
|
||||||
$this->min_value_count = $min;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMaxValueCount() {
|
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
|
||||||
debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->max_value_count);
|
|
||||||
|
|
||||||
return $this->max_value_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setMaxValueCount($max) {
|
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
|
||||||
debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs);
|
|
||||||
|
|
||||||
$this->max_value_count = $max;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function haveMoreValues() {
|
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
|
||||||
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
|
||||||
|
|
||||||
if ($this->getMaxValueCount() < 0 || ($this->getValueCount() < $this->getMaxValueCount()))
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function justModified() {
|
public function justModified() {
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||||||
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||||||
@ -347,20 +459,6 @@ class Attribute {
|
|||||||
$this->justModified();
|
$this->justModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isInternal() {
|
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
|
||||||
debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->internal);
|
|
||||||
|
|
||||||
return $this->internal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setInternal() {
|
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
|
||||||
debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs);
|
|
||||||
|
|
||||||
$this->internal = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isRequired() {
|
public function isRequired() {
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||||||
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||||||
@ -637,30 +735,11 @@ class Attribute {
|
|||||||
return $this->verify;
|
return $this->verify;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setRDN($rdn) {
|
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
|
||||||
debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs);
|
|
||||||
|
|
||||||
$this->rdn = $rdn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return if this attribute is an RDN attribute
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function isRDN() {
|
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
|
||||||
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs,$this->rdn);
|
|
||||||
|
|
||||||
return $this->rdn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Capture all the LDAP details we are interested in
|
* Capture all the LDAP details we are interested in
|
||||||
*
|
*
|
||||||
* @param sattr Schema Attribute
|
* @param sattr Schema Attribute
|
||||||
*/
|
*
|
||||||
private function setLDAPdetails($sattr) {
|
private function setLDAPdetails($sattr) {
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||||||
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||||||
@ -680,7 +759,7 @@ class Attribute {
|
|||||||
/**
|
/**
|
||||||
* Return a list of aliases for this Attribute (as defined by the schema)
|
* Return a list of aliases for this Attribute (as defined by the schema)
|
||||||
* This list will be lowercase.
|
* This list will be lowercase.
|
||||||
*/
|
*
|
||||||
public function getAliases() {
|
public function getAliases() {
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||||||
debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->aliases);
|
debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->aliases);
|
||||||
@ -846,7 +925,7 @@ class Attribute {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the values removed in an attribute.
|
* Display the values removed in an attribute.
|
||||||
*/
|
*
|
||||||
public function getRemovedValues() {
|
public function getRemovedValues() {
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||||||
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||||||
@ -856,7 +935,7 @@ class Attribute {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the values removed in an attribute.
|
* Display the values removed in an attribute.
|
||||||
*/
|
*
|
||||||
public function getAddedValues() {
|
public function getAddedValues() {
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||||||
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||||||
@ -872,7 +951,7 @@ class Attribute {
|
|||||||
*
|
*
|
||||||
* @param string $attr_name The name of the attribute to examine.
|
* @param string $attr_name The name of the attribute to examine.
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*
|
||||||
private function real_attr_name() {
|
private function real_attr_name() {
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||||||
debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->name);
|
debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->name);
|
||||||
@ -882,7 +961,7 @@ class Attribute {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Does this attribute need supporting JS
|
* Does this attribute need supporting JS
|
||||||
*/
|
*
|
||||||
public function needJS($type=null) {
|
public function needJS($type=null) {
|
||||||
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||||||
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||||||
@ -913,5 +992,5 @@ class Attribute {
|
|||||||
} else
|
} else
|
||||||
debug_dump_backtrace(sprintf('Unknown JS request %s',$type),1);
|
debug_dump_backtrace(sprintf('Unknown JS request %s',$type),1);
|
||||||
}
|
}
|
||||||
}
|
*/
|
||||||
?>
|
}
|
12
app/Classes/LDAP/Attribute/Binary.php
Normal file
12
app/Classes/LDAP/Attribute/Binary.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an attribute whose values are binary
|
||||||
|
*/
|
||||||
|
class Binary extends Attribute
|
||||||
|
{
|
||||||
|
}
|
33
app/Classes/LDAP/Attribute/Binary/JpegPhoto.php
Normal file
33
app/Classes/LDAP/Attribute/Binary/JpegPhoto.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute\Binary;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute\Binary;
|
||||||
|
use App\Traits\MD5Updates;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an JpegPhoto Attribute
|
||||||
|
*/
|
||||||
|
final class JpegPhoto extends Binary
|
||||||
|
{
|
||||||
|
use MD5Updates;
|
||||||
|
|
||||||
|
public function __construct(string $name,array $values)
|
||||||
|
{
|
||||||
|
parent::__construct($name,$values);
|
||||||
|
|
||||||
|
$this->internal = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
|
{
|
||||||
|
return view('components.attribute.binary.jpegphoto')
|
||||||
|
->with('o',$this)
|
||||||
|
->with('edit',$edit)
|
||||||
|
->with('old',$old)
|
||||||
|
->with('new',$new)
|
||||||
|
->with('f',new \finfo);
|
||||||
|
}
|
||||||
|
}
|
58
app/Classes/LDAP/Attribute/Factory.php
Normal file
58
app/Classes/LDAP/Attribute/Factory.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This factory is used to return LDAP attributes as an object
|
||||||
|
*
|
||||||
|
* If there is no specific Attribute defined, then the default Attribute::class is return
|
||||||
|
*/
|
||||||
|
class Factory
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'LAf';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of attributes to appropriate class
|
||||||
|
*/
|
||||||
|
public const map = [
|
||||||
|
'createtimestamp' => Internal\Timestamp::class,
|
||||||
|
'creatorsname' => Internal\DN::class,
|
||||||
|
'contextcsn' => Internal\CSN::class,
|
||||||
|
'entrycsn' => Internal\CSN::class,
|
||||||
|
'entrydn' => Internal\DN::class,
|
||||||
|
'entryuuid' => Internal\UUID::class,
|
||||||
|
'gidnumber' => GidNumber::class,
|
||||||
|
'hassubordinates' => Internal\HasSubordinates::class,
|
||||||
|
'jpegphoto' => Binary\JpegPhoto::class,
|
||||||
|
'modifytimestamp' => Internal\Timestamp::class,
|
||||||
|
'modifiersname' => Internal\DN::class,
|
||||||
|
'objectclass' => ObjectClass::class,
|
||||||
|
'structuralobjectclass' => Internal\StructuralObjectClass::class,
|
||||||
|
'subschemasubentry' => Internal\SubschemaSubentry::class,
|
||||||
|
'supportedcontrol' => Schema\OID::class,
|
||||||
|
'supportedextension' => Schema\OID::class,
|
||||||
|
'supportedfeatures' => Schema\OID::class,
|
||||||
|
'supportedsaslmechanisms' => Schema\Mechanisms::class,
|
||||||
|
'userpassword' => Password::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the new Object for an attribute
|
||||||
|
*
|
||||||
|
* @param string $attribute
|
||||||
|
* @param array $values
|
||||||
|
* @return Attribute
|
||||||
|
*/
|
||||||
|
public static function create(string $attribute,array $values): Attribute
|
||||||
|
{
|
||||||
|
$class = Arr::get(self::map,strtolower($attribute),Attribute::class);
|
||||||
|
Log::debug(sprintf('%s:Creating LDAP Attribute [%s] as [%s]',static::LOGKEY,$attribute,$class));
|
||||||
|
|
||||||
|
return new $class($attribute,$values);
|
||||||
|
}
|
||||||
|
}
|
12
app/Classes/LDAP/Attribute/GidNumber.php
Normal file
12
app/Classes/LDAP/Attribute/GidNumber.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an GidNumber Attribute
|
||||||
|
*/
|
||||||
|
final class GidNumber extends Attribute
|
||||||
|
{
|
||||||
|
}
|
22
app/Classes/LDAP/Attribute/Internal.php
Normal file
22
app/Classes/LDAP/Attribute/Internal.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an attribute whose values are internal
|
||||||
|
*/
|
||||||
|
abstract class Internal extends Attribute
|
||||||
|
{
|
||||||
|
protected bool $is_internal = TRUE;
|
||||||
|
|
||||||
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
|
{
|
||||||
|
// @note Internal attributes cannot be edited
|
||||||
|
return view('components.attribute.internal')
|
||||||
|
->with('o',$this);
|
||||||
|
}
|
||||||
|
}
|
12
app/Classes/LDAP/Attribute/Internal/CSN.php
Normal file
12
app/Classes/LDAP/Attribute/Internal/CSN.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an CSN Attribute
|
||||||
|
*/
|
||||||
|
final class CSN extends Internal
|
||||||
|
{
|
||||||
|
}
|
12
app/Classes/LDAP/Attribute/Internal/DN.php
Normal file
12
app/Classes/LDAP/Attribute/Internal/DN.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an DN Attribute
|
||||||
|
*/
|
||||||
|
final class DN extends Internal
|
||||||
|
{
|
||||||
|
}
|
12
app/Classes/LDAP/Attribute/Internal/HasSubordinates.php
Normal file
12
app/Classes/LDAP/Attribute/Internal/HasSubordinates.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an HasSubordinates Attribute
|
||||||
|
*/
|
||||||
|
final class HasSubordinates extends Internal
|
||||||
|
{
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an StructuralObjectClass Attribute
|
||||||
|
*/
|
||||||
|
final class StructuralObjectClass extends Internal
|
||||||
|
{
|
||||||
|
}
|
12
app/Classes/LDAP/Attribute/Internal/SubschemaSubentry.php
Normal file
12
app/Classes/LDAP/Attribute/Internal/SubschemaSubentry.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an SubschemaSubentry Attribute
|
||||||
|
*/
|
||||||
|
final class SubschemaSubentry extends Internal
|
||||||
|
{
|
||||||
|
}
|
20
app/Classes/LDAP/Attribute/Internal/Timestamp.php
Normal file
20
app/Classes/LDAP/Attribute/Internal/Timestamp.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an attribute whose values are timestamps
|
||||||
|
*/
|
||||||
|
final class Timestamp extends Internal
|
||||||
|
{
|
||||||
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
|
{
|
||||||
|
// @note Internal attributes cannot be edited
|
||||||
|
return view('components.attribute.internal.timestamp')
|
||||||
|
->with('o',$this);
|
||||||
|
}
|
||||||
|
}
|
12
app/Classes/LDAP/Attribute/Internal/UUID.php
Normal file
12
app/Classes/LDAP/Attribute/Internal/UUID.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an UUID Attribute
|
||||||
|
*/
|
||||||
|
final class UUID extends Internal
|
||||||
|
{
|
||||||
|
}
|
50
app/Classes/LDAP/Attribute/ObjectClass.php
Normal file
50
app/Classes/LDAP/Attribute/ObjectClass.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\{Attribute,Server};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an ObjectClass Attribute
|
||||||
|
*/
|
||||||
|
final class ObjectClass extends Attribute
|
||||||
|
{
|
||||||
|
// Which of the values is the structural object class
|
||||||
|
protected Collection $structural;
|
||||||
|
|
||||||
|
public function __construct(string $name,array $values)
|
||||||
|
{
|
||||||
|
parent::__construct($name,$values);
|
||||||
|
|
||||||
|
$this->structural = collect();
|
||||||
|
|
||||||
|
// Determine which of the values is the structural objectclass
|
||||||
|
foreach ($values as $oc) {
|
||||||
|
if ((new Server)->schema('objectclasses',$oc)->isStructural())
|
||||||
|
$this->structural->push($oc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is a specific value the structural objectclass
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isStructural(string $value): bool
|
||||||
|
{
|
||||||
|
return $this->structural->search($value) !== FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
|
{
|
||||||
|
return view('components.attribute.objectclass')
|
||||||
|
->with('o',$this)
|
||||||
|
->with('edit',$edit)
|
||||||
|
->with('old',$old)
|
||||||
|
->with('new',$new);
|
||||||
|
}
|
||||||
|
}
|
25
app/Classes/LDAP/Attribute/Password.php
Normal file
25
app/Classes/LDAP/Attribute/Password.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute;
|
||||||
|
use App\Traits\MD5Updates;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an attribute whose values are passwords
|
||||||
|
*/
|
||||||
|
final class Password extends Attribute
|
||||||
|
{
|
||||||
|
use MD5Updates;
|
||||||
|
|
||||||
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
|
{
|
||||||
|
return view('components.attribute.password')
|
||||||
|
->with('o',$this)
|
||||||
|
->with('edit',$edit)
|
||||||
|
->with('old',$old)
|
||||||
|
->with('new',$new);
|
||||||
|
}
|
||||||
|
}
|
58
app/Classes/LDAP/Attribute/Schema.php
Normal file
58
app/Classes/LDAP/Attribute/Schema.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an attribute whose values are schema related
|
||||||
|
*/
|
||||||
|
abstract class Schema extends Attribute
|
||||||
|
{
|
||||||
|
protected bool $internal = TRUE;
|
||||||
|
|
||||||
|
protected static function _get(string $filename,string $string,string $key): ?string
|
||||||
|
{
|
||||||
|
$array = Cache::remember($filename,86400,function() use ($filename) {
|
||||||
|
try {
|
||||||
|
$f = fopen($filename,'r');
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = collect();
|
||||||
|
|
||||||
|
while (! feof($f)) {
|
||||||
|
$line = trim(fgets($f));
|
||||||
|
|
||||||
|
if (! $line OR preg_match('/^#/',$line))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$fields = explode(':',$line);
|
||||||
|
|
||||||
|
$result->put($x=Arr::get($fields,0),[
|
||||||
|
'title'=>Arr::get($fields,1,$x),
|
||||||
|
'ref'=>Arr::get($fields,2),
|
||||||
|
'desc'=>Arr::get($fields,3,__('No description available, can you help with one?')),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
fclose($f);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
});
|
||||||
|
|
||||||
|
return Arr::get(($array ? $array->get($string) : []),$key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
|
{
|
||||||
|
// @note Schema attributes cannot be edited
|
||||||
|
return view('components.attribute.internal')
|
||||||
|
->with('o',$this);
|
||||||
|
}
|
||||||
|
}
|
42
app/Classes/LDAP/Attribute/Schema/Mechanisms.php
Normal file
42
app/Classes/LDAP/Attribute/Schema/Mechanisms.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute\Schema;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute\Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a Mechanisms Attribute
|
||||||
|
*/
|
||||||
|
final class Mechanisms extends Schema
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Given an SASL Mechanism name, returns a verbose description of the Mechanism.
|
||||||
|
* This function parses ldap_supported_saslmechanisms.txt and looks up the specified
|
||||||
|
* Mechanism, and returns the verbose message defined in that file.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* "SCRAM-SHA-1" => array:3 [▼
|
||||||
|
* "title" => "Salted Challenge Response Authentication Mechanism (SCRAM) SHA1"
|
||||||
|
* "ref" => "RFC 5802"
|
||||||
|
* "desc" => "This specification describes a family of authentication mechanisms called the Salted Challenge Response Authentication Mechanism (SCRAM) which addresses the req ▶"
|
||||||
|
* ]
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $string The SASL Mechanism (ie, "SCRAM-SHA-1") of interest.
|
||||||
|
* @param string $key The title|ref|desc to return
|
||||||
|
* @return string|NULL
|
||||||
|
*/
|
||||||
|
public static function get(string $string,string $key): ?string
|
||||||
|
{
|
||||||
|
return parent::_get(config_path('ldap_supported_saslmechanisms.txt'),$string,$key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
|
{
|
||||||
|
// @note Schema attributes cannot be edited
|
||||||
|
return view('components.attribute.schema.mechanisms')
|
||||||
|
->with('o',$this);
|
||||||
|
}
|
||||||
|
}
|
43
app/Classes/LDAP/Attribute/Schema/OID.php
Normal file
43
app/Classes/LDAP/Attribute/Schema/OID.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute\Schema;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute\Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an OID Attribute
|
||||||
|
*/
|
||||||
|
final class OID extends Schema
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Given an LDAP OID number, returns a verbose description of the OID.
|
||||||
|
* This function parses ldap_supported_oids.txt and looks up the specified
|
||||||
|
* OID, and returns the verbose message defined in that file.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* "1.3.6.1.4.1.4203.1.5.1" => array:3 [
|
||||||
|
* [title] => All Operational Attribute
|
||||||
|
* [ref] => RFC 3673
|
||||||
|
* [desc] => An LDAP extension which clients may use to request the return of all operational attributes.
|
||||||
|
* ]
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $string The OID number (ie, "1.3.6.1.4.1.4203.1.5.1") of the OID of interest.
|
||||||
|
* @param string $key The title|ref|desc to return
|
||||||
|
* @return string|null
|
||||||
|
* @testedby TranslateOidTest::testRootDSE()
|
||||||
|
*/
|
||||||
|
public static function get(string $string,string $key): ?string
|
||||||
|
{
|
||||||
|
return parent::_get(config_path('ldap_supported_oids.txt'),$string,$key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
|
{
|
||||||
|
// @note Schema attributes cannot be edited
|
||||||
|
return view('components.attribute.schema.oid')
|
||||||
|
->with('o',$this);
|
||||||
|
}
|
||||||
|
}
|
53
app/Classes/LDAP/Export.php
Normal file
53
app/Classes/LDAP/Export.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use LdapRecord\Query\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export Class
|
||||||
|
*
|
||||||
|
* This abstract classes provides all the common methods and variables for the
|
||||||
|
* export classes.
|
||||||
|
*/
|
||||||
|
abstract class Export
|
||||||
|
{
|
||||||
|
// Line Break
|
||||||
|
protected string $br = "\r\n";
|
||||||
|
|
||||||
|
// Item(s) being Exported
|
||||||
|
protected Collection $items;
|
||||||
|
|
||||||
|
// Type of export
|
||||||
|
protected const type = 'Unknown';
|
||||||
|
|
||||||
|
public function __construct(Collection $items)
|
||||||
|
{
|
||||||
|
$this->items = $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function __toString(): string;
|
||||||
|
|
||||||
|
protected function header()
|
||||||
|
{
|
||||||
|
$output = '';
|
||||||
|
|
||||||
|
$output .= sprintf('# %s %s',__(static::type.' for'),($x=$this->items->first())).$this->br;
|
||||||
|
$output .= sprintf('# %s: %s (%s)',
|
||||||
|
__('Server'),
|
||||||
|
$x->getConnection()->getConfiguration()->get('name'),
|
||||||
|
$x->getConnection()->getLdapConnection()->getHost()).$this->br;
|
||||||
|
//$output .= sprintf('# %s: %s',__('Search Scope'),$this->scope).$this->br;
|
||||||
|
//$output .= sprintf('# %s: %s',__('Search Filter'),$this->entry->dn).$this->br;
|
||||||
|
$output .= sprintf('# %s: %s',__('Total Entries'),$this->items->count()).$this->br;
|
||||||
|
$output .= '#'.$this->br;
|
||||||
|
$output .= sprintf('# %s %s (%s) on %s',__('Generated by'),config('app.name'),config('app.url'),date('F j, Y g:i a')).$this->br;
|
||||||
|
$output .= sprintf('# %s %s',__('Exported by'),Auth::user() ?: 'Anonymous').$this->br;
|
||||||
|
$output .= sprintf('# %s: %s',__('Version'),config('app.version')).$this->br;
|
||||||
|
|
||||||
|
$output .= $this->br;
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
}
|
78
app/Classes/LDAP/Export/LDIF.php
Normal file
78
app/Classes/LDAP/Export/LDIF.php
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Export;
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Export;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export from LDAP using an LDIF format
|
||||||
|
*/
|
||||||
|
class LDIF extends Export
|
||||||
|
{
|
||||||
|
// The maximum length of the ldif line
|
||||||
|
private int $line_length = 76;
|
||||||
|
protected const type = 'LDIF Export';
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
$result = parent::header();
|
||||||
|
$result .= 'version: 1';
|
||||||
|
$result .= $this->br;
|
||||||
|
|
||||||
|
$c = 1;
|
||||||
|
foreach ($this->items as $o) {
|
||||||
|
if ($c > 1)
|
||||||
|
$result .= $this->br;
|
||||||
|
|
||||||
|
$title = (string)$o;
|
||||||
|
if (strlen($title) > $this->line_length)
|
||||||
|
$title = Str::of($title)->limit($this->line_length-3-5,'...'.substr($title,-5));
|
||||||
|
|
||||||
|
$result .= sprintf('# %s %s: %s',__('Entry'),$c++,$title).$this->br;
|
||||||
|
|
||||||
|
// Display DN
|
||||||
|
$result .= $this->multiLineDisplay(
|
||||||
|
Str::isAscii($o)
|
||||||
|
? sprintf('dn: %s',$o)
|
||||||
|
: sprintf('dn:: %s',base64_encode($o))
|
||||||
|
,$this->br);
|
||||||
|
|
||||||
|
// Display Attributes
|
||||||
|
foreach ($o->getObjects() as $ao) {
|
||||||
|
foreach ($ao->values as $value) {
|
||||||
|
$result .= $this->multiLineDisplay(
|
||||||
|
Str::isAscii($value)
|
||||||
|
? sprintf('%s: %s',$ao->name,$value)
|
||||||
|
: sprintf('%s:: %s',$ao->name,base64_encode($value))
|
||||||
|
,$this->br);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to wrap LDIF lines
|
||||||
|
*
|
||||||
|
* @param string $str The line to be wrapped if needed.
|
||||||
|
*/
|
||||||
|
private function multiLineDisplay(string $str,string $br): string
|
||||||
|
{
|
||||||
|
$length_string = strlen($str);
|
||||||
|
$length_max = $this->line_length;
|
||||||
|
|
||||||
|
$output = '';
|
||||||
|
while ($length_string > $length_max) {
|
||||||
|
$output .= substr($str,0,$length_max).$br;
|
||||||
|
$str = ' '.substr($str,$length_max);
|
||||||
|
$length_string = strlen($str);
|
||||||
|
}
|
||||||
|
|
||||||
|
$output .= $str.$br;
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
}
|
79
app/Classes/LDAP/Import.php
Normal file
79
app/Classes/LDAP/Import.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
use App\Exceptions\Import\GeneralException;
|
||||||
|
use App\Exceptions\Import\ObjectExistsException;
|
||||||
|
use App\Ldap\Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import Class
|
||||||
|
*
|
||||||
|
* This abstract classes provides all the common methods and variables for the
|
||||||
|
* import classes.
|
||||||
|
*/
|
||||||
|
abstract class Import
|
||||||
|
{
|
||||||
|
// Valid LDIF commands
|
||||||
|
protected const LDAP_IMPORT_ADD = 1;
|
||||||
|
protected const LDAP_IMPORT_DELETE = 2;
|
||||||
|
protected const LDAP_IMPORT_MODRDN = 3;
|
||||||
|
protected const LDAP_IMPORT_MODDN = 4;
|
||||||
|
protected const LDAP_IMPORT_MODIFY = 5;
|
||||||
|
|
||||||
|
protected const LDAP_ACTIONS = [
|
||||||
|
'add' => self::LDAP_IMPORT_ADD,
|
||||||
|
'delete' => self::LDAP_IMPORT_DELETE,
|
||||||
|
'modrdn' => self::LDAP_IMPORT_MODRDN,
|
||||||
|
'moddn' => self::LDAP_IMPORT_MODDN,
|
||||||
|
'modify' => self::LDAP_IMPORT_MODIFY,
|
||||||
|
];
|
||||||
|
|
||||||
|
// The import data to process
|
||||||
|
protected string $input;
|
||||||
|
// The attributes the server knows about
|
||||||
|
protected Collection $server_attributes;
|
||||||
|
|
||||||
|
public function __construct(string $input) {
|
||||||
|
$this->input = $input;
|
||||||
|
$this->server_attributes = config('server')->schema('attributetypes');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to commit an entry and return the result.
|
||||||
|
*
|
||||||
|
* @param Entry $o
|
||||||
|
* @param int $action
|
||||||
|
* @return Collection
|
||||||
|
* @throws GeneralException
|
||||||
|
* @throws ObjectExistsException
|
||||||
|
*/
|
||||||
|
final protected function commit(Entry $o,int $action): Collection
|
||||||
|
{
|
||||||
|
switch ($action) {
|
||||||
|
case static::LDAP_IMPORT_ADD:
|
||||||
|
try {
|
||||||
|
$o->save();
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return collect([
|
||||||
|
'dn'=>$o->getDN(),
|
||||||
|
'result'=>sprintf('%d: %s (%s)',
|
||||||
|
($x=$e->getDetailedError())->getErrorCode(),
|
||||||
|
$x->getErrorMessage(),
|
||||||
|
$x->getDiagnosticMessage(),
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return collect(['dn'=>$o->getDN(),'result'=>__('Created')]);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new GeneralException('Unhandled action during commit: '.$action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function process(): Collection;
|
||||||
|
}
|
233
app/Classes/LDAP/Import/LDIF.php
Normal file
233
app/Classes/LDAP/Import/LDIF.php
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Import;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Nette\NotImplementedException;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Import;
|
||||||
|
use App\Exceptions\Import\{GeneralException,VersionException};
|
||||||
|
use App\Ldap\Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import LDIF to LDAP using an LDIF format
|
||||||
|
*
|
||||||
|
* The LDIF spec is described by RFC2849
|
||||||
|
* http://www.ietf.org/rfc/rfc2849.txt
|
||||||
|
*/
|
||||||
|
class LDIF extends Import
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'ILF';
|
||||||
|
|
||||||
|
public function process(): Collection
|
||||||
|
{
|
||||||
|
$c = 0;
|
||||||
|
$action = NULL;
|
||||||
|
$attribute = NULL;
|
||||||
|
$base64encoded = FALSE;
|
||||||
|
$o = NULL;
|
||||||
|
$value = '';
|
||||||
|
$version = NULL;
|
||||||
|
$result = collect();
|
||||||
|
|
||||||
|
// @todo When renaming DNs, the hotlink should point to the new entry on success, or the old entry on failure.
|
||||||
|
foreach (preg_split('/(\r?\n|\r)/',$this->input) as $line) {
|
||||||
|
$c++;
|
||||||
|
Log::debug(sprintf('%s: LDIF Line [%s]',self::LOGKEY,$line));
|
||||||
|
$line = trim($line);
|
||||||
|
|
||||||
|
// If the line starts with a comment, ignore it
|
||||||
|
if (preg_match('/^#/',$line))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If we have a blank line, then that completes this command
|
||||||
|
if (! $line) {
|
||||||
|
if (! is_null($o)) {
|
||||||
|
// Add the last attribute;
|
||||||
|
$o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s: Committing Entry [%s]',self::LOGKEY,$o->getDN()));
|
||||||
|
|
||||||
|
// Commit
|
||||||
|
$result->push($this->commit($o,$action));
|
||||||
|
$result->last()->put('line',$c);
|
||||||
|
|
||||||
|
$o = NULL;
|
||||||
|
$action = NULL;
|
||||||
|
$base64encoded = FALSE;
|
||||||
|
$attribute = NULL;
|
||||||
|
$value = '';
|
||||||
|
|
||||||
|
// Else its a blank line
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$m = [];
|
||||||
|
preg_match('/^([a-zA-Z0-9;-]+)(:+)\s+(.*)$/',$line,$m);
|
||||||
|
|
||||||
|
switch ($x=Arr::get($m,1)) {
|
||||||
|
case 'changetype':
|
||||||
|
if ($m[2] !== ':')
|
||||||
|
throw new GeneralException(sprintf('ChangeType cannot be base64 encoded set at [%d]. (line %d)',$version,$c));
|
||||||
|
|
||||||
|
switch ($m[3]) {
|
||||||
|
// if (preg_match('/^changetype:[ ]*(delete|add|modrdn|moddn|modify)/i',$lines[0])) {
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException(sprintf('Unknown change type [%s]? (line %d)',$m[3],$c));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'version':
|
||||||
|
if (! is_null($version))
|
||||||
|
throw new VersionException(sprintf('Version has already been set at [%d]. (line %d)',$version,$c));
|
||||||
|
|
||||||
|
if ($m[2] !== ':')
|
||||||
|
throw new VersionException(sprintf('Version cannot be base64 encoded set at [%d]. (line %d)',$version,$c));
|
||||||
|
|
||||||
|
$version = (int)$m[3];
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Treat it as an attribute
|
||||||
|
default:
|
||||||
|
// If $m is NULL, then this is the 2nd (or more) line of a base64 encoded value
|
||||||
|
if (! $m) {
|
||||||
|
$value .= $line;
|
||||||
|
Log::debug(sprintf('%s: Attribute [%s] adding [%s] (%d)',self::LOGKEY,$attribute,$line,$c));
|
||||||
|
|
||||||
|
// add to last attr value
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are ready to create the entry or add the attribute
|
||||||
|
if ($attribute) {
|
||||||
|
if ($attribute === 'dn') {
|
||||||
|
if (! is_null($o))
|
||||||
|
throw new GeneralException(sprintf('Previous Entry not complete? (line %d)',$c));
|
||||||
|
|
||||||
|
$dn = $base64encoded ? base64_decode($value) : $value;
|
||||||
|
Log::debug(sprintf('%s: Creating new entry:',self::LOGKEY,$dn));
|
||||||
|
//$o = Entry::find($dn);
|
||||||
|
|
||||||
|
// If it doesnt exist, we'll create it
|
||||||
|
//if (! $o) {
|
||||||
|
$o = new Entry;
|
||||||
|
$o->setDn($dn);
|
||||||
|
//}
|
||||||
|
|
||||||
|
$action = self::LDAP_IMPORT_ADD;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s: Adding Attribute [%s] value [%s] (%d)',self::LOGKEY,$attribute,$value,$c));
|
||||||
|
|
||||||
|
if ($value)
|
||||||
|
$o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||||
|
else
|
||||||
|
throw new GeneralException(sprintf('Attribute has no value [%s] (line %d)',$attribute,$c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start of a new attribute
|
||||||
|
$base64encoded = ($m[2] === '::');
|
||||||
|
// @todo Need to parse attributes with ';' options
|
||||||
|
$attribute = $m[1];
|
||||||
|
$value = $m[3];
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s: New Attribute [%s] with [%s] (%d)',self::LOGKEY,$attribute,$value,$c));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($version !== 1)
|
||||||
|
throw new VersionException('LDIF import cannot handle version: '.($version ?: __('NOT DEFINED')));
|
||||||
|
}
|
||||||
|
|
||||||
|
// We may still have a pending action
|
||||||
|
if ($action) {
|
||||||
|
// Add the last attribute;
|
||||||
|
$o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s: Committing Entry [%s]',self::LOGKEY,$o->getDN()));
|
||||||
|
|
||||||
|
// Commit
|
||||||
|
$result->push($this->commit($o,$action));
|
||||||
|
$result->last()->put('line',$c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function readEntry() {
|
||||||
|
static $haveVersion = false;
|
||||||
|
|
||||||
|
if ($lines = $this->nextLines()) {
|
||||||
|
|
||||||
|
$server = $this->getServer();
|
||||||
|
|
||||||
|
# The first line should be the DN
|
||||||
|
if (preg_match('/^dn:/',$lines[0])) {
|
||||||
|
list($text,$dn) = $this->getAttrValue(array_shift($lines));
|
||||||
|
|
||||||
|
# The second line should be our changetype
|
||||||
|
if (preg_match('/^changetype:[ ]*(delete|add|modrdn|moddn|modify)/i',$lines[0])) {
|
||||||
|
$attrvalue = $this->getAttrValue($lines[0]);
|
||||||
|
$changetype = $attrvalue[1];
|
||||||
|
array_shift($lines);
|
||||||
|
|
||||||
|
} else
|
||||||
|
$changetype = 'add';
|
||||||
|
|
||||||
|
$this->template = new Template($this->server_id,null,null,$changetype);
|
||||||
|
|
||||||
|
switch ($changetype) {
|
||||||
|
case 'add':
|
||||||
|
$rdn = get_rdn($dn);
|
||||||
|
$container = $server->getContainer($dn);
|
||||||
|
|
||||||
|
$this->template->setContainer($container);
|
||||||
|
$this->template->accept();
|
||||||
|
|
||||||
|
$this->getAddDetails($lines);
|
||||||
|
$this->template->setRDNAttributes($rdn);
|
||||||
|
|
||||||
|
return $this->template;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'modify':
|
||||||
|
if (! $server->dnExists($dn))
|
||||||
|
return $this->error(sprintf('%s %s',_('DN does not exist'),$dn),$lines);
|
||||||
|
|
||||||
|
$this->template->setDN($dn);
|
||||||
|
$this->template->accept(false,true);
|
||||||
|
|
||||||
|
return $this->getModifyDetails($lines);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'moddn':
|
||||||
|
case 'modrdn':
|
||||||
|
if (! $server->dnExists($dn))
|
||||||
|
return $this->error(sprintf('%s %s',_('DN does not exist'),$dn),$lines);
|
||||||
|
|
||||||
|
$this->template->setDN($dn);
|
||||||
|
$this->template->accept();
|
||||||
|
|
||||||
|
return $this->getModRDNAttributes($lines);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (! $server->dnExists($dn))
|
||||||
|
return $this->error(_('Unkown change type'),$lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else
|
||||||
|
return $this->error(_('A valid dn line is required'),$lines);
|
||||||
|
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
598
app/Classes/LDAP/Schema/AttributeType.php
Normal file
598
app/Classes/LDAP/Schema/AttributeType.php
Normal file
@ -0,0 +1,598 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Schema;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an LDAP AttributeType
|
||||||
|
*
|
||||||
|
* @package phpLDAPadmin
|
||||||
|
* @subpackage Schema
|
||||||
|
*/
|
||||||
|
final class AttributeType extends Base {
|
||||||
|
// The attribute from which this attribute inherits (if any)
|
||||||
|
private ?string $sup_attribute = NULL;
|
||||||
|
|
||||||
|
// Array of AttributeTypes which inherit from this one
|
||||||
|
private Collection $children;
|
||||||
|
|
||||||
|
// The equality rule used
|
||||||
|
private ?string $equality = NULL;
|
||||||
|
|
||||||
|
// The ordering of the attributeType
|
||||||
|
private ?string $ordering = NULL;
|
||||||
|
|
||||||
|
// Supports substring matching?
|
||||||
|
private ?string $sub_str_rule = NULL;
|
||||||
|
|
||||||
|
// The full syntax string, ie 1.2.3.4{16}
|
||||||
|
private ?string $syntax = NULL;
|
||||||
|
private ?string $syntax_oid = NULL;
|
||||||
|
|
||||||
|
// boolean: is single valued only?
|
||||||
|
private bool $is_single_value = FALSE;
|
||||||
|
|
||||||
|
// boolean: is collective?
|
||||||
|
private bool $is_collective = FALSE;
|
||||||
|
|
||||||
|
// boolean: can use modify?
|
||||||
|
private bool $is_no_user_modification = FALSE;
|
||||||
|
|
||||||
|
// The usage string set by the LDAP schema
|
||||||
|
private ?string $usage = NULL;
|
||||||
|
|
||||||
|
// An array of alias attribute names, strings
|
||||||
|
private Collection $aliases;
|
||||||
|
|
||||||
|
// The max number of characters this attribute can be
|
||||||
|
private ?int $max_length = NULL;
|
||||||
|
|
||||||
|
// A string description of the syntax type (taken from the LDAPSyntaxes)
|
||||||
|
/**
|
||||||
|
* @deprecated - reference syntaxes directly if possible
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private ?string $type = NULL;
|
||||||
|
|
||||||
|
// An array of objectClasses which use this attributeType (must be set by caller)
|
||||||
|
private Collection $used_in_object_classes;
|
||||||
|
|
||||||
|
// A list of object class names that require this attribute type.
|
||||||
|
private Collection $required_by_object_classes;
|
||||||
|
|
||||||
|
// This attribute has been forced a MAY attribute by the configuration.
|
||||||
|
private bool $forced_as_may = FALSE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new AttributeType object from a raw LDAP AttributeType string.
|
||||||
|
*
|
||||||
|
* eg: ( 2.5.4.0 NAME 'objectClass' DESC 'RFC4512: object classes of the entity' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
|
||||||
|
*/
|
||||||
|
public function __construct(string $line) {
|
||||||
|
Log::debug(sprintf('Parsing AttributeType [%s]',$line));
|
||||||
|
|
||||||
|
parent::__construct($line);
|
||||||
|
|
||||||
|
$strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE);
|
||||||
|
|
||||||
|
// Init
|
||||||
|
$this->children = collect();
|
||||||
|
$this->aliases = collect();
|
||||||
|
$this->used_in_object_classes = collect();
|
||||||
|
$this->required_by_object_classes = collect();
|
||||||
|
|
||||||
|
for ($i=0; $i < count($strings); $i++) {
|
||||||
|
switch ($strings[$i]) {
|
||||||
|
case '(':
|
||||||
|
case ')':
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'NAME':
|
||||||
|
// @note Some schema's return a (' instead of a ( '
|
||||||
|
if ($strings[$i+1] != '(' && ! preg_match('/^\(/',$strings[$i+1])) {
|
||||||
|
do {
|
||||||
|
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||||
|
|
||||||
|
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||||
|
|
||||||
|
// This attribute has no aliases
|
||||||
|
//$this->aliases = collect();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// In case we came here becaues of a ('
|
||||||
|
if (preg_match('/^\(/',$strings[$i]))
|
||||||
|
$strings[$i] = preg_replace('/^\(/','',$strings[$i]);
|
||||||
|
else
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||||
|
|
||||||
|
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||||
|
|
||||||
|
// Add alias names for this attribute
|
||||||
|
while ($strings[++$i] != ')') {
|
||||||
|
$alias = $strings[$i];
|
||||||
|
$alias = preg_replace("/^\'(.*)\'$/",'$1',$alias);
|
||||||
|
$this->addAlias($alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->name = preg_replace("/^\'(.*)\'$/",'$1',$this->name);
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case NAME returned (%s)',$this->name),['aliases'=>$this->aliases]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'DESC':
|
||||||
|
do {
|
||||||
|
$this->description .= (strlen($this->description) ? ' ' : '').$strings[++$i];
|
||||||
|
|
||||||
|
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||||
|
|
||||||
|
$this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description);
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case DESC returned (%s)',$this->description));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'OBSOLETE':
|
||||||
|
$this->is_obsolete = TRUE;
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case OBSOLETE returned (%s)',$this->is_obsolete));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SUP':
|
||||||
|
$i++;
|
||||||
|
$this->sup_attribute = preg_replace("/^\'(.*)\'$/",'$1',$strings[$i]);
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case SUP returned (%s)',$this->sup_attribute));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'EQUALITY':
|
||||||
|
$this->equality = $strings[++$i];
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case EQUALITY returned (%s)',$this->equality));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ORDERING':
|
||||||
|
$this->ordering = $strings[++$i];
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case ORDERING returned (%s)',$this->ordering));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SUBSTR':
|
||||||
|
$this->sub_str_rule = $strings[++$i];
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case SUBSTR returned (%s)',$this->sub_str_rule));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SYNTAX':
|
||||||
|
$this->syntax = $strings[++$i];
|
||||||
|
$this->syntax_oid = preg_replace('/{\d+}$/','',$this->syntax);
|
||||||
|
Log::debug(sprintf('/ Evaluating SYNTAX returned (%s) [%s]',$this->syntax,$this->syntax_oid));
|
||||||
|
|
||||||
|
// Does this SYNTAX string specify a max length (ie, 1.2.3.4{16})
|
||||||
|
$m = [];
|
||||||
|
if (preg_match('/{(\d+)}$/',$this->syntax,$m))
|
||||||
|
$this->max_length = $m[1];
|
||||||
|
else
|
||||||
|
$this->max_length = NULL;
|
||||||
|
|
||||||
|
if ($i < count($strings) - 1 && $strings[$i+1] == '{')
|
||||||
|
do {
|
||||||
|
$this->name .= ' '.$strings[++$i];
|
||||||
|
} while ($strings[$i] != '}');
|
||||||
|
|
||||||
|
$this->syntax = preg_replace("/^\'(.*)\'$/",'$1',$this->syntax);
|
||||||
|
$this->syntax_oid = preg_replace("/^\'(.*)\'$/",'$1',$this->syntax_oid);
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case SYNTAX returned (%s) [%s] {%d}',$this->syntax,$this->syntax_oid,$this->max_length));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SINGLE-VALUE':
|
||||||
|
$this->is_single_value = TRUE;
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case SINGLE-VALUE returned (%s)',$this->is_single_value));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'COLLECTIVE':
|
||||||
|
$this->is_collective = TRUE;
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case COLLECTIVE returned (%s)',$this->is_collective));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'NO-USER-MODIFICATION':
|
||||||
|
$this->is_no_user_modification = TRUE;
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case NO-USER-MODIFICATION returned (%s)',$this->is_no_user_modification));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'USAGE':
|
||||||
|
$this->usage = $strings[++$i];
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case USAGE returned (%s)',$this->usage));
|
||||||
|
break;
|
||||||
|
|
||||||
|
// @note currently not captured
|
||||||
|
case 'X-ORDERED':
|
||||||
|
Log::error(sprintf('- Case X-ORDERED returned (%s)',$strings[++$i]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
// @note currently not captured
|
||||||
|
case 'X-ORIGIN':
|
||||||
|
$value = '';
|
||||||
|
|
||||||
|
do {
|
||||||
|
$value .= (strlen($value) ? ' ' : '').$strings[++$i];
|
||||||
|
|
||||||
|
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||||
|
|
||||||
|
Log::error(sprintf('- Case X-ORIGIN returned (%s)',$value));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) {
|
||||||
|
$this->oid = $strings[$i];
|
||||||
|
Log::debug(sprintf('- Case default returned (%s)',$this->oid));
|
||||||
|
|
||||||
|
} elseif ($strings[$i])
|
||||||
|
Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __clone()
|
||||||
|
{
|
||||||
|
// When we clone, we need to break the reference too
|
||||||
|
$this->aliases = clone $this->aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get(string $key): mixed
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'aliases': return $this->aliases;
|
||||||
|
case 'children': return $this->children;
|
||||||
|
case 'forced_as_may': return $this->forced_as_may;
|
||||||
|
case 'is_collective': return $this->is_collective;
|
||||||
|
case 'is_editable': return ! $this->is_no_user_modification;
|
||||||
|
case 'is_no_user_modification': return $this->is_no_user_modification;
|
||||||
|
case 'is_single_value': return $this->is_single_value;
|
||||||
|
case 'equality': return $this->equality;
|
||||||
|
case 'max_length': return $this->max_length;
|
||||||
|
case 'ordering': return $this->ordering;
|
||||||
|
case 'required_by_object_classes': return $this->required_by_object_classes;
|
||||||
|
case 'sub_str_rule': return $this->sub_str_rule;
|
||||||
|
case 'sup_attribute': return $this->sup_attribute;
|
||||||
|
case 'syntax': return $this->syntax;
|
||||||
|
case 'syntax_oid': return $this->syntax_oid;
|
||||||
|
case 'type': return $this->type;
|
||||||
|
case 'usage': return $this->usage;
|
||||||
|
case 'used_in_object_classes': return $this->used_in_object_classes;
|
||||||
|
case 'validation': return Arr::get(config('ldap.validation'),$this->name_lc);
|
||||||
|
|
||||||
|
default: return parent::__get($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an attribute name to the alias array.
|
||||||
|
*
|
||||||
|
* @param string $alias The name of a new attribute to add to this attribute's list of aliases.
|
||||||
|
*/
|
||||||
|
public function addAlias(string $alias): void
|
||||||
|
{
|
||||||
|
$this->aliases->push($alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Children of this attribute type that inherit from this one
|
||||||
|
*
|
||||||
|
* @param string $child
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addChild(string $child): void
|
||||||
|
{
|
||||||
|
$this->children->push($child);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an objectClass name to this attribute's list of "required by" objectClasses,
|
||||||
|
* that is the list of objectClasses which must have this attribute.
|
||||||
|
*
|
||||||
|
* @param string $name The name of the objectClass to add.
|
||||||
|
*/
|
||||||
|
public function addRequiredByObjectClass(string $name): void
|
||||||
|
{
|
||||||
|
if ($this->required_by_object_classes->search($name) === FALSE)
|
||||||
|
$this->required_by_object_classes->push($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an objectClass name to this attribute's list of "used in" objectClasses,
|
||||||
|
* that is the list of objectClasses which provide this attribute.
|
||||||
|
*
|
||||||
|
* @param string $name The name of the objectClass to add.
|
||||||
|
*/
|
||||||
|
public function addUsedInObjectClass(string $name): void
|
||||||
|
{
|
||||||
|
if ($this->used_in_object_classes->search($name) === FALSE)
|
||||||
|
$this->used_in_object_classes->push($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the names of attributes that are an alias for this attribute (if any).
|
||||||
|
*
|
||||||
|
* @return Collection An array of names of attributes which alias this attribute or
|
||||||
|
* an empty array if no attribute aliases this object.
|
||||||
|
* @deprecated use class->aliases
|
||||||
|
*/
|
||||||
|
public function getAliases(): Collection
|
||||||
|
{
|
||||||
|
return $this->aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets this attribute's equality string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @deprecated use $this->equality
|
||||||
|
*/
|
||||||
|
public function getEquality()
|
||||||
|
{
|
||||||
|
return $this->equality;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether this attribute is collective.
|
||||||
|
*
|
||||||
|
* @return boolean Returns TRUE if this attribute is collective and FALSE otherwise.
|
||||||
|
* @deprecated use $this->is_collective
|
||||||
|
*/
|
||||||
|
public function getIsCollective(): bool
|
||||||
|
{
|
||||||
|
return $this->is_collective;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether this attribute is not modifiable by users.
|
||||||
|
*
|
||||||
|
* @return boolean Returns TRUE if this attribute is not modifiable by users.
|
||||||
|
* @deprecated use $this->is_no_user_modification
|
||||||
|
*/
|
||||||
|
public function getIsNoUserModification(): bool
|
||||||
|
{
|
||||||
|
return $this->is_no_user_modification;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether this attribute is single-valued. If this attribute only supports single values, TRUE
|
||||||
|
* is returned. If this attribute supports multiple values, FALSE is returned.
|
||||||
|
*
|
||||||
|
* @return boolean Returns TRUE if this attribute is single-valued or FALSE otherwise.
|
||||||
|
* @deprecated use class->is_single_value
|
||||||
|
*/
|
||||||
|
public function getIsSingleValue(): bool
|
||||||
|
{
|
||||||
|
return $this->is_single_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets this attribute's the maximum length. If no maximum is defined by the LDAP server, NULL is returned.
|
||||||
|
*
|
||||||
|
* @return int The maximum length (in characters) of this attribute or NULL if no maximum is specified.
|
||||||
|
* @deprecated use $this->max_length;
|
||||||
|
*/
|
||||||
|
public function getMaxLength()
|
||||||
|
{
|
||||||
|
return $this->max_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets this attribute's ordering specification.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @deprecated use $this->ordering
|
||||||
|
*/
|
||||||
|
public function getOrdering(): string
|
||||||
|
{
|
||||||
|
return $this->ordering;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of "required by" objectClasses, that is the list of objectClasses
|
||||||
|
* which provide must have attribute.
|
||||||
|
*
|
||||||
|
* @return array An array of names of objectclasses (strings) which provide this attribute
|
||||||
|
*/
|
||||||
|
public function getRequiredByObjectClasses() {
|
||||||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||||||
|
debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->required_by_object_classes);
|
||||||
|
|
||||||
|
return $this->required_by_object_classes;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Gets this attribute's substring matching specification
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @deprecated use $this->sub_str_rule;
|
||||||
|
*/
|
||||||
|
public function getSubstr() {
|
||||||
|
return $this->sub_str_rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets this attribute's parent attribute (if any). If this attribute does not
|
||||||
|
* inherit from another attribute, NULL is returned.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @deprecated use $class->sup_attribute directly
|
||||||
|
*/
|
||||||
|
public function getSupAttribute() {
|
||||||
|
return $this->sup_attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets this attribute's syntax OID. Differs from getSyntaxString() in that this
|
||||||
|
* function only returns the actual OID with any length specification removed.
|
||||||
|
* Ie, if the syntax string is "1.2.3.4{16}", this function only retruns
|
||||||
|
* "1.2.3.4".
|
||||||
|
*
|
||||||
|
* @return string The syntax OID string.
|
||||||
|
* @deprecated use $this->syntax_oid;
|
||||||
|
*/
|
||||||
|
public function getSyntaxOID()
|
||||||
|
{
|
||||||
|
return $this->syntax_oid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets this attribute's raw syntax string (ie: "1.2.3.4{16}").
|
||||||
|
*
|
||||||
|
* @return string The raw syntax string
|
||||||
|
*/
|
||||||
|
public function getSyntaxString() {
|
||||||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||||||
|
debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->syntax);
|
||||||
|
|
||||||
|
return $this->syntax;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets this attribute's type
|
||||||
|
*
|
||||||
|
* @return string The attribute's type.
|
||||||
|
* @deprecated use $this->type;
|
||||||
|
*/
|
||||||
|
public function getType()
|
||||||
|
{
|
||||||
|
return $this->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets this attribute's usage string as defined by the LDAP server
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @deprecated use $this->usage
|
||||||
|
*/
|
||||||
|
public function getUsage()
|
||||||
|
{
|
||||||
|
return $this->usage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of "used in" objectClasses, that is the list of objectClasses
|
||||||
|
* which provide this attribute.
|
||||||
|
*
|
||||||
|
* @return Collection An array of names of objectclasses (strings) which provide this attribute
|
||||||
|
* @deprecated use $this->used_in_object_classes
|
||||||
|
*/
|
||||||
|
public function getUsedInObjectClasses(): Collection
|
||||||
|
{
|
||||||
|
return $this->used_in_object_classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the specified attribute is an alias for this one (based on this attribute's alias list).
|
||||||
|
*
|
||||||
|
* @param string $attr_name The name of the attribute to check.
|
||||||
|
* @return boolean TRUE if the specified attribute is an alias for this one, or FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public function isAliasFor($attr_name) {
|
||||||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||||||
|
debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||||||
|
|
||||||
|
foreach ($this->aliases as $alias_attr_name)
|
||||||
|
if (strcasecmp($alias_attr_name,$attr_name) == 0)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
* @deprecated use $this->forced_as_may
|
||||||
|
*/
|
||||||
|
public function isForceMay(): bool
|
||||||
|
{
|
||||||
|
return $this->forced_as_may;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an attribute name from this attribute's alias array.
|
||||||
|
*
|
||||||
|
* @param string $alias The name of the attribute to remove.
|
||||||
|
*/
|
||||||
|
public function removeAlias(string $alias): void
|
||||||
|
{
|
||||||
|
if (($x=$this->aliases->search($alias)) !== FALSE)
|
||||||
|
$this->aliases->forget($x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a list of object classes, determine if this is a required attribute
|
||||||
|
*
|
||||||
|
* @param Collection $oc List of objectclasses to compare.
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function required_by(Collection $oc): Collection
|
||||||
|
{
|
||||||
|
return $oc->diff($this->required_by_object_classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this attribute's list of aliases.
|
||||||
|
*
|
||||||
|
* @param Collection $aliases The array of alias names (strings)
|
||||||
|
* @deprecated use $this->aliases =
|
||||||
|
*/
|
||||||
|
public function setAliases(Collection $aliases): void
|
||||||
|
{
|
||||||
|
$this->aliases = $aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will mark this attribute as a forced MAY attribute
|
||||||
|
*/
|
||||||
|
public function setForceMay() {
|
||||||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||||||
|
debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs);
|
||||||
|
|
||||||
|
$this->forced_as_may = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether this attribute is single-valued.
|
||||||
|
*
|
||||||
|
* @param boolean $is
|
||||||
|
*/
|
||||||
|
public function setIsSingleValue(bool $is): void
|
||||||
|
{
|
||||||
|
$this->is_single_value = $is;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this attribute's SUP attribute (ie, the attribute from which this attribute inherits).
|
||||||
|
*
|
||||||
|
* @param string $attr The name of the new parent (SUP) attribute
|
||||||
|
*/
|
||||||
|
public function setSupAttribute(string $attr): void
|
||||||
|
{
|
||||||
|
$this->sup_attribute = trim($attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this attribute's type.
|
||||||
|
*
|
||||||
|
* @param string $type The new type.
|
||||||
|
*/
|
||||||
|
public function setType($type) {
|
||||||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||||||
|
debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs);
|
||||||
|
|
||||||
|
$this->type = $type;
|
||||||
|
}
|
||||||
|
}
|
120
app/Classes/LDAP/Schema/Base.php
Normal file
120
app/Classes/LDAP/Schema/Base.php
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Schema;
|
||||||
|
|
||||||
|
use App\Exceptions\InvalidUsage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic parent class for all schema items.
|
||||||
|
*
|
||||||
|
* A schema item is an ObjectClass, an AttributeBype, a MatchingRule, or a Syntax.
|
||||||
|
* All schema items have at least two things in common: An OID and a Description.
|
||||||
|
*/
|
||||||
|
abstract class Base {
|
||||||
|
// Record the LDAP String
|
||||||
|
private string $line;
|
||||||
|
|
||||||
|
// The schema item's name.
|
||||||
|
protected ?string $name = NULL;
|
||||||
|
|
||||||
|
// The OID of this schema item.
|
||||||
|
protected string $oid;
|
||||||
|
|
||||||
|
# The description of this schema item.
|
||||||
|
protected ?string $description = NULL;
|
||||||
|
|
||||||
|
// Boolean value indicating whether this objectClass is obsolete
|
||||||
|
private bool $is_obsolete = FALSE;
|
||||||
|
|
||||||
|
public function __construct(string $line)
|
||||||
|
{
|
||||||
|
$this->line = $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get(string $key): mixed
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'description': return $this->description;
|
||||||
|
case 'is_obsolete': return $this->is_obsolete;
|
||||||
|
case 'line': return $this->line;
|
||||||
|
case 'name': return $this->name;
|
||||||
|
case 'name_lc': return strtolower($this->name);
|
||||||
|
case 'oid': return $this->oid;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new InvalidUsage('Unknown key:'.$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __isset(string $key): bool
|
||||||
|
{
|
||||||
|
return isset($this->{$key});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
* @deprecated replace with $class->description
|
||||||
|
*/
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether this item is flagged as obsolete by the LDAP server.
|
||||||
|
*
|
||||||
|
* @deprecated replace with $this->is_obsolete
|
||||||
|
*/
|
||||||
|
public function getIsObsolete(): bool
|
||||||
|
{
|
||||||
|
return $this->is_obsolete;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the objects name.
|
||||||
|
*
|
||||||
|
* @param boolean $lower Return the name in lower case (default)
|
||||||
|
* @return string The name
|
||||||
|
* @deprecated use object->name
|
||||||
|
*/
|
||||||
|
public function getName(bool $lower=TRUE): string
|
||||||
|
{
|
||||||
|
return $lower ? strtolower($this->name) : $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the objects name.
|
||||||
|
*
|
||||||
|
* @return string The name
|
||||||
|
* @deprecated use object->oid
|
||||||
|
*/
|
||||||
|
public function getOID(): string
|
||||||
|
{
|
||||||
|
return $this->oid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDescription(string $desc): void
|
||||||
|
{
|
||||||
|
$this->description = $desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this attribute's name.
|
||||||
|
*
|
||||||
|
* @param string $name The new name to give this attribute.
|
||||||
|
*/
|
||||||
|
public function setName($name): void
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setOID(string $oid): void
|
||||||
|
{
|
||||||
|
$this->oid = $oid;
|
||||||
|
}
|
||||||
|
}
|
79
app/Classes/LDAP/Schema/LDAPSyntax.php
Normal file
79
app/Classes/LDAP/Schema/LDAPSyntax.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Schema;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an LDAP Syntax
|
||||||
|
*
|
||||||
|
* @package phpLDAPadmin
|
||||||
|
* @subpackage Schema
|
||||||
|
*/
|
||||||
|
final class LDAPSyntax extends Base {
|
||||||
|
// Is human readable?
|
||||||
|
private ?bool $is_not_human_readable = NULL;
|
||||||
|
|
||||||
|
// Binary transfer required?
|
||||||
|
private ?bool $binary_transfer_required = NULL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Syntax object from a raw LDAP syntax string.
|
||||||
|
*/
|
||||||
|
public function __construct(string $line) {
|
||||||
|
Log::debug(sprintf('Parsing LDAPSyntax [%s]',$line));
|
||||||
|
|
||||||
|
parent::__construct($line);
|
||||||
|
|
||||||
|
$strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE);
|
||||||
|
|
||||||
|
for ($i=0; $i<count($strings); $i++) {
|
||||||
|
switch($strings[$i]) {
|
||||||
|
case '(':
|
||||||
|
case ')':
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'DESC':
|
||||||
|
do {
|
||||||
|
$this->description .= (strlen($this->description) ? ' ' : '').$strings[++$i];
|
||||||
|
|
||||||
|
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||||
|
|
||||||
|
$this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description);
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case DESC returned (%s)',$this->description));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'X-BINARY-TRANSFER-REQUIRED':
|
||||||
|
$this->binary_transfer_required = (str_replace("'",'',$strings[++$i]) === 'TRUE');
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case X-BINARY-TRANSFER-REQUIRED returned (%s)',$this->binary_transfer_required));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'X-NOT-HUMAN-READABLE':
|
||||||
|
$this->is_not_human_readable = (str_replace("'",'',$strings[++$i]) === 'TRUE');
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case X-NOT-HUMAN-READABLE returned (%s)',$this->is_not_human_readable));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) {
|
||||||
|
$this->oid = $strings[$i];
|
||||||
|
Log::debug(sprintf('- Case default returned (%s)',$this->oid));
|
||||||
|
|
||||||
|
} elseif ($strings[$i])
|
||||||
|
Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get(string $key): mixed
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'binary_transfer_required': return $this->binary_transfer_required;
|
||||||
|
case 'is_not_human_readable': return $this->is_not_human_readable;
|
||||||
|
|
||||||
|
default: return parent::__get($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
142
app/Classes/LDAP/Schema/MatchingRule.php
Normal file
142
app/Classes/LDAP/Schema/MatchingRule.php
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Schema;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an LDAP MatchingRule
|
||||||
|
*
|
||||||
|
* @package phpLDAPadmin
|
||||||
|
* @subpackage Schema
|
||||||
|
*/
|
||||||
|
final class MatchingRule extends Base {
|
||||||
|
// This rule's syntax OID
|
||||||
|
private ?string $syntax = NULL;
|
||||||
|
|
||||||
|
// An array of attribute names who use this MatchingRule
|
||||||
|
private Collection $used_by_attrs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new MatchingRule object from a raw LDAP MatchingRule string.
|
||||||
|
*/
|
||||||
|
function __construct(string $line) {
|
||||||
|
Log::debug(sprintf('Parsing MatchingRule [%s]',$line));
|
||||||
|
|
||||||
|
parent::__construct($line);
|
||||||
|
|
||||||
|
$strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE);
|
||||||
|
|
||||||
|
// Init
|
||||||
|
$this->used_by_attrs = collect();
|
||||||
|
|
||||||
|
for ($i=0; $i<count($strings); $i++) {
|
||||||
|
switch ($strings[$i]) {
|
||||||
|
case '(':
|
||||||
|
case ')':
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'NAME':
|
||||||
|
if ($strings[$i+1] != '(') {
|
||||||
|
do {
|
||||||
|
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||||
|
|
||||||
|
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||||
|
|
||||||
|
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||||
|
|
||||||
|
do {
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
} while (! preg_match('/\)+\)?/',$strings[$i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->name = preg_replace("/^\'/",'',$this->name);
|
||||||
|
$this->name = preg_replace("/\'$/",'',$this->name);
|
||||||
|
|
||||||
|
Log::debug(sprintf(sprintf('- Case NAME returned (%s)',$this->name)));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'DESC':
|
||||||
|
do {
|
||||||
|
$this->description .= (strlen($this->description) ? ' ' : '').$strings[++$i];
|
||||||
|
|
||||||
|
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||||
|
|
||||||
|
$this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description);
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case DESC returned (%s)',$this->description));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'OBSOLETE':
|
||||||
|
$this->is_obsolete = TRUE;
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case OBSOLETE returned (%s)',$this->is_obsolete));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SYNTAX':
|
||||||
|
$this->syntax = $strings[++$i];
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case SYNTAX returned (%s)',$this->syntax));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) {
|
||||||
|
$this->oid = $strings[$i];
|
||||||
|
Log::debug(sprintf('- Case default returned (%s)',$this->oid));
|
||||||
|
|
||||||
|
} elseif ($strings[$i])
|
||||||
|
Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get(string $key): mixed
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'syntax': return $this->syntax;
|
||||||
|
case 'used_by_attrs': return $this->used_by_attrs;
|
||||||
|
|
||||||
|
default: return parent::__get($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an attribute name to the list of attributes who use this MatchingRule
|
||||||
|
*/
|
||||||
|
public function addUsedByAttr(string $name): void
|
||||||
|
{
|
||||||
|
$name = trim($name);
|
||||||
|
|
||||||
|
if ($this->used_by_attrs->search($name) === FALSE)
|
||||||
|
$this->used_by_attrs->push($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an array of attribute names (strings) which use this MatchingRule
|
||||||
|
*
|
||||||
|
* @return array The array of attribute names (strings).
|
||||||
|
* @deprecated use $this->used_by_attrs
|
||||||
|
*/
|
||||||
|
public function getUsedByAttrs()
|
||||||
|
{
|
||||||
|
return $this->used_by_attrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the list of used_by_attrs to the array specified by $attrs;
|
||||||
|
*
|
||||||
|
* @param Collection $attrs The array of attribute names (strings) which use this MatchingRule
|
||||||
|
*/
|
||||||
|
public function setUsedByAttrs(Collection $attrs): void
|
||||||
|
{
|
||||||
|
$this->used_by_attrs = $attrs;
|
||||||
|
}
|
||||||
|
}
|
99
app/Classes/LDAP/Schema/MatchingRuleUse.php
Normal file
99
app/Classes/LDAP/Schema/MatchingRuleUse.php
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Schema;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an LDAP schema matchingRuleUse entry
|
||||||
|
*
|
||||||
|
* @package phpLDAPadmin
|
||||||
|
* @subpackage Schema
|
||||||
|
*/
|
||||||
|
final class MatchingRuleUse extends Base {
|
||||||
|
// An array of attribute names who use this MatchingRule
|
||||||
|
private Collection $used_by_attrs;
|
||||||
|
|
||||||
|
function __construct(string $line) {
|
||||||
|
Log::debug(sprintf('Parsing MatchingRuleUse [%s]',$line));
|
||||||
|
|
||||||
|
parent::__construct($line);
|
||||||
|
|
||||||
|
$strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE);
|
||||||
|
|
||||||
|
// Init
|
||||||
|
$this->used_by_attrs = collect();
|
||||||
|
|
||||||
|
for ($i=0; $i<count($strings); $i++) {
|
||||||
|
switch ($strings[$i]) {
|
||||||
|
case '(':
|
||||||
|
case ')':
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'NAME':
|
||||||
|
if ($strings[$i+1] != '(') {
|
||||||
|
do {
|
||||||
|
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||||
|
|
||||||
|
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||||
|
|
||||||
|
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||||
|
|
||||||
|
do {
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
} while (! preg_match('/\)+\)?/',$strings[$i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->name = preg_replace("/^\'(.*)\'$/",'$1',$this->name);
|
||||||
|
|
||||||
|
Log::debug(sprintf(sprintf('- Case NAME returned (%s)',$this->name)));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'APPLIES':
|
||||||
|
if ($strings[$i+1] != '(') {
|
||||||
|
// Has a single attribute name
|
||||||
|
$this->used_by_attrs = collect($strings[++$i]);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Has multiple attribute names
|
||||||
|
while ($strings[++$i] != ')') {
|
||||||
|
$new_attr = $strings[++$i];
|
||||||
|
$new_attr = preg_replace("/^\'(.*)\'$/",'$1',$new_attr);
|
||||||
|
|
||||||
|
$this->used_by_attrs->push($new_attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case APPLIES returned (%s)',$this->used_by_attrs->join(',')));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) {
|
||||||
|
$this->oid = $strings[$i];
|
||||||
|
Log::debug(sprintf('- Case default returned (%s)',$this->oid));
|
||||||
|
|
||||||
|
} elseif ($strings[$i])
|
||||||
|
Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an array of attribute names (strings) which use this MatchingRuleUse object.
|
||||||
|
*
|
||||||
|
* @return array The array of attribute names (strings).
|
||||||
|
* @deprecated use $this->used_by_attrs
|
||||||
|
*/
|
||||||
|
public function getUsedByAttrs()
|
||||||
|
{
|
||||||
|
return $this->used_by_attrs;
|
||||||
|
}
|
||||||
|
}
|
540
app/Classes/LDAP/Schema/ObjectClass.php
Normal file
540
app/Classes/LDAP/Schema/ObjectClass.php
Normal file
@ -0,0 +1,540 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Schema;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Server;
|
||||||
|
use App\Exceptions\InvalidUsage;
|
||||||
|
use App\Ldap\Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an LDAP Schema objectClass
|
||||||
|
*
|
||||||
|
* @package phpLDAPadmin
|
||||||
|
* @subpackage Schema
|
||||||
|
*/
|
||||||
|
final class ObjectClass extends Base {
|
||||||
|
// The server ID that this objectclass belongs to.
|
||||||
|
private Server $server;
|
||||||
|
|
||||||
|
// Array of objectClass names from which this objectClass inherits
|
||||||
|
private Collection $sup_classes;
|
||||||
|
|
||||||
|
// One of STRUCTURAL, ABSTRACT, or AUXILIARY
|
||||||
|
private int $type;
|
||||||
|
|
||||||
|
// Arrays of attribute names that this objectClass requires
|
||||||
|
private Collection $must_attrs;
|
||||||
|
|
||||||
|
// Arrays of attribute names that this objectClass allows, but does not require
|
||||||
|
private Collection $may_attrs;
|
||||||
|
|
||||||
|
// Arrays of attribute names that this objectClass has been forced to MAY attrs, due to configuration
|
||||||
|
private Collection $may_force;
|
||||||
|
|
||||||
|
// Array of objectClasses which inherit from this one
|
||||||
|
private Collection $child_objectclasses;
|
||||||
|
|
||||||
|
private bool $is_obsolete;
|
||||||
|
|
||||||
|
/* ObjectClass Types */
|
||||||
|
private const OC_STRUCTURAL = 0x01;
|
||||||
|
private const OC_ABSTRACT = 0x02;
|
||||||
|
private const OC_AUXILIARY = 0x03;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ObjectClass object given a raw LDAP objectClass string.
|
||||||
|
*
|
||||||
|
* eg: ( 2.5.6.0 NAME 'top' DESC 'top of the superclass chain' ABSTRACT MUST objectClass )
|
||||||
|
*/
|
||||||
|
public function __construct(string $line,Server $server)
|
||||||
|
{
|
||||||
|
parent::__construct($line);
|
||||||
|
|
||||||
|
Log::debug(sprintf('Parsing ObjectClass [%s]',$line));
|
||||||
|
|
||||||
|
$strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE);
|
||||||
|
|
||||||
|
// Init
|
||||||
|
$this->server = $server;
|
||||||
|
$this->may_attrs = collect();
|
||||||
|
$this->may_force = collect();
|
||||||
|
$this->must_attrs = collect();
|
||||||
|
$this->sup_classes = collect();
|
||||||
|
$this->child_objectclasses = collect();
|
||||||
|
|
||||||
|
for ($i=0; $i < count($strings); $i++) {
|
||||||
|
switch ($strings[$i]) {
|
||||||
|
case '(':
|
||||||
|
case ')':
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'NAME':
|
||||||
|
if ($strings[$i+1] != '(') {
|
||||||
|
do {
|
||||||
|
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||||
|
|
||||||
|
} while (! preg_match('/\'$/s',$strings[$i]));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||||
|
|
||||||
|
} while (! preg_match('/\'$/s',$strings[$i]));
|
||||||
|
|
||||||
|
do {
|
||||||
|
$i++;
|
||||||
|
} while (! preg_match('/\)+\)?/',$strings[$i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->name = preg_replace("/^\'(.*)\'$/",'$1',$this->name);
|
||||||
|
|
||||||
|
Log::debug(sprintf(sprintf('- Case NAME returned (%s)',$this->name)));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'DESC':
|
||||||
|
do {
|
||||||
|
$this->description .= (strlen($this->description) ? ' ' : '').$strings[++$i];
|
||||||
|
|
||||||
|
} while (! preg_match('/\'$/s',$strings[$i]));
|
||||||
|
|
||||||
|
$this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description);
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case DESC returned (%s)',$this->description));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'OBSOLETE':
|
||||||
|
$this->is_obsolete = TRUE;
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case OBSOLETE returned (%s)',$this->is_obsolete));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SUP':
|
||||||
|
if ($strings[$i+1] != '(') {
|
||||||
|
$this->sup_classes->push(preg_replace("/'/",'',$strings[++$i]));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
if ($strings[$i] != '$')
|
||||||
|
$this->sup_classes->push(preg_replace("/'/",'',$strings[$i]));
|
||||||
|
|
||||||
|
} while (! preg_match('/\)+\)?/',$strings[$i+1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case SUP returned (%s)',$this->sup_classes->join(',')));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ABSTRACT':
|
||||||
|
$this->type = self::OC_ABSTRACT;
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case ABSTRACT returned (%s)',$this->type));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'STRUCTURAL':
|
||||||
|
$this->type = self::OC_STRUCTURAL;
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case STRUCTURAL returned (%s)',$this->type));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'AUXILIARY':
|
||||||
|
$this->type = self::OC_AUXILIARY;
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case AUXILIARY returned (%s)',$this->type));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'MUST':
|
||||||
|
$attrs = collect();
|
||||||
|
|
||||||
|
$i = $this->parseList(++$i,$strings,$attrs);
|
||||||
|
|
||||||
|
Log::debug(sprintf('= parseList returned %d (%s)',$i,$attrs->join(',')));
|
||||||
|
|
||||||
|
foreach ($attrs as $string) {
|
||||||
|
$attr = new ObjectClassAttribute($string,$this->name);
|
||||||
|
|
||||||
|
if ($server->isForceMay($attr->getName())) {
|
||||||
|
$this->may_force->push($attr);
|
||||||
|
$this->may_attrs->push($attr);
|
||||||
|
|
||||||
|
} else
|
||||||
|
$this->must_attrs->push($attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case MUST returned (%s) (%s)',$this->must_attrs->join(','),$this->may_force->join(',')));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'MAY':
|
||||||
|
$attrs = collect();
|
||||||
|
|
||||||
|
$i = $this->parseList(++$i,$strings,$attrs);
|
||||||
|
|
||||||
|
Log::debug(sprintf('parseList returned %d (%s)',$i,$attrs->join(',')));
|
||||||
|
|
||||||
|
foreach ($attrs as $string) {
|
||||||
|
$attr = new ObjectClassAttribute($string,$this->name);
|
||||||
|
$this->may_attrs->push($attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf('- Case MAY returned (%s)',$this->may_attrs->join(',')));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) {
|
||||||
|
$this->oid = $strings[$i];
|
||||||
|
Log::debug(sprintf('- Case default returned (%s)',$this->oid));
|
||||||
|
|
||||||
|
} elseif ($strings[$i])
|
||||||
|
Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get(string $key): mixed
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'attributes':
|
||||||
|
return $this->getAllAttrs();
|
||||||
|
|
||||||
|
case 'sup':
|
||||||
|
return $this->sup_classes;
|
||||||
|
|
||||||
|
case 'type_name':
|
||||||
|
switch ($this->type) {
|
||||||
|
case self::OC_STRUCTURAL: return 'Structural';
|
||||||
|
case self::OC_ABSTRACT: return 'Abstract';
|
||||||
|
case self::OC_AUXILIARY: return 'Auxiliary';
|
||||||
|
default:
|
||||||
|
throw new InvalidUsage('Unknown ObjectClass Type: '.$this->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
default: return parent::__get($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of attributes that this objectClass provides
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getAllAttrs(): Collection
|
||||||
|
{
|
||||||
|
return $this->getMustAttrs()->merge($this->getMayAttrs());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an objectClass to the list of objectClasses that inherit
|
||||||
|
* from this objectClass.
|
||||||
|
*
|
||||||
|
* @param String $name The name of the objectClass to add
|
||||||
|
*/
|
||||||
|
public function addChildObjectClass(string $name): void
|
||||||
|
{
|
||||||
|
if ($this->child_objectclasses->search($name) === FALSE) {
|
||||||
|
$this->child_objectclasses->push($name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the array of objectClass names which inherit from this objectClass.
|
||||||
|
*
|
||||||
|
* @return Collection Names of objectClasses which inherit from this objectClass.
|
||||||
|
* @deprecated use $this->child_objectclasses
|
||||||
|
*/
|
||||||
|
public function getChildObjectClasses(): Collection
|
||||||
|
{
|
||||||
|
return $this->child_objectclasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behaves identically to addMustAttrs, but it operates on the MAY
|
||||||
|
* attributes of this objectClass.
|
||||||
|
*
|
||||||
|
* @param array $attr An array of attribute names (strings) to add.
|
||||||
|
*/
|
||||||
|
private function addMayAttrs(array $attr): void
|
||||||
|
{
|
||||||
|
if (! is_array($attr) || ! count($attr))
|
||||||
|
return;
|
||||||
|
|
||||||
|
$this->may_attrs = $this->may_attrs->merge($attr)->unique();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the specified array of attributes to this objectClass' list of
|
||||||
|
* MUST attributes. The resulting array of must attributes will contain
|
||||||
|
* unique members.
|
||||||
|
*
|
||||||
|
* @param array $attr An array of attribute names (strings) to add.
|
||||||
|
*/
|
||||||
|
private function addMustAttrs(array $attr): void
|
||||||
|
{
|
||||||
|
if (! is_array($attr) || ! count($attr))
|
||||||
|
return;
|
||||||
|
|
||||||
|
$this->must_attrs = $this->must_attrs->merge($attr)->unique();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
* @deprecated use $this->may_force
|
||||||
|
*/
|
||||||
|
public function getForceMayAttrs(): Collection
|
||||||
|
{
|
||||||
|
return $this->may_force;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an array of AttributeType objects that entries of this ObjectClass may define.
|
||||||
|
* This differs from getMayAttrNames in that it returns an array of AttributeType objects
|
||||||
|
*
|
||||||
|
* @param bool $parents Also get the may attrs of our parents.
|
||||||
|
* @return Collection The array of allowed AttributeType objects.
|
||||||
|
*
|
||||||
|
* @throws InvalidUsage
|
||||||
|
* @see getMustAttrNames
|
||||||
|
* @see getMustAttrs
|
||||||
|
* @see getMayAttrNames
|
||||||
|
* @see AttributeType
|
||||||
|
*/
|
||||||
|
public function getMayAttrs(bool $parents=FALSE): Collection
|
||||||
|
{
|
||||||
|
// If we dont need our parents, then we'll just return ours.
|
||||||
|
if (! $parents)
|
||||||
|
return $this->may_attrs->sortBy(function($item) { return strtolower($item->name.$item->source); });
|
||||||
|
|
||||||
|
$attrs = $this->may_attrs;
|
||||||
|
|
||||||
|
foreach ($this->getParents() as $object_class) {
|
||||||
|
$sc = $this->server->schema('objectclasses',$object_class);
|
||||||
|
$attrs = $attrs->merge($sc->getMayAttrs($parents));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any duplicates
|
||||||
|
$attrs = $attrs->unique(function($item) { return $item->name; });
|
||||||
|
|
||||||
|
// Return a sorted list
|
||||||
|
return $attrs->sortBy(function($item) { return strtolower($item->name.$item->source); });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an array of attribute names (strings) that entries of this ObjectClass must define.
|
||||||
|
* This differs from getMayAttrs in that it returns an array of strings rather than
|
||||||
|
* array of AttributeType objects
|
||||||
|
*
|
||||||
|
* @param bool $parents An array of ObjectClass objects to use when traversing
|
||||||
|
* the inheritance tree. This presents some what of a bootstrapping problem
|
||||||
|
* as we must fetch all objectClasses to determine through inheritance which
|
||||||
|
* attributes this objectClass provides.
|
||||||
|
* @return Collection The array of allowed attribute names (strings).
|
||||||
|
*
|
||||||
|
* @throws InvalidUsage
|
||||||
|
* @see getMustAttrs
|
||||||
|
* @see getMayAttrs
|
||||||
|
* @see getMustAttrNames
|
||||||
|
*/
|
||||||
|
public function getMayAttrNames(bool $parents=FALSE): Collection
|
||||||
|
{
|
||||||
|
return $this->getMayAttrs($parents)->ppluck('name');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an array of AttributeType objects that entries of this ObjectClass must define.
|
||||||
|
* This differs from getMustAttrNames in that it returns an array of AttributeType objects
|
||||||
|
*
|
||||||
|
* @param bool $parents Also get the must attrs of our parents.
|
||||||
|
* @return Collection The array of required AttributeType objects.
|
||||||
|
*
|
||||||
|
* @throws InvalidUsage
|
||||||
|
* @see getMustAttrNames
|
||||||
|
* @see getMayAttrs
|
||||||
|
* @see getMayAttrNames
|
||||||
|
*/
|
||||||
|
public function getMustAttrs(bool $parents=FALSE): Collection
|
||||||
|
{
|
||||||
|
// If we dont need our parents, then we'll just return ours.
|
||||||
|
if (! $parents)
|
||||||
|
return $this->must_attrs->sortBy(function($item) { return strtolower($item->name.$item->source); });
|
||||||
|
|
||||||
|
$attrs = $this->must_attrs;
|
||||||
|
|
||||||
|
foreach ($this->getParents() as $object_class) {
|
||||||
|
$sc = $this->server->schema('objectclasses',$object_class);
|
||||||
|
$attrs = $attrs->merge($sc->getMustAttrs($parents));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any duplicates
|
||||||
|
$attrs = $attrs->unique(function($item) { return $item->name; });
|
||||||
|
|
||||||
|
// Return a sorted list
|
||||||
|
return $attrs->sortBy(function($item) { return strtolower($item->name.$item->source); });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an array of attribute names (strings) that entries of this ObjectClass must define.
|
||||||
|
* This differs from getMustAttrs in that it returns an array of strings rather than
|
||||||
|
* array of AttributeType objects
|
||||||
|
*
|
||||||
|
* @param bool $parents An array of ObjectClass objects to use when traversing
|
||||||
|
* the inheritance tree. This presents some what of a bootstrapping problem
|
||||||
|
* as we must fetch all objectClasses to determine through inheritance which
|
||||||
|
* attributes this objectClass provides.
|
||||||
|
* @return Collection The array of allowed attribute names (strings).
|
||||||
|
*
|
||||||
|
* @throws InvalidUsage
|
||||||
|
* @see getMustAttrs
|
||||||
|
* @see getMayAttrs
|
||||||
|
* @see getMayAttrNames
|
||||||
|
*/
|
||||||
|
public function getMustAttrNames(bool $parents=FALSE): Collection
|
||||||
|
{
|
||||||
|
return $this->getMustAttrs($parents)->ppluck('name');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will return all our parent ObjectClass Objects
|
||||||
|
*/
|
||||||
|
public function getParents(): Collection
|
||||||
|
{
|
||||||
|
// If the only class is 'top', then we have no more parents
|
||||||
|
if (($this->sup_classes->count() === 1) && (strtolower($this->sup_classes->first()) === 'top'))
|
||||||
|
return collect();
|
||||||
|
|
||||||
|
$result = collect();
|
||||||
|
|
||||||
|
foreach ($this->sup_classes as $object_class) {
|
||||||
|
$result->push($object_class);
|
||||||
|
|
||||||
|
$oc = $this->server->schema('objectclasses',$object_class);
|
||||||
|
|
||||||
|
if ($oc)
|
||||||
|
$result = $result->merge($oc->getParents());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the objectClass names from which this objectClass inherits.
|
||||||
|
*
|
||||||
|
* @return Collection An array of objectClass names (strings)
|
||||||
|
* @deprecated use $this->sup_classes;
|
||||||
|
*/
|
||||||
|
public function getSupClasses(): Collection
|
||||||
|
{
|
||||||
|
return $this->sup_classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the type of this objectClass: STRUCTURAL, ABSTRACT, or AUXILIARY.
|
||||||
|
*
|
||||||
|
* @deprecated use $this->type_name
|
||||||
|
*/
|
||||||
|
public function getType()
|
||||||
|
{
|
||||||
|
return $this->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if an array is listed in the may_force attrs
|
||||||
|
*/
|
||||||
|
public function isForceMay(string $attr): bool
|
||||||
|
{
|
||||||
|
return $this->may_force->ppluck('name')->contains($attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if this objectClass is related to $oclass
|
||||||
|
*
|
||||||
|
* @param array $oclass ObjectClasses that this attribute may be related to
|
||||||
|
* @return bool
|
||||||
|
* @throws InvalidUsage
|
||||||
|
*/
|
||||||
|
public function isRelated(array $oclass): bool
|
||||||
|
{
|
||||||
|
// If I am in the array, we'll just return false
|
||||||
|
if (in_array_ignore_case($this->name,$oclass))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
foreach ($oclass as $object_class) {
|
||||||
|
$oc = $this->server->schema('objectclasses',$object_class);
|
||||||
|
|
||||||
|
if ($oc->isStructural() && in_array_ignore_case($this->name,$oc->getParents()))
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isStructural(): bool
|
||||||
|
{
|
||||||
|
return $this->type === self::OC_STRUCTURAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an LDAP schema list
|
||||||
|
*
|
||||||
|
* A list starts with a ( followed by a list of attributes separated by $ terminated by )
|
||||||
|
* The first token can therefore be a ( or a (NAME or a (NAME)
|
||||||
|
* The last token can therefore be a ) or NAME)
|
||||||
|
* The last token may be terminated by more than one bracket
|
||||||
|
*/
|
||||||
|
private function parseList(int $i,array $strings,Collection &$attrs): int
|
||||||
|
{
|
||||||
|
$string = $strings[$i];
|
||||||
|
|
||||||
|
if (! preg_match('/^\(/',$string)) {
|
||||||
|
// A bareword only - can be terminated by a ) if the last item
|
||||||
|
if (preg_match('/\)+$/',$string))
|
||||||
|
$string = preg_replace('/\)+$/','',$string);
|
||||||
|
|
||||||
|
$attrs->push($string);
|
||||||
|
|
||||||
|
} elseif (preg_match('/^\(.*\)$/',$string)) {
|
||||||
|
$string = preg_replace('/^\(/','',$string);
|
||||||
|
$string = preg_replace('/\)+$/','',$string);
|
||||||
|
|
||||||
|
$attrs->push($string);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Handle the opening cases first
|
||||||
|
if ($string === '(') {
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
} elseif (preg_match('/^\(./',$string)) {
|
||||||
|
$string = preg_replace('/^\(/','',$string);
|
||||||
|
$attrs->push($string);
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token is either a name, a $ or a ')'
|
||||||
|
// NAME can be terminated by one or more ')'
|
||||||
|
while (! preg_match('/\)+$/',$strings[$i])) {
|
||||||
|
$string = $strings[$i];
|
||||||
|
|
||||||
|
if ($string === '$') {
|
||||||
|
$i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/\)$/',$string))
|
||||||
|
$string = preg_replace('/\)+$/','',$string);
|
||||||
|
else
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
$attrs->push($string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$attrs = $attrs->sort();
|
||||||
|
|
||||||
|
return $i;
|
||||||
|
}
|
||||||
|
}
|
41
app/Classes/LDAP/Schema/ObjectClassAttribute.php
Normal file
41
app/Classes/LDAP/Schema/ObjectClassAttribute.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple class for representing AttributeTypes used only by the ObjectClass class.
|
||||||
|
*
|
||||||
|
* Users should never instantiate this class. It represents an attribute internal to
|
||||||
|
* an ObjectClass. If PHP supported inner-classes and variable permissions, this would
|
||||||
|
* be interior to class ObjectClass and flagged private. The reason this class is used
|
||||||
|
* and not the "real" class AttributeType is because this class supports the notion of
|
||||||
|
* a "source" objectClass, meaning that it keeps track of which objectClass originally
|
||||||
|
* specified it. This class is therefore used by the class ObjectClass to determine
|
||||||
|
* inheritance.
|
||||||
|
*/
|
||||||
|
final class ObjectClassAttribute extends Base {
|
||||||
|
// This Attribute's root.
|
||||||
|
private string $source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ObjectClassAttribute with specified name and source objectClass.
|
||||||
|
*
|
||||||
|
* @param string $name the name of the new attribute.
|
||||||
|
* @param string $source the name of the ObjectClass which specifies this attribute.
|
||||||
|
*/
|
||||||
|
public function __construct($name,$source)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->source = $source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get(string $key): mixed
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'source':
|
||||||
|
return $this->source;
|
||||||
|
|
||||||
|
default: return parent::__get($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
551
app/Classes/LDAP/Server.php
Normal file
551
app/Classes/LDAP/Server.php
Normal file
@ -0,0 +1,551 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Illuminate\Support\Facades\Cookie;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use LdapRecord\LdapRecordException;
|
||||||
|
use LdapRecord\Models\Model;
|
||||||
|
use LdapRecord\Query\Collection as LDAPCollection;
|
||||||
|
use LdapRecord\Query\ObjectNotFoundException;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Schema\{AttributeType,Base,LDAPSyntax,MatchingRule,MatchingRuleUse,ObjectClass};
|
||||||
|
use App\Exceptions\InvalidUsage;
|
||||||
|
use App\Ldap\Entry;
|
||||||
|
|
||||||
|
final class Server
|
||||||
|
{
|
||||||
|
// This servers schema objectclasses
|
||||||
|
private Collection $attributetypes;
|
||||||
|
private Collection $ldapsyntaxes;
|
||||||
|
private Collection $matchingrules;
|
||||||
|
private Collection $matchingruleuse;
|
||||||
|
private Collection $objectclasses;
|
||||||
|
|
||||||
|
// Valid items that can be fetched
|
||||||
|
public const schema_types = [
|
||||||
|
'objectclasses',
|
||||||
|
'attributetypes',
|
||||||
|
'ldapsyntaxes',
|
||||||
|
'matchingrules',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __get(string $key): mixed
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'attributetypes': return $this->attributetypes;
|
||||||
|
case 'ldapsyntaxes': return $this->ldapsyntaxes;
|
||||||
|
case 'matchingrules': return $this->matchingrules;
|
||||||
|
case 'objectclasses': return $this->objectclasses;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception('Unknown key:'.$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STATIC METHODS */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the root DN of the specified LDAPServer, or throws an exception if it
|
||||||
|
* can't find it.
|
||||||
|
*
|
||||||
|
* @param null $connection Return a collection of baseDNs
|
||||||
|
* @param bool $objects Return a collection of Entry Models
|
||||||
|
* @return Collection
|
||||||
|
* @throws ObjectNotFoundException
|
||||||
|
* @testedin GetBaseDNTest::testBaseDNExists();
|
||||||
|
* @todo Need to allow for the scenario if the baseDN is not readable by ACLs
|
||||||
|
*/
|
||||||
|
public static function baseDNs($connection=NULL,bool $objects=TRUE): Collection
|
||||||
|
{
|
||||||
|
$cachetime = Carbon::now()->addSeconds(Config::get('ldap.cache.time'));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$base = self::rootDSE($connection,$cachetime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LDAP Error Codes:
|
||||||
|
* https://ldap.com/ldap-result-code-reference/
|
||||||
|
* + success 0
|
||||||
|
* + operationsError 1
|
||||||
|
* + protocolError 2
|
||||||
|
* + timeLimitExceeded 3
|
||||||
|
* + sizeLimitExceeded 4
|
||||||
|
* + compareFalse 5
|
||||||
|
* + compareTrue 6
|
||||||
|
* + authMethodNotSupported 7
|
||||||
|
* + strongerAuthRequired 8
|
||||||
|
* + referral 10
|
||||||
|
* + adminLimitExceeded 11
|
||||||
|
* + unavailableCriticalExtension 12
|
||||||
|
* + confidentialityRequired 13
|
||||||
|
* + saslBindInProgress 14
|
||||||
|
* + noSuchAttribute 16
|
||||||
|
* + undefinedAttributeType 17
|
||||||
|
* + inappropriateMatching 18
|
||||||
|
* + constraintViolation 19
|
||||||
|
* + attributeOrValueExists 20
|
||||||
|
* + invalidAttributeSyntax 21
|
||||||
|
* + noSuchObject 32
|
||||||
|
* + aliasProblem 33
|
||||||
|
* + invalidDNSyntax 34
|
||||||
|
* + isLeaf 35
|
||||||
|
* + aliasDereferencingProblem 36
|
||||||
|
* + inappropriateAuthentication 48
|
||||||
|
* + invalidCredentials 49
|
||||||
|
* + insufficientAccessRights 50
|
||||||
|
* + busy 51
|
||||||
|
* + unavailable 52
|
||||||
|
* + unwillingToPerform 53
|
||||||
|
* + loopDetect 54
|
||||||
|
* + sortControlMissing 60
|
||||||
|
* + offsetRangeError 61
|
||||||
|
* + namingViolation 64
|
||||||
|
* + objectClassViolation 65
|
||||||
|
* + notAllowedOnNonLeaf 66
|
||||||
|
* + notAllowedOnRDN 67
|
||||||
|
* + entryAlreadyExists 68
|
||||||
|
* + objectClassModsProhibited 69
|
||||||
|
* + resultsTooLarge 70
|
||||||
|
* + affectsMultipleDSAs 71
|
||||||
|
* + virtualListViewError or controlError 76
|
||||||
|
* + other 80
|
||||||
|
* + serverDown 81
|
||||||
|
* + localError 82
|
||||||
|
* + encodingError 83
|
||||||
|
* + decodingError 84
|
||||||
|
* + timeout 85
|
||||||
|
* + authUnknown 86
|
||||||
|
* + filterError 87
|
||||||
|
* + userCanceled 88
|
||||||
|
* + paramError 89
|
||||||
|
* + noMemory 90
|
||||||
|
* + connectError 91
|
||||||
|
* + notSupported 92
|
||||||
|
* + controlNotFound 93
|
||||||
|
* + noResultsReturned 94
|
||||||
|
* + moreResultsToReturn 95
|
||||||
|
* + clientLoop 96
|
||||||
|
* + referralLimitExceeded 97
|
||||||
|
* + invalidResponse 100
|
||||||
|
* + ambiguousResponse 101
|
||||||
|
* + tlsNotSupported 112
|
||||||
|
* + intermediateResponse 113
|
||||||
|
* + unknownType 114
|
||||||
|
* + canceled 118
|
||||||
|
* + noSuchOperation 119
|
||||||
|
* + tooLate 120
|
||||||
|
* + cannotCancel 121
|
||||||
|
* + assertionFailed 122
|
||||||
|
* + authorizationDenied 123
|
||||||
|
* + e-syncRefreshRequired 4096
|
||||||
|
* + noOperation 16654
|
||||||
|
*
|
||||||
|
* LDAP Tag Codes:
|
||||||
|
* + A client bind operation 97
|
||||||
|
* + The entry for which you were searching 100
|
||||||
|
* + The result from a search operation 101
|
||||||
|
* + The result from a modify operation 103
|
||||||
|
* + The result from an add operation 105
|
||||||
|
* + The result from a delete operation 107
|
||||||
|
* + The result from a modify DN operation 109
|
||||||
|
* + The result from a compare operation 111
|
||||||
|
* + A search reference when the entry you perform your search on holds a referral to the entry you require.
|
||||||
|
* + Search references are expressed in terms of a referral.
|
||||||
|
* 115
|
||||||
|
* + A result from an extended operation 120
|
||||||
|
*/
|
||||||
|
// If we cannot get to our LDAP server we'll head straight to the error page
|
||||||
|
} catch (LdapRecordException $e) {
|
||||||
|
switch ($e->getDetailedError()->getErrorCode()) {
|
||||||
|
case 49:
|
||||||
|
// Since we failed authentication, we should delete our auth cookie
|
||||||
|
if (Cookie::has('password_encrypt')) {
|
||||||
|
Log::alert('Clearing user credentials and logging out');
|
||||||
|
|
||||||
|
Cookie::queue(Cookie::forget('password_encrypt'));
|
||||||
|
Cookie::queue(Cookie::forget('username_encrypt'));
|
||||||
|
|
||||||
|
Session::invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
abort(401,$e->getDetailedError()->getErrorMessage());
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort(597,$e->getDetailedError()->getErrorMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $objects)
|
||||||
|
return collect($base->namingcontexts);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @note While we are caching our baseDNs, it seems if we have more than 1,
|
||||||
|
* our caching doesnt generate a hit on a subsequent call to this function (before the cache expires).
|
||||||
|
* IE: If we have 5 baseDNs, it takes 5 calls to this function to case them all.
|
||||||
|
* @todo Possibly a bug wtih ldaprecord, so need to investigate
|
||||||
|
*/
|
||||||
|
$result = collect();
|
||||||
|
foreach ($base->namingcontexts as $dn) {
|
||||||
|
$result->push((new Entry)->cache($cachetime)->findOrFail($dn));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain the rootDSE for the server, that gives us server information
|
||||||
|
*
|
||||||
|
* @param null $connection
|
||||||
|
* @return Entry|null
|
||||||
|
* @throws ObjectNotFoundException
|
||||||
|
* @testedin TranslateOidTest::testRootDSE();
|
||||||
|
*/
|
||||||
|
public static function rootDSE($connection=NULL,Carbon $cachetime=NULL): ?Model
|
||||||
|
{
|
||||||
|
$e = new Entry;
|
||||||
|
|
||||||
|
return Entry::on($connection ?? $e->getConnectionName())
|
||||||
|
->cache($cachetime)
|
||||||
|
->in(NULL)
|
||||||
|
->read()
|
||||||
|
->select(['+'])
|
||||||
|
->whereHas('objectclass')
|
||||||
|
->firstOrFail();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Schema DN
|
||||||
|
*
|
||||||
|
* @param $connection
|
||||||
|
* @return string
|
||||||
|
* @throws ObjectNotFoundException
|
||||||
|
*/
|
||||||
|
public static function schemaDN($connection=NULL): string
|
||||||
|
{
|
||||||
|
$cachetime = Carbon::now()->addSeconds(Config::get('ldap.cache.time'));
|
||||||
|
|
||||||
|
return collect(self::rootDSE($connection,$cachetime)->subschemasubentry)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query the server for a DN and return its children and if those children have children.
|
||||||
|
*
|
||||||
|
* @param string $dn
|
||||||
|
* @return LDAPCollection|NULL
|
||||||
|
*/
|
||||||
|
public function children(string $dn): ?LDAPCollection
|
||||||
|
{
|
||||||
|
return ($x=(new Entry)
|
||||||
|
->query()
|
||||||
|
->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time')))
|
||||||
|
->select(['*','hassubordinates'])
|
||||||
|
->setDn($dn)
|
||||||
|
->list()
|
||||||
|
->get()) ? $x : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a DN from the server
|
||||||
|
*
|
||||||
|
* @param string $dn
|
||||||
|
* @param array $attrs
|
||||||
|
* @return Entry|null
|
||||||
|
*/
|
||||||
|
public function fetch(string $dn,array $attrs=['*','+']): ?Entry
|
||||||
|
{
|
||||||
|
return ($x=(new Entry)
|
||||||
|
->query()
|
||||||
|
->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time')))
|
||||||
|
->select($attrs)
|
||||||
|
->find($dn)) ? $x : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function determines if the specified attribute is contained in the force_may list
|
||||||
|
* as configured in config.php.
|
||||||
|
*
|
||||||
|
* @return boolean True if the specified attribute is configured to be force as a may attribute
|
||||||
|
*/
|
||||||
|
public function isForceMay($attr_name): bool
|
||||||
|
{
|
||||||
|
return in_array($attr_name,config('pla.force_may',[]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this server support RFC3666 language tags
|
||||||
|
* OID: 1.3.6.1.4.1.4203.1.5.4
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws ObjectNotFoundException
|
||||||
|
*/
|
||||||
|
public function isLanguageTags(): bool
|
||||||
|
{
|
||||||
|
return in_array('1.3.6.1.4.1.4203.1.5.4',$this->rootDSE()->supportedfeatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the server's schema
|
||||||
|
*
|
||||||
|
* @param string $item Schema Item to Fetch
|
||||||
|
* @param string|null $key
|
||||||
|
* @return Collection|Base|NULL
|
||||||
|
* @throws InvalidUsage
|
||||||
|
*/
|
||||||
|
public function schema(string $item,string $key=NULL): Collection|Base|NULL
|
||||||
|
{
|
||||||
|
// Ensure our item to fetch is lower case
|
||||||
|
$item = strtolower($item);
|
||||||
|
if ($key)
|
||||||
|
$key = strtolower($key);
|
||||||
|
|
||||||
|
// This error message is not localized as only developers should ever see it
|
||||||
|
if (! in_array($item,self::schema_types))
|
||||||
|
throw new InvalidUsage('Invalid request to fetch schema: '.$item);
|
||||||
|
|
||||||
|
$result = Cache::remember('schema'.$item,config('ldap.cache.time'),function() use ($item) {
|
||||||
|
// First pass if we have already retrieved the schema item
|
||||||
|
switch ($item) {
|
||||||
|
case 'attributetypes':
|
||||||
|
if (isset($this->attributetypes))
|
||||||
|
return $this->attributetypes;
|
||||||
|
else
|
||||||
|
$this->attributetypes = collect();
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ldapsyntaxes':
|
||||||
|
if (isset($this->ldapsyntaxes))
|
||||||
|
return $this->ldapsyntaxes;
|
||||||
|
else
|
||||||
|
$this->ldapsyntaxes = collect();
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'matchingrules':
|
||||||
|
if (isset($this->matchingrules))
|
||||||
|
return $this->matchingrules;
|
||||||
|
else
|
||||||
|
$this->matchingrules = collect();
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
case 'matchingruleuse':
|
||||||
|
if (isset($this->matchingruleuse))
|
||||||
|
return is_null($key) ? $this->matchingruleuse : $this->matchingruleuse->get($key);
|
||||||
|
else
|
||||||
|
$this->matchingruleuse = collect();
|
||||||
|
|
||||||
|
break;
|
||||||
|
*/
|
||||||
|
|
||||||
|
case 'objectclasses':
|
||||||
|
if (isset($this->objectclasses))
|
||||||
|
return $this->objectclasses;
|
||||||
|
else
|
||||||
|
$this->objectclasses = collect();
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Shouldnt get here
|
||||||
|
default:
|
||||||
|
throw new InvalidUsage('Invalid request to fetch schema: '.$item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get the schema DN from the specified entry.
|
||||||
|
$schema_dn = $this->schemaDN();
|
||||||
|
$schema = $this->fetch($schema_dn);
|
||||||
|
|
||||||
|
switch ($item) {
|
||||||
|
case 'attributetypes':
|
||||||
|
Log::debug('Attribute Types');
|
||||||
|
// build the array of attribueTypes
|
||||||
|
//$syntaxes = $this->SchemaSyntaxes($dn);
|
||||||
|
|
||||||
|
foreach ($schema->{$item} as $line) {
|
||||||
|
if (is_null($line) || ! strlen($line))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$o = new AttributeType($line);
|
||||||
|
$this->attributetypes->put($o->name_lc,$o);
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (isset($syntaxes[$attr->getSyntaxOID()])) {
|
||||||
|
$syntax = $syntaxes[$attr->getSyntaxOID()];
|
||||||
|
$attr->setType($syntax->getDescription());
|
||||||
|
}
|
||||||
|
$this->attributetypes[$attr->getName()] = $attr;
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bug 856832: create an entry in the $attrs_oid array too. This
|
||||||
|
* will be a ref to the $attrs entry for maintenance and performance
|
||||||
|
* reasons
|
||||||
|
*/
|
||||||
|
//$attrs_oid[$attr->getOID()] = &$attrs[$attr->getName()];
|
||||||
|
}
|
||||||
|
|
||||||
|
// go back and add data from aliased attributeTypes
|
||||||
|
foreach ($this->attributetypes as $o) {
|
||||||
|
/* foreach of the attribute's aliases, create a new entry in the attrs array
|
||||||
|
* with its name set to the alias name, and all other data copied.*/
|
||||||
|
|
||||||
|
if ($o->aliases->count()) {
|
||||||
|
Log::debug(sprintf('\ Attribute [%s] has the following aliases [%s]',$o->name,$o->aliases->join(',')));
|
||||||
|
|
||||||
|
foreach ($o->aliases as $alias) {
|
||||||
|
$new_attr = clone $o;
|
||||||
|
$new_attr->setName($alias);
|
||||||
|
$new_attr->addAlias($o->name);
|
||||||
|
$new_attr->removeAlias($alias);
|
||||||
|
|
||||||
|
$this->attributetypes->put(strtolower($alias),$new_attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now go through and reference the parent/child relationships
|
||||||
|
foreach ($this->attributetypes as $o)
|
||||||
|
if ($o->sup_attribute) {
|
||||||
|
$parent = strtolower($o->sup_attribute);
|
||||||
|
|
||||||
|
if ($this->attributetypes->has($parent) !== FALSE)
|
||||||
|
$this->attributetypes[$parent]->addChild($o->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// go through any children and add details if the child doesnt have them (ie, cn inherits name)
|
||||||
|
// @todo This doesnt traverse children properly, so children of children may not get the settings they should
|
||||||
|
foreach ($this->attributetypes as $parent) {
|
||||||
|
foreach ($parent->children as $child) {
|
||||||
|
$child = strtolower($child);
|
||||||
|
|
||||||
|
/* only overwrite the child's SINGLE-VALUE property if the parent has it set, and the child doesnt
|
||||||
|
* (note: All LDAP attributes default to multi-value if not explicitly set SINGLE-VALUE) */
|
||||||
|
if (! is_null($parent->is_single_value) && is_null($this->attributetypes[$child]->is_single_value))
|
||||||
|
$this->attributetypes[$child]->setIsSingleValue($parent->is_single_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the used in and required_by values.
|
||||||
|
foreach ($this->schema('objectclasses') as $object_class) {
|
||||||
|
$must_attrs = $object_class->getMustAttrNames();
|
||||||
|
$may_attrs = $object_class->getMayAttrNames();
|
||||||
|
$oclass_attrs = $must_attrs->merge($may_attrs)->unique();
|
||||||
|
|
||||||
|
// Add Used In.
|
||||||
|
foreach ($oclass_attrs as $attr_name)
|
||||||
|
if ($this->attributetypes->has(strtolower($attr_name)))
|
||||||
|
$this->attributetypes[strtolower($attr_name)]->addUsedInObjectClass($object_class->name);
|
||||||
|
|
||||||
|
// Add Required By.
|
||||||
|
foreach ($must_attrs as $attr_name)
|
||||||
|
if ($this->attributetypes->has(strtolower($attr_name)))
|
||||||
|
$this->attributetypes[strtolower($attr_name)]->addRequiredByObjectClass($object_class->name);
|
||||||
|
|
||||||
|
// Force May
|
||||||
|
foreach ($object_class->getForceMayAttrs() as $attr_name)
|
||||||
|
if ($this->attributetypes->has(strtolower($attr_name->name)))
|
||||||
|
$this->attributetypes[strtolower($attr_name->name)]->setForceMay();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->attributetypes;
|
||||||
|
|
||||||
|
case 'ldapsyntaxes':
|
||||||
|
Log::debug('LDAP Syntaxes');
|
||||||
|
|
||||||
|
foreach ($schema->{$item} as $line) {
|
||||||
|
if (is_null($line) || ! strlen($line))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$o = new LDAPSyntax($line);
|
||||||
|
$this->ldapsyntaxes->put(strtolower($o->oid),$o);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->ldapsyntaxes;
|
||||||
|
|
||||||
|
case 'matchingrules':
|
||||||
|
Log::debug('Matching Rules');
|
||||||
|
$this->matchingruleuse = collect();
|
||||||
|
|
||||||
|
foreach ($schema->{$item} as $line) {
|
||||||
|
if (is_null($line) || ! strlen($line))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$o = new MatchingRule($line);
|
||||||
|
$this->matchingrules->put($o->name_lc,$o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For each MatchingRuleUse entry, add the attributes who use it to the
|
||||||
|
* MatchingRule in the $rules array.
|
||||||
|
*/
|
||||||
|
if ($schema->matchingruleuse) {
|
||||||
|
foreach ($schema->matchingruleuse as $line) {
|
||||||
|
if (is_null($line) || ! strlen($line))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$o = new MatchingRuleUse($line);
|
||||||
|
$this->matchingruleuse->put($o->name_lc,$o);
|
||||||
|
|
||||||
|
if ($this->matchingrules->has($o->name_lc) !== FALSE)
|
||||||
|
$this->matchingrules[$o->name_lc]->setUsedByAttrs($o->getUsedByAttrs());
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/* No MatchingRuleUse entry in the subschema, so brute-forcing
|
||||||
|
* the reverse-map for the "$rule->getUsedByAttrs()" data.*/
|
||||||
|
foreach ($this->schema('attributetypes') as $attr) {
|
||||||
|
$rule_key = strtolower($attr->getEquality());
|
||||||
|
|
||||||
|
if ($this->matchingrules->has($rule_key) !== FALSE)
|
||||||
|
$this->matchingrules[$rule_key]->addUsedByAttr($attr->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->matchingrules;
|
||||||
|
|
||||||
|
case 'objectclasses':
|
||||||
|
Log::debug('Object Classes');
|
||||||
|
|
||||||
|
foreach ($schema->{$item} as $line) {
|
||||||
|
if (is_null($line) || ! strlen($line))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$o = new ObjectClass($line,$this);
|
||||||
|
$this->objectclasses->put($o->name_lc,$o);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now go through and reference the parent/child relationships
|
||||||
|
foreach ($this->objectclasses as $o)
|
||||||
|
foreach ($o->getSupClasses() as $parent) {
|
||||||
|
$parent = strtolower($parent);
|
||||||
|
if ($this->objectclasses->has($parent) !== FALSE)
|
||||||
|
$this->objectclasses[$parent]->addChildObjectClass($o->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->objectclasses;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return is_null($key) ? $result : $result->get($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an OID, return the ldapsyntax for the OID
|
||||||
|
*
|
||||||
|
* @param string $oid
|
||||||
|
* @return LDAPSyntax|null
|
||||||
|
* @throws InvalidUsage
|
||||||
|
*/
|
||||||
|
public function schemaSyntaxName(string $oid): ?LDAPSyntax
|
||||||
|
{
|
||||||
|
return $this->schema('ldapsyntaxes',$oid);
|
||||||
|
}
|
||||||
|
}
|
41
app/Console/Kernel.php
Normal file
41
app/Console/Kernel.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console;
|
||||||
|
|
||||||
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
|
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||||
|
|
||||||
|
class Kernel extends ConsoleKernel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Artisan commands provided by your application.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $commands = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the application's command schedule.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function schedule(Schedule $schedule)
|
||||||
|
{
|
||||||
|
// $schedule->command('inspire')->hourly();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the commands for the application.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function commands()
|
||||||
|
{
|
||||||
|
$this->load(__DIR__.'/Commands');
|
||||||
|
|
||||||
|
require base_path('routes/console.php');
|
||||||
|
}
|
||||||
|
}
|
50
app/Exceptions/Handler.php
Normal file
50
app/Exceptions/Handler.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class Handler extends ExceptionHandler
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A list of exception types with their corresponding custom log levels.
|
||||||
|
*
|
||||||
|
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
|
||||||
|
*/
|
||||||
|
protected $levels = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of the exception types that are not reported.
|
||||||
|
*
|
||||||
|
* @var array<int, class-string<\Throwable>>
|
||||||
|
*/
|
||||||
|
protected $dontReport = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of the inputs that are never flashed to the session on validation exceptions.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $dontFlash = [
|
||||||
|
'current_password',
|
||||||
|
'password',
|
||||||
|
'password_confirmation',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the exception handling callbacks for the application.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
$this->reportable(function (Throwable $e) {
|
||||||
|
//
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
7
app/Exceptions/Import/AttributeException.php
Normal file
7
app/Exceptions/Import/AttributeException.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions\Import;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class AttributeException extends Exception {}
|
7
app/Exceptions/Import/GeneralException.php
Normal file
7
app/Exceptions/Import/GeneralException.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions\Import;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class GeneralException extends Exception {}
|
7
app/Exceptions/Import/ObjectExistsException.php
Normal file
7
app/Exceptions/Import/ObjectExistsException.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions\Import;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class ObjectExistsException extends Exception {}
|
7
app/Exceptions/Import/VersionException.php
Normal file
7
app/Exceptions/Import/VersionException.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions\Import;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class VersionException extends Exception {}
|
10
app/Exceptions/InvalidUsage.php
Normal file
10
app/Exceptions/InvalidUsage.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class InvalidUsage extends Exception
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
85
app/Http/Controllers/APIController.php
Normal file
85
app/Http/Controllers/APIController.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Crypt;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Server;
|
||||||
|
|
||||||
|
class APIController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the LDAP server BASE DNs
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
* @throws LdapRecord\Query\ObjectNotFoundException
|
||||||
|
*/
|
||||||
|
public function bases(): Collection
|
||||||
|
{
|
||||||
|
$base = Server::baseDNs() ?: collect();
|
||||||
|
|
||||||
|
return $base->transform(function($item) {
|
||||||
|
return [
|
||||||
|
'title'=>$item->getRdn(),
|
||||||
|
'item'=>$item->getDNSecure(),
|
||||||
|
'lazy'=>TRUE,
|
||||||
|
'icon'=>'fa-fw fas fa-sitemap',
|
||||||
|
'tooltip'=>$item->getDn(),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function children(Request $request): Collection
|
||||||
|
{
|
||||||
|
$levels = $request->query('depth',1);
|
||||||
|
$dn = Crypt::decryptString($request->query('key'));
|
||||||
|
Log::debug(sprintf('%s: Query [%s] - Levels [%d]',__METHOD__,$dn,$levels));
|
||||||
|
|
||||||
|
return (config('server'))
|
||||||
|
->children($dn)
|
||||||
|
->transform(function($item) {
|
||||||
|
return [
|
||||||
|
'title'=>$item->getRdn(),
|
||||||
|
'item'=>$item->getDNSecure(),
|
||||||
|
'icon'=>$item->icon(),
|
||||||
|
'lazy'=>Arr::get($item->getAttribute('hassubordinates'),0) == 'TRUE',
|
||||||
|
'tooltip'=>$item->getDn(),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function schema_view(Request $request)
|
||||||
|
{
|
||||||
|
$server = new Server;
|
||||||
|
|
||||||
|
switch($request->type) {
|
||||||
|
case 'objectclasses':
|
||||||
|
return view('fragment.schema.objectclasses')
|
||||||
|
->with('objectclasses',$server->schema('objectclasses')->sortBy(function($item) { return strtolower($item->name); }));
|
||||||
|
|
||||||
|
case 'attributetypes':
|
||||||
|
return view('fragment.schema.attributetypes')
|
||||||
|
->with('server',$server)
|
||||||
|
->with('attributetypes',$server->schema('attributetypes')->sortBy(function($item) { return strtolower($item->name); }));
|
||||||
|
|
||||||
|
case 'ldapsyntaxes':
|
||||||
|
return view('fragment.schema.ldapsyntaxes')
|
||||||
|
->with('ldapsyntaxes',$server->schema('ldapsyntaxes')->sortBy(function($item) { return strtolower($item->description); }));
|
||||||
|
|
||||||
|
case 'matchingrules':
|
||||||
|
return view('fragment.schema.matchingrules')
|
||||||
|
->with('matchingrules',$server->schema('matchingrules')->sortBy(function($item) { return strtolower($item->name); }));
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
104
app/Http/Controllers/Auth/LoginController.php
Normal file
104
app/Http/Controllers/Auth/LoginController.php
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Cookie;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
|
|
||||||
|
class LoginController extends Controller
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Login Controller
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This controller handles authenticating users for the application and
|
||||||
|
| redirecting them to your home screen. The controller uses a trait
|
||||||
|
| to conveniently provide its functionality to your applications.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
use AuthenticatesUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where to redirect users after login.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $redirectTo = RouteServiceProvider::HOME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new controller instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('guest')->except('logout');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function credentials(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
login_attr_name() => $request->get(login_attr_name()),
|
||||||
|
'password' => $request->get('password'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We need to delete our encrypted username/password cookies
|
||||||
|
*
|
||||||
|
* @note The rest of this function is the same as a normal laravel logout as in AuthenticatesUsers::class
|
||||||
|
* @param Request $request
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|mixed
|
||||||
|
*/
|
||||||
|
public function logout(Request $request)
|
||||||
|
{
|
||||||
|
// Delete our LDAP authentication cookies
|
||||||
|
Cookie::queue(Cookie::forget('username_encrypt'));
|
||||||
|
Cookie::queue(Cookie::forget('password_encrypt'));
|
||||||
|
|
||||||
|
$this->guard()->logout();
|
||||||
|
|
||||||
|
$request->session()->invalidate();
|
||||||
|
|
||||||
|
$request->session()->regenerateToken();
|
||||||
|
|
||||||
|
if ($response = $this->loggedOut($request)) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $request->wantsJson()
|
||||||
|
? new JsonResponse([], 204)
|
||||||
|
: redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Show our themed login page
|
||||||
|
*/
|
||||||
|
public function showLoginForm()
|
||||||
|
{
|
||||||
|
$login_note = '';
|
||||||
|
|
||||||
|
if (file_exists('login_note.txt'))
|
||||||
|
$login_note = file_get_contents('login_note.txt');
|
||||||
|
|
||||||
|
return view('architect::auth.login')->with('login_note',$login_note);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the login username to be used by the controller.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function username()
|
||||||
|
{
|
||||||
|
return login_attr_name();
|
||||||
|
}
|
||||||
|
}
|
13
app/Http/Controllers/Controller.php
Normal file
13
app/Http/Controllers/Controller.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
|
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||||
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
|
|
||||||
|
class Controller extends BaseController
|
||||||
|
{
|
||||||
|
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||||
|
}
|
309
app/Http/Controllers/HomeController.php
Normal file
309
app/Http/Controllers/HomeController.php
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Crypt;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Facades\Redirect;
|
||||||
|
use LdapRecord\Exceptions\InsufficientAccessException;
|
||||||
|
use LdapRecord\LdapRecordException;
|
||||||
|
use LdapRecord\Query\ObjectNotFoundException;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\{Attribute,Server};
|
||||||
|
use App\Classes\LDAP\Import\LDIF as LDIFImport;
|
||||||
|
use App\Classes\LDAP\Export\LDIF as LDIFExport;
|
||||||
|
use App\Exceptions\Import\{GeneralException,VersionException};
|
||||||
|
use App\Exceptions\InvalidUsage;
|
||||||
|
use App\Http\Requests\{EntryRequest,ImportRequest};
|
||||||
|
use App\Ldap\Entry;
|
||||||
|
use App\View\Components\AttributeType;
|
||||||
|
use Nette\NotImplementedException;
|
||||||
|
|
||||||
|
class HomeController extends Controller
|
||||||
|
{
|
||||||
|
private function bases()
|
||||||
|
{
|
||||||
|
$base = Server::baseDNs() ?: collect();
|
||||||
|
|
||||||
|
return $base->transform(function($item) {
|
||||||
|
return [
|
||||||
|
'title'=>$item->getRdn(),
|
||||||
|
'item'=>$item->getDNSecure(),
|
||||||
|
'lazy'=>TRUE,
|
||||||
|
'icon'=>'fa-fw fas fa-sitemap',
|
||||||
|
'tooltip'=>$item->getDn(),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug Page
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function debug()
|
||||||
|
{
|
||||||
|
return view('debug');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a specific DN
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function dn_frame(Request $request)
|
||||||
|
{
|
||||||
|
$dn = Crypt::decryptString($request->post('key'));
|
||||||
|
|
||||||
|
$page_actions = collect(['edit'=>TRUE,'copy'=>TRUE]);
|
||||||
|
|
||||||
|
return view('frames.dn')
|
||||||
|
->with('o',config('server')->fetch($dn))
|
||||||
|
->with('dn',$dn)
|
||||||
|
->with('page_actions',$page_actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function entry_export(Request $request,string $id)
|
||||||
|
{
|
||||||
|
$dn = Crypt::decryptString($id);
|
||||||
|
|
||||||
|
$result = (new Entry)
|
||||||
|
->query()
|
||||||
|
//->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time')))
|
||||||
|
//->select(['*'])
|
||||||
|
->setDn($dn)
|
||||||
|
->recursive()
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return view('fragment.export')
|
||||||
|
->with('result',new LDIFExport($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function entry_newattr(string $id)
|
||||||
|
{
|
||||||
|
$x = new AttributeType(new Attribute($id,[]),TRUE);
|
||||||
|
return $x->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a confirmation to update a DN
|
||||||
|
*
|
||||||
|
* @param EntryRequest $request
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application|\Illuminate\Http\RedirectResponse
|
||||||
|
* @throws ObjectNotFoundException
|
||||||
|
*/
|
||||||
|
public function entry_pending_update(EntryRequest $request)
|
||||||
|
{
|
||||||
|
$dn = Crypt::decryptString($request->dn);
|
||||||
|
|
||||||
|
$o = config('server')->fetch($dn);
|
||||||
|
|
||||||
|
foreach ($request->except(['_token','dn']) as $key => $value)
|
||||||
|
$o->{$key} = array_filter($value);
|
||||||
|
|
||||||
|
if (! $o->getDirty())
|
||||||
|
return back()
|
||||||
|
->withInput()
|
||||||
|
->with('note',__('No attributes changed'));
|
||||||
|
|
||||||
|
return view('update')
|
||||||
|
->with('bases',$this->bases())
|
||||||
|
->with('dn',$dn)
|
||||||
|
->with('o',$o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a DN entry
|
||||||
|
*
|
||||||
|
* @param EntryRequest $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
* @throws ObjectNotFoundException
|
||||||
|
*/
|
||||||
|
public function entry_update(EntryRequest $request)
|
||||||
|
{
|
||||||
|
$dn = Crypt::decryptString($request->dn);
|
||||||
|
|
||||||
|
$o = config('server')->fetch($dn);
|
||||||
|
|
||||||
|
foreach ($request->except(['_token','dn']) as $key => $value)
|
||||||
|
$o->{$key} = array_filter($value);
|
||||||
|
|
||||||
|
if (! $dirty=$o->getDirty())
|
||||||
|
return back()
|
||||||
|
->withInput()
|
||||||
|
->with('note',__('No attributes changed'));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$o->update($request->except(['_token','dn']));
|
||||||
|
|
||||||
|
} catch (InsufficientAccessException $e) {
|
||||||
|
$request->flash();
|
||||||
|
|
||||||
|
switch ($x=$e->getDetailedError()->getErrorCode()) {
|
||||||
|
case 50:
|
||||||
|
return Redirect::to('/')
|
||||||
|
->withInput()
|
||||||
|
->withErrors(sprintf('%s: %s (%s)',__('LDAP Server Error Code'),$x,__($e->getDetailedError()->getErrorMessage())));
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort(599,$e->getDetailedError()->getErrorMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (LdapRecordException $e) {
|
||||||
|
$request->flash();
|
||||||
|
|
||||||
|
switch ($x=$e->getDetailedError()->getErrorCode()) {
|
||||||
|
case 8:
|
||||||
|
return Redirect::to('/')
|
||||||
|
->withInput()
|
||||||
|
->withErrors(sprintf('%s: %s (%s)',__('LDAP Server Error Code'),$x,__($e->getDetailedError()->getErrorMessage())));
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort(599,$e->getDetailedError()->getErrorMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Redirect::to('/')
|
||||||
|
->withInput()
|
||||||
|
->with('success',__('Entry updated'))
|
||||||
|
->with('updated',$dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application home page
|
||||||
|
*/
|
||||||
|
public function home()
|
||||||
|
{
|
||||||
|
if (old('dn'))
|
||||||
|
return view('frame')
|
||||||
|
->with('subframe','dn')
|
||||||
|
->with('bases',$this->bases())
|
||||||
|
->with('o',config('server')->fetch($dn=Crypt::decryptString(old('dn'))))
|
||||||
|
->with('dn',$dn);
|
||||||
|
|
||||||
|
elseif (old('frame'))
|
||||||
|
return view('frame')
|
||||||
|
->with('subframe',old('frame'))
|
||||||
|
->with('bases',$this->bases());
|
||||||
|
|
||||||
|
else
|
||||||
|
return view('home')
|
||||||
|
->with('bases',$this->bases())
|
||||||
|
->with('server',config('ldap.connections.default.name'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the incoming LDIF file or LDIF text
|
||||||
|
*
|
||||||
|
* @param ImportRequest $request
|
||||||
|
* @param string $type
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application
|
||||||
|
* @throws GeneralException
|
||||||
|
* @throws VersionException
|
||||||
|
*/
|
||||||
|
public function import(ImportRequest $request,string $type)
|
||||||
|
{
|
||||||
|
switch ($type) {
|
||||||
|
case 'ldif':
|
||||||
|
$import = new LDIFImport($x=($request->text ?: $request->file->get()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort(404,'Unknown import type: '.$type);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = $import->process();
|
||||||
|
|
||||||
|
} catch (NotImplementedException $e) {
|
||||||
|
abort(555,$e->getMessage());
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
abort(598,$e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('frame')
|
||||||
|
->with('subframe','import_result')
|
||||||
|
->with('bases',$this->bases())
|
||||||
|
->with('result',$result)
|
||||||
|
->with('ldif',htmlspecialchars($x));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function import_frame()
|
||||||
|
{
|
||||||
|
return view('frames.import');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LDAP Server INFO
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function info()
|
||||||
|
{
|
||||||
|
return view('frames.info')
|
||||||
|
->with('s',config('server'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the Schema Viewer
|
||||||
|
*
|
||||||
|
* @note Our route will validate that types are valid.
|
||||||
|
* @param Request $request
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
|
* @throws InvalidUsage
|
||||||
|
*/
|
||||||
|
public function schema_frame(Request $request)
|
||||||
|
{
|
||||||
|
$s = config('server');
|
||||||
|
|
||||||
|
// If an invalid key, we'll 404
|
||||||
|
if ($request->type && $request->key && ($s->schema($request->type)->has($request->key) === FALSE))
|
||||||
|
abort(404);
|
||||||
|
|
||||||
|
return view('frames.schema')
|
||||||
|
->with('type',$request->type)
|
||||||
|
->with('key',$request->key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort the attributes
|
||||||
|
*
|
||||||
|
* @param Collection $attrs
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
private function sortAttrs(Collection $attrs): Collection
|
||||||
|
{
|
||||||
|
return $attrs->sortKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the image for the logged in user or anonymous
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function user_image(Request $request)
|
||||||
|
{
|
||||||
|
$image = NULL;
|
||||||
|
$content = NULL;
|
||||||
|
|
||||||
|
if (Auth::check()) {
|
||||||
|
$image = Arr::get(Auth::user()->getAttribute('jpegphoto'),0);
|
||||||
|
$content = 'image/jpeg';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $image) {
|
||||||
|
$image = File::get('../resources/images/user-secret-solid.svg');
|
||||||
|
$content = 'image/svg+xml';
|
||||||
|
}
|
||||||
|
|
||||||
|
return response($image)
|
||||||
|
->header('Content-Type',$content);
|
||||||
|
}
|
||||||
|
}
|
82
app/Http/Kernel.php
Normal file
82
app/Http/Kernel.php
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||||
|
|
||||||
|
use App\Http\Middleware\{ApplicationSession,CheckUpdate,SwapinAuthUser};
|
||||||
|
|
||||||
|
class Kernel extends HttpKernel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The application's global HTTP middleware stack.
|
||||||
|
*
|
||||||
|
* These middleware are run during every request to your application.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $middleware = [
|
||||||
|
// \App\Http\Middleware\TrustHosts::class,
|
||||||
|
\App\Http\Middleware\TrustProxies::class,
|
||||||
|
\Illuminate\Http\Middleware\HandleCors::class,
|
||||||
|
\App\Http\Middleware\CheckForMaintenanceMode::class,
|
||||||
|
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||||
|
\App\Http\Middleware\TrimStrings::class,
|
||||||
|
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The application's route middleware groups.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $middlewareGroups = [
|
||||||
|
'web' => [
|
||||||
|
\App\Http\Middleware\EncryptCookies::class,
|
||||||
|
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||||
|
\Illuminate\Session\Middleware\StartSession::class,
|
||||||
|
ApplicationSession::class,
|
||||||
|
SwapinAuthUser::class,
|
||||||
|
\Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||||
|
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||||
|
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||||
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
|
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
|
||||||
|
CheckUpdate::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
'api' => [
|
||||||
|
'throttle:60,1',
|
||||||
|
\App\Http\Middleware\EncryptCookies::class,
|
||||||
|
SwapinAuthUser::class,
|
||||||
|
ApplicationSession::class,
|
||||||
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The application's route middleware.
|
||||||
|
*
|
||||||
|
* These middleware may be assigned to groups or used individually.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $middlewareAliases = [
|
||||||
|
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||||
|
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||||
|
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
|
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||||
|
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||||
|
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||||
|
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||||
|
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
||||||
|
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||||
|
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||||
|
|
||||||
|
'localize' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRoutes::class,
|
||||||
|
'localizationRedirect' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRedirectFilter::class,
|
||||||
|
'localeSessionRedirect' => \Mcamara\LaravelLocalization\Middleware\LocaleSessionRedirect::class,
|
||||||
|
'localeCookieRedirect' => \Mcamara\LaravelLocalization\Middleware\LocaleCookieRedirect::class,
|
||||||
|
'localeViewPath' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationViewPath::class
|
||||||
|
];
|
||||||
|
}
|
29
app/Http/Middleware/ApplicationSession.php
Normal file
29
app/Http/Middleware/ApplicationSession.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Server;
|
||||||
|
use App\Ldap\User;
|
||||||
|
use Closure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This sets up our application session with any required values, ultimately for cache optimisation reasons
|
||||||
|
*/
|
||||||
|
class ApplicationSession
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request,Closure $next)
|
||||||
|
{
|
||||||
|
\Config::set('server',new Server);
|
||||||
|
|
||||||
|
view()->share('user', auth()->user() ?: new User);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
21
app/Http/Middleware/Authenticate.php
Normal file
21
app/Http/Middleware/Authenticate.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Auth\Middleware\Authenticate as Middleware;
|
||||||
|
|
||||||
|
class Authenticate extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the path the user should be redirected to when they are not authenticated.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
protected function redirectTo($request)
|
||||||
|
{
|
||||||
|
if (! $request->expectsJson()) {
|
||||||
|
return route('login');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
app/Http/Middleware/CheckForMaintenanceMode.php
Normal file
17
app/Http/Middleware/CheckForMaintenanceMode.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode as Middleware;
|
||||||
|
|
||||||
|
class CheckForMaintenanceMode extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The URIs that should be reachable while maintenance mode is enabled.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $except = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
61
app/Http/Middleware/CheckUpdate.php
Normal file
61
app/Http/Middleware/CheckUpdate.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class CheckUpdate
|
||||||
|
{
|
||||||
|
private const UPDATE_SERVER = 'https://version.phpldapadmin.org';
|
||||||
|
private const UPDATE_TIME = 60*60*6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, Closure $next)
|
||||||
|
{
|
||||||
|
\Config::set('update_available',Cache::get('upstream_version'));
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle tasks after the response has been sent to the browser.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function terminate()
|
||||||
|
{
|
||||||
|
Cache::remember('upstream_version',self::UPDATE_TIME,function() {
|
||||||
|
// CURL call to URL to see if there is a new version
|
||||||
|
Log::debug(sprintf('CU_:Checking for updates for [%s]',config('app.version')));
|
||||||
|
|
||||||
|
$client = new Client;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
$response = $client->request('POST',sprintf('%s/%s',self::UPDATE_SERVER,strtolower(config('app.version'))));
|
||||||
|
|
||||||
|
if ($response->getStatusCode() === 200) {
|
||||||
|
$result = json_decode($response->getBody());
|
||||||
|
|
||||||
|
Log::debug(sprintf('CU_:- Update server returned...'),['update'=>$result]);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::debug(sprintf('CU_:- Exception connecting to update server'),['e'=>get_class($e)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
17
app/Http/Middleware/EncryptCookies.php
Normal file
17
app/Http/Middleware/EncryptCookies.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
||||||
|
|
||||||
|
class EncryptCookies extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The names of the cookies that should not be encrypted.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $except = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
27
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
27
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class RedirectIfAuthenticated
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @param string|null $guard
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, Closure $next, $guard = null)
|
||||||
|
{
|
||||||
|
if (Auth::guard($guard)->check()) {
|
||||||
|
return redirect(RouteServiceProvider::HOME);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
50
app/Http/Middleware/SwapinAuthUser.php
Normal file
50
app/Http/Middleware/SwapinAuthUser.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Illuminate\Support\Facades\Cookie;
|
||||||
|
// use Illuminate\Support\Facades\Crypt;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
// use Illuminate\Support\Facades\Session;
|
||||||
|
use LdapRecord\Container;
|
||||||
|
|
||||||
|
use App\Ldap\Connection;
|
||||||
|
|
||||||
|
class SwapinAuthUser
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
||||||
|
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next)
|
||||||
|
{
|
||||||
|
$key = config('ldap.default');
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Rebuild our connection with the authenticated user.
|
||||||
|
if (Session::has('username_encrypt') && Session::has('password_encrypt')) {
|
||||||
|
Config::set('ldap.connections.'.$key.'.username',Crypt::decryptString(Session::get('username_encrypt')));
|
||||||
|
Config::set('ldap.connections.'.$key.'.password',Crypt::decryptString(Session::get('password_encrypt')));
|
||||||
|
|
||||||
|
} else
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (Cookie::has('username_encrypt') && Cookie::has('password_encrypt')) {
|
||||||
|
Config::set('ldap.connections.'.$key.'.username',Cookie::get('username_encrypt'));
|
||||||
|
Config::set('ldap.connections.'.$key.'.password',Cookie::get('password_encrypt'));
|
||||||
|
|
||||||
|
Log::debug('Swapping out configured LDAP credentials with the user\'s cookie.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to override our Connection object so that we can store and retrieve the logged in user and swap out the credentials to use them.
|
||||||
|
Container::getInstance()->addConnection(new Connection(config('ldap.connections.'.$key)),$key);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
18
app/Http/Middleware/TrimStrings.php
Normal file
18
app/Http/Middleware/TrimStrings.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
||||||
|
|
||||||
|
class TrimStrings extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The names of the attributes that should not be trimmed.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $except = [
|
||||||
|
'password',
|
||||||
|
'password_confirmation',
|
||||||
|
];
|
||||||
|
}
|
20
app/Http/Middleware/TrustHosts.php
Normal file
20
app/Http/Middleware/TrustHosts.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Http\Middleware\TrustHosts as Middleware;
|
||||||
|
|
||||||
|
class TrustHosts extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the host patterns that should be trusted.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function hosts()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
$this->allSubdomainsOfApplicationUrl(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
28
app/Http/Middleware/TrustProxies.php
Normal file
28
app/Http/Middleware/TrustProxies.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Http\Middleware\TrustProxies as Middleware;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class TrustProxies extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The trusted proxies for this application.
|
||||||
|
*
|
||||||
|
* @var array<int, string>|string|null
|
||||||
|
*/
|
||||||
|
protected $proxies;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The headers that should be used to detect proxies.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $headers =
|
||||||
|
Request::HEADER_X_FORWARDED_FOR |
|
||||||
|
Request::HEADER_X_FORWARDED_HOST |
|
||||||
|
Request::HEADER_X_FORWARDED_PORT |
|
||||||
|
Request::HEADER_X_FORWARDED_PROTO |
|
||||||
|
Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||||
|
}
|
17
app/Http/Middleware/VerifyCsrfToken.php
Normal file
17
app/Http/Middleware/VerifyCsrfToken.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
||||||
|
|
||||||
|
class VerifyCsrfToken extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The URIs that should be excluded from CSRF verification.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $except = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
34
app/Http/Requests/EntryRequest.php
Normal file
34
app/Http/Requests/EntryRequest.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class EntryRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return config('server')
|
||||||
|
->schema('attributetypes')
|
||||||
|
->intersectByKeys($this->request)
|
||||||
|
->transform(function($item) { return $item->validation; })
|
||||||
|
->filter()
|
||||||
|
->flatMap(function($item) { return $item; })
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
}
|
22
app/Http/Requests/ImportRequest.php
Normal file
22
app/Http/Requests/ImportRequest.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class ImportRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'frame' => 'required|string|in:import',
|
||||||
|
'file' => 'nullable|extensions:ldif|required_without:text',
|
||||||
|
'text'=> 'nullable|prohibits:file|string|min:16',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
20
app/Ldap/Connection.php
Normal file
20
app/Ldap/Connection.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ldap;
|
||||||
|
|
||||||
|
use LdapRecord\Connection as ConnectionBase;
|
||||||
|
use LdapRecord\LdapInterface;
|
||||||
|
|
||||||
|
class Connection extends ConnectionBase
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct($config = [], LdapInterface $ldap = null)
|
||||||
|
{
|
||||||
|
parent::__construct($config,$ldap);
|
||||||
|
|
||||||
|
// We need to override this so that we use our own Guard, that stores the users credentials in the session
|
||||||
|
$this->authGuardResolver = function () {
|
||||||
|
return new Guard($this->ldap, $this->configuration);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
423
app/Ldap/Entry.php
Normal file
423
app/Ldap/Entry.php
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ldap;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Crypt;
|
||||||
|
use LdapRecord\Support\Arr;
|
||||||
|
use LdapRecord\Models\Model;
|
||||||
|
use LdapRecord\Query\Model\Builder;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute;
|
||||||
|
use App\Classes\LDAP\Attribute\Factory;
|
||||||
|
use App\Classes\LDAP\Export\LDIF;
|
||||||
|
use App\Exceptions\Import\AttributeException;
|
||||||
|
|
||||||
|
class Entry extends Model
|
||||||
|
{
|
||||||
|
private Collection $objects;
|
||||||
|
private bool $noObjectAttributes = FALSE;
|
||||||
|
|
||||||
|
/* OVERRIDES */
|
||||||
|
|
||||||
|
public function __construct(array $attributes = [])
|
||||||
|
{
|
||||||
|
$this->objects = collect();
|
||||||
|
|
||||||
|
parent::__construct($attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function discardChanges(): static
|
||||||
|
{
|
||||||
|
parent::discardChanges();
|
||||||
|
|
||||||
|
// If we are discharging changes, we need to reset our $objects;
|
||||||
|
$this->objects = $this->getAttributesAsObjects($this->attributes);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function overrides getAttributes to use our collection of Attribute objects instead of the models attributes.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @note $this->attributes may not be updated with changes
|
||||||
|
*/
|
||||||
|
public function getAttributes(): array
|
||||||
|
{
|
||||||
|
return $this->objects->map(function($item) { return $item->values->toArray(); })->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the new and old values for a given key are equivalent.
|
||||||
|
*/
|
||||||
|
protected function originalIsEquivalent(string $key): bool
|
||||||
|
{
|
||||||
|
$key = $this->normalizeAttributeKey($key);
|
||||||
|
|
||||||
|
if ((! array_key_exists($key, $this->original)) && (! $this->objects->has($key))) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current = $this->attributes[$key];
|
||||||
|
$original = $this->objects->get($key)->values;
|
||||||
|
|
||||||
|
if ($current === $original) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ! $this->getObject($key)->isDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function query(bool $noattrs=false): Builder
|
||||||
|
{
|
||||||
|
$o = new static;
|
||||||
|
|
||||||
|
if ($noattrs)
|
||||||
|
$o->noObjectAttributes();
|
||||||
|
|
||||||
|
return $o->newQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As attribute values are updated, or new ones created, we need to mirror that
|
||||||
|
* into our $objects
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setAttribute(string $key, mixed $value): static
|
||||||
|
{
|
||||||
|
parent::setAttribute($key,$value);
|
||||||
|
|
||||||
|
$key = $this->normalizeAttributeKey($key);
|
||||||
|
|
||||||
|
if ((! $this->objects->get($key)) && $value) {
|
||||||
|
$o = new Attribute($key,[]);
|
||||||
|
$o->value = $value;
|
||||||
|
|
||||||
|
$this->objects->put($key,$o);
|
||||||
|
|
||||||
|
} elseif ($this->objects->get($key)) {
|
||||||
|
$this->objects->get($key)->value = $this->attributes[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We'll shadow $this->attributes to $this->objects - a collection of Attribute objects
|
||||||
|
*
|
||||||
|
* Using the objects, it'll make it easier to work with attribute values
|
||||||
|
*
|
||||||
|
* @param array $attributes
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setRawAttributes(array $attributes = []): static
|
||||||
|
{
|
||||||
|
parent::setRawAttributes($attributes);
|
||||||
|
|
||||||
|
// We only set our objects on DN entries (otherwise we might get into a recursion loop if this is the schema DN)
|
||||||
|
if ($this->dn && (! in_array($this->dn,Arr::get($this->attributes,'subschemasubentry',[])))) {
|
||||||
|
$this->objects = $this->getAttributesAsObjects($this->attributes);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$this->objects = collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ATTRIBUTES */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a key to use for sorting
|
||||||
|
*
|
||||||
|
* @todo This should be the DN in reverse order
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSortKeyAttribute(): string
|
||||||
|
{
|
||||||
|
return $this->getDn();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* METHODS */
|
||||||
|
|
||||||
|
public function addAttribute(string $key,mixed $value): void
|
||||||
|
{
|
||||||
|
$key = $this->normalizeAttributeKey($key);
|
||||||
|
|
||||||
|
if (config('server')->schema('attributetypes')->has($key) === FALSE)
|
||||||
|
throw new AttributeException('Schema doesnt have attribute [%s]',$key);
|
||||||
|
|
||||||
|
if ($x=$this->objects->get($key)) {
|
||||||
|
$x->addValue($value);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$this->objects->put($key,Attribute\Factory::create($key,Arr::wrap($value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert all our attribute values into an array of Objects
|
||||||
|
*
|
||||||
|
* @param array $attributes
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
protected function getAttributesAsObjects(array $attributes): Collection
|
||||||
|
{
|
||||||
|
$result = collect();
|
||||||
|
|
||||||
|
foreach ($attributes as $attribute => $value) {
|
||||||
|
// If the attribute name has language tags
|
||||||
|
$matches = [];
|
||||||
|
if (preg_match('/^([a-zA-Z]+)(;([a-zA-Z-;]+))+/',$attribute,$matches)) {
|
||||||
|
$attribute = $matches[1];
|
||||||
|
|
||||||
|
// If the attribute doesnt exist we'll create it
|
||||||
|
$o = Arr::get($result,$attribute,Factory::create($attribute,[]));
|
||||||
|
$o->setLangTag($matches[3],$value);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$o = Factory::create($attribute,$value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $result->has($attribute)) {
|
||||||
|
// Set the rdn flag
|
||||||
|
if (preg_match('/^'.$attribute.'=/i',$this->dn))
|
||||||
|
$o->setRDN();
|
||||||
|
|
||||||
|
// Set required flag
|
||||||
|
$o->required_by(collect($this->getAttribute('objectclass')));
|
||||||
|
|
||||||
|
// Store our original value to know if this attribute has changed
|
||||||
|
if ($x=Arr::get($this->original,$attribute))
|
||||||
|
$o->oldValues($x);
|
||||||
|
|
||||||
|
$result->put($attribute,$o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$sort = collect(config('ldap.attr_display_order',[]))->transform(function($item) { return strtolower($item); });
|
||||||
|
|
||||||
|
// Order the attributes
|
||||||
|
$result = $result->sortBy([function(Attribute $a,Attribute $b) use ($sort): int {
|
||||||
|
if ($a === $b)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Check if $a/$b are in the configuration to be sorted first, if so get it's key
|
||||||
|
$a_key = $sort->search($a->name_lc);
|
||||||
|
$b_key = $sort->search($b->name_lc);
|
||||||
|
|
||||||
|
// If the keys were not in the sort list, set the key to be the count of elements (ie: so it is last to be sorted)
|
||||||
|
if ($a_key === FALSE)
|
||||||
|
$a_key = $sort->count()+1;
|
||||||
|
|
||||||
|
if ($b_key === FALSE)
|
||||||
|
$b_key = $sort->count()+1;
|
||||||
|
|
||||||
|
// Case where neither $a, nor $b are in ldap.attr_display_order, $a_key = $b_key = one greater than num elements.
|
||||||
|
// So we sort them alphabetically
|
||||||
|
if ($a_key === $b_key)
|
||||||
|
return strcasecmp($a->name,$b->name);
|
||||||
|
|
||||||
|
// Case where at least one attribute or its friendly name is in $attrs_display_order
|
||||||
|
// return -1 if $a before $b in $attrs_display_order
|
||||||
|
return ($a_key < $b_key) ? -1 : 1;
|
||||||
|
} ]);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of available attributes - as per the objectClass entry of the record
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getAvailableAttributes(): Collection
|
||||||
|
{
|
||||||
|
$result = collect();
|
||||||
|
|
||||||
|
foreach ($this->objectclass as $oc)
|
||||||
|
$result = $result->merge(config('server')->schema('objectclasses',$oc)->attributes);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a secure version of the DN
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDNSecure(): string
|
||||||
|
{
|
||||||
|
return Crypt::encryptString($this->getDn());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of LDAP internal attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getInternalAttributes(): Collection
|
||||||
|
{
|
||||||
|
return $this->objects->filter(function($item) {
|
||||||
|
return $item->is_internal;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an attribute as an object
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return Attribute|null
|
||||||
|
*/
|
||||||
|
public function getObject(string $key): Attribute|null
|
||||||
|
{
|
||||||
|
return $this->objects->get($this->normalizeAttributeKey($key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getObjects(): Collection
|
||||||
|
{
|
||||||
|
// In case we havent built our objects yet (because they werent available while determining the schema DN)
|
||||||
|
if ((! $this->objects->count()) && $this->attributes)
|
||||||
|
$this->objects = $this->getAttributesAsObjects($this->attributes);
|
||||||
|
|
||||||
|
return $this->objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of attributes without any values
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getMissingAttributes(): Collection
|
||||||
|
{
|
||||||
|
return $this->getAvailableAttributes()->diff($this->getVisibleAttributes());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return this list of user attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getVisibleAttributes(): Collection
|
||||||
|
{
|
||||||
|
return $this->objects->filter(function($item) {
|
||||||
|
return ! $item->is_internal;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasAttribute(int|string $key): bool
|
||||||
|
{
|
||||||
|
return $this->objects->has($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export this record
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param string $scope
|
||||||
|
* @return string
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function export(string $method,string $scope): string
|
||||||
|
{
|
||||||
|
// @todo To implement
|
||||||
|
switch ($scope) {
|
||||||
|
case 'base':
|
||||||
|
case 'one':
|
||||||
|
case 'sub':
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception('Export scope unknown:'.$scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($method) {
|
||||||
|
case 'ldif':
|
||||||
|
return new LDIF(collect($this));
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception('Export method not implemented:'.$method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an icon for a DN based on objectClass
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function icon(): string
|
||||||
|
{
|
||||||
|
$objectclasses = array_map('strtolower',$this->objectclass);
|
||||||
|
|
||||||
|
// Return icon based upon objectClass value
|
||||||
|
if (in_array('person',$objectclasses) ||
|
||||||
|
in_array('organizationalperson',$objectclasses) ||
|
||||||
|
in_array('inetorgperson',$objectclasses) ||
|
||||||
|
in_array('account',$objectclasses) ||
|
||||||
|
in_array('posixaccount',$objectclasses))
|
||||||
|
|
||||||
|
return 'fas fa-user';
|
||||||
|
|
||||||
|
elseif (in_array('organization',$objectclasses))
|
||||||
|
return 'fas fa-university';
|
||||||
|
|
||||||
|
elseif (in_array('organizationalunit',$objectclasses))
|
||||||
|
return 'fas fa-object-group';
|
||||||
|
|
||||||
|
elseif (in_array('posixgroup',$objectclasses) ||
|
||||||
|
in_array('groupofnames',$objectclasses) ||
|
||||||
|
in_array('groupofuniquenames',$objectclasses) ||
|
||||||
|
in_array('group',$objectclasses))
|
||||||
|
|
||||||
|
return 'fas fa-users';
|
||||||
|
|
||||||
|
elseif (in_array('dcobject',$objectclasses) ||
|
||||||
|
in_array('domainrelatedobject',$objectclasses) ||
|
||||||
|
in_array('domain',$objectclasses) ||
|
||||||
|
in_array('builtindomain',$objectclasses))
|
||||||
|
|
||||||
|
return 'fas fa-network-wired';
|
||||||
|
|
||||||
|
elseif (in_array('alias',$objectclasses))
|
||||||
|
return 'fas fa-theater-masks';
|
||||||
|
|
||||||
|
elseif (in_array('country',$objectclasses))
|
||||||
|
return sprintf('flag %s',strtolower(Arr::get($this->c,0)));
|
||||||
|
|
||||||
|
elseif (in_array('device',$objectclasses))
|
||||||
|
return 'fas fa-mobile-alt';
|
||||||
|
|
||||||
|
elseif (in_array('document',$objectclasses))
|
||||||
|
return 'fas fa-file-alt';
|
||||||
|
|
||||||
|
elseif (in_array('iphost',$objectclasses))
|
||||||
|
return 'fas fa-wifi';
|
||||||
|
|
||||||
|
elseif (in_array('room',$objectclasses))
|
||||||
|
return 'fas fa-door-open';
|
||||||
|
|
||||||
|
elseif (in_array('server',$objectclasses))
|
||||||
|
return 'fas fa-server';
|
||||||
|
|
||||||
|
elseif (in_array('openldaprootdse',$objectclasses))
|
||||||
|
return 'fas fa-info';
|
||||||
|
|
||||||
|
// Default
|
||||||
|
return 'fa-fw fas fa-cog';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dont convert our $this->attributes to $this->objects when creating a new Entry::class
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function noObjectAttributes(): static
|
||||||
|
{
|
||||||
|
$this->noObjectAttributes = TRUE;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
29
app/Ldap/Guard.php
Normal file
29
app/Ldap/Guard.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ldap;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Cookie;
|
||||||
|
// use Illuminate\Support\Facades\Crypt;
|
||||||
|
use LdapRecord\Auth\Guard as GuardBase;
|
||||||
|
|
||||||
|
class Guard extends GuardBase
|
||||||
|
{
|
||||||
|
public function attempt(string $username, string $password, bool $stayBound = false): bool
|
||||||
|
{
|
||||||
|
if ($result = parent::attempt($username,$password,$stayBound)) {
|
||||||
|
/*
|
||||||
|
* We can either use our session or cookies to store this. If using session, then Http/Kernel needs to be
|
||||||
|
* updated to start a session for API calls.
|
||||||
|
// We need to store our password so that we can swap in the user in during SwapinAuthUser::class middleware
|
||||||
|
request()->session()->put('username_encrypt',Crypt::encryptString($username));
|
||||||
|
request()->session()->put('password_encrypt',Crypt::encryptString($password));
|
||||||
|
*/
|
||||||
|
|
||||||
|
// For our API calls, we store the cookie - which our cookies are already encrypted
|
||||||
|
Cookie::queue('username_encrypt',$username);
|
||||||
|
Cookie::queue('password_encrypt',$password);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
79
app/Ldap/LdapUserRepository.php
Normal file
79
app/Ldap/LdapUserRepository.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ldap;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Support\Arrayable;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use LdapRecord\Laravel\Events\Auth\DiscoveredWithCredentials;
|
||||||
|
use LdapRecord\Laravel\LdapUserRepository as LdapUserRepositoryBase;
|
||||||
|
use LdapRecord\Models\Model;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Server;
|
||||||
|
|
||||||
|
class LdapUserRepository extends LdapUserRepositoryBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieve a user by the given credentials.
|
||||||
|
*
|
||||||
|
* @param array $credentials
|
||||||
|
*
|
||||||
|
* @return Model|null
|
||||||
|
* @throws \LdapRecord\Query\ObjectNotFoundException
|
||||||
|
*/
|
||||||
|
public function findByCredentials(array $credentials = []): ?Model
|
||||||
|
{
|
||||||
|
if (empty($credentials)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For DN based logins
|
||||||
|
if (! empty($credentials['dn']))
|
||||||
|
return $this->query()->find($credentials['dn']);
|
||||||
|
|
||||||
|
// Look for a user using all our baseDNs
|
||||||
|
foreach (Server::baseDNs() as $base) {
|
||||||
|
$query = $this->query()->setBaseDn($base);
|
||||||
|
|
||||||
|
foreach ($credentials as $key => $value) {
|
||||||
|
if (Str::contains($key, $this->bypassCredentialKeys)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($value) || $value instanceof Arrayable) {
|
||||||
|
$query->whereIn($key, $value);
|
||||||
|
} else {
|
||||||
|
$query->where($key, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! is_null($user = $query->first())) {
|
||||||
|
event(new DiscoveredWithCredentials($user));
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a user by their object GUID.
|
||||||
|
*
|
||||||
|
* @param string $guid
|
||||||
|
*
|
||||||
|
* @return Model|null
|
||||||
|
* @throws \LdapRecord\Query\ObjectNotFoundException
|
||||||
|
*/
|
||||||
|
public function findByGuid($guid): ?Model
|
||||||
|
{
|
||||||
|
// Look for a user using all our baseDNs
|
||||||
|
foreach (Server::baseDNs() as $base) {
|
||||||
|
$user = $this->query()->setBaseDn($base)->findByGuid($guid);
|
||||||
|
|
||||||
|
if ($user)
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
27
app/Ldap/Rules/LoginObjectclassRule.php
Normal file
27
app/Ldap/Rules/LoginObjectclassRule.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ldap\Rules;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model as Eloquent;
|
||||||
|
use LdapRecord\Laravel\Auth\Rule;
|
||||||
|
use LdapRecord\Models\Model as LdapRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User must have this objectClass to login
|
||||||
|
*
|
||||||
|
* This is overridden by LDAP_LOGIN_OBJECTCLASS
|
||||||
|
* @see User::$objectClasses
|
||||||
|
*/
|
||||||
|
class LoginObjectclassRule implements Rule
|
||||||
|
{
|
||||||
|
public function passes(LdapRecord $user, Eloquent $model = null): bool
|
||||||
|
{
|
||||||
|
if ($x=config('ldap.login.objectclass')) {
|
||||||
|
return count(array_intersect($user->objectclass,$x));
|
||||||
|
|
||||||
|
// Otherwise allow the user to login
|
||||||
|
} else {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
app/Ldap/User.php
Normal file
29
app/Ldap/User.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ldap;
|
||||||
|
|
||||||
|
use Laravel\Passport\HasApiTokens;
|
||||||
|
use LdapRecord\Models\OpenLDAP\User as Model;
|
||||||
|
|
||||||
|
use App\Ldap\Rules\LoginObjectclassRule;
|
||||||
|
|
||||||
|
class User extends Model
|
||||||
|
{
|
||||||
|
use HasApiTokens;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The object classes of the LDAP model.
|
||||||
|
*
|
||||||
|
* @note We set this to an empty array so that any objectclass can login
|
||||||
|
* @see LoginObjectclassRule::class
|
||||||
|
*/
|
||||||
|
public static array $objectClasses = [
|
||||||
|
];
|
||||||
|
|
||||||
|
/* METHODS */
|
||||||
|
|
||||||
|
public function getDn(): string
|
||||||
|
{
|
||||||
|
return $this->exists ? parent::getDn() : 'Anonymous';
|
||||||
|
}
|
||||||
|
}
|
44
app/Providers/AppServiceProvider.php
Normal file
44
app/Providers/AppServiceProvider.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use LdapRecord\Configuration\DomainConfiguration;
|
||||||
|
use LdapRecord\Laravel\LdapRecord;
|
||||||
|
|
||||||
|
use App\Ldap\LdapUserRepository;
|
||||||
|
|
||||||
|
class AppServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Register any application services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
// Add a new option available to be set in the configuration:
|
||||||
|
DomainConfiguration::extend('name', $default = null);
|
||||||
|
|
||||||
|
// Use our LdapUserRepository to support multiple baseDN querying
|
||||||
|
LdapRecord::locateUsersUsing(LdapUserRepository::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bootstrap any application services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
$this->loadViewsFrom(__DIR__.'/../../resources/themes/architect/views/','architect');
|
||||||
|
|
||||||
|
// Enable pluck on collections to work on private values
|
||||||
|
Collection::macro('ppluck', function ($attr) {
|
||||||
|
return $this->map(function (object $item) use ($attr) {
|
||||||
|
return $item->{$attr};
|
||||||
|
})->values();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
27
app/Providers/AuthServiceProvider.php
Normal file
27
app/Providers/AuthServiceProvider.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||||
|
|
||||||
|
class AuthServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The model to policy mappings for the application.
|
||||||
|
*
|
||||||
|
* @var array<class-string, class-string>
|
||||||
|
*/
|
||||||
|
protected $policies = [
|
||||||
|
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register any authentication / authorization services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
21
app/Providers/BroadcastServiceProvider.php
Normal file
21
app/Providers/BroadcastServiceProvider.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Broadcast;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
|
class BroadcastServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Bootstrap any application services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
Broadcast::routes();
|
||||||
|
|
||||||
|
require base_path('routes/channels.php');
|
||||||
|
}
|
||||||
|
}
|
30
app/Providers/EventServiceProvider.php
Normal file
30
app/Providers/EventServiceProvider.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Auth\Events\Registered;
|
||||||
|
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||||
|
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
|
|
||||||
|
class EventServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The event listener mappings for the application.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $listen = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register any events for your application.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
parent::boot();
|
||||||
|
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
52
app/Providers/RouteServiceProvider.php
Normal file
52
app/Providers/RouteServiceProvider.php
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Cache\RateLimiting\Limit;
|
||||||
|
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\RateLimiter;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
class RouteServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The path to the "home" route for your application.
|
||||||
|
*
|
||||||
|
* Typically, users are redirected here after authentication.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public const HOME = '/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define your route model bindings, pattern filters, and other route configuration.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
$this->configureRateLimiting();
|
||||||
|
|
||||||
|
$this->routes(function () {
|
||||||
|
Route::middleware('api')
|
||||||
|
->prefix('api')
|
||||||
|
->group(base_path('routes/api.php'));
|
||||||
|
|
||||||
|
Route::middleware('web')
|
||||||
|
->group(base_path('routes/web.php'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the rate limiters for the application.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function configureRateLimiting()
|
||||||
|
{
|
||||||
|
RateLimiter::for('api', function (Request $request) {
|
||||||
|
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
23
app/Traits/MD5Updates.php
Normal file
23
app/Traits/MD5Updates.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a value has changed by comparing its MD5 value
|
||||||
|
*/
|
||||||
|
namespace App\Traits;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
trait MD5Updates
|
||||||
|
{
|
||||||
|
public function isDirty(): bool
|
||||||
|
{
|
||||||
|
if (! parent::isDirty())
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
foreach ($this->values->diff($this->oldValues) as $key => $value)
|
||||||
|
if (md5(Arr::get($this->oldValues,$key)) !== $value)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
38
app/View/Components/Attribute.php
Normal file
38
app/View/Components/Attribute.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\View\Components;
|
||||||
|
|
||||||
|
use Illuminate\View\Component;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute as LDAPAttribute;
|
||||||
|
|
||||||
|
class Attribute extends Component
|
||||||
|
{
|
||||||
|
public LDAPAttribute $o;
|
||||||
|
public bool $edit;
|
||||||
|
public bool $new;
|
||||||
|
public bool $old;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new component instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(LDAPAttribute $o,bool $edit,bool $old=FALSE,bool $new=FALSE)
|
||||||
|
{
|
||||||
|
$this->o = $o;
|
||||||
|
$this->edit = $edit;
|
||||||
|
$this->old = $old;
|
||||||
|
$this->new = $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the view / contents that represent the component.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\View\View|\Closure|string
|
||||||
|
*/
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return $this->o->render($this->edit,$this->old,$this->new);
|
||||||
|
}
|
||||||
|
}
|
34
app/View/Components/AttributeType.php
Normal file
34
app/View/Components/AttributeType.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\View\Components;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\View\Component;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute as LDAPAttribute;
|
||||||
|
|
||||||
|
class AttributeType extends Component
|
||||||
|
{
|
||||||
|
public LDAPAttribute $o;
|
||||||
|
public bool $new;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new component instance.
|
||||||
|
*/
|
||||||
|
public function __construct(LDAPAttribute $o,bool $new=FALSE)
|
||||||
|
{
|
||||||
|
$this->o = $o;
|
||||||
|
$this->new = $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the view / contents that represent the component.
|
||||||
|
*/
|
||||||
|
public function render(): View|Closure|string
|
||||||
|
{
|
||||||
|
return view('components.attribute-type')
|
||||||
|
->with('o',$this->o)
|
||||||
|
->with('new',$this->new);
|
||||||
|
}
|
||||||
|
}
|
13
app/helpers.php
Normal file
13
app/helpers.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
function login_attr_description(): string
|
||||||
|
{
|
||||||
|
return Arr::get(config('ldap.login.attr'),login_attr_name());
|
||||||
|
}
|
||||||
|
|
||||||
|
function login_attr_name(): string
|
||||||
|
{
|
||||||
|
return key(config('ldap.login.attr'));
|
||||||
|
}
|
53
artisan
Executable file
53
artisan
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
define('LARAVEL_START', microtime(true));
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Register The Auto Loader
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Composer provides a convenient, automatically generated class loader
|
||||||
|
| for our application. We just need to utilize it! We'll require it
|
||||||
|
| into the script here so that we do not have to worry about the
|
||||||
|
| loading of any of our classes manually. It's great to relax.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
require __DIR__.'/vendor/autoload.php';
|
||||||
|
|
||||||
|
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Run The Artisan Application
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| When we run the console application, the current CLI command will be
|
||||||
|
| executed in this console and the response sent back to a terminal
|
||||||
|
| or another output device for the developers. Here goes nothing!
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
|
||||||
|
|
||||||
|
$status = $kernel->handle(
|
||||||
|
$input = new Symfony\Component\Console\Input\ArgvInput,
|
||||||
|
new Symfony\Component\Console\Output\ConsoleOutput
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Shutdown The Application
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Once Artisan has finished running, we will fire off the shutdown events
|
||||||
|
| so that any final work may be done by the application before we shut
|
||||||
|
| down the process. This is the last thing to happen to the request.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
$kernel->terminate($input, $status);
|
||||||
|
|
||||||
|
exit($status);
|
55
bootstrap/app.php
Normal file
55
bootstrap/app.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Create The Application
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The first thing we will do is create a new Laravel application instance
|
||||||
|
| which serves as the "glue" for all the components of Laravel, and is
|
||||||
|
| the IoC container for the system binding all of the various parts.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
$app = new Illuminate\Foundation\Application(
|
||||||
|
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Bind Important Interfaces
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Next, we need to bind some important interfaces into the container so
|
||||||
|
| we will be able to resolve them when needed. The kernels serve the
|
||||||
|
| incoming requests to this application from both the web and CLI.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
$app->singleton(
|
||||||
|
Illuminate\Contracts\Http\Kernel::class,
|
||||||
|
App\Http\Kernel::class
|
||||||
|
);
|
||||||
|
|
||||||
|
$app->singleton(
|
||||||
|
Illuminate\Contracts\Console\Kernel::class,
|
||||||
|
App\Console\Kernel::class
|
||||||
|
);
|
||||||
|
|
||||||
|
$app->singleton(
|
||||||
|
Illuminate\Contracts\Debug\ExceptionHandler::class,
|
||||||
|
App\Exceptions\Handler::class
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Return The Application
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This script returns the application instance. The instance is given to
|
||||||
|
| the calling script so we can separate the building of the instances
|
||||||
|
| from the actual running of the application and sending responses.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
return $app;
|
2
bootstrap/cache/.gitignore
vendored
Normal file
2
bootstrap/cache/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
65
composer.json
Normal file
65
composer.json
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"name": "laravel/laravel",
|
||||||
|
"type": "project",
|
||||||
|
"description": "The Laravel Framework.",
|
||||||
|
"keywords": ["framework","laravel"],
|
||||||
|
"license": "MIT",
|
||||||
|
"require": {
|
||||||
|
"ext-fileinfo": "*",
|
||||||
|
"ext-ldap": "*",
|
||||||
|
"php": "^8.1|8.2",
|
||||||
|
"directorytree/ldaprecord-laravel": "^3.0",
|
||||||
|
"guzzlehttp/guzzle": "^7.2",
|
||||||
|
"laravel/framework": "^10.0",
|
||||||
|
"laravel/passport": "^11.0",
|
||||||
|
"laravel/ui": "^4.0",
|
||||||
|
"mcamara/laravel-localization": "^1.7"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"barryvdh/laravel-debugbar": "^3.5",
|
||||||
|
"fakerphp/faker": "^1.9.1",
|
||||||
|
"mockery/mockery": "^1.4.4",
|
||||||
|
"nunomaduro/collision": "^7.10",
|
||||||
|
"phpunit/phpunit": "^10.0",
|
||||||
|
"spatie/laravel-ignition": "^2.4"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"app/helpers.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"App\\": "app/",
|
||||||
|
"Database\\Factories\\": "database/factories/",
|
||||||
|
"Database\\Seeders\\": "database/seeders/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"post-autoload-dump": [
|
||||||
|
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||||
|
"@php artisan package:discover --ansi"
|
||||||
|
],
|
||||||
|
"post-root-package-install": [
|
||||||
|
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||||
|
],
|
||||||
|
"post-create-project-cmd": [
|
||||||
|
"@php artisan key:generate --ansi"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"dont-discover": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"optimize-autoloader": true,
|
||||||
|
"preferred-install": "dist",
|
||||||
|
"sort-packages": true
|
||||||
|
},
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"prefer-stable": true
|
||||||
|
}
|
9231
composer.lock
generated
Normal file
9231
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
203
config/app.php
Normal file
203
config/app.php
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Facade;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Application Name
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value is the name of your application. This value is used when the
|
||||||
|
| framework needs to place the application's name in a notification or
|
||||||
|
| any other location as required by the application or its packages.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'name' => 'PLA',
|
||||||
|
'name_html_long' => '<b>php</b>LDAPadmin',
|
||||||
|
'version' => (trim(file_get_contents(__DIR__.'/../public/VERSION')) ?? 'UNKNOWN').'-'.(trim(file_get_contents(__DIR__.'/../VERSION')) ?? 'UNKNOWN'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Application Environment
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value determines the "environment" your application is currently
|
||||||
|
| running in. This may determine how you prefer to configure various
|
||||||
|
| services the application utilizes. Set this in your ".env" file.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'env' => env('APP_ENV', 'production'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Application Debug Mode
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| When your application is in debug mode, detailed error messages with
|
||||||
|
| stack traces will be shown on every error that occurs within your
|
||||||
|
| application. If disabled, a simple generic error page is shown.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'debug' => (bool) env('APP_DEBUG', false),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Application URL
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This URL is used by the console to properly generate URLs when using
|
||||||
|
| the Artisan command line tool. You should set this to the root of
|
||||||
|
| your application so that it is used when running Artisan tasks.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'url' => env('APP_URL', 'http://localhost'),
|
||||||
|
|
||||||
|
'asset_url' => env('ASSET_URL', null),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Application Timezone
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may specify the default timezone for your application, which
|
||||||
|
| will be used by the PHP date and date-time functions. We have gone
|
||||||
|
| ahead and set this to a sensible default for you out of the box.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'timezone' => 'UTC',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Application Locale Configuration
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The application locale determines the default locale that will be used
|
||||||
|
| by the translation service provider. You are free to set this value
|
||||||
|
| to any of the locales which will be supported by the application.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'locale' => 'en',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Application Fallback Locale
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The fallback locale determines the locale to use when the current one
|
||||||
|
| is not available. You may change the value to correspond to any of
|
||||||
|
| the language folders that are provided through your application.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'fallback_locale' => 'en',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Faker Locale
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This locale will be used by the Faker PHP library when generating fake
|
||||||
|
| data for your database seeds. For example, this will be used to get
|
||||||
|
| localized telephone numbers, street address information and more.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'faker_locale' => 'en_US',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Encryption Key
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This key is used by the Illuminate encrypter service and should be set
|
||||||
|
| to a random, 32 character string, otherwise these encrypted strings
|
||||||
|
| will not be safe. Please do this before deploying an application!
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'key' => env('APP_KEY'),
|
||||||
|
|
||||||
|
'cipher' => 'AES-256-CBC',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Autoloaded Service Providers
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The service providers listed here will be automatically loaded on the
|
||||||
|
| request to your application. Feel free to add your own services to
|
||||||
|
| this array to grant expanded functionality to your applications.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'providers' => [
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Laravel Framework Service Providers...
|
||||||
|
*/
|
||||||
|
Illuminate\Auth\AuthServiceProvider::class,
|
||||||
|
Illuminate\Broadcasting\BroadcastServiceProvider::class,
|
||||||
|
Illuminate\Bus\BusServiceProvider::class,
|
||||||
|
Illuminate\Cache\CacheServiceProvider::class,
|
||||||
|
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
|
||||||
|
Illuminate\Cookie\CookieServiceProvider::class,
|
||||||
|
Illuminate\Database\DatabaseServiceProvider::class,
|
||||||
|
Illuminate\Encryption\EncryptionServiceProvider::class,
|
||||||
|
Illuminate\Filesystem\FilesystemServiceProvider::class,
|
||||||
|
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
|
||||||
|
Illuminate\Hashing\HashServiceProvider::class,
|
||||||
|
Illuminate\Mail\MailServiceProvider::class,
|
||||||
|
Illuminate\Notifications\NotificationServiceProvider::class,
|
||||||
|
Illuminate\Pagination\PaginationServiceProvider::class,
|
||||||
|
Illuminate\Pipeline\PipelineServiceProvider::class,
|
||||||
|
Illuminate\Queue\QueueServiceProvider::class,
|
||||||
|
Illuminate\Redis\RedisServiceProvider::class,
|
||||||
|
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
|
||||||
|
Illuminate\Session\SessionServiceProvider::class,
|
||||||
|
Illuminate\Translation\TranslationServiceProvider::class,
|
||||||
|
Illuminate\Validation\ValidationServiceProvider::class,
|
||||||
|
Illuminate\View\ViewServiceProvider::class,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Package Service Providers...
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Application Service Providers...
|
||||||
|
*/
|
||||||
|
App\Providers\AppServiceProvider::class,
|
||||||
|
App\Providers\AuthServiceProvider::class,
|
||||||
|
// App\Providers\BroadcastServiceProvider::class,
|
||||||
|
App\Providers\EventServiceProvider::class,
|
||||||
|
App\Providers\RouteServiceProvider::class,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Other Service Providers...
|
||||||
|
*/
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Class Aliases
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This array of class aliases will be registered when this application
|
||||||
|
| is started. However, feel free to register as many as you wish as
|
||||||
|
| the aliases are "lazy" loaded so they don't hinder performance.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'aliases' => Facade::defaultAliases()->merge([
|
||||||
|
// 'ExampleClass' => App\Example\ExampleClass::class,
|
||||||
|
])->toArray(),
|
||||||
|
|
||||||
|
];
|
125
config/auth.php
Normal file
125
config/auth.php
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Authentication Defaults
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This option controls the default authentication "guard" and password
|
||||||
|
| reset options for your application. You may change these defaults
|
||||||
|
| as required, but they're a perfect start for most applications.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'defaults' => [
|
||||||
|
'guard' => 'web',
|
||||||
|
'passwords' => 'users',
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Authentication Guards
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Next, you may define every authentication guard for your application.
|
||||||
|
| Of course, a great default configuration has been defined for you
|
||||||
|
| here which uses session storage and the Eloquent user provider.
|
||||||
|
|
|
||||||
|
| All authentication drivers have a user provider. This defines how the
|
||||||
|
| users are actually retrieved out of your database or other storage
|
||||||
|
| mechanisms used by this application to persist your user's data.
|
||||||
|
|
|
||||||
|
| Supported: "session", "token"
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'guards' => [
|
||||||
|
'web' => [
|
||||||
|
'driver' => 'session',
|
||||||
|
'provider' => 'ldap',
|
||||||
|
],
|
||||||
|
|
||||||
|
'api' => [
|
||||||
|
'driver' => 'passport',
|
||||||
|
'provider' => 'users',
|
||||||
|
'hash' => false,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| User Providers
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| All authentication drivers have a user provider. This defines how the
|
||||||
|
| users are actually retrieved out of your database or other storage
|
||||||
|
| mechanisms used by this application to persist your user's data.
|
||||||
|
|
|
||||||
|
| If you have multiple user tables or models you may configure multiple
|
||||||
|
| sources which represent each model / table. These sources may then
|
||||||
|
| be assigned to any extra authentication guards you have defined.
|
||||||
|
|
|
||||||
|
| Supported: "database", "eloquent"
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'providers' => [
|
||||||
|
'users' => [
|
||||||
|
'driver' => 'ldap',
|
||||||
|
'model' => App\Ldap\User::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
// 'users' => [
|
||||||
|
// 'driver' => 'database',
|
||||||
|
// 'table' => 'users',
|
||||||
|
// ],
|
||||||
|
|
||||||
|
'ldap' => [
|
||||||
|
'driver' => 'ldap',
|
||||||
|
'model' => App\Ldap\User::class,
|
||||||
|
'rules' => [
|
||||||
|
App\Ldap\Rules\LoginObjectclassRule::class,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Resetting Passwords
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| You may specify multiple password reset configurations if you have more
|
||||||
|
| than one user table or model in the application and you want to have
|
||||||
|
| separate password reset settings based on the specific user types.
|
||||||
|
|
|
||||||
|
| The expire time is the number of minutes that the reset token should be
|
||||||
|
| considered valid. This security feature keeps tokens short-lived so
|
||||||
|
| they have less time to be guessed. You may change this as needed.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'passwords' => [
|
||||||
|
'users' => [
|
||||||
|
'provider' => 'users',
|
||||||
|
'table' => 'password_resets',
|
||||||
|
'expire' => 60,
|
||||||
|
'throttle' => 60,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Password Confirmation Timeout
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may define the amount of seconds before a password confirmation
|
||||||
|
| times out and the user is prompted to re-enter their password via the
|
||||||
|
| confirmation screen. By default, the timeout lasts for three hours.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'password_timeout' => 10800,
|
||||||
|
|
||||||
|
];
|
59
config/broadcasting.php
Normal file
59
config/broadcasting.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Default Broadcaster
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This option controls the default broadcaster that will be used by the
|
||||||
|
| framework when an event needs to be broadcast. You may set this to
|
||||||
|
| any of the connections defined in the "connections" array below.
|
||||||
|
|
|
||||||
|
| Supported: "pusher", "redis", "log", "null"
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'default' => env('BROADCAST_DRIVER', 'null'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Broadcast Connections
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may define all of the broadcast connections that will be used
|
||||||
|
| to broadcast events to other systems or over websockets. Samples of
|
||||||
|
| each available type of connection are provided inside this array.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'connections' => [
|
||||||
|
|
||||||
|
'pusher' => [
|
||||||
|
'driver' => 'pusher',
|
||||||
|
'key' => env('PUSHER_APP_KEY'),
|
||||||
|
'secret' => env('PUSHER_APP_SECRET'),
|
||||||
|
'app_id' => env('PUSHER_APP_ID'),
|
||||||
|
'options' => [
|
||||||
|
'cluster' => env('PUSHER_APP_CLUSTER'),
|
||||||
|
'useTLS' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'redis' => [
|
||||||
|
'driver' => 'redis',
|
||||||
|
'connection' => 'default',
|
||||||
|
],
|
||||||
|
|
||||||
|
'log' => [
|
||||||
|
'driver' => 'log',
|
||||||
|
],
|
||||||
|
|
||||||
|
'null' => [
|
||||||
|
'driver' => 'null',
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
104
config/cache.php
Normal file
104
config/cache.php
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Default Cache Store
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This option controls the default cache connection that gets used while
|
||||||
|
| using this caching library. This connection is used when another is
|
||||||
|
| not explicitly specified when executing a given caching function.
|
||||||
|
|
|
||||||
|
| Supported: "apc", "array", "database", "file",
|
||||||
|
| "memcached", "redis", "dynamodb"
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'default' => env('CACHE_DRIVER', 'file'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Cache Stores
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may define all of the cache "stores" for your application as
|
||||||
|
| well as their drivers. You may even define multiple stores for the
|
||||||
|
| same cache driver to group types of items stored in your caches.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'stores' => [
|
||||||
|
|
||||||
|
'apc' => [
|
||||||
|
'driver' => 'apc',
|
||||||
|
],
|
||||||
|
|
||||||
|
'array' => [
|
||||||
|
'driver' => 'array',
|
||||||
|
'serialize' => false,
|
||||||
|
],
|
||||||
|
|
||||||
|
'database' => [
|
||||||
|
'driver' => 'database',
|
||||||
|
'table' => 'cache',
|
||||||
|
'connection' => null,
|
||||||
|
],
|
||||||
|
|
||||||
|
'file' => [
|
||||||
|
'driver' => 'file',
|
||||||
|
'path' => storage_path('framework/cache/data'),
|
||||||
|
],
|
||||||
|
|
||||||
|
'memcached' => [
|
||||||
|
'driver' => 'memcached',
|
||||||
|
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
|
||||||
|
'sasl' => [
|
||||||
|
env('MEMCACHED_USERNAME'),
|
||||||
|
env('MEMCACHED_PASSWORD'),
|
||||||
|
],
|
||||||
|
'options' => [
|
||||||
|
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
|
||||||
|
],
|
||||||
|
'servers' => [
|
||||||
|
[
|
||||||
|
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
|
||||||
|
'port' => env('MEMCACHED_PORT', 11211),
|
||||||
|
'weight' => 100,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'redis' => [
|
||||||
|
'driver' => 'redis',
|
||||||
|
'connection' => 'cache',
|
||||||
|
],
|
||||||
|
|
||||||
|
'dynamodb' => [
|
||||||
|
'driver' => 'dynamodb',
|
||||||
|
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||||
|
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||||
|
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||||
|
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
|
||||||
|
'endpoint' => env('DYNAMODB_ENDPOINT'),
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Cache Key Prefix
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| When utilizing a RAM based store such as APC or Memcached, there might
|
||||||
|
| be other applications utilizing the same cache. So, we'll specify a
|
||||||
|
| value to get prefixed to all our keys so we can avoid collisions.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache'),
|
||||||
|
|
||||||
|
];
|
@ -235,7 +235,7 @@ $config->custom->appearance['friendly_attrs'] = array(
|
|||||||
*********************************************/
|
*********************************************/
|
||||||
|
|
||||||
/* Add "modify group members" link to the attribute. */
|
/* Add "modify group members" link to the attribute. */
|
||||||
// $config->custom->modify_member['groupattr'] = array('member','uniqueMember','memberUid');
|
// $config->custom->modify_member['groupattr'] = array('member','uniqueMember','memberUid','sudoUser');
|
||||||
|
|
||||||
/* Configure filter for member search. This only applies to "modify group members" feature */
|
/* Configure filter for member search. This only applies to "modify group members" feature */
|
||||||
// $config->custom->modify_member['filter'] = '(objectclass=Person)';
|
// $config->custom->modify_member['filter'] = '(objectclass=Person)';
|
||||||
@ -243,34 +243,12 @@ $config->custom->appearance['friendly_attrs'] = array(
|
|||||||
/* Attribute that is added to the group member attribute. */
|
/* Attribute that is added to the group member attribute. */
|
||||||
// $config->custom->modify_member['attr'] = 'dn';
|
// $config->custom->modify_member['attr'] = 'dn';
|
||||||
|
|
||||||
|
|
||||||
/* For Posix attributes */
|
/* For Posix attributes */
|
||||||
// $config->custom->modify_member['posixattr'] = 'uid';
|
// $config->custom->modify_member['posixattr'] = 'uid';
|
||||||
// $config->custom->modify_member['posixfilter'] = '(uid=*)';
|
// $config->custom->modify_member['posixfilter'] = '(uid=*)';
|
||||||
// $config->custom->modify_member['posixgroupattr'] = 'memberUid';
|
// $config->custom->modify_member['posixgroupattr'] = 'memberUid';
|
||||||
|
|
||||||
/*********************************************
|
|
||||||
* Support for attrs display order *
|
|
||||||
*********************************************/
|
|
||||||
|
|
||||||
/* Use this array if you want to have your attributes displayed in a specific
|
|
||||||
order. You can use default attribute names or their fridenly names.
|
|
||||||
For example, "sn" will be displayed right after "givenName". All the other
|
|
||||||
attributes that are not specified in this array will be displayed after in
|
|
||||||
alphabetical order. */
|
|
||||||
// $config->custom->appearance['attr_display_order'] = array();
|
|
||||||
# $config->custom->appearance['attr_display_order'] = array(
|
|
||||||
# 'givenName',
|
|
||||||
# 'sn',
|
|
||||||
# 'cn',
|
|
||||||
# 'displayName',
|
|
||||||
# 'uid',
|
|
||||||
# 'uidNumber',
|
|
||||||
# 'gidNumber',
|
|
||||||
# 'homeDirectory',
|
|
||||||
# 'mail',
|
|
||||||
# 'userPassword'
|
|
||||||
# );
|
|
||||||
|
|
||||||
/*********************************************
|
/*********************************************
|
||||||
* Define your LDAP servers in this section *
|
* Define your LDAP servers in this section *
|
||||||
*********************************************/
|
*********************************************/
|
||||||
@ -341,11 +319,16 @@ $servers->setValue('server','name','My LDAP Server');
|
|||||||
/* Enable SASL authentication LDAP SASL authentication requires PHP 5.x
|
/* Enable SASL authentication LDAP SASL authentication requires PHP 5.x
|
||||||
configured with --with-ldap-sasl=DIR. If this option is disabled (ie, set to
|
configured with --with-ldap-sasl=DIR. If this option is disabled (ie, set to
|
||||||
false), then all other sasl options are ignored. */
|
false), then all other sasl options are ignored. */
|
||||||
// $servers->setValue('login','auth_type','sasl');
|
# $servers->setValue('login','auth_type','sasl');
|
||||||
|
|
||||||
/* SASL auth mechanism */
|
/* SASL GSSAPI auth mechanism (requires auth_type of sasl) */
|
||||||
// $servers->setValue('sasl','mech','GSSAPI');
|
// $servers->setValue('sasl','mech','GSSAPI');
|
||||||
|
|
||||||
|
/* SASL PLAIN support... this mech converts simple binds to SASL
|
||||||
|
PLAIN binds using any auth_type (or other bind_id/pass) as credentials.
|
||||||
|
NOTE: auth_type must be simple auth compatible (ie not sasl) */
|
||||||
|
# $servers->setValue('sasl','mech','PLAIN');
|
||||||
|
|
||||||
/* SASL authentication realm name */
|
/* SASL authentication realm name */
|
||||||
// $servers->setValue('sasl','realm','');
|
// $servers->setValue('sasl','realm','');
|
||||||
# $servers->setValue('sasl','realm','EXAMPLE.COM');
|
# $servers->setValue('sasl','realm','EXAMPLE.COM');
|
||||||
@ -379,7 +362,7 @@ $servers->setValue('server','name','My LDAP Server');
|
|||||||
|
|
||||||
/* Default password hashing algorithm. One of md5, ssha, sha, md5crpyt, smd5,
|
/* Default password hashing algorithm. One of md5, ssha, sha, md5crpyt, smd5,
|
||||||
blowfish, crypt or leave blank for now default algorithm. */
|
blowfish, crypt or leave blank for now default algorithm. */
|
||||||
// $servers->setValue('appearance','password_hash','md5');
|
// $servers->setValue('appearance','pla_password_hash','md5');
|
||||||
|
|
||||||
/* If you specified 'cookie' or 'session' as the auth_type above, you can
|
/* If you specified 'cookie' or 'session' as the auth_type above, you can
|
||||||
optionally specify here an attribute to use when logging in. If you enter
|
optionally specify here an attribute to use when logging in. If you enter
|
||||||
@ -394,11 +377,11 @@ $servers->setValue('server','name','My LDAP Server');
|
|||||||
Base DNs are used. */
|
Base DNs are used. */
|
||||||
// $servers->setValue('login','base',array());
|
// $servers->setValue('login','base',array());
|
||||||
|
|
||||||
/* If 'login,attr' is used above such that phpLDAPadmin will search for your DN
|
/* If login_attr was set to 'dn', it is possible to specify a template string to
|
||||||
at login, you may restrict the search to a specific objectClasses. EG, set this
|
build the DN from. Use '%s' where user input should be inserted. A user may
|
||||||
to array('posixAccount') or array('inetOrgPerson',..), depending upon your
|
still enter the complete DN. In this case the template will not be used. */
|
||||||
setup. */
|
// $servers->setValue('login','bind_dn_template',null);
|
||||||
// $servers->setValue('login','class',array());
|
# $servers->setValue('login','bind_dn_template','cn=%s,ou=people,dc=example,dc=com');
|
||||||
|
|
||||||
/* If you specified something different from 'dn', for example 'uid', as the
|
/* If you specified something different from 'dn', for example 'uid', as the
|
||||||
login_attr above, you can optionally specify here to fall back to
|
login_attr above, you can optionally specify here to fall back to
|
||||||
@ -420,6 +403,9 @@ $servers->setValue('server','name','My LDAP Server');
|
|||||||
/* Set to true if you would like to initially open the first level of each tree. */
|
/* Set to true if you would like to initially open the first level of each tree. */
|
||||||
// $servers->setValue('appearance','open_tree',false);
|
// $servers->setValue('appearance','open_tree',false);
|
||||||
|
|
||||||
|
/* Set to true to display authorization ID in place of login dn (PHP 7.2+) */
|
||||||
|
// $servers->setValue('appearance','show_authz',false);
|
||||||
|
|
||||||
/* This feature allows phpLDAPadmin to automatically determine the next
|
/* This feature allows phpLDAPadmin to automatically determine the next
|
||||||
available uidNumber for a new entry. */
|
available uidNumber for a new entry. */
|
||||||
// $servers->setValue('auto_number','enable',true);
|
// $servers->setValue('auto_number','enable',true);
|
||||||
@ -492,15 +478,6 @@ $servers->setValue('server','name','My LDAP Server');
|
|||||||
// $servers->setValue('server','custom_attrs',array(''));
|
// $servers->setValue('server','custom_attrs',array(''));
|
||||||
# $servers->setValue('server','custom_attrs',array('nsRoleDN','nsRole','nsAccountLock'));
|
# $servers->setValue('server','custom_attrs',array('nsRoleDN','nsRole','nsAccountLock'));
|
||||||
|
|
||||||
/* These attributes will be forced to MAY attributes and become option in the
|
|
||||||
templates. If they are not defined in the templates, then they wont appear
|
|
||||||
as per normal template processing. You may want to do this because your LDAP
|
|
||||||
server may automatically calculate a default value.
|
|
||||||
In Fedora Directory Server using the DNA Plugin one could ignore uidNumber,
|
|
||||||
gidNumber and sambaSID. */
|
|
||||||
// $servers->setValue('server','force_may',array(''));
|
|
||||||
# $servers->setValue('server','force_may',array('uidNumber','gidNumber','sambaSID'));
|
|
||||||
|
|
||||||
/*********************************************
|
/*********************************************
|
||||||
* Unique attributes *
|
* Unique attributes *
|
||||||
*********************************************/
|
*********************************************/
|
||||||
@ -546,7 +523,7 @@ $servers->setValue('sasl','authz_id_regex','/^uid=([^,]+)(.+)/i');
|
|||||||
$servers->setValue('sasl','authz_id_replacement','$1');
|
$servers->setValue('sasl','authz_id_replacement','$1');
|
||||||
$servers->setValue('sasl','props',null);
|
$servers->setValue('sasl','props',null);
|
||||||
|
|
||||||
$servers->setValue('appearance','password_hash','md5');
|
$servers->setValue('appearance','pla_password_hash','md5');
|
||||||
$servers->setValue('login','attr','dn');
|
$servers->setValue('login','attr','dn');
|
||||||
$servers->setValue('login','fallback_dn',false);
|
$servers->setValue('login','fallback_dn',false);
|
||||||
$servers->setValue('login','class',null);
|
$servers->setValue('login','class',null);
|
||||||
@ -573,4 +550,19 @@ $servers->setValue('server','custom_sys_attrs',array('passwordExpirationTime','p
|
|||||||
$servers->setValue('server','custom_attrs',array('nsRoleDN','nsRole','nsAccountLock'));
|
$servers->setValue('server','custom_attrs',array('nsRoleDN','nsRole','nsAccountLock'));
|
||||||
$servers->setValue('server','force_may',array('uidNumber','gidNumber','sambaSID'));
|
$servers->setValue('server','force_may',array('uidNumber','gidNumber','sambaSID'));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/***********************************************************************************
|
||||||
|
* If you want to configure Google reCAPTCHA on autentication form, do so below. *
|
||||||
|
* Remove the commented lines and use this section as a template for all *
|
||||||
|
* reCAPTCHA v2 Generate on https://www.google.com/recaptcha/ *
|
||||||
|
* *
|
||||||
|
* IMPORTANT: Select reCAPTCHA v2 on Type of reCAPTCHA *
|
||||||
|
***********************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
$config->custom->session['reCAPTCHA-enable'] = false;
|
||||||
|
$config->custom->session['reCAPTCHA-key-site'] = '<put-here-key-site>';
|
||||||
|
$config->custom->session['reCAPTCHA-key-server'] = '<put-here-key-server>';
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
34
config/cors.php
Normal file
34
config/cors.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Cross-Origin Resource Sharing (CORS) Configuration
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may configure your settings for cross-origin resource sharing
|
||||||
|
| or "CORS". This determines what cross-origin operations may execute
|
||||||
|
| in web browsers. You are free to adjust these settings as needed.
|
||||||
|
|
|
||||||
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'paths' => ['api/*', 'sanctum/csrf-cookie'],
|
||||||
|
|
||||||
|
'allowed_methods' => ['*'],
|
||||||
|
|
||||||
|
'allowed_origins' => ['*'],
|
||||||
|
|
||||||
|
'allowed_origins_patterns' => [],
|
||||||
|
|
||||||
|
'allowed_headers' => ['*'],
|
||||||
|
|
||||||
|
'exposed_headers' => [],
|
||||||
|
|
||||||
|
'max_age' => 0,
|
||||||
|
|
||||||
|
'supports_credentials' => false,
|
||||||
|
|
||||||
|
];
|
147
config/database.php
Normal file
147
config/database.php
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Default Database Connection Name
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may specify which of the database connections below you wish
|
||||||
|
| to use as your default connection for all database work. Of course
|
||||||
|
| you may use many connections at once using the Database library.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'default' => env('DB_CONNECTION', 'mysql'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Database Connections
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here are each of the database connections setup for your application.
|
||||||
|
| Of course, examples of configuring each database platform that is
|
||||||
|
| supported by Laravel is shown below to make development simple.
|
||||||
|
|
|
||||||
|
|
|
||||||
|
| All database work in Laravel is done through the PHP PDO facilities
|
||||||
|
| so make sure you have the driver for your particular database of
|
||||||
|
| choice installed on your machine before you begin development.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'connections' => [
|
||||||
|
|
||||||
|
'sqlite' => [
|
||||||
|
'driver' => 'sqlite',
|
||||||
|
'url' => env('DATABASE_URL'),
|
||||||
|
'database' => env('DB_DATABASE', database_path('database.sqlite')),
|
||||||
|
'prefix' => '',
|
||||||
|
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
|
||||||
|
],
|
||||||
|
|
||||||
|
'mysql' => [
|
||||||
|
'driver' => 'mysql',
|
||||||
|
'url' => env('DATABASE_URL'),
|
||||||
|
'host' => env('DB_HOST', '127.0.0.1'),
|
||||||
|
'port' => env('DB_PORT', '3306'),
|
||||||
|
'database' => env('DB_DATABASE', 'forge'),
|
||||||
|
'username' => env('DB_USERNAME', 'forge'),
|
||||||
|
'password' => env('DB_PASSWORD', ''),
|
||||||
|
'unix_socket' => env('DB_SOCKET', ''),
|
||||||
|
'charset' => 'utf8mb4',
|
||||||
|
'collation' => 'utf8mb4_unicode_ci',
|
||||||
|
'prefix' => '',
|
||||||
|
'prefix_indexes' => true,
|
||||||
|
'strict' => true,
|
||||||
|
'engine' => null,
|
||||||
|
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||||
|
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
|
||||||
|
]) : [],
|
||||||
|
],
|
||||||
|
|
||||||
|
'pgsql' => [
|
||||||
|
'driver' => 'pgsql',
|
||||||
|
'url' => env('DATABASE_URL'),
|
||||||
|
'host' => env('DB_HOST', '127.0.0.1'),
|
||||||
|
'port' => env('DB_PORT', '5432'),
|
||||||
|
'database' => env('DB_DATABASE', 'forge'),
|
||||||
|
'username' => env('DB_USERNAME', 'forge'),
|
||||||
|
'password' => env('DB_PASSWORD', ''),
|
||||||
|
'charset' => 'utf8',
|
||||||
|
'prefix' => '',
|
||||||
|
'prefix_indexes' => true,
|
||||||
|
'schema' => 'public',
|
||||||
|
'sslmode' => 'prefer',
|
||||||
|
],
|
||||||
|
|
||||||
|
'sqlsrv' => [
|
||||||
|
'driver' => 'sqlsrv',
|
||||||
|
'url' => env('DATABASE_URL'),
|
||||||
|
'host' => env('DB_HOST', 'localhost'),
|
||||||
|
'port' => env('DB_PORT', '1433'),
|
||||||
|
'database' => env('DB_DATABASE', 'forge'),
|
||||||
|
'username' => env('DB_USERNAME', 'forge'),
|
||||||
|
'password' => env('DB_PASSWORD', ''),
|
||||||
|
'charset' => 'utf8',
|
||||||
|
'prefix' => '',
|
||||||
|
'prefix_indexes' => true,
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Migration Repository Table
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This table keeps track of all the migrations that have already run for
|
||||||
|
| your application. Using this information, we can determine which of
|
||||||
|
| the migrations on disk haven't actually been run in the database.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'migrations' => 'migrations',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Redis Databases
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Redis is an open source, fast, and advanced key-value store that also
|
||||||
|
| provides a richer body of commands than a typical key-value system
|
||||||
|
| such as APC or Memcached. Laravel makes it easy to dig right in.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'redis' => [
|
||||||
|
|
||||||
|
'client' => env('REDIS_CLIENT', 'phpredis'),
|
||||||
|
|
||||||
|
'options' => [
|
||||||
|
'cluster' => env('REDIS_CLUSTER', 'redis'),
|
||||||
|
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
|
||||||
|
],
|
||||||
|
|
||||||
|
'default' => [
|
||||||
|
'url' => env('REDIS_URL'),
|
||||||
|
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||||
|
'password' => env('REDIS_PASSWORD', null),
|
||||||
|
'port' => env('REDIS_PORT', '6379'),
|
||||||
|
'database' => env('REDIS_DB', '0'),
|
||||||
|
],
|
||||||
|
|
||||||
|
'cache' => [
|
||||||
|
'url' => env('REDIS_URL'),
|
||||||
|
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||||
|
'password' => env('REDIS_PASSWORD', null),
|
||||||
|
'port' => env('REDIS_PORT', '6379'),
|
||||||
|
'database' => env('REDIS_CACHE_DB', '1'),
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user