Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
caf89ff4e5 | ||
|
e084621082 | ||
|
181cc4ca20 | ||
|
a8f534b463 | ||
|
b140dbb1b6 | ||
|
2e134ea609 | ||
|
715f7efe9b | ||
|
5ba2cf67e9 |
@ -1,14 +0,0 @@
|
||||
.dockerignore
|
||||
.editorconfig
|
||||
.env.testing
|
||||
.idea
|
||||
.git*
|
||||
.phpunit.result.cache
|
||||
.styleci.yml
|
||||
node_modules/
|
||||
package.json
|
||||
package-lock.json
|
||||
phpunit.xml
|
||||
vendor/
|
||||
webpack.mix.js
|
||||
yarn.lock
|
@ -1,18 +0,0 @@
|
||||
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
|
18
.env.example
18
.env.example
@ -1,18 +0,0 @@
|
||||
APP_NAME=Laravel
|
||||
APP_ENV=production
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
LOG_CHANNEL=daily
|
||||
|
||||
CACHE_DRIVER=file
|
||||
QUEUE_CONNECTION=sync
|
||||
SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
LDAP_HOST=
|
||||
LDAP_BASE_DN=
|
||||
LDAP_USERNAME=
|
||||
LDAP_PASSWORD=
|
||||
LDAP_CACHE=true
|
51
.env.testing
51
.env.testing
@ -1,51 +0,0 @@
|
||||
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
5
.gitattributes
vendored
@ -1,5 +0,0 @@
|
||||
* text=auto
|
||||
*.css linguist-vendored
|
||||
*.scss linguist-vendored
|
||||
*.js linguist-vendored
|
||||
CHANGELOG.md export-ignore
|
@ -1,196 +0,0 @@
|
||||
name: Create Docker Image
|
||||
run-name: ${{ gitea.actor }} Building Docker Image 🐳
|
||||
on: [push]
|
||||
env:
|
||||
VERSION: latest
|
||||
DOCKER_HOST: tcp://127.0.0.1:2375
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
arch:
|
||||
- x86_64
|
||||
# arm64
|
||||
|
||||
name: Test Application
|
||||
runs-on: docker-${{ matrix.arch }}
|
||||
container:
|
||||
image: docker:dind
|
||||
privileged: true
|
||||
|
||||
steps:
|
||||
- name: Environment Setup
|
||||
run: |
|
||||
# If we have a proxy use it
|
||||
if [ -n "${HTTP_PROXY}" ]; then echo "HTTP PROXY [${HTTP_PROXY}]"; sed -i -e s'/https/http/' /etc/apk/repositories; fi
|
||||
# Some pre-reqs
|
||||
apk add git nodejs npm tar zstd
|
||||
## Some debugging info
|
||||
# env|sort
|
||||
|
||||
- name: Code Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build Assets
|
||||
run: |
|
||||
# Build assets
|
||||
npm i
|
||||
npm run prod
|
||||
|
||||
# - name: Run Tests
|
||||
# run: |
|
||||
# mv .env.testing .env
|
||||
# # Install Composer and project dependencies.
|
||||
# mkdir -p ${COMPOSER_HOME}
|
||||
# if [ -n "${{ secrets.COMPOSER_GITHUB_TOKEN }}" ]; then composer config github-oauth.github.com ${{ secrets.COMPOSER_GITHUB_TOKEN }}; fi
|
||||
# composer install
|
||||
# # Generate an application key. Re-cache.
|
||||
# php artisan key:generate
|
||||
# php artisan migrate
|
||||
# php artisan db:seed
|
||||
# # run laravel tests
|
||||
# # XDEBUG_MODE=coverage php vendor/bin/phpunit --coverage-text --colors=never
|
||||
|
||||
- name: Cache page assets
|
||||
id: cache-page-assets
|
||||
uses: actions/cache@v3
|
||||
# env:
|
||||
# cache-name: page-assets
|
||||
with:
|
||||
path: |
|
||||
public/css/app.css
|
||||
public/fonts
|
||||
public/images
|
||||
public/js/app.js
|
||||
public/js/manifest.js
|
||||
public/js/vendor.js
|
||||
#key: build-pla-page-assets-${{ hashFiles('**/package-lock.json') }}
|
||||
key: build-pla-page-assets-5166780
|
||||
#restore-keys: |
|
||||
# build-pla-page-assets-
|
||||
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
arch:
|
||||
- x86_64
|
||||
- arm64
|
||||
needs: [test]
|
||||
|
||||
name: Build Docker Image
|
||||
runs-on: docker-${{ matrix.arch }}
|
||||
container:
|
||||
image: docker:dind
|
||||
privileged: true
|
||||
env:
|
||||
ARCH: ${{ matrix.arch }}
|
||||
VERSIONARCH: ${{ env.VERSION }}-${{ env.ARCH }}
|
||||
|
||||
steps:
|
||||
- name: Environment Setup
|
||||
run: |
|
||||
# If we have a proxy use it
|
||||
if [ -n "${HTTP_PROXY}" ]; then echo "HTTP PROXY [${HTTP_PROXY}]"; sed -i -e s'/https/http/' /etc/apk/repositories; fi
|
||||
# Some pre-reqs
|
||||
apk add git curl nodejs npm tar zstd
|
||||
# Start docker
|
||||
( dockerd --host=tcp://0.0.0.0:2375 --tls=false & ) && sleep 3
|
||||
## Some debugging info
|
||||
# docker info && docker version
|
||||
# env|sort
|
||||
|
||||
- name: Registry FQDN Setup
|
||||
id: registry
|
||||
run: |
|
||||
registry=${{ github.server_url }}
|
||||
echo "registry=${registry##http*://}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Container Registry Login
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ steps.registry.outputs.registry }}
|
||||
username: ${{ gitea.actor }}
|
||||
password: ${{ secrets.PKG_WRITE_TOKEN }}
|
||||
|
||||
- name: Code Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cache page assets
|
||||
id: cache-page-assets
|
||||
uses: actions/cache@v3
|
||||
# env:
|
||||
# cache-name: page-assets
|
||||
with:
|
||||
path: |
|
||||
public/css/app.css
|
||||
public/fonts
|
||||
public/images
|
||||
public/js/app.js
|
||||
public/js/manifest.js
|
||||
public/js/vendor.js
|
||||
#key: build-pla-page-assets-${{ hashFiles('**/package-lock.json') }}
|
||||
key: build-pla-page-assets-5166780
|
||||
#restore-keys: |
|
||||
# build-pla-page-assets-
|
||||
|
||||
- if: ${{ steps.cache-page-assets.outputs.cache-hit != 'true' }}
|
||||
name: List the state of page assets
|
||||
continue-on-error: false
|
||||
run: |
|
||||
echo CACHE-MISS:${{ steps.cache-page-assets.outputs.cache-hit }}
|
||||
ls -al public/css/
|
||||
ls -al public/js/
|
||||
|
||||
- name: Record version and Delete Unnecessary files
|
||||
run: |
|
||||
echo ${GITHUB_SHA::8} > VERSION
|
||||
rm -rf .git* tests/ storage/app/test/
|
||||
ls -al public/css/
|
||||
ls -al public/js/
|
||||
|
||||
- name: Build and Push Docker Image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
push: true
|
||||
tags: "${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSIONARCH }}"
|
||||
|
||||
manifest:
|
||||
name: Final Docker Image Manifest
|
||||
runs-on: docker-x86_64
|
||||
container:
|
||||
image: docker:dind
|
||||
privileged: true
|
||||
needs: [build]
|
||||
|
||||
steps:
|
||||
- name: Environment Setup
|
||||
run: |
|
||||
# If we have a proxy use it
|
||||
if [ -n "${HTTP_PROXY}" ]; then echo "HTTP PROXY [${HTTP_PROXY}]"; sed -i -e s'/https/http/' /etc/apk/repositories; fi
|
||||
# Some pre-reqs
|
||||
apk add git curl nodejs
|
||||
# Start docker
|
||||
( dockerd --host=tcp://0.0.0.0:2375 --tls=false & ) && sleep 3
|
||||
|
||||
- name: Registry FQDN Setup
|
||||
id: registry
|
||||
run: |
|
||||
registry=${{ github.server_url }}
|
||||
echo "registry=${registry##http*://}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Container Registry Login
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ steps.registry.outputs.registry }}
|
||||
username: ${{ gitea.actor }}
|
||||
password: ${{ secrets.PKG_WRITE_TOKEN }}
|
||||
|
||||
- name: Build Docker Manifest
|
||||
run: |
|
||||
docker manifest create ${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSION }} \
|
||||
${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSION }}-x86_64 \
|
||||
${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSION }}-arm64
|
||||
docker manifest push --purge ${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSION }}
|
13
.github/FUNDING.yml
vendored
13
.github/FUNDING.yml
vendored
@ -1,13 +0,0 @@
|
||||
# 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
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,38 +0,0 @@
|
||||
---
|
||||
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. (One issue per report please.)
|
||||
|
||||
**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,19 +1,3 @@
|
||||
/node_modules
|
||||
/public/hot
|
||||
/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
|
||||
config/config.php
|
||||
queries/custom_*
|
||||
templates/*/custom_*
|
||||
|
21
.htaccess
Normal file
21
.htaccess
Normal file
@ -0,0 +1,21 @@
|
||||
# Turn on URL rewriting
|
||||
RewriteEngine On
|
||||
|
||||
# Installation directory
|
||||
RewriteBase /pla
|
||||
|
||||
# Protect hidden files from being viewed
|
||||
<Files .*>
|
||||
Order Deny,Allow
|
||||
Deny From All
|
||||
</Files>
|
||||
|
||||
# Protect application and system files from being viewed
|
||||
RewriteRule ^(?:application|modules|includes/kohana)\b.* index.php/$0 [L]
|
||||
|
||||
# Allow any files or directories that exist to be displayed directly
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
|
||||
# Rewrite all other URLs to index.php/URL
|
||||
RewriteRule .* index.php/$0 [PT]
|
23
INSTALL
Normal file
23
INSTALL
Normal file
@ -0,0 +1,23 @@
|
||||
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
|
84
README.md
84
README.md
@ -1,84 +0,0 @@
|
||||
# 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.
|
||||
|
||||
## Version 2 Progress
|
||||
|
||||
The update to v2 is progressing well - here is a list of work to do and done:
|
||||
|
||||
- [ ] Creating new LDAP entries
|
||||
- [X] Updating existing LDAP Entries
|
||||
- [X] Password attributes
|
||||
- [X] Support different password hash options
|
||||
- [X] Validate password is correct
|
||||
- [ ] JpegPhoto Create/Delete
|
||||
- [X] JpegPhoto Display
|
||||
- [ ] ObjectClass Add/Remove
|
||||
- [ ] Add additional required attributes (for ObjectClass Addition)
|
||||
- [ ] Remove existing required attributes (for ObjectClass Removal)
|
||||
- [X] Add additional values to Attributes that support multiple values
|
||||
- [X] Delete extra values for Attributes that support multiple values
|
||||
- [ ] Delete Attributes
|
||||
- [ ] Templates to enable entries to conform to a custom standard
|
||||
- [X] Login to LDAP server
|
||||
- [X] Configure login by a specific attribute
|
||||
- [X] Logout LDAP server
|
||||
- [X] Export entries as an LDAP
|
||||
- [ ] Import LDIF
|
||||
- [X] Schema Browser
|
||||
- [ ] Is there something missing?
|
||||
|
||||
Support is known for these LDAP servers:
|
||||
- [X] OpenLDAP
|
||||
- [ ] Microsoft Active Directory
|
||||
|
||||
If there is an LDAP server that you have that you would like to have supported, please open an issue to request it.
|
||||
You might need to provide access, provide a copy or instructions to get an environment for testing. If you have enabled
|
||||
support for an LDAP server not listed above, please provide a pull request for consideration.
|
||||
|
||||
## 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
3
app/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
q*
|
||||
!public/
|
||||
!.gitignore
|
File diff suppressed because it is too large
Load Diff
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
|
||||
/**
|
||||
* Represents an attribute whose values are binary
|
||||
*/
|
||||
class Binary extends Attribute
|
||||
{
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
<?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 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);
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
|
||||
/**
|
||||
* Represents an GidNumber Attribute
|
||||
*/
|
||||
final class GidNumber extends Attribute
|
||||
{
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
/**
|
||||
* Represents an CSN Attribute
|
||||
*/
|
||||
final class CSN extends Internal
|
||||
{
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
/**
|
||||
* Represents an DN Attribute
|
||||
*/
|
||||
final class DN extends Internal
|
||||
{
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
/**
|
||||
* Represents an HasSubordinates Attribute
|
||||
*/
|
||||
final class HasSubordinates extends Internal
|
||||
{
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
/**
|
||||
* Represents an StructuralObjectClass Attribute
|
||||
*/
|
||||
final class StructuralObjectClass extends Internal
|
||||
{
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
/**
|
||||
* Represents an SubschemaSubentry Attribute
|
||||
*/
|
||||
final class SubschemaSubentry extends Internal
|
||||
{
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
/**
|
||||
* Represents an UUID Attribute
|
||||
*/
|
||||
final class UUID extends Internal
|
||||
{
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
<?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->contains($value);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute;
|
||||
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Traits\MD5Updates;
|
||||
|
||||
/**
|
||||
* Represents an attribute whose values are passwords
|
||||
*/
|
||||
final class Password extends Attribute
|
||||
{
|
||||
use MD5Updates;
|
||||
private const password_helpers = 'Classes/LDAP/Attribute/Password';
|
||||
public const commands = 'App\\Classes\\LDAP\\Attribute\\Password\\';
|
||||
|
||||
private static function helpers(): Collection
|
||||
{
|
||||
$helpers = collect();
|
||||
|
||||
foreach (preg_grep('/^([^.])/',scandir(app_path(self::password_helpers))) as $file) {
|
||||
if (($file === 'Base.php') || (! str_ends_with(strtolower($file),'.php')))
|
||||
continue;
|
||||
|
||||
$class = self::commands.preg_replace('/\.php$/','',$file);
|
||||
|
||||
$helpers = $helpers
|
||||
->merge([$class::id()=>$class]);
|
||||
}
|
||||
|
||||
return $helpers->sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an LDAP password syntax {xxx}yyyyyy, this function will return the object for xxx
|
||||
*
|
||||
* @param string $password
|
||||
* @return Attribute\Password\Base|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function hash(string $password): ?Attribute\Password\Base
|
||||
{
|
||||
$m = [];
|
||||
preg_match('/^{([A-Z0-9]+)}(.*)$/',$password,$m);
|
||||
|
||||
$hash = Arr::get($m,1,'*clear*');
|
||||
|
||||
if (($potential=static::helpers()->filter(fn($hasher)=>str_starts_with($hasher::key,$hash)))->count() > 1) {
|
||||
foreach ($potential as $item) {
|
||||
if ($item::subid($password))
|
||||
return new $item;
|
||||
}
|
||||
|
||||
throw new \Exception(sprintf('Couldnt figure out a password hash for %s',$password));
|
||||
|
||||
} elseif (! $potential->count()) {
|
||||
throw new \Exception(sprintf('Couldnt figure out a password hash for %s',$password));
|
||||
}
|
||||
|
||||
return new ($potential->pop());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object that will process a password
|
||||
*
|
||||
* @param string $id
|
||||
* @return Attribute\Password\Base|null
|
||||
*/
|
||||
public static function hash_id(string $id): ?Attribute\Password\Base
|
||||
{
|
||||
return ($helpers=static::helpers())->has($id) ? new ($helpers->get($id)) : NULL;
|
||||
}
|
||||
|
||||
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)
|
||||
->with('helpers',static::helpers()->map(fn($item,$key)=>['id'=>$key,'value'=>$key])->sort());
|
||||
}
|
||||
|
||||
public function render_item_old(int $key): ?string
|
||||
{
|
||||
$pw = Arr::get($this->oldValues,$key);
|
||||
return $pw
|
||||
? (((($x=$this->hash($pw)) && ($x::id() !== '*clear*')) ? sprintf('{%s}',$x::shortid()) : '').str_repeat('*',16))
|
||||
: NULL;
|
||||
}
|
||||
|
||||
public function render_item_new(int $key): ?string
|
||||
{
|
||||
$pw = Arr::get($this->values,$key);
|
||||
return $pw
|
||||
? (((($x=$this->hash($pw)) && ($x::id() !== '*clear*')) ? sprintf('{%s}',$x::shortid()) : '').str_repeat('*',16))
|
||||
: NULL;
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class Argon2i extends Base
|
||||
{
|
||||
public const key = 'ARGON2';
|
||||
protected const subkey = 'argon2i';
|
||||
protected const identifier = '$argon2i';
|
||||
|
||||
public static function subid(string $password): bool
|
||||
{
|
||||
return str_starts_with(base64_decode(self::password($password)),self::identifier.'$');
|
||||
}
|
||||
|
||||
public function compare(string $source,string $compare): bool
|
||||
{
|
||||
return password_verify($compare,base64_decode($this->password($source)));
|
||||
}
|
||||
|
||||
public function encode(string $password): string
|
||||
{
|
||||
return sprintf('{%s}%s',self::key,base64_encode(password_hash($password,PASSWORD_ARGON2I)));
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class Argon2id extends Base
|
||||
{
|
||||
public const key = 'ARGON2';
|
||||
protected const subkey = 'argon2id';
|
||||
protected const identifier = '$argon2id';
|
||||
|
||||
public static function subid(string $password): bool
|
||||
{
|
||||
return str_starts_with(base64_decode(self::password($password)),self::identifier.'$');
|
||||
}
|
||||
|
||||
public function compare(string $source,string $compare): bool
|
||||
{
|
||||
return password_verify($compare,base64_decode($this->password($source)));
|
||||
}
|
||||
|
||||
public function encode(string $password): string
|
||||
{
|
||||
return sprintf('{%s}%s',self::key,base64_encode(password_hash($password,PASSWORD_ARGON2ID)));
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
abstract class Base
|
||||
{
|
||||
protected const subkey = '';
|
||||
|
||||
abstract public function encode(string $password): string;
|
||||
|
||||
public static function id(): string
|
||||
{
|
||||
return static::subkey ? strtoupper(static::subkey) : static::key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the hash {TEXT}xxxx from the password
|
||||
*
|
||||
* @param string $password
|
||||
* @return string
|
||||
*/
|
||||
protected static function password(string $password): string
|
||||
{
|
||||
return preg_replace('/^{'.static::key.'}/','',$password);
|
||||
}
|
||||
|
||||
public static function shortid(): string
|
||||
{
|
||||
return static::key;
|
||||
}
|
||||
|
||||
/**
|
||||
* When multiple passwords share the same ID, this determines which hash is responsible for the presented password
|
||||
*
|
||||
* @param string $password
|
||||
* @return bool
|
||||
*/
|
||||
public static function subid(string $password): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare our password to see if it is the same as that stored
|
||||
*
|
||||
* @param string $source Encoded source password
|
||||
* @param string $compare Password entered by user
|
||||
* @return bool
|
||||
*/
|
||||
public function compare(string $source,string $compare): bool
|
||||
{
|
||||
return $source === $this->encode($compare);
|
||||
}
|
||||
|
||||
protected function salted_hash(string $password,string $algo,int $salt_size=8,string $salt=NULL): string
|
||||
{
|
||||
if (is_null($salt))
|
||||
$salt = hex2bin(random_salt($salt_size));
|
||||
|
||||
return base64_encode(hash($algo,$password.$salt,true).$salt);
|
||||
}
|
||||
|
||||
protected function salted_salt(string $source): string
|
||||
{
|
||||
$hash = base64_decode(substr($source,strlen(static::key)+2));
|
||||
return substr($hash,strlen($hash)-static::salt/2);
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class Bcrypt extends Base
|
||||
{
|
||||
public const key = 'BCRYPT';
|
||||
|
||||
private const options = [
|
||||
'cost' => 8,
|
||||
];
|
||||
|
||||
public function compare(string $source,string $compare): bool
|
||||
{
|
||||
return password_verify($compare,base64_decode($this->password($source)));
|
||||
}
|
||||
|
||||
public function encode(string $password): string
|
||||
{
|
||||
return sprintf('{%s}%s',self::key,base64_encode(password_hash($password,PASSWORD_BCRYPT,self::options)));
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class Blowfish extends Base
|
||||
{
|
||||
public const key = 'CRYPT';
|
||||
protected const subkey = 'blowfish';
|
||||
private const cost = 12;
|
||||
protected const salt = 22;
|
||||
private const identifier = '$2a$';
|
||||
|
||||
public static function subid(string $password): bool
|
||||
{
|
||||
return preg_match('/^\\$2.\\$/',self::password($password));
|
||||
}
|
||||
|
||||
public function compare(string $source,string $compare): bool
|
||||
{
|
||||
return hash_equals($cp=self::password($source),crypt($compare,$cp));
|
||||
}
|
||||
|
||||
public function encode(string $password,string $salt=NULL): string
|
||||
{
|
||||
if (is_null($salt))
|
||||
$salt = sprintf('%s%d$%s',self::identifier,self::cost,random_salt(self::salt));
|
||||
|
||||
return sprintf('{%s}%s',self::key,crypt($password,$salt));
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class Clear extends Base
|
||||
{
|
||||
public const key = '*clear*';
|
||||
|
||||
public function encode(string $password): string
|
||||
{
|
||||
return $password;
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class Crypt extends Base
|
||||
{
|
||||
public const key = 'CRYPT';
|
||||
protected const subkey = 'crypt';
|
||||
protected const salt = 2;
|
||||
private const identifier = '';
|
||||
|
||||
public static function subid(string $password): bool
|
||||
{
|
||||
return preg_match('/^[\da-f]{2}/',self::password($password));
|
||||
}
|
||||
|
||||
public function compare(string $source,string $compare): bool
|
||||
{
|
||||
return hash_equals($cp=self::password($source),crypt($compare,$cp));
|
||||
}
|
||||
|
||||
public function encode(string $password,string $salt=NULL): string
|
||||
{
|
||||
if (is_null($salt))
|
||||
$salt = sprintf('%s%s',self::identifier,random_salt(self::salt));
|
||||
|
||||
return sprintf('{%s}%s',self::key,crypt($password,$salt));
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class ExtDes extends Base
|
||||
{
|
||||
public const key = 'CRYPT';
|
||||
protected const subkey = 'ext_des';
|
||||
protected const salt = 8;
|
||||
private const identifier = '_';
|
||||
|
||||
public static function subid(string $password): bool
|
||||
{
|
||||
return str_starts_with(self::password($password),self::identifier);
|
||||
}
|
||||
|
||||
public function compare(string $source,string $compare): bool
|
||||
{
|
||||
return hash_equals($cp=self::password($source),crypt($compare,$cp));
|
||||
}
|
||||
|
||||
public function encode(string $password,string $salt=NULL): string
|
||||
{
|
||||
if (is_null($salt))
|
||||
$salt = sprintf('%s%s',self::identifier,random_salt(self::salt));
|
||||
|
||||
return sprintf('{%s}%s',self::key,crypt($password,$salt));
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class MD5 extends Base
|
||||
{
|
||||
public const key = 'MD5';
|
||||
|
||||
public function encode(string $password): string
|
||||
{
|
||||
return sprintf('{%s}%s',self::key,base64_encode(hash('md5',$password,true)));
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class MD5crypt extends Base
|
||||
{
|
||||
public const key = 'CRYPT';
|
||||
protected const subkey = 'md5crypt';
|
||||
protected const salt = 9;
|
||||
private const identifier = '$1$';
|
||||
|
||||
public static function subid(string $password): bool
|
||||
{
|
||||
return str_starts_with(self::password($password),self::identifier);
|
||||
}
|
||||
|
||||
public function compare(string $source,string $compare): bool
|
||||
{
|
||||
return hash_equals($cp=self::password($source),crypt($compare,$cp));
|
||||
}
|
||||
|
||||
public function encode(string $password,string $salt=NULL): string
|
||||
{
|
||||
if (is_null($salt))
|
||||
$salt = sprintf('%s$%s',self::identifier,random_salt(self::salt));
|
||||
|
||||
return sprintf('{%s}%s',self::key,crypt($password,$salt));
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class SASL extends Base
|
||||
{
|
||||
public const key = 'SASL';
|
||||
|
||||
public function encode(string $password): string
|
||||
{
|
||||
if (! str_contains($password,'@'))
|
||||
return '';
|
||||
|
||||
// Ensure our id is lowercase, and realm is uppercase
|
||||
list($id,$realm) = explode('@',$password);
|
||||
|
||||
return sprintf('{%s}%s@%s',self::key,strtolower($id),strtoupper($realm));
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class SHA extends Base
|
||||
{
|
||||
public const key = 'SHA';
|
||||
|
||||
public function encode(string $password): string
|
||||
{
|
||||
return sprintf('{%s}%s',self::key,base64_encode(hash('sha1',$password,true)));
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class SHA256 extends Base
|
||||
{
|
||||
public const key = 'SHA256';
|
||||
|
||||
public function encode(string $password): string
|
||||
{
|
||||
return sprintf('{%s}%s',self::key,base64_encode(hash('sha256',$password,true)));
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class SHA256crypt extends Base
|
||||
{
|
||||
public const key = 'CRYPT';
|
||||
protected const subkey = 'sha256crypt';
|
||||
protected const salt = 5;
|
||||
private const identifier = '$5$';
|
||||
|
||||
public static function subid(string $password): bool
|
||||
{
|
||||
return str_starts_with(self::password($password),self::identifier);
|
||||
}
|
||||
|
||||
public function compare(string $source,string $compare): bool
|
||||
{
|
||||
return hash_equals($cp=self::password($source),crypt($compare,$cp));
|
||||
}
|
||||
|
||||
public function encode(string $password,string $salt=NULL): string
|
||||
{
|
||||
if (is_null($salt))
|
||||
$salt = sprintf('%s%s',self::identifier,random_salt(self::salt));
|
||||
|
||||
return sprintf('{%s}%s',self::key,crypt($password,$salt));
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class SHA384 extends Base
|
||||
{
|
||||
public const key = 'SHA384';
|
||||
|
||||
public function encode(string $password): string
|
||||
{
|
||||
return sprintf('{%s}%s',self::key,base64_encode(hash('sha384',$password,true)));
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class SHA512 extends Base
|
||||
{
|
||||
public const key = 'SHA512';
|
||||
|
||||
public function encode(string $password): string
|
||||
{
|
||||
return sprintf('{%s}%s',self::key,base64_encode(hash('sha512',$password,true)));
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class SHA512crypt extends Base
|
||||
{
|
||||
public const key = 'CRYPT';
|
||||
protected const subkey = 'sha512crypt';
|
||||
protected const salt = 2;
|
||||
private const identifier = '$6$';
|
||||
|
||||
public static function subid(string $password): bool
|
||||
{
|
||||
return str_starts_with(self::password($password),self::identifier);
|
||||
}
|
||||
|
||||
public function compare(string $source,string $compare): bool
|
||||
{
|
||||
return hash_equals($cp=self::password($source),crypt($compare,$cp));
|
||||
}
|
||||
|
||||
public function encode(string $password,string $salt=NULL): string
|
||||
{
|
||||
if (is_null($salt))
|
||||
$salt = sprintf('%s%s',self::identifier,random_salt(self::salt));
|
||||
|
||||
return sprintf('{%s}%s',self::key,crypt($password,$salt));
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class SMD5 extends Base
|
||||
{
|
||||
public const key = 'SMD5';
|
||||
protected const salt = 8;
|
||||
|
||||
public function compare(string $source,string $compare): bool
|
||||
{
|
||||
return $source === $this->encode($compare,$this->salted_salt($source));
|
||||
}
|
||||
|
||||
public function encode(string $password,string $salt=NULL): string
|
||||
{
|
||||
if (is_null($salt))
|
||||
$salt = hex2bin(random_salt(self::salt));
|
||||
|
||||
return sprintf('{%s}%s',self::key,$this->salted_hash($password,'md5',self::salt,$salt));
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class SSHA extends Base
|
||||
{
|
||||
public const key = 'SSHA';
|
||||
protected const salt = 8;
|
||||
|
||||
public function compare(string $source,string $compare): bool
|
||||
{
|
||||
return $source === $this->encode($compare,$this->salted_salt($source));
|
||||
}
|
||||
|
||||
public function encode(string $password,string $salt=NULL): string
|
||||
{
|
||||
return sprintf('{%s}%s',self::key,$this->salted_hash($password,'sha1',self::salt,$salt));
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class SSHA256 extends Base
|
||||
{
|
||||
public const key = 'SSHA256';
|
||||
protected const salt = 8;
|
||||
|
||||
public function compare(string $source,string $compare): bool
|
||||
{
|
||||
return $source === $this->encode($compare,$this->salted_salt($source));
|
||||
}
|
||||
|
||||
public function encode(string $password,string $salt=NULL): string
|
||||
{
|
||||
return sprintf('{%s}%s',self::key,$this->salted_hash($password,'sha256',self::salt,$salt));
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class SSHA384 extends Base
|
||||
{
|
||||
public const key = 'SSHA384';
|
||||
protected const salt = 8;
|
||||
|
||||
public function compare(string $source,string $compare): bool
|
||||
{
|
||||
return $source === $this->encode($compare,$this->salted_salt($source));
|
||||
}
|
||||
|
||||
public function encode(string $password,string $salt=NULL): string
|
||||
{
|
||||
return sprintf('{%s}%s',self::key,$this->salted_hash($password,'sha384',self::salt,$salt));
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Password;
|
||||
|
||||
final class SSHA512 extends Base
|
||||
{
|
||||
public const key = 'SSHA512';
|
||||
protected const salt = 8;
|
||||
|
||||
public function compare(string $source,string $compare): bool
|
||||
{
|
||||
return $source === $this->encode($compare,$this->salted_salt($source));
|
||||
}
|
||||
|
||||
public function encode(string $password,string $salt=NULL): string
|
||||
{
|
||||
return sprintf('{%s}%s',self::key,$this->salted_hash($password,'sha512',self::salt,$salt));
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
<?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;
|
||||
}
|
@ -1,233 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
@ -1,615 +0,0 @@
|
||||
<?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) {
|
||||
if (static::DEBUG_VERBOSE)
|
||||
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 .= ($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 .= ($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);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case NAME returned (%s)',$this->name),['aliases'=>$this->aliases]);
|
||||
break;
|
||||
|
||||
case 'DESC':
|
||||
do {
|
||||
$this->description .= ($this->description ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||
|
||||
$this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case DESC returned (%s)',$this->description));
|
||||
break;
|
||||
|
||||
case 'OBSOLETE':
|
||||
$this->is_obsolete = TRUE;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case OBSOLETE returned (%s)',$this->is_obsolete));
|
||||
break;
|
||||
|
||||
case 'SUP':
|
||||
$i++;
|
||||
$this->sup_attribute = preg_replace("/^\'(.*)\'$/",'$1',$strings[$i]);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case SUP returned (%s)',$this->sup_attribute));
|
||||
break;
|
||||
|
||||
case 'EQUALITY':
|
||||
$this->equality = $strings[++$i];
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case EQUALITY returned (%s)',$this->equality));
|
||||
break;
|
||||
|
||||
case 'ORDERING':
|
||||
$this->ordering = $strings[++$i];
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case ORDERING returned (%s)',$this->ordering));
|
||||
break;
|
||||
|
||||
case 'SUBSTR':
|
||||
$this->sub_str_rule = $strings[++$i];
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
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);
|
||||
if (static::DEBUG_VERBOSE)
|
||||
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);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
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;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case SINGLE-VALUE returned (%s)',$this->is_single_value));
|
||||
break;
|
||||
|
||||
case 'COLLECTIVE':
|
||||
$this->is_collective = TRUE;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case COLLECTIVE returned (%s)',$this->is_collective));
|
||||
break;
|
||||
|
||||
case 'NO-USER-MODIFICATION':
|
||||
$this->is_no_user_modification = TRUE;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case NO-USER-MODIFICATION returned (%s)',$this->is_no_user_modification));
|
||||
break;
|
||||
|
||||
case 'USAGE':
|
||||
$this->usage = $strings[++$i];
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case USAGE returned (%s)',$this->usage));
|
||||
break;
|
||||
|
||||
// @note currently not captured
|
||||
case 'X-ORDERED':
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::error(sprintf('- Case X-ORDERED returned (%s)',$strings[++$i]));
|
||||
break;
|
||||
|
||||
// @note currently not captured
|
||||
case 'X-ORIGIN':
|
||||
$value = '';
|
||||
|
||||
do {
|
||||
$value .= ($value ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::error(sprintf('- Case X-ORIGIN returned (%s)',$value));
|
||||
break;
|
||||
|
||||
default:
|
||||
if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) {
|
||||
$this->oid = $strings[$i];
|
||||
if (static::DEBUG_VERBOSE)
|
||||
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->contains($name))
|
||||
$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->contains($name))
|
||||
$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;
|
||||
}
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
<?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 {
|
||||
protected const DEBUG_VERBOSE = FALSE;
|
||||
|
||||
// Record the LDAP String
|
||||
private string $line;
|
||||
|
||||
// The schema item's name.
|
||||
protected string $name = '';
|
||||
|
||||
// The OID of this schema item.
|
||||
protected string $oid;
|
||||
|
||||
# The description of this schema item.
|
||||
protected string $description = '';
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
<?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->contains($name))
|
||||
$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;
|
||||
}
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
@ -1,538 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Schema;
|
||||
|
||||
use Config;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use App\Classes\LDAP\Server;
|
||||
use App\Exceptions\InvalidUsage;
|
||||
|
||||
/**
|
||||
* Represents an LDAP Schema objectClass
|
||||
*
|
||||
* @package phpLDAPadmin
|
||||
* @subpackage Schema
|
||||
*/
|
||||
final class ObjectClass extends Base
|
||||
{
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* 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 )
|
||||
*
|
||||
* @param string $line Schema Line
|
||||
* @param Server $server
|
||||
* @todo Change $server to $connection, no need to store the server object here
|
||||
*/
|
||||
public function __construct(string $line,Server $server)
|
||||
{
|
||||
parent::__construct($line);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('Parsing ObjectClass [%s]',$line));
|
||||
|
||||
$strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
// Init
|
||||
$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);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
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);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case DESC returned (%s)',$this->description));
|
||||
break;
|
||||
|
||||
case 'OBSOLETE':
|
||||
$this->is_obsolete = TRUE;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
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]));
|
||||
}
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case SUP returned (%s)',$this->sup_classes->join(',')));
|
||||
break;
|
||||
|
||||
case 'ABSTRACT':
|
||||
$this->type = Server::OC_ABSTRACT;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case ABSTRACT returned (%s)',$this->type));
|
||||
break;
|
||||
|
||||
case 'STRUCTURAL':
|
||||
$this->type = Server::OC_STRUCTURAL;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case STRUCTURAL returned (%s)',$this->type));
|
||||
break;
|
||||
|
||||
case 'AUXILIARY':
|
||||
$this->type = Server::OC_AUXILIARY;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case AUXILIARY returned (%s)',$this->type));
|
||||
break;
|
||||
|
||||
case 'MUST':
|
||||
$attrs = collect();
|
||||
|
||||
$i = $this->parseList(++$i,$strings,$attrs);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
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);
|
||||
}
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
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);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
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);
|
||||
}
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
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];
|
||||
if (static::DEBUG_VERBOSE)
|
||||
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
|
||||
{
|
||||
return match ($key) {
|
||||
'attributes' => $this->getAllAttrs(),
|
||||
'sup' => $this->sup_classes,
|
||||
'type_name' => match ($this->type) {
|
||||
Server::OC_STRUCTURAL => 'Structural',
|
||||
Server::OC_ABSTRACT => 'Abstract',
|
||||
Server::OC_AUXILIARY => 'Auxiliary',
|
||||
default => throw new InvalidUsage('Unknown ObjectClass Type: ' . $this->type),
|
||||
},
|
||||
default => parent::__get($key),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of attributes that this objectClass provides
|
||||
*
|
||||
* @return Collection
|
||||
* @throws InvalidUsage
|
||||
*/
|
||||
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->contains($name))
|
||||
$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(fn($item)=>strtolower($item->name.$item->source));
|
||||
|
||||
$attrs = $this->may_attrs;
|
||||
|
||||
foreach ($this->getParents() as $object_class)
|
||||
$attrs = $attrs->merge($object_class->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)
|
||||
$attrs = $attrs->merge($object_class->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) {
|
||||
$oc = Config::get('server')
|
||||
->schema('objectclasses',$object_class);
|
||||
|
||||
if ($oc) {
|
||||
$result->push($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)
|
||||
if ($object_class->isStructural() && in_array_ignore_case($this->name,$object_class->getParents()->pluck('name')))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
public function isStructural(): bool
|
||||
{
|
||||
return $this->type === Server::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;
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,556 +0,0 @@
|
||||
<?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
|
||||
{
|
||||
// Connection information used for these object and children
|
||||
private ?string $connection;
|
||||
|
||||
// This servers schema objectclasses
|
||||
private Collection $attributetypes;
|
||||
private Collection $ldapsyntaxes;
|
||||
private Collection $matchingrules;
|
||||
private Collection $matchingruleuse;
|
||||
private Collection $objectclasses;
|
||||
|
||||
/* ObjectClass Types */
|
||||
public const OC_STRUCTURAL = 0x01;
|
||||
public const OC_ABSTRACT = 0x02;
|
||||
public const OC_AUXILIARY = 0x03;
|
||||
|
||||
public function __construct(string $connection=NULL)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
public function __get(string $key): mixed
|
||||
{
|
||||
return match($key) {
|
||||
'attributetypes' => $this->attributetypes,
|
||||
'connection' => $this->connection,
|
||||
'ldapsyntaxes' => $this->ldapsyntaxes,
|
||||
'matchingrules' => $this->matchingrules,
|
||||
'objectclasses' => $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(string $connection='default',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() ?: $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
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(string $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(string $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)
|
||||
->on($this->connection)
|
||||
->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)
|
||||
->on($this->connection)
|
||||
->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|LDAPSyntax|Base|NULL
|
||||
{
|
||||
// Ensure our item to fetch is lower case
|
||||
$item = strtolower($item);
|
||||
if ($key)
|
||||
$key = strtolower($key);
|
||||
|
||||
$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;
|
||||
|
||||
// This error message is not localized as only developers should ever see it
|
||||
default:
|
||||
throw new InvalidUsage('Invalid request to fetch schema: '.$item);
|
||||
}
|
||||
|
||||
// Try to get the schema DN from the specified entry.
|
||||
$schema_dn = $this->schemaDN('default');
|
||||
$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->contains($parent))
|
||||
$this->objectclasses[$parent]->addChildObjectClass($o->name);
|
||||
}
|
||||
|
||||
return $this->objectclasses;
|
||||
|
||||
// Shouldnt get here
|
||||
default:
|
||||
throw new InvalidUsage('Invalid request to fetch schema: '.$item);
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Import;
|
||||
|
||||
use Exception;
|
||||
|
||||
class AttributeException extends Exception {}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Import;
|
||||
|
||||
use Exception;
|
||||
|
||||
class GeneralException extends Exception {}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Import;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ObjectExistsException extends Exception {}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Import;
|
||||
|
||||
use Exception;
|
||||
|
||||
class VersionException extends Exception {}
|
@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidUsage extends Exception
|
||||
{
|
||||
//
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
<?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;
|
||||
|
||||
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 = '/';
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller extends \Illuminate\Routing\Controller
|
||||
{
|
||||
//
|
||||
}
|
@ -1,343 +0,0 @@
|
||||
<?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();
|
||||
}
|
||||
|
||||
public function entry_password_check(Request $request)
|
||||
{
|
||||
$dn = Crypt::decryptString($request->dn);
|
||||
$o = config('server')->fetch($dn);
|
||||
|
||||
$password = $o->getObject('userpassword');
|
||||
|
||||
$result = collect();
|
||||
foreach ($password as $key => $value) {
|
||||
$hash = $password->hash($value);
|
||||
$compare = Arr::get($request->password,$key);
|
||||
//Log::debug(sprintf('comparing [%s] with [%s] type [%s]',$value,$compare,$hash::id()),['object'=>$hash]);
|
||||
|
||||
$result->push((($compare !== NULL) && $hash->compare($value,$compare)) ? 'OK' :'FAIL');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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','userpassword_hash','userpassword']) as $key => $value)
|
||||
$o->{$key} = array_filter($value,fn($item)=>! is_null($item));
|
||||
|
||||
// We need to process and encrypt the password
|
||||
$passwords = [];
|
||||
foreach ($request->userpassword as $key => $value) {
|
||||
// If the password is still the MD5 of the old password, then it hasnt changed
|
||||
if (($old=Arr::get($o->userpassword,$key)) && ($value === md5($old))) {
|
||||
array_push($passwords,$old);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value) {
|
||||
$type = Arr::get($request->userpassword_hash,$key);
|
||||
array_push($passwords,Attribute\Password::hash_id($type)->encode($value));
|
||||
}
|
||||
}
|
||||
$o->userpassword = $passwords;
|
||||
|
||||
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('updated',collect($dirty)->map(fn($key,$item)=>$o->getObject($item)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Config;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use App\Classes\LDAP\Server;
|
||||
use App\Ldap\User;
|
||||
|
||||
/**
|
||||
* This sets up our application session with any required values, ultimately for cache optimisation reasons
|
||||
*/
|
||||
class ApplicationSession
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request,Closure $next): mixed
|
||||
{
|
||||
Config::set('server',new Server);
|
||||
|
||||
view()->share('user', auth()->user() ?: new User);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Config;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Http\Request;
|
||||
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 Request $request
|
||||
* @param Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): mixed
|
||||
{
|
||||
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(): void
|
||||
{
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
<?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\Log;
|
||||
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
|
||||
* @throws \LdapRecord\Configuration\ConfigurationException
|
||||
*/
|
||||
public function handle(Request $request,Closure $next): mixed
|
||||
{
|
||||
$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
|
||||
*/
|
||||
|
||||
// @todo it seems sometimes we have cookies that show the logged in user, but Auth::user() has expired?
|
||||
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.',['key'=>$key,'user'=>Cookie::get('username_encrypt')]);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
<?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',
|
||||
];
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
<?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);
|
||||
};
|
||||
}
|
||||
}
|
@ -1,420 +0,0 @@
|
||||
<?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();
|
||||
|
||||
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(fn($item)=>$item->values->toArray())
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the new and old values for a given key are equivalent.
|
||||
*
|
||||
* @todo This function barfs on language tags, eg: key = givenname;lang-ja
|
||||
*/
|
||||
protected function originalIsEquivalent(string $key): bool
|
||||
{
|
||||
$key = $this->normalizeAttributeKey($key);
|
||||
|
||||
// @todo Silently ignore keys of language tags - we should work with them
|
||||
if (str_contains($key,';'))
|
||||
return TRUE;
|
||||
|
||||
return ((! array_key_exists($key,$this->original)) && (! $this->objects->has($key)))
|
||||
|| (! $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();
|
||||
|
||||
} else {
|
||||
$this->objects = collect();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/* ATTRIBUTES */
|
||||
|
||||
/**
|
||||
* Return a key to use for sorting
|
||||
*
|
||||
* @return string
|
||||
* @todo This should be the DN in reverse order
|
||||
*/
|
||||
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')->contains($key))
|
||||
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
|
||||
*/
|
||||
public function getAttributesAsObjects(): Collection
|
||||
{
|
||||
$result = collect();
|
||||
|
||||
foreach ($this->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('pla.attr_display_order',[]))->map(fn($item)=>strtolower($item));
|
||||
|
||||
// Order the attributes
|
||||
return $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 pla.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 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(fn($item)=>$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();
|
||||
|
||||
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(fn($item)=>! $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;
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
<?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('pla.login.objectclass')) {
|
||||
return count(array_intersect($user->objectclass,$x));
|
||||
|
||||
// Otherwise allow the user to login
|
||||
} else {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Ldap;
|
||||
|
||||
use Laravel\Sanctum\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';
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
<?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.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
// 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.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$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();
|
||||
});
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
<?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
|
||||
{
|
||||
foreach ($this->values->diff($this->oldValues) as $key => $value)
|
||||
if (md5(Arr::get($this->oldValues,$key)) !== $value)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
<?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;
|
||||
public ?string $na;
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(?LDAPAttribute $o,bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,string $na=NULL)
|
||||
{
|
||||
$this->o = $o;
|
||||
$this->edit = $edit;
|
||||
$this->old = $old;
|
||||
$this->new = $new;
|
||||
$this->na = $na;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\View|\Closure|string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return $this->o
|
||||
? $this->o
|
||||
->render($this->edit,$this->old,$this->new)
|
||||
: $this->na;
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
function login_attr_description(): string
|
||||
{
|
||||
return Arr::get(config('pla.login.attr'),login_attr_name());
|
||||
}
|
||||
|
||||
function login_attr_name(): string
|
||||
{
|
||||
return key(config('pla.login.attr'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to generate a random salt for crypt-style passwords. Salt strings are used
|
||||
* to make pre-built hash cracking dictionaries difficult to use as the hash algorithm uses
|
||||
* not only the user's password but also a randomly generated string. The string is
|
||||
* stored as the first N characters of the hash for reference of hashing algorithms later.
|
||||
*
|
||||
* @param int $length The length of the salt string to generate.
|
||||
* @return string The generated salt string.
|
||||
* @throws \Random\RandomException
|
||||
*/
|
||||
function random_salt(int $length): string
|
||||
{
|
||||
$str = bin2hex(random_bytes(ceil($length/2)));
|
||||
if ($length%2 === 1)
|
||||
return substr($str,0,-1);
|
||||
|
||||
return $str;
|
||||
}
|
150
application/bootstrap.php
Normal file
150
application/bootstrap.php
Normal file
@ -0,0 +1,150 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
// -- Environment setup --------------------------------------------------------
|
||||
|
||||
// Load the core Kohana class
|
||||
require SYSPATH.'classes/Kohana/Core'.EXT;
|
||||
|
||||
if (is_file(APPPATH.'classes/Kohana'.EXT))
|
||||
{
|
||||
// Application extends the core
|
||||
require APPPATH.'classes/Kohana'.EXT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load empty core extension
|
||||
require SYSPATH.'classes/Kohana'.EXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default time zone.
|
||||
*
|
||||
* @link http://kohanaframework.org/guide/using.configuration
|
||||
* @link http://www.php.net/manual/timezones
|
||||
*/
|
||||
date_default_timezone_set('Australia/Melbourne');
|
||||
|
||||
/**
|
||||
* Set the default locale.
|
||||
*
|
||||
* @link http://kohanaframework.org/guide/using.configuration
|
||||
* @link http://www.php.net/manual/function.setlocale
|
||||
*/
|
||||
setlocale(LC_ALL, 'en_US.utf-8');
|
||||
|
||||
/**
|
||||
* Enable the Kohana auto-loader.
|
||||
*
|
||||
* @link http://kohanaframework.org/guide/using.autoloading
|
||||
* @link http://www.php.net/manual/function.spl-autoload-register
|
||||
*/
|
||||
spl_autoload_register(array('Kohana', 'auto_load'));
|
||||
|
||||
/**
|
||||
* Optionally, you can enable a compatibility auto-loader for use with
|
||||
* older modules that have not been updated for PSR-0.
|
||||
*
|
||||
* It is recommended to not enable this unless absolutely necessary.
|
||||
*/
|
||||
//spl_autoload_register(array('Kohana', 'auto_load_lowercase'));
|
||||
|
||||
/**
|
||||
* Enable the Kohana auto-loader for unserialization.
|
||||
*
|
||||
* @link http://www.php.net/manual/function.spl-autoload-call
|
||||
* @link http://www.php.net/manual/var.configuration#unserialize-callback-func
|
||||
*/
|
||||
ini_set('unserialize_callback_func', 'spl_autoload_call');
|
||||
|
||||
// -- Configuration and initialization -----------------------------------------
|
||||
|
||||
/**
|
||||
* Set the default language
|
||||
*/
|
||||
I18n::lang('en-us');
|
||||
|
||||
/**
|
||||
* Set Kohana::$environment if a 'KOHANA_ENV' environment variable has been supplied.
|
||||
*
|
||||
* Note: If you supply an invalid environment name, a PHP warning will be thrown
|
||||
* saying "Couldn't find constant Kohana::<INVALID_ENV_NAME>"
|
||||
*/
|
||||
if (isset($_SERVER['KOHANA_ENV']))
|
||||
{
|
||||
Kohana::$environment = constant('Kohana::'.strtoupper($_SERVER['KOHANA_ENV']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Kohana, setting the default options.
|
||||
*
|
||||
* The following options are available:
|
||||
*
|
||||
* - string base_url path, and optionally domain, of your application NULL
|
||||
* - string index_file name of your index file, usually "index.php" index.php
|
||||
* - string charset internal character set used for input and output utf-8
|
||||
* - string cache_dir set the internal cache directory APPPATH/cache
|
||||
* - integer cache_life lifetime, in seconds, of items cached 60
|
||||
* - boolean errors enable or disable error handling TRUE
|
||||
* - boolean profile enable or disable internal profiling TRUE
|
||||
* - boolean caching enable or disable internal caching FALSE
|
||||
* - boolean expose set the X-Powered-By header FALSE
|
||||
*/
|
||||
Kohana::init(array(
|
||||
'base_url' => '/pla',
|
||||
'caching' => TRUE,
|
||||
'index_file' => '',
|
||||
));
|
||||
|
||||
/**
|
||||
* Attach the file write to logging. Multiple writers are supported.
|
||||
*/
|
||||
Kohana::$log->attach(new Log_File(APPPATH.'logs'));
|
||||
|
||||
/**
|
||||
* Attach a file reader to config. Multiple readers are supported.
|
||||
*/
|
||||
Kohana::$config->attach(new Config_File);
|
||||
|
||||
/**
|
||||
* Enable modules. Modules are referenced by a relative or absolute path.
|
||||
*/
|
||||
Kohana::modules(array(
|
||||
'lnapp' => MODPATH.'lnApp',
|
||||
'auth' => SMDPATH.'auth', // Basic authentication
|
||||
'cache' => SMDPATH.'cache', // Caching with multiple backends
|
||||
// 'codebench' => SMDPATH.'codebench', // Benchmarking tool
|
||||
'database' => SMDPATH.'database', // Database access
|
||||
// 'image' => SMDPATH.'image', // Image manipulation
|
||||
'minion' => SMDPATH.'minion', // CLI Tasks
|
||||
'orm' => SMDPATH.'orm', // Object Relationship Mapping
|
||||
'pagination' => SMDPATH.'pagination', // Kohana Pagination module for Kohana 3 PHP Framework
|
||||
// 'unittest' => SMDPATH.'unittest', // Unit testing
|
||||
// 'userguide' => SMDPATH.'userguide', // User guide and API documentation
|
||||
'xml' => SMDPATH.'xml', // XML module for Kohana 3 PHP Framework
|
||||
));
|
||||
|
||||
// Static file serving (CSS, JS, images)
|
||||
Route::set('default/media', 'media(/<file>)', array('file' => '.+'))
|
||||
->defaults(array(
|
||||
'controller' => 'media',
|
||||
'action' => 'get',
|
||||
));
|
||||
|
||||
/**
|
||||
* Set the routes. Each route must have a minimum of a name, a URI and a set of
|
||||
* defaults for the URI.
|
||||
*/
|
||||
Route::set('default', '(<controller>(/<action>(/<id>)))', array('id'=>'[a-zA-Z0-9_.-]+'))
|
||||
->defaults(array(
|
||||
'controller' => 'welcome',
|
||||
'action' => 'index',
|
||||
));
|
||||
|
||||
/**
|
||||
* If APC is enabled, and we need to clear the cache
|
||||
*/
|
||||
if (file_exists(APPPATH.'cache/CLEAR_APC_CACHE') AND function_exists('apc_clear_cache') AND (PHP_SAPI !== 'cli')) {
|
||||
if (! apc_clear_cache() OR ! unlink(APPPATH.'cache/CLEAR_APC_CACHE'))
|
||||
throw new Kohana_Exception('Unable to clear the APC cache.');
|
||||
}
|
||||
?>
|
4
application/classes/Auth/LDAP.php
Normal file
4
application/classes/Auth/LDAP.php
Normal file
@ -0,0 +1,4 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
class Auth_LDAP extends PLA_Auth_Ldap {}
|
||||
?>
|
4
application/classes/Block.php
Normal file
4
application/classes/Block.php
Normal file
@ -0,0 +1,4 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
class Block extends PLA_Block {}
|
||||
?>
|
107
application/classes/Config.php
Normal file
107
application/classes/Config.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
/**
|
||||
* This class extends the core Kohana class by adding some core application
|
||||
* specific functions, and configuration.
|
||||
*
|
||||
* @package PLA
|
||||
* @subpackage Config
|
||||
* @category Helpers
|
||||
* @author Deon George
|
||||
* @copyright (c) phpLDAPadmin Development Team
|
||||
* @license http://dev.phpldapadmin.org/license.html
|
||||
*/
|
||||
class Config extends Kohana_Config {
|
||||
// Our default logo, if there is no site logo
|
||||
public static $logo = 'img/logo-small.png';
|
||||
|
||||
/**
|
||||
* Some early initialisation
|
||||
*
|
||||
* At this point, KH hasnt been fully initialised either, so we cant rely on
|
||||
* too many KH functions yet.
|
||||
*
|
||||
* NOTE: Kohana doesnt provide a parent construct for the Kohana_Config class.
|
||||
*/
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singleton instance of Config.
|
||||
*
|
||||
* $config = Config::instance();
|
||||
*
|
||||
* @return Config
|
||||
* @compat Restore KH 3.1 functionality
|
||||
*/
|
||||
public static function instance() {
|
||||
if (Config::$_instance === NULL)
|
||||
// Create a new instance
|
||||
Config::$_instance = new Config;
|
||||
|
||||
return Config::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return our caching mechanism
|
||||
*/
|
||||
public static function cachetype() {
|
||||
return is_null(Kohana::$config->load('config')->cache_type) ? 'file' : Kohana::$config->load('config')->cache_type;
|
||||
}
|
||||
|
||||
public static function copywrite() {
|
||||
return '(c) phpLDAPadmin Development Team';
|
||||
}
|
||||
|
||||
public static function country() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public static function language() {
|
||||
// @todo To implement
|
||||
return 'auto';
|
||||
}
|
||||
|
||||
/**
|
||||
* The URI to show for the login prompt.
|
||||
* Normally if the user is logged in, we can replace it with something else
|
||||
*/
|
||||
public static function login_uri() {
|
||||
return ($ao = Auth::instance()->get_user() AND is_object($ao)) ? $ao->name() : HTML::anchor('login',_('Login'));
|
||||
}
|
||||
|
||||
public static function logo() {
|
||||
return HTML::image(static::logo_uri(),array('class'=>'headlogo','alt'=>_('Logo')));
|
||||
}
|
||||
|
||||
public static function logo_uri($protocol=NULL) {
|
||||
list ($path,$suffix) = explode('.',static::$logo);
|
||||
|
||||
return URL::site(Route::get('default/media')->uri(array('file'=>$path.'.'.$suffix),array('alt'=>static::sitename())),$protocol);
|
||||
}
|
||||
|
||||
public static function siteid($format=FALSE) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Work out our site mode (dev,test,prod)
|
||||
*/
|
||||
public static function sitemode() {
|
||||
return Kohana::$config->load('config.site')->mode;
|
||||
}
|
||||
|
||||
public static function sitename() {
|
||||
return 'phpLDAPadmin';
|
||||
}
|
||||
|
||||
public static function theme() {
|
||||
return Kohana::$config->load('config')->theme;
|
||||
}
|
||||
|
||||
public static function version() {
|
||||
// @todo Work out our versioning
|
||||
return 'TBA';
|
||||
}
|
||||
}
|
||||
?>
|
4
application/classes/Controller/Template.php
Normal file
4
application/classes/Controller/Template.php
Normal file
@ -0,0 +1,4 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
class Controller_Template extends PLA_Controller_Template {}
|
||||
?>
|
47
application/classes/Controller/TemplateDefault.php
Normal file
47
application/classes/Controller/TemplateDefault.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
/**
|
||||
* This class provides the default template controller for rendering pages.
|
||||
*
|
||||
* @package OSB
|
||||
* @subpackage Page/Template
|
||||
* @category Controllers
|
||||
* @author Deon George
|
||||
* @copyright (c) phpLDAPadmin Development Team
|
||||
* @license http://dev.phpldapadmin.org/license.html
|
||||
*/
|
||||
class Controller_TemplateDefault extends lnApp_Controller_TemplateDefault {
|
||||
public function __construct(Request $request, Response $response) {
|
||||
if (Config::theme())
|
||||
$this->template = Config::theme().'/page';
|
||||
|
||||
return parent::__construct($request,$response);
|
||||
}
|
||||
|
||||
protected function _headimages() {
|
||||
// This is where we should be able to change our country
|
||||
// @todo To implement
|
||||
$co = Config::country();
|
||||
/*
|
||||
HeadImages::add(array(
|
||||
'img'=>sprintf('img/country/%s.png',strtolower($co->two_code)),
|
||||
'attrs'=>array('onclick'=>"target='_blank';",'title'=>$co->display('name'))
|
||||
));
|
||||
*/
|
||||
|
||||
return HeadImages::factory();
|
||||
}
|
||||
|
||||
protected function _left() {
|
||||
if ($this->template->left)
|
||||
return $this->template->left;
|
||||
|
||||
elseif (Auth::instance()->logged_in(NULL,get_class($this).'|'.__METHOD__))
|
||||
return Controller_Tree::js();
|
||||
}
|
||||
|
||||
protected function _right() {
|
||||
return empty($this->template->right) ? '' : $this->template->right;
|
||||
}
|
||||
}
|
||||
?>
|
8
application/classes/Controller/Welcome.php
Normal file
8
application/classes/Controller/Welcome.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Controller_Welcome extends Controller_Template {
|
||||
|
||||
public function action_index() {
|
||||
$this->response->body('hello, world!');
|
||||
}
|
||||
} // End Welcome
|
16
application/classes/Cookie.php
Normal file
16
application/classes/Cookie.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
/**
|
||||
* This class overrides Kohana's Cookie
|
||||
*
|
||||
* @package PLA/Modifications
|
||||
* @category Classes
|
||||
* @category Helpers
|
||||
* @author Deon George
|
||||
* @copyright (c) 2010 Deon George
|
||||
* @license http://dev.phpldapadmin.org/license.html
|
||||
*/
|
||||
class Cookie extends Kohana_Cookie {
|
||||
public static $salt = 'PLA';
|
||||
}
|
||||
?>
|
4
application/classes/Database/LDAP.php
Normal file
4
application/classes/Database/LDAP.php
Normal file
@ -0,0 +1,4 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
class Database_LDAP extends PLA_Database_LDAP {}
|
||||
?>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user