Compare commits

..

9 Commits

Author SHA1 Message Date
Deon George
0941331781 Updated for new Gitlab 2015-09-29 16:48:35 +10:00
Deon George
7cd2251af3 Moved KH LDAP functions into KH module 2013-07-12 13:48:14 +10:00
Deon George
6fcc09f437 Local config 2013-06-25 16:47:42 +10:00
Deon George
15bd1fbb6e Basic layout and login functioning 2013-06-25 13:46:39 +10:00
Deon George
7e2cdac7e9 Basic layout and login functioning 2013-06-25 13:18:36 +10:00
Deon George
808766bb87 Initial application work 2013-06-25 13:18:27 +10:00
Deon George
cfd7b5db83 Added KH 3.3.0 2013-06-24 21:36:06 +10:00
Deon George
715f7efe9b Version Change 2013-03-19 15:55:32 +11:00
Nic Bernstein
5ba2cf67e9 SF Bug #1008 getContainerPath doesn't properly traverse to baseDN and back & #1009 - return_ldap_hash should not return container object in result set 2013-03-19 15:55:32 +11:00
938 changed files with 25076 additions and 45165 deletions

View File

@ -1,10 +0,0 @@
.dockerignore
.editorconfig
.env.testing
.git*
docker/
package.json
package-lock.json
phpunit.xml
webpack.mix.js
yarn.lock

View File

@ -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

View File

@ -1,51 +0,0 @@
APP_NAME=Laravel
APP_ENV=production
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
BROADCAST_DRIVER=log
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
LDAP_HOST=
LDAP_BASE_DN=
LDAP_USERNAME=
LDAP_PASSWORD=

View File

@ -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
View File

@ -1,5 +0,0 @@
* text=auto
*.css linguist-vendored
*.scss linguist-vendored
*.js linguist-vendored
CHANGELOG.md export-ignore

13
.github/FUNDING.yml vendored
View File

@ -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']

View File

@ -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.
**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.

23
.gitignore vendored
View File

@ -1,19 +1,4 @@
/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_*
devel

View File

@ -1,38 +0,0 @@
stages:
- test
- build
- build-manifest
variables:
DOCKER_HOST: tcp://docker:2375
VERSION: latest
VERSIONARCH: ${VERSION}-${ARCH}
# This folder is cached between builds
# http://docs.gitlab.com/ce/ci/yaml/README.html#cache
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- public/css/app.css
- public/js/app.js
- public/js/manifest.js
- public/js/vendor.js
- public/*/vendor/
- node_modules/
- vendor/
image: docker:latest
services:
- docker:dind
before_script:
- docker info && docker version
- echo "$CI_JOB_TOKEN" | docker login -u "$CI_REGISTRY_USER" "$CI_REGISTRY" --password-stdin
- if [ -n "$GITHUB_TOKEN" ]; then cat $GITHUB_TOKEN |base64 -d > auth.json; fi
include:
- .gitlab-test.yml
- .gitlab-docker-x86_64.yml
- .gitlab-docker-armv7l.yml
- .gitlab-docker-arm64.yml
- .gitlab-docker-manifest.yml

View File

@ -1,18 +0,0 @@
arm64:build:
variables:
ARCH: arm64
stage: build
script:
- if [ -f init ]; then chmod 500 init; fi
- echo -n ${CI_COMMIT_SHORT_SHA} > VERSION
- rm -rf node_modules database/seeds database/schema database/factories/*
- docker build -f docker/Dockerfile -t ${CI_REGISTRY_IMAGE}:${VERSIONARCH} .
- docker push ${CI_REGISTRY_IMAGE}:${VERSIONARCH}
tags:
- docker
- arm64
only:
- master

View File

@ -1,18 +0,0 @@
armv7l:build:
variables:
ARCH: armv7l
stage: build
script:
- if [ -f init ]; then chmod 500 init; fi
- echo -n ${CI_COMMIT_SHORT_SHA} > VERSION
- rm -rf node_modules database/seeds database/schema database/factories/*
- docker build -f docker/Dockerfile -t ${CI_REGISTRY_IMAGE}:${VERSIONARCH} .
- docker push ${CI_REGISTRY_IMAGE}:${VERSIONARCH}
tags:
- docker
- armv7l
only:
- master

View File

@ -1,10 +0,0 @@
x86_64:build-manifest:
stage: build-manifest
script:
- docker manifest create ${CI_REGISTRY_IMAGE}:${VERSION} ${CI_REGISTRY_IMAGE}:${VERSION}-x86_64 ${CI_REGISTRY_IMAGE}:${VERSION}-armv7l ${CI_REGISTRY_IMAGE}:${VERSION}-arm64
- docker manifest push --purge ${CI_REGISTRY_IMAGE}:${VERSION}
tags:
- docker
- x86_64
only:
- master

View File

@ -1,18 +0,0 @@
x86_64:build:
variables:
ARCH: x86_64
stage: build
script:
- if [ -f init ]; then chmod 500 init; fi
- echo -n ${CI_COMMIT_SHORT_SHA} > VERSION
- rm -rf node_modules database/seeds database/schema database/factories/*
- docker build -f docker/Dockerfile -t ${CI_REGISTRY_IMAGE}:${VERSIONARCH} .
- docker push ${CI_REGISTRY_IMAGE}:${VERSIONARCH}
tags:
- docker
- x86_64
only:
- master

View File

@ -1,52 +0,0 @@
test:
image: ${CI_REGISTRY}/leenooks/php:8.3-fpm-ldap-test
stage: test
# NOTE: This service is dependant on project file configuration, which is not there if the cache was deleted
# resulting in the testing to fail on the first run.
services:
- name: osixia/openldap:latest
alias: test_ldap
command: ["--loglevel","debug"]
variables:
LDAP_SEED_INTERNAL_LDIF_PATH: "${CI_PROJECT_DIR}/tests/server/openldap/data"
LDAP_SEED_INTERNAL_SCHEMA_PATH: "${CI_PROJECT_DIR}/tests/server/openldap/schema"
LDAP_BASE_DN: "dc=Test"
LDAP_DOMAIN: "Test"
LDAP_ADMIN_PASSWORD: test
#CI_DEBUG_SERVICES: "true"
tags:
- php
only:
- master
before_script:
- mv .env.testing .env
# Install npm and dependancies
- apk add --no-cache npm
- npm i
- npm run prod
# Install Composer and project dependencies.
- mkdir -p ${COMPOSER_HOME}
- if [ -n "$GITHUB_TOKEN" ]; then cat $GITHUB_TOKEN |base64 -d > ${COMPOSER_HOME}/auth.json; fi
- composer install
# Generate an application key. Re-cache.
- php artisan key:generate
script:
# Sleep if we need to, in case we want to jump in and see what is going on during the test
- if [ -n "$DEBUG_PAUSE" ]; then echo "Pausing for $DEBUG_PAUSE seconds, so you can jump into the containers"; sleep $DEBUG_PAUSE; fi
# run laravel tests
- XDEBUG_MODE=coverage php vendor/bin/phpunit --coverage-text --colors=never
# run frontend tests
# if you have any task for testing frontend
# set it in your package.json script
# comment this out if you don't have a frontend test
# npm test

9
.gitmodules vendored Normal file
View File

@ -0,0 +1,9 @@
[submodule "includes/kohana"]
path = includes/kohana
url = git@dev.leenooks.net:deon/lnkohana.git
[submodule "modules/ldap"]
path = modules/ldap
url = git@dev.leenooks.net:deon/lnldap.git
[submodule "modules/lnapp"]
path = modules/lnapp
url = git@dev.leenooks.net:deon/lnapp.git

21
.htaccess Normal file
View 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
View 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

View File

@ -1,50 +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.
## 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)

View File

@ -1 +1 @@
00000000
DEVEL-2.0

3
app/.gitignore vendored
View File

@ -1,3 +0,0 @@
q*
!public/
!.gitignore

View File

@ -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
{
}

View File

@ -1,33 +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 __construct(string $name,array $values)
{
parent::__construct($name,$values);
$this->internal = FALSE;
}
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
{
return view('components.attribute.binary.jpegphoto')
->with('o',$this)
->with('edit',$edit)
->with('old',$old)
->with('new',$new)
->with('f',new \finfo);
}
}

View File

@ -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);
}
}

View File

@ -1,12 +0,0 @@
<?php
namespace App\Classes\LDAP\Attribute;
use App\Classes\LDAP\Attribute;
/**
* Represents an GidNumber Attribute
*/
final class GidNumber extends Attribute
{
}

View File

@ -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);
}
}

View File

@ -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
{
}

View File

@ -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
{
}

View File

@ -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
{
}

View File

@ -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
{
}

View File

@ -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
{
}

View File

@ -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);
}
}

View File

@ -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
{
}

View File

@ -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->search($value) !== FALSE;
}
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
{
return view('components.attribute.objectclass')
->with('o',$this)
->with('edit',$edit)
->with('old',$old)
->with('new',$new);
}
}

View File

@ -1,25 +0,0 @@
<?php
namespace App\Classes\LDAP\Attribute;
use Illuminate\Contracts\View\View;
use App\Classes\LDAP\Attribute;
use App\Traits\MD5Updates;
/**
* Represents an attribute whose values are passwords
*/
final class Password extends Attribute
{
use MD5Updates;
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
{
return view('components.attribute.password')
->with('o',$this)
->with('edit',$edit)
->with('old',$old)
->with('new',$new);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -1,598 +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) {
Log::debug(sprintf('Parsing AttributeType [%s]',$line));
parent::__construct($line);
$strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE);
// Init
$this->children = collect();
$this->aliases = collect();
$this->used_in_object_classes = collect();
$this->required_by_object_classes = collect();
for ($i=0; $i < count($strings); $i++) {
switch ($strings[$i]) {
case '(':
case ')':
break;
case 'NAME':
// @note Some schema's return a (' instead of a ( '
if ($strings[$i+1] != '(' && ! preg_match('/^\(/',$strings[$i+1])) {
do {
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
} while (! preg_match("/\'$/s",$strings[$i]));
// This attribute has no aliases
//$this->aliases = collect();
} else {
$i++;
do {
// In case we came here becaues of a ('
if (preg_match('/^\(/',$strings[$i]))
$strings[$i] = preg_replace('/^\(/','',$strings[$i]);
else
$i++;
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
} while (! preg_match("/\'$/s",$strings[$i]));
// Add alias names for this attribute
while ($strings[++$i] != ')') {
$alias = $strings[$i];
$alias = preg_replace("/^\'(.*)\'$/",'$1',$alias);
$this->addAlias($alias);
}
}
$this->name = preg_replace("/^\'(.*)\'$/",'$1',$this->name);
Log::debug(sprintf('- Case NAME returned (%s)',$this->name),['aliases'=>$this->aliases]);
break;
case 'DESC':
do {
$this->description .= (strlen($this->description) ? ' ' : '').$strings[++$i];
} while (! preg_match("/\'$/s",$strings[$i]));
$this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description);
Log::debug(sprintf('- Case DESC returned (%s)',$this->description));
break;
case 'OBSOLETE':
$this->is_obsolete = TRUE;
Log::debug(sprintf('- Case OBSOLETE returned (%s)',$this->is_obsolete));
break;
case 'SUP':
$i++;
$this->sup_attribute = preg_replace("/^\'(.*)\'$/",'$1',$strings[$i]);
Log::debug(sprintf('- Case SUP returned (%s)',$this->sup_attribute));
break;
case 'EQUALITY':
$this->equality = $strings[++$i];
Log::debug(sprintf('- Case EQUALITY returned (%s)',$this->equality));
break;
case 'ORDERING':
$this->ordering = $strings[++$i];
Log::debug(sprintf('- Case ORDERING returned (%s)',$this->ordering));
break;
case 'SUBSTR':
$this->sub_str_rule = $strings[++$i];
Log::debug(sprintf('- Case SUBSTR returned (%s)',$this->sub_str_rule));
break;
case 'SYNTAX':
$this->syntax = $strings[++$i];
$this->syntax_oid = preg_replace('/{\d+}$/','',$this->syntax);
Log::debug(sprintf('/ Evaluating SYNTAX returned (%s) [%s]',$this->syntax,$this->syntax_oid));
// Does this SYNTAX string specify a max length (ie, 1.2.3.4{16})
$m = [];
if (preg_match('/{(\d+)}$/',$this->syntax,$m))
$this->max_length = $m[1];
else
$this->max_length = NULL;
if ($i < count($strings) - 1 && $strings[$i+1] == '{')
do {
$this->name .= ' '.$strings[++$i];
} while ($strings[$i] != '}');
$this->syntax = preg_replace("/^\'(.*)\'$/",'$1',$this->syntax);
$this->syntax_oid = preg_replace("/^\'(.*)\'$/",'$1',$this->syntax_oid);
Log::debug(sprintf('- Case SYNTAX returned (%s) [%s] {%d}',$this->syntax,$this->syntax_oid,$this->max_length));
break;
case 'SINGLE-VALUE':
$this->is_single_value = TRUE;
Log::debug(sprintf('- Case SINGLE-VALUE returned (%s)',$this->is_single_value));
break;
case 'COLLECTIVE':
$this->is_collective = TRUE;
Log::debug(sprintf('- Case COLLECTIVE returned (%s)',$this->is_collective));
break;
case 'NO-USER-MODIFICATION':
$this->is_no_user_modification = TRUE;
Log::debug(sprintf('- Case NO-USER-MODIFICATION returned (%s)',$this->is_no_user_modification));
break;
case 'USAGE':
$this->usage = $strings[++$i];
Log::debug(sprintf('- Case USAGE returned (%s)',$this->usage));
break;
// @note currently not captured
case 'X-ORDERED':
Log::error(sprintf('- Case X-ORDERED returned (%s)',$strings[++$i]));
break;
// @note currently not captured
case 'X-ORIGIN':
$value = '';
do {
$value .= (strlen($value) ? ' ' : '').$strings[++$i];
} while (! preg_match("/\'$/s",$strings[$i]));
Log::error(sprintf('- Case X-ORIGIN returned (%s)',$value));
break;
default:
if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) {
$this->oid = $strings[$i];
Log::debug(sprintf('- Case default returned (%s)',$this->oid));
} elseif ($strings[$i])
Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]);
}
}
}
public function __clone()
{
// When we clone, we need to break the reference too
$this->aliases = clone $this->aliases;
}
public function __get(string $key): mixed
{
switch ($key) {
case 'aliases': return $this->aliases;
case 'children': return $this->children;
case 'forced_as_may': return $this->forced_as_may;
case 'is_collective': return $this->is_collective;
case 'is_editable': return ! $this->is_no_user_modification;
case 'is_no_user_modification': return $this->is_no_user_modification;
case 'is_single_value': return $this->is_single_value;
case 'equality': return $this->equality;
case 'max_length': return $this->max_length;
case 'ordering': return $this->ordering;
case 'required_by_object_classes': return $this->required_by_object_classes;
case 'sub_str_rule': return $this->sub_str_rule;
case 'sup_attribute': return $this->sup_attribute;
case 'syntax': return $this->syntax;
case 'syntax_oid': return $this->syntax_oid;
case 'type': return $this->type;
case 'usage': return $this->usage;
case 'used_in_object_classes': return $this->used_in_object_classes;
case 'validation': return Arr::get(config('ldap.validation'),$this->name_lc);
default: return parent::__get($key);
}
}
/**
* Adds an attribute name to the alias array.
*
* @param string $alias The name of a new attribute to add to this attribute's list of aliases.
*/
public function addAlias(string $alias): void
{
$this->aliases->push($alias);
}
/**
* Children of this attribute type that inherit from this one
*
* @param string $child
* @return void
*/
public function addChild(string $child): void
{
$this->children->push($child);
}
/**
* Adds an objectClass name to this attribute's list of "required by" objectClasses,
* that is the list of objectClasses which must have this attribute.
*
* @param string $name The name of the objectClass to add.
*/
public function addRequiredByObjectClass(string $name): void
{
if ($this->required_by_object_classes->search($name) === FALSE)
$this->required_by_object_classes->push($name);
}
/**
* Adds an objectClass name to this attribute's list of "used in" objectClasses,
* that is the list of objectClasses which provide this attribute.
*
* @param string $name The name of the objectClass to add.
*/
public function addUsedInObjectClass(string $name): void
{
if ($this->used_in_object_classes->search($name) === FALSE)
$this->used_in_object_classes->push($name);
}
/**
* Gets the names of attributes that are an alias for this attribute (if any).
*
* @return Collection An array of names of attributes which alias this attribute or
* an empty array if no attribute aliases this object.
* @deprecated use class->aliases
*/
public function getAliases(): Collection
{
return $this->aliases;
}
/**
* Gets this attribute's equality string
*
* @return string
* @deprecated use $this->equality
*/
public function getEquality()
{
return $this->equality;
}
/**
* Gets whether this attribute is collective.
*
* @return boolean Returns TRUE if this attribute is collective and FALSE otherwise.
* @deprecated use $this->is_collective
*/
public function getIsCollective(): bool
{
return $this->is_collective;
}
/**
* Gets whether this attribute is not modifiable by users.
*
* @return boolean Returns TRUE if this attribute is not modifiable by users.
* @deprecated use $this->is_no_user_modification
*/
public function getIsNoUserModification(): bool
{
return $this->is_no_user_modification;
}
/**
* Gets whether this attribute is single-valued. If this attribute only supports single values, TRUE
* is returned. If this attribute supports multiple values, FALSE is returned.
*
* @return boolean Returns TRUE if this attribute is single-valued or FALSE otherwise.
* @deprecated use class->is_single_value
*/
public function getIsSingleValue(): bool
{
return $this->is_single_value;
}
/**
* Gets this attribute's the maximum length. If no maximum is defined by the LDAP server, NULL is returned.
*
* @return int The maximum length (in characters) of this attribute or NULL if no maximum is specified.
* @deprecated use $this->max_length;
*/
public function getMaxLength()
{
return $this->max_length;
}
/**
* Gets this attribute's ordering specification.
*
* @return string
* @deprecated use $this->ordering
*/
public function getOrdering(): string
{
return $this->ordering;
}
/**
* Gets the list of "required by" objectClasses, that is the list of objectClasses
* which provide must have attribute.
*
* @return array An array of names of objectclasses (strings) which provide this attribute
*/
public function getRequiredByObjectClasses() {
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->required_by_object_classes);
return $this->required_by_object_classes;
}
/**
* Gets this attribute's substring matching specification
*
* @return string
* @deprecated use $this->sub_str_rule;
*/
public function getSubstr() {
return $this->sub_str_rule;
}
/**
* Gets this attribute's parent attribute (if any). If this attribute does not
* inherit from another attribute, NULL is returned.
*
* @return string
* @deprecated use $class->sup_attribute directly
*/
public function getSupAttribute() {
return $this->sup_attribute;
}
/**
* Gets this attribute's syntax OID. Differs from getSyntaxString() in that this
* function only returns the actual OID with any length specification removed.
* Ie, if the syntax string is "1.2.3.4{16}", this function only retruns
* "1.2.3.4".
*
* @return string The syntax OID string.
* @deprecated use $this->syntax_oid;
*/
public function getSyntaxOID()
{
return $this->syntax_oid;
}
/**
* Gets this attribute's raw syntax string (ie: "1.2.3.4{16}").
*
* @return string The raw syntax string
*/
public function getSyntaxString() {
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->syntax);
return $this->syntax;
}
/**
* Gets this attribute's type
*
* @return string The attribute's type.
* @deprecated use $this->type;
*/
public function getType()
{
return $this->type;
}
/**
* Gets this attribute's usage string as defined by the LDAP server
*
* @return string
* @deprecated use $this->usage
*/
public function getUsage()
{
return $this->usage;
}
/**
* Gets the list of "used in" objectClasses, that is the list of objectClasses
* which provide this attribute.
*
* @return Collection An array of names of objectclasses (strings) which provide this attribute
* @deprecated use $this->used_in_object_classes
*/
public function getUsedInObjectClasses(): Collection
{
return $this->used_in_object_classes;
}
/**
* Returns whether the specified attribute is an alias for this one (based on this attribute's alias list).
*
* @param string $attr_name The name of the attribute to check.
* @return boolean TRUE if the specified attribute is an alias for this one, or FALSE otherwise.
*/
public function isAliasFor($attr_name) {
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs);
foreach ($this->aliases as $alias_attr_name)
if (strcasecmp($alias_attr_name,$attr_name) == 0)
return TRUE;
return FALSE;
}
/**
* @return bool
* @deprecated use $this->forced_as_may
*/
public function isForceMay(): bool
{
return $this->forced_as_may;
}
/**
* Removes an attribute name from this attribute's alias array.
*
* @param string $alias The name of the attribute to remove.
*/
public function removeAlias(string $alias): void
{
if (($x=$this->aliases->search($alias)) !== FALSE)
$this->aliases->forget($x);
}
/**
* Given a list of object classes, determine if this is a required attribute
*
* @param Collection $oc List of objectclasses to compare.
* @return Collection
*/
public function required_by(Collection $oc): Collection
{
return $oc->diff($this->required_by_object_classes);
}
/**
* Sets this attribute's list of aliases.
*
* @param Collection $aliases The array of alias names (strings)
* @deprecated use $this->aliases =
*/
public function setAliases(Collection $aliases): void
{
$this->aliases = $aliases;
}
/**
* This function will mark this attribute as a forced MAY attribute
*/
public function setForceMay() {
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs);
$this->forced_as_may = TRUE;
}
/**
* Sets whether this attribute is single-valued.
*
* @param boolean $is
*/
public function setIsSingleValue(bool $is): void
{
$this->is_single_value = $is;
}
/**
* Sets this attribute's SUP attribute (ie, the attribute from which this attribute inherits).
*
* @param string $attr The name of the new parent (SUP) attribute
*/
public function setSupAttribute(string $attr): void
{
$this->sup_attribute = trim($attr);
}
/**
* Sets this attribute's type.
*
* @param string $type The new type.
*/
public function setType($type) {
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs);
$this->type = $type;
}
}

View File

@ -1,120 +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 {
// Record the LDAP String
private string $line;
// The schema item's name.
protected ?string $name = NULL;
// The OID of this schema item.
protected string $oid;
# The description of this schema item.
protected ?string $description = NULL;
// Boolean value indicating whether this objectClass is obsolete
private bool $is_obsolete = FALSE;
public function __construct(string $line)
{
$this->line = $line;
}
public function __get(string $key): mixed
{
switch ($key) {
case 'description': return $this->description;
case 'is_obsolete': return $this->is_obsolete;
case 'line': return $this->line;
case 'name': return $this->name;
case 'name_lc': return strtolower($this->name);
case 'oid': return $this->oid;
default:
throw new InvalidUsage('Unknown key:'.$key);
}
}
public function __isset(string $key): bool
{
return isset($this->{$key});
}
public function __toString(): string
{
return $this->name;
}
/**
* @return string
* @deprecated replace with $class->description
*/
public function getDescription(): string
{
return $this->description;
}
/**
* Gets whether this item is flagged as obsolete by the LDAP server.
*
* @deprecated replace with $this->is_obsolete
*/
public function getIsObsolete(): bool
{
return $this->is_obsolete;
}
/**
* Return the objects name.
*
* @param boolean $lower Return the name in lower case (default)
* @return string The name
* @deprecated use object->name
*/
public function getName(bool $lower=TRUE): string
{
return $lower ? strtolower($this->name) : $this->name;
}
/**
* Return the objects name.
*
* @return string The name
* @deprecated use object->oid
*/
public function getOID(): string
{
return $this->oid;
}
public function setDescription(string $desc): void
{
$this->description = $desc;
}
/**
* Sets this attribute's name.
*
* @param string $name The new name to give this attribute.
*/
public function setName($name): void
{
$this->name = $name;
}
public function setOID(string $oid): void
{
$this->oid = $oid;
}
}

View File

@ -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);
}
}
}

View File

@ -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->search($name) === FALSE)
$this->used_by_attrs->push($name);
}
/**
* Gets an array of attribute names (strings) which use this MatchingRule
*
* @return array The array of attribute names (strings).
* @deprecated use $this->used_by_attrs
*/
public function getUsedByAttrs()
{
return $this->used_by_attrs;
}
/**
* Sets the list of used_by_attrs to the array specified by $attrs;
*
* @param Collection $attrs The array of attribute names (strings) which use this MatchingRule
*/
public function setUsedByAttrs(Collection $attrs): void
{
$this->used_by_attrs = $attrs;
}
}

View File

@ -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;
}
}

View File

@ -1,540 +0,0 @@
<?php
namespace App\Classes\LDAP\Schema;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use App\Classes\LDAP\Server;
use App\Exceptions\InvalidUsage;
use App\Ldap\Entry;
/**
* Represents an LDAP Schema objectClass
*
* @package phpLDAPadmin
* @subpackage Schema
*/
final class ObjectClass extends Base {
// The server ID that this objectclass belongs to.
private Server $server;
// Array of objectClass names from which this objectClass inherits
private Collection $sup_classes;
// One of STRUCTURAL, ABSTRACT, or AUXILIARY
private int $type;
// Arrays of attribute names that this objectClass requires
private Collection $must_attrs;
// Arrays of attribute names that this objectClass allows, but does not require
private Collection $may_attrs;
// Arrays of attribute names that this objectClass has been forced to MAY attrs, due to configuration
private Collection $may_force;
// Array of objectClasses which inherit from this one
private Collection $child_objectclasses;
private bool $is_obsolete;
/* ObjectClass Types */
private const OC_STRUCTURAL = 0x01;
private const OC_ABSTRACT = 0x02;
private const OC_AUXILIARY = 0x03;
/**
* Creates a new ObjectClass object given a raw LDAP objectClass string.
*
* eg: ( 2.5.6.0 NAME 'top' DESC 'top of the superclass chain' ABSTRACT MUST objectClass )
*/
public function __construct(string $line,Server $server)
{
parent::__construct($line);
Log::debug(sprintf('Parsing ObjectClass [%s]',$line));
$strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE);
// Init
$this->server = $server;
$this->may_attrs = collect();
$this->may_force = collect();
$this->must_attrs = collect();
$this->sup_classes = collect();
$this->child_objectclasses = collect();
for ($i=0; $i < count($strings); $i++) {
switch ($strings[$i]) {
case '(':
case ')':
break;
case 'NAME':
if ($strings[$i+1] != '(') {
do {
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
} while (! preg_match('/\'$/s',$strings[$i]));
} else {
$i++;
do {
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
} while (! preg_match('/\'$/s',$strings[$i]));
do {
$i++;
} while (! preg_match('/\)+\)?/',$strings[$i]));
}
$this->name = preg_replace("/^\'(.*)\'$/",'$1',$this->name);
Log::debug(sprintf(sprintf('- Case NAME returned (%s)',$this->name)));
break;
case 'DESC':
do {
$this->description .= (strlen($this->description) ? ' ' : '').$strings[++$i];
} while (! preg_match('/\'$/s',$strings[$i]));
$this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description);
Log::debug(sprintf('- Case DESC returned (%s)',$this->description));
break;
case 'OBSOLETE':
$this->is_obsolete = TRUE;
Log::debug(sprintf('- Case OBSOLETE returned (%s)',$this->is_obsolete));
break;
case 'SUP':
if ($strings[$i+1] != '(') {
$this->sup_classes->push(preg_replace("/'/",'',$strings[++$i]));
} else {
$i++;
do {
$i++;
if ($strings[$i] != '$')
$this->sup_classes->push(preg_replace("/'/",'',$strings[$i]));
} while (! preg_match('/\)+\)?/',$strings[$i+1]));
}
Log::debug(sprintf('- Case SUP returned (%s)',$this->sup_classes->join(',')));
break;
case 'ABSTRACT':
$this->type = self::OC_ABSTRACT;
Log::debug(sprintf('- Case ABSTRACT returned (%s)',$this->type));
break;
case 'STRUCTURAL':
$this->type = self::OC_STRUCTURAL;
Log::debug(sprintf('- Case STRUCTURAL returned (%s)',$this->type));
break;
case 'AUXILIARY':
$this->type = self::OC_AUXILIARY;
Log::debug(sprintf('- Case AUXILIARY returned (%s)',$this->type));
break;
case 'MUST':
$attrs = collect();
$i = $this->parseList(++$i,$strings,$attrs);
Log::debug(sprintf('= parseList returned %d (%s)',$i,$attrs->join(',')));
foreach ($attrs as $string) {
$attr = new ObjectClassAttribute($string,$this->name);
if ($server->isForceMay($attr->getName())) {
$this->may_force->push($attr);
$this->may_attrs->push($attr);
} else
$this->must_attrs->push($attr);
}
Log::debug(sprintf('- Case MUST returned (%s) (%s)',$this->must_attrs->join(','),$this->may_force->join(',')));
break;
case 'MAY':
$attrs = collect();
$i = $this->parseList(++$i,$strings,$attrs);
Log::debug(sprintf('parseList returned %d (%s)',$i,$attrs->join(',')));
foreach ($attrs as $string) {
$attr = new ObjectClassAttribute($string,$this->name);
$this->may_attrs->push($attr);
}
Log::debug(sprintf('- Case MAY returned (%s)',$this->may_attrs->join(',')));
break;
default:
if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) {
$this->oid = $strings[$i];
Log::debug(sprintf('- Case default returned (%s)',$this->oid));
} elseif ($strings[$i])
Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]);
}
}
}
public function __get(string $key): mixed
{
switch ($key) {
case 'attributes':
return $this->getAllAttrs();
case 'sup':
return $this->sup_classes;
case 'type_name':
switch ($this->type) {
case self::OC_STRUCTURAL: return 'Structural';
case self::OC_ABSTRACT: return 'Abstract';
case self::OC_AUXILIARY: return 'Auxiliary';
default:
throw new InvalidUsage('Unknown ObjectClass Type: '.$this->type);
}
default: return parent::__get($key);
}
}
/**
* Return a list of attributes that this objectClass provides
*
* @return Collection
*/
public function getAllAttrs(): Collection
{
return $this->getMustAttrs()->merge($this->getMayAttrs());
}
/**
* Adds an objectClass to the list of objectClasses that inherit
* from this objectClass.
*
* @param String $name The name of the objectClass to add
*/
public function addChildObjectClass(string $name): void
{
if ($this->child_objectclasses->search($name) === FALSE) {
$this->child_objectclasses->push($name);
}
}
/**
* Returns the array of objectClass names which inherit from this objectClass.
*
* @return Collection Names of objectClasses which inherit from this objectClass.
* @deprecated use $this->child_objectclasses
*/
public function getChildObjectClasses(): Collection
{
return $this->child_objectclasses;
}
/**
* Behaves identically to addMustAttrs, but it operates on the MAY
* attributes of this objectClass.
*
* @param array $attr An array of attribute names (strings) to add.
*/
private function addMayAttrs(array $attr): void
{
if (! is_array($attr) || ! count($attr))
return;
$this->may_attrs = $this->may_attrs->merge($attr)->unique();
}
/**
* Adds the specified array of attributes to this objectClass' list of
* MUST attributes. The resulting array of must attributes will contain
* unique members.
*
* @param array $attr An array of attribute names (strings) to add.
*/
private function addMustAttrs(array $attr): void
{
if (! is_array($attr) || ! count($attr))
return;
$this->must_attrs = $this->must_attrs->merge($attr)->unique();
}
/**
* @return Collection
* @deprecated use $this->may_force
*/
public function getForceMayAttrs(): Collection
{
return $this->may_force;
}
/**
* Gets an array of AttributeType objects that entries of this ObjectClass may define.
* This differs from getMayAttrNames in that it returns an array of AttributeType objects
*
* @param bool $parents Also get the may attrs of our parents.
* @return Collection The array of allowed AttributeType objects.
*
* @throws InvalidUsage
* @see getMustAttrNames
* @see getMustAttrs
* @see getMayAttrNames
* @see AttributeType
*/
public function getMayAttrs(bool $parents=FALSE): Collection
{
// If we dont need our parents, then we'll just return ours.
if (! $parents)
return $this->may_attrs->sortBy(function($item) { return strtolower($item->name.$item->source); });
$attrs = $this->may_attrs;
foreach ($this->getParents() as $object_class) {
$sc = $this->server->schema('objectclasses',$object_class);
$attrs = $attrs->merge($sc->getMayAttrs($parents));
}
// Remove any duplicates
$attrs = $attrs->unique(function($item) { return $item->name; });
// Return a sorted list
return $attrs->sortBy(function($item) { return strtolower($item->name.$item->source); });
}
/**
* Gets an array of attribute names (strings) that entries of this ObjectClass must define.
* This differs from getMayAttrs in that it returns an array of strings rather than
* array of AttributeType objects
*
* @param bool $parents An array of ObjectClass objects to use when traversing
* the inheritance tree. This presents some what of a bootstrapping problem
* as we must fetch all objectClasses to determine through inheritance which
* attributes this objectClass provides.
* @return Collection The array of allowed attribute names (strings).
*
* @throws InvalidUsage
* @see getMustAttrs
* @see getMayAttrs
* @see getMustAttrNames
*/
public function getMayAttrNames(bool $parents=FALSE): Collection
{
return $this->getMayAttrs($parents)->ppluck('name');
}
/**
* Gets an array of AttributeType objects that entries of this ObjectClass must define.
* This differs from getMustAttrNames in that it returns an array of AttributeType objects
*
* @param bool $parents Also get the must attrs of our parents.
* @return Collection The array of required AttributeType objects.
*
* @throws InvalidUsage
* @see getMustAttrNames
* @see getMayAttrs
* @see getMayAttrNames
*/
public function getMustAttrs(bool $parents=FALSE): Collection
{
// If we dont need our parents, then we'll just return ours.
if (! $parents)
return $this->must_attrs->sortBy(function($item) { return strtolower($item->name.$item->source); });
$attrs = $this->must_attrs;
foreach ($this->getParents() as $object_class) {
$sc = $this->server->schema('objectclasses',$object_class);
$attrs = $attrs->merge($sc->getMustAttrs($parents));
}
// Remove any duplicates
$attrs = $attrs->unique(function($item) { return $item->name; });
// Return a sorted list
return $attrs->sortBy(function($item) { return strtolower($item->name.$item->source); });
}
/**
* Gets an array of attribute names (strings) that entries of this ObjectClass must define.
* This differs from getMustAttrs in that it returns an array of strings rather than
* array of AttributeType objects
*
* @param bool $parents An array of ObjectClass objects to use when traversing
* the inheritance tree. This presents some what of a bootstrapping problem
* as we must fetch all objectClasses to determine through inheritance which
* attributes this objectClass provides.
* @return Collection The array of allowed attribute names (strings).
*
* @throws InvalidUsage
* @see getMustAttrs
* @see getMayAttrs
* @see getMayAttrNames
*/
public function getMustAttrNames(bool $parents=FALSE): Collection
{
return $this->getMustAttrs($parents)->ppluck('name');
}
/**
* This will return all our parent ObjectClass Objects
*/
public function getParents(): Collection
{
// If the only class is 'top', then we have no more parents
if (($this->sup_classes->count() === 1) && (strtolower($this->sup_classes->first()) === 'top'))
return collect();
$result = collect();
foreach ($this->sup_classes as $object_class) {
$result->push($object_class);
$oc = $this->server->schema('objectclasses',$object_class);
if ($oc)
$result = $result->merge($oc->getParents());
}
return $result;
}
/**
* Gets the objectClass names from which this objectClass inherits.
*
* @return Collection An array of objectClass names (strings)
* @deprecated use $this->sup_classes;
*/
public function getSupClasses(): Collection
{
return $this->sup_classes;
}
/**
* Gets the type of this objectClass: STRUCTURAL, ABSTRACT, or AUXILIARY.
*
* @deprecated use $this->type_name
*/
public function getType()
{
return $this->type;
}
/**
* Determine if an array is listed in the may_force attrs
*/
public function isForceMay(string $attr): bool
{
return $this->may_force->ppluck('name')->contains($attr);
}
/**
* Return if this objectClass is related to $oclass
*
* @param array $oclass ObjectClasses that this attribute may be related to
* @return bool
* @throws InvalidUsage
*/
public function isRelated(array $oclass): bool
{
// If I am in the array, we'll just return false
if (in_array_ignore_case($this->name,$oclass))
return FALSE;
foreach ($oclass as $object_class) {
$oc = $this->server->schema('objectclasses',$object_class);
if ($oc->isStructural() && in_array_ignore_case($this->name,$oc->getParents()))
return TRUE;
}
return FALSE;
}
public function isStructural(): bool
{
return $this->type === self::OC_STRUCTURAL;
}
/**
* Parse an LDAP schema list
*
* A list starts with a ( followed by a list of attributes separated by $ terminated by )
* The first token can therefore be a ( or a (NAME or a (NAME)
* The last token can therefore be a ) or NAME)
* The last token may be terminated by more than one bracket
*/
private function parseList(int $i,array $strings,Collection &$attrs): int
{
$string = $strings[$i];
if (! preg_match('/^\(/',$string)) {
// A bareword only - can be terminated by a ) if the last item
if (preg_match('/\)+$/',$string))
$string = preg_replace('/\)+$/','',$string);
$attrs->push($string);
} elseif (preg_match('/^\(.*\)$/',$string)) {
$string = preg_replace('/^\(/','',$string);
$string = preg_replace('/\)+$/','',$string);
$attrs->push($string);
} else {
// Handle the opening cases first
if ($string === '(') {
$i++;
} elseif (preg_match('/^\(./',$string)) {
$string = preg_replace('/^\(/','',$string);
$attrs->push($string);
$i++;
}
// Token is either a name, a $ or a ')'
// NAME can be terminated by one or more ')'
while (! preg_match('/\)+$/',$strings[$i])) {
$string = $strings[$i];
if ($string === '$') {
$i++;
continue;
}
if (preg_match('/\)$/',$string))
$string = preg_replace('/\)+$/','',$string);
else
$i++;
$attrs->push($string);
}
}
$attrs = $attrs->sort();
return $i;
}
}

View File

@ -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);
}
}
}

View File

@ -1,551 +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
{
// This servers schema objectclasses
private Collection $attributetypes;
private Collection $ldapsyntaxes;
private Collection $matchingrules;
private Collection $matchingruleuse;
private Collection $objectclasses;
// Valid items that can be fetched
public const schema_types = [
'objectclasses',
'attributetypes',
'ldapsyntaxes',
'matchingrules',
];
public function __get(string $key): mixed
{
switch ($key) {
case 'attributetypes': return $this->attributetypes;
case 'ldapsyntaxes': return $this->ldapsyntaxes;
case 'matchingrules': return $this->matchingrules;
case 'objectclasses': return $this->objectclasses;
default:
throw new Exception('Unknown key:'.$key);
}
}
/* STATIC METHODS */
/**
* Gets the root DN of the specified LDAPServer, or throws an exception if it
* can't find it.
*
* @param null $connection Return a collection of baseDNs
* @param bool $objects Return a collection of Entry Models
* @return Collection
* @throws ObjectNotFoundException
* @testedin GetBaseDNTest::testBaseDNExists();
* @todo Need to allow for the scenario if the baseDN is not readable by ACLs
*/
public static function baseDNs($connection=NULL,bool $objects=TRUE): Collection
{
$cachetime = Carbon::now()->addSeconds(Config::get('ldap.cache.time'));
try {
$base = self::rootDSE($connection,$cachetime);
/**
* LDAP Error Codes:
* https://ldap.com/ldap-result-code-reference/
* + success 0
* + operationsError 1
* + protocolError 2
* + timeLimitExceeded 3
* + sizeLimitExceeded 4
* + compareFalse 5
* + compareTrue 6
* + authMethodNotSupported 7
* + strongerAuthRequired 8
* + referral 10
* + adminLimitExceeded 11
* + unavailableCriticalExtension 12
* + confidentialityRequired 13
* + saslBindInProgress 14
* + noSuchAttribute 16
* + undefinedAttributeType 17
* + inappropriateMatching 18
* + constraintViolation 19
* + attributeOrValueExists 20
* + invalidAttributeSyntax 21
* + noSuchObject 32
* + aliasProblem 33
* + invalidDNSyntax 34
* + isLeaf 35
* + aliasDereferencingProblem 36
* + inappropriateAuthentication 48
* + invalidCredentials 49
* + insufficientAccessRights 50
* + busy 51
* + unavailable 52
* + unwillingToPerform 53
* + loopDetect 54
* + sortControlMissing 60
* + offsetRangeError 61
* + namingViolation 64
* + objectClassViolation 65
* + notAllowedOnNonLeaf 66
* + notAllowedOnRDN 67
* + entryAlreadyExists 68
* + objectClassModsProhibited 69
* + resultsTooLarge 70
* + affectsMultipleDSAs 71
* + virtualListViewError or controlError 76
* + other 80
* + serverDown 81
* + localError 82
* + encodingError 83
* + decodingError 84
* + timeout 85
* + authUnknown 86
* + filterError 87
* + userCanceled 88
* + paramError 89
* + noMemory 90
* + connectError 91
* + notSupported 92
* + controlNotFound 93
* + noResultsReturned 94
* + moreResultsToReturn 95
* + clientLoop 96
* + referralLimitExceeded 97
* + invalidResponse 100
* + ambiguousResponse 101
* + tlsNotSupported 112
* + intermediateResponse 113
* + unknownType 114
* + canceled 118
* + noSuchOperation 119
* + tooLate 120
* + cannotCancel 121
* + assertionFailed 122
* + authorizationDenied 123
* + e-syncRefreshRequired 4096
* + noOperation 16654
*
* LDAP Tag Codes:
* + A client bind operation 97
* + The entry for which you were searching 100
* + The result from a search operation 101
* + The result from a modify operation 103
* + The result from an add operation 105
* + The result from a delete operation 107
* + The result from a modify DN operation 109
* + The result from a compare operation 111
* + A search reference when the entry you perform your search on holds a referral to the entry you require.
* + Search references are expressed in terms of a referral.
* 115
* + A result from an extended operation 120
*/
// If we cannot get to our LDAP server we'll head straight to the error page
} catch (LdapRecordException $e) {
switch ($e->getDetailedError()->getErrorCode()) {
case 49:
// Since we failed authentication, we should delete our auth cookie
if (Cookie::has('password_encrypt')) {
Log::alert('Clearing user credentials and logging out');
Cookie::queue(Cookie::forget('password_encrypt'));
Cookie::queue(Cookie::forget('username_encrypt'));
Session::invalidate();
}
abort(401,$e->getDetailedError()->getErrorMessage());
default:
abort(597,$e->getDetailedError()->getErrorMessage());
}
}
if (! $objects)
return collect($base->namingcontexts);
/**
* @note While we are caching our baseDNs, it seems if we have more than 1,
* our caching doesnt generate a hit on a subsequent call to this function (before the cache expires).
* IE: If we have 5 baseDNs, it takes 5 calls to this function to case them all.
* @todo Possibly a bug wtih ldaprecord, so need to investigate
*/
$result = collect();
foreach ($base->namingcontexts as $dn) {
$result->push((new Entry)->cache($cachetime)->findOrFail($dn));
}
return $result;
}
/**
* Obtain the rootDSE for the server, that gives us server information
*
* @param null $connection
* @return Entry|null
* @throws ObjectNotFoundException
* @testedin TranslateOidTest::testRootDSE();
*/
public static function rootDSE($connection=NULL,Carbon $cachetime=NULL): ?Model
{
$e = new Entry;
return Entry::on($connection ?? $e->getConnectionName())
->cache($cachetime)
->in(NULL)
->read()
->select(['+'])
->whereHas('objectclass')
->firstOrFail();
}
/**
* Get the Schema DN
*
* @param $connection
* @return string
* @throws ObjectNotFoundException
*/
public static function schemaDN($connection=NULL): string
{
$cachetime = Carbon::now()->addSeconds(Config::get('ldap.cache.time'));
return collect(self::rootDSE($connection,$cachetime)->subschemasubentry)->first();
}
/**
* Query the server for a DN and return its children and if those children have children.
*
* @param string $dn
* @return LDAPCollection|NULL
*/
public function children(string $dn): ?LDAPCollection
{
return ($x=(new Entry)
->query()
->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time')))
->select(['*','hassubordinates'])
->setDn($dn)
->list()
->get()) ? $x : NULL;
}
/**
* Fetch a DN from the server
*
* @param string $dn
* @param array $attrs
* @return Entry|null
*/
public function fetch(string $dn,array $attrs=['*','+']): ?Entry
{
return ($x=(new Entry)
->query()
->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time')))
->select($attrs)
->find($dn)) ? $x : NULL;
}
/**
* This function determines if the specified attribute is contained in the force_may list
* as configured in config.php.
*
* @return boolean True if the specified attribute is configured to be force as a may attribute
*/
public function isForceMay($attr_name): bool
{
return in_array($attr_name,config('pla.force_may',[]));
}
/**
* Does this server support RFC3666 language tags
* OID: 1.3.6.1.4.1.4203.1.5.4
*
* @return bool
* @throws ObjectNotFoundException
*/
public function isLanguageTags(): bool
{
return in_array('1.3.6.1.4.1.4203.1.5.4',$this->rootDSE()->supportedfeatures);
}
/**
* Return the server's schema
*
* @param string $item Schema Item to Fetch
* @param string|null $key
* @return Collection|Base|NULL
* @throws InvalidUsage
*/
public function schema(string $item,string $key=NULL): Collection|Base|NULL
{
// Ensure our item to fetch is lower case
$item = strtolower($item);
if ($key)
$key = strtolower($key);
// This error message is not localized as only developers should ever see it
if (! in_array($item,self::schema_types))
throw new InvalidUsage('Invalid request to fetch schema: '.$item);
$result = Cache::remember('schema'.$item,config('ldap.cache.time'),function() use ($item) {
// First pass if we have already retrieved the schema item
switch ($item) {
case 'attributetypes':
if (isset($this->attributetypes))
return $this->attributetypes;
else
$this->attributetypes = collect();
break;
case 'ldapsyntaxes':
if (isset($this->ldapsyntaxes))
return $this->ldapsyntaxes;
else
$this->ldapsyntaxes = collect();
break;
case 'matchingrules':
if (isset($this->matchingrules))
return $this->matchingrules;
else
$this->matchingrules = collect();
break;
/*
case 'matchingruleuse':
if (isset($this->matchingruleuse))
return is_null($key) ? $this->matchingruleuse : $this->matchingruleuse->get($key);
else
$this->matchingruleuse = collect();
break;
*/
case 'objectclasses':
if (isset($this->objectclasses))
return $this->objectclasses;
else
$this->objectclasses = collect();
break;
// Shouldnt get here
default:
throw new InvalidUsage('Invalid request to fetch schema: '.$item);
}
// Try to get the schema DN from the specified entry.
$schema_dn = $this->schemaDN();
$schema = $this->fetch($schema_dn);
switch ($item) {
case 'attributetypes':
Log::debug('Attribute Types');
// build the array of attribueTypes
//$syntaxes = $this->SchemaSyntaxes($dn);
foreach ($schema->{$item} as $line) {
if (is_null($line) || ! strlen($line))
continue;
$o = new AttributeType($line);
$this->attributetypes->put($o->name_lc,$o);
/*
if (isset($syntaxes[$attr->getSyntaxOID()])) {
$syntax = $syntaxes[$attr->getSyntaxOID()];
$attr->setType($syntax->getDescription());
}
$this->attributetypes[$attr->getName()] = $attr;
*/
/**
* bug 856832: create an entry in the $attrs_oid array too. This
* will be a ref to the $attrs entry for maintenance and performance
* reasons
*/
//$attrs_oid[$attr->getOID()] = &$attrs[$attr->getName()];
}
// go back and add data from aliased attributeTypes
foreach ($this->attributetypes as $o) {
/* foreach of the attribute's aliases, create a new entry in the attrs array
* with its name set to the alias name, and all other data copied.*/
if ($o->aliases->count()) {
Log::debug(sprintf('\ Attribute [%s] has the following aliases [%s]',$o->name,$o->aliases->join(',')));
foreach ($o->aliases as $alias) {
$new_attr = clone $o;
$new_attr->setName($alias);
$new_attr->addAlias($o->name);
$new_attr->removeAlias($alias);
$this->attributetypes->put(strtolower($alias),$new_attr);
}
}
}
// Now go through and reference the parent/child relationships
foreach ($this->attributetypes as $o)
if ($o->sup_attribute) {
$parent = strtolower($o->sup_attribute);
if ($this->attributetypes->has($parent) !== FALSE)
$this->attributetypes[$parent]->addChild($o->name);
}
// go through any children and add details if the child doesnt have them (ie, cn inherits name)
// @todo This doesnt traverse children properly, so children of children may not get the settings they should
foreach ($this->attributetypes as $parent) {
foreach ($parent->children as $child) {
$child = strtolower($child);
/* only overwrite the child's SINGLE-VALUE property if the parent has it set, and the child doesnt
* (note: All LDAP attributes default to multi-value if not explicitly set SINGLE-VALUE) */
if (! is_null($parent->is_single_value) && is_null($this->attributetypes[$child]->is_single_value))
$this->attributetypes[$child]->setIsSingleValue($parent->is_single_value);
}
}
// Add the used in and required_by values.
foreach ($this->schema('objectclasses') as $object_class) {
$must_attrs = $object_class->getMustAttrNames();
$may_attrs = $object_class->getMayAttrNames();
$oclass_attrs = $must_attrs->merge($may_attrs)->unique();
// Add Used In.
foreach ($oclass_attrs as $attr_name)
if ($this->attributetypes->has(strtolower($attr_name)))
$this->attributetypes[strtolower($attr_name)]->addUsedInObjectClass($object_class->name);
// Add Required By.
foreach ($must_attrs as $attr_name)
if ($this->attributetypes->has(strtolower($attr_name)))
$this->attributetypes[strtolower($attr_name)]->addRequiredByObjectClass($object_class->name);
// Force May
foreach ($object_class->getForceMayAttrs() as $attr_name)
if ($this->attributetypes->has(strtolower($attr_name->name)))
$this->attributetypes[strtolower($attr_name->name)]->setForceMay();
}
return $this->attributetypes;
case 'ldapsyntaxes':
Log::debug('LDAP Syntaxes');
foreach ($schema->{$item} as $line) {
if (is_null($line) || ! strlen($line))
continue;
$o = new LDAPSyntax($line);
$this->ldapsyntaxes->put(strtolower($o->oid),$o);
}
return $this->ldapsyntaxes;
case 'matchingrules':
Log::debug('Matching Rules');
$this->matchingruleuse = collect();
foreach ($schema->{$item} as $line) {
if (is_null($line) || ! strlen($line))
continue;
$o = new MatchingRule($line);
$this->matchingrules->put($o->name_lc,$o);
}
/*
* For each MatchingRuleUse entry, add the attributes who use it to the
* MatchingRule in the $rules array.
*/
if ($schema->matchingruleuse) {
foreach ($schema->matchingruleuse as $line) {
if (is_null($line) || ! strlen($line))
continue;
$o = new MatchingRuleUse($line);
$this->matchingruleuse->put($o->name_lc,$o);
if ($this->matchingrules->has($o->name_lc) !== FALSE)
$this->matchingrules[$o->name_lc]->setUsedByAttrs($o->getUsedByAttrs());
}
} else {
/* No MatchingRuleUse entry in the subschema, so brute-forcing
* the reverse-map for the "$rule->getUsedByAttrs()" data.*/
foreach ($this->schema('attributetypes') as $attr) {
$rule_key = strtolower($attr->getEquality());
if ($this->matchingrules->has($rule_key) !== FALSE)
$this->matchingrules[$rule_key]->addUsedByAttr($attr->name);
}
}
return $this->matchingrules;
case 'objectclasses':
Log::debug('Object Classes');
foreach ($schema->{$item} as $line) {
if (is_null($line) || ! strlen($line))
continue;
$o = new ObjectClass($line,$this);
$this->objectclasses->put($o->name_lc,$o);
}
// Now go through and reference the parent/child relationships
foreach ($this->objectclasses as $o)
foreach ($o->getSupClasses() as $parent) {
$parent = strtolower($parent);
if ($this->objectclasses->has($parent) !== FALSE)
$this->objectclasses[$parent]->addChildObjectClass($o->name);
}
return $this->objectclasses;
}
});
return is_null($key) ? $result : $result->get($key);
}
/**
* Given an OID, return the ldapsyntax for the OID
*
* @param string $oid
* @return LDAPSyntax|null
* @throws InvalidUsage
*/
public function schemaSyntaxName(string $oid): ?LDAPSyntax
{
return $this->schema('ldapsyntaxes',$oid);
}
}

View File

@ -1,41 +0,0 @@
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
//
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')->hourly();
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}

View File

@ -1,50 +0,0 @@
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of exception types with their corresponding custom log levels.
*
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
*/
protected $levels = [
//
];
/**
* A list of the exception types that are not reported.
*
* @var array<int, class-string<\Throwable>>
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
//
});
}
}

View File

@ -1,7 +0,0 @@
<?php
namespace App\Exceptions\Import;
use Exception;
class AttributeException extends Exception {}

View File

@ -1,7 +0,0 @@
<?php
namespace App\Exceptions\Import;
use Exception;
class GeneralException extends Exception {}

View File

@ -1,7 +0,0 @@
<?php
namespace App\Exceptions\Import;
use Exception;
class ObjectExistsException extends Exception {}

View File

@ -1,7 +0,0 @@
<?php
namespace App\Exceptions\Import;
use Exception;
class VersionException extends Exception {}

View File

@ -1,10 +0,0 @@
<?php
namespace App\Exceptions;
use Exception;
class InvalidUsage extends Exception
{
//
}

View File

@ -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);
}
}
}

View File

@ -1,104 +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;
use App\Providers\RouteServiceProvider;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
protected function credentials(Request $request): array
{
return [
login_attr_name() => $request->get(login_attr_name()),
'password' => $request->get('password'),
];
}
/**
* We need to delete our encrypted username/password cookies
*
* @note The rest of this function is the same as a normal laravel logout as in AuthenticatesUsers::class
* @param Request $request
* @return \Illuminate\Contracts\Foundation\Application|JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|mixed
*/
public function logout(Request $request)
{
// Delete our LDAP authentication cookies
Cookie::queue(Cookie::forget('username_encrypt'));
Cookie::queue(Cookie::forget('password_encrypt'));
$this->guard()->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
if ($response = $this->loggedOut($request)) {
return $response;
}
return $request->wantsJson()
? new JsonResponse([], 204)
: redirect('/');
}
/**
*
* Show our themed login page
*/
public function showLoginForm()
{
$login_note = '';
if (file_exists('login_note.txt'))
$login_note = file_get_contents('login_note.txt');
return view('architect::auth.login')->with('login_note',$login_note);
}
/**
* Get the login username to be used by the controller.
*
* @return string
*/
public function username()
{
return login_attr_name();
}
}

View File

@ -1,13 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

View File

@ -1,309 +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();
}
/**
* Show a confirmation to update a DN
*
* @param EntryRequest $request
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application|\Illuminate\Http\RedirectResponse
* @throws ObjectNotFoundException
*/
public function entry_pending_update(EntryRequest $request)
{
$dn = Crypt::decryptString($request->dn);
$o = config('server')->fetch($dn);
foreach ($request->except(['_token','dn']) as $key => $value)
$o->{$key} = array_filter($value);
if (! $o->getDirty())
return back()
->withInput()
->with('note',__('No attributes changed'));
return view('update')
->with('bases',$this->bases())
->with('dn',$dn)
->with('o',$o);
}
/**
* Update a DN entry
*
* @param EntryRequest $request
* @return \Illuminate\Http\RedirectResponse
* @throws ObjectNotFoundException
*/
public function entry_update(EntryRequest $request)
{
$dn = Crypt::decryptString($request->dn);
$o = config('server')->fetch($dn);
foreach ($request->except(['_token','dn']) as $key => $value)
$o->{$key} = array_filter($value);
if (! $dirty=$o->getDirty())
return back()
->withInput()
->with('note',__('No attributes changed'));
try {
$o->update($request->except(['_token','dn']));
} catch (InsufficientAccessException $e) {
$request->flash();
switch ($x=$e->getDetailedError()->getErrorCode()) {
case 50:
return Redirect::to('/')
->withInput()
->withErrors(sprintf('%s: %s (%s)',__('LDAP Server Error Code'),$x,__($e->getDetailedError()->getErrorMessage())));
default:
abort(599,$e->getDetailedError()->getErrorMessage());
}
} catch (LdapRecordException $e) {
$request->flash();
switch ($x=$e->getDetailedError()->getErrorCode()) {
case 8:
return Redirect::to('/')
->withInput()
->withErrors(sprintf('%s: %s (%s)',__('LDAP Server Error Code'),$x,__($e->getDetailedError()->getErrorMessage())));
default:
abort(599,$e->getDetailedError()->getErrorMessage());
}
}
return Redirect::to('/')
->withInput()
->with('success',__('Entry updated'))
->with('updated',$dirty);
}
/**
* Application home page
*/
public function home()
{
if (old('dn'))
return view('frame')
->with('subframe','dn')
->with('bases',$this->bases())
->with('o',config('server')->fetch($dn=Crypt::decryptString(old('dn'))))
->with('dn',$dn);
elseif (old('frame'))
return view('frame')
->with('subframe',old('frame'))
->with('bases',$this->bases());
else
return view('home')
->with('bases',$this->bases())
->with('server',config('ldap.connections.default.name'));
}
/**
* Process the incoming LDIF file or LDIF text
*
* @param ImportRequest $request
* @param string $type
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application
* @throws GeneralException
* @throws VersionException
*/
public function import(ImportRequest $request,string $type)
{
switch ($type) {
case 'ldif':
$import = new LDIFImport($x=($request->text ?: $request->file->get()));
break;
default:
abort(404,'Unknown import type: '.$type);
}
try {
$result = $import->process();
} catch (NotImplementedException $e) {
abort(555,$e->getMessage());
} catch (\Exception $e) {
abort(598,$e->getMessage());
}
return view('frame')
->with('subframe','import_result')
->with('bases',$this->bases())
->with('result',$result)
->with('ldif',htmlspecialchars($x));
}
public function import_frame()
{
return view('frames.import');
}
/**
* LDAP Server INFO
*
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
*/
public function info()
{
return view('frames.info')
->with('s',config('server'));
}
/**
* Show the Schema Viewer
*
* @note Our route will validate that types are valid.
* @param Request $request
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
* @throws InvalidUsage
*/
public function schema_frame(Request $request)
{
$s = config('server');
// If an invalid key, we'll 404
if ($request->type && $request->key && ($s->schema($request->type)->has($request->key) === FALSE))
abort(404);
return view('frames.schema')
->with('type',$request->type)
->with('key',$request->key);
}
/**
* Sort the attributes
*
* @param Collection $attrs
* @return Collection
*/
private function sortAttrs(Collection $attrs): Collection
{
return $attrs->sortKeys();
}
/**
* Return the image for the logged in user or anonymous
*
* @param Request $request
* @return mixed
*/
public function user_image(Request $request)
{
$image = NULL;
$content = NULL;
if (Auth::check()) {
$image = Arr::get(Auth::user()->getAttribute('jpegphoto'),0);
$content = 'image/jpeg';
}
if (! $image) {
$image = File::get('../resources/images/user-secret-solid.svg');
$content = 'image/svg+xml';
}
return response($image)
->header('Content-Type',$content);
}
}

View File

@ -1,82 +0,0 @@
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
use App\Http\Middleware\{ApplicationSession,CheckUpdate,SwapinAuthUser};
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array
*/
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
/**
* The application's route middleware groups.
*
* @var array
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
ApplicationSession::class,
SwapinAuthUser::class,
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
CheckUpdate::class,
],
'api' => [
'throttle:60,1',
\App\Http\Middleware\EncryptCookies::class,
SwapinAuthUser::class,
ApplicationSession::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $middlewareAliases = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'localize' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRoutes::class,
'localizationRedirect' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRedirectFilter::class,
'localeSessionRedirect' => \Mcamara\LaravelLocalization\Middleware\LocaleSessionRedirect::class,
'localeCookieRedirect' => \Mcamara\LaravelLocalization\Middleware\LocaleCookieRedirect::class,
'localeViewPath' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationViewPath::class
];
}

View File

@ -1,29 +0,0 @@
<?php
namespace App\Http\Middleware;
use App\Classes\LDAP\Server;
use App\Ldap\User;
use Closure;
/**
* This sets up our application session with any required values, ultimately for cache optimisation reasons
*/
class ApplicationSession
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request,Closure $next)
{
\Config::set('server',new Server);
view()->share('user', auth()->user() ?: new User);
return $next($request);
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
}

View File

@ -1,17 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode as Middleware;
class CheckForMaintenanceMode extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array
*/
protected $except = [
//
];
}

View File

@ -1,61 +0,0 @@
<?php
namespace App\Http\Middleware;
use Closure;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
class CheckUpdate
{
private const UPDATE_SERVER = 'https://version.phpldapadmin.org';
private const UPDATE_TIME = 60*60*6;
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
\Config::set('update_available',Cache::get('upstream_version'));
return $next($request);
}
/**
* Handle tasks after the response has been sent to the browser.
*
* @return void
*/
public function terminate()
{
Cache::remember('upstream_version',self::UPDATE_TIME,function() {
// CURL call to URL to see if there is a new version
Log::debug(sprintf('CU_:Checking for updates for [%s]',config('app.version')));
$client = new Client;
try {
$response = $client->request('POST',sprintf('%s/%s',self::UPDATE_SERVER,strtolower(config('app.version'))));
if ($response->getStatusCode() === 200) {
$result = json_decode($response->getBody());
Log::debug(sprintf('CU_:- Update server returned...'),['update'=>$result]);
return $result;
}
} catch (\Exception $e) {
Log::debug(sprintf('CU_:- Exception connecting to update server'),['e'=>get_class($e)]);
}
return NULL;
});
}
}

View File

@ -1,17 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array
*/
protected $except = [
//
];
}

View File

@ -1,27 +0,0 @@
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $guard
* @return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
return $next($request);
}
}

View File

@ -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\Crypt;
use Illuminate\Support\Facades\Log;
// use Illuminate\Support\Facades\Session;
use LdapRecord\Container;
use App\Ldap\Connection;
class SwapinAuthUser
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$key = config('ldap.default');
/*
// Rebuild our connection with the authenticated user.
if (Session::has('username_encrypt') && Session::has('password_encrypt')) {
Config::set('ldap.connections.'.$key.'.username',Crypt::decryptString(Session::get('username_encrypt')));
Config::set('ldap.connections.'.$key.'.password',Crypt::decryptString(Session::get('password_encrypt')));
} else
*/
if (Cookie::has('username_encrypt') && Cookie::has('password_encrypt')) {
Config::set('ldap.connections.'.$key.'.username',Cookie::get('username_encrypt'));
Config::set('ldap.connections.'.$key.'.password',Cookie::get('password_encrypt'));
Log::debug('Swapping out configured LDAP credentials with the user\'s cookie.');
}
// We need to override our Connection object so that we can store and retrieve the logged in user and swap out the credentials to use them.
Container::getInstance()->addConnection(new Connection(config('ldap.connections.'.$key)),$key);
return $next($request);
}
}

View File

@ -1,18 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
class TrimStrings extends Middleware
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array
*/
protected $except = [
'password',
'password_confirmation',
];
}

View File

@ -1,20 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as Middleware;
class TrustHosts extends Middleware
{
/**
* Get the host patterns that should be trusted.
*
* @return array
*/
public function hosts()
{
return [
$this->allSubdomainsOfApplicationUrl(),
];
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array<int, string>|string|null
*/
protected $proxies;
/**
* The headers that should be used to detect proxies.
*
* @var int
*/
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
}

View File

@ -1,17 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
//
];
}

View File

@ -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();
}
}

View File

@ -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',
];
}
}

View File

@ -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);
};
}
}

View File

@ -1,423 +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($this->attributes);
return $this;
}
/**
* This function overrides getAttributes to use our collection of Attribute objects instead of the models attributes.
*
* @return array
* @note $this->attributes may not be updated with changes
*/
public function getAttributes(): array
{
return $this->objects->map(function($item) { return $item->values->toArray(); })->toArray();
}
/**
* Determine if the new and old values for a given key are equivalent.
*/
protected function originalIsEquivalent(string $key): bool
{
$key = $this->normalizeAttributeKey($key);
if ((! array_key_exists($key, $this->original)) && (! $this->objects->has($key))) {
return TRUE;
}
$current = $this->attributes[$key];
$original = $this->objects->get($key)->values;
if ($current === $original) {
return true;
}
return ! $this->getObject($key)->isDirty();
}
public static function query(bool $noattrs=false): Builder
{
$o = new static;
if ($noattrs)
$o->noObjectAttributes();
return $o->newQuery();
}
/**
* As attribute values are updated, or new ones created, we need to mirror that
* into our $objects
*
* @param string $key
* @param mixed $value
* @return $this
*/
public function setAttribute(string $key, mixed $value): static
{
parent::setAttribute($key,$value);
$key = $this->normalizeAttributeKey($key);
if ((! $this->objects->get($key)) && $value) {
$o = new Attribute($key,[]);
$o->value = $value;
$this->objects->put($key,$o);
} elseif ($this->objects->get($key)) {
$this->objects->get($key)->value = $this->attributes[$key];
}
return $this;
}
/**
* We'll shadow $this->attributes to $this->objects - a collection of Attribute objects
*
* Using the objects, it'll make it easier to work with attribute values
*
* @param array $attributes
* @return $this
*/
public function setRawAttributes(array $attributes = []): static
{
parent::setRawAttributes($attributes);
// We only set our objects on DN entries (otherwise we might get into a recursion loop if this is the schema DN)
if ($this->dn && (! in_array($this->dn,Arr::get($this->attributes,'subschemasubentry',[])))) {
$this->objects = $this->getAttributesAsObjects($this->attributes);
} else {
$this->objects = collect();
}
return $this;
}
/* ATTRIBUTES */
/**
* Return a key to use for sorting
*
* @todo This should be the DN in reverse order
* @return string
*/
public function getSortKeyAttribute(): string
{
return $this->getDn();
}
/* METHODS */
public function addAttribute(string $key,mixed $value): void
{
$key = $this->normalizeAttributeKey($key);
if (config('server')->schema('attributetypes')->has($key) === FALSE)
throw new AttributeException('Schema doesnt have attribute [%s]',$key);
if ($x=$this->objects->get($key)) {
$x->addValue($value);
} else {
$this->objects->put($key,Attribute\Factory::create($key,Arr::wrap($value)));
}
}
/**
* Convert all our attribute values into an array of Objects
*
* @param array $attributes
* @return Collection
*/
protected function getAttributesAsObjects(array $attributes): Collection
{
$result = collect();
foreach ($attributes as $attribute => $value) {
// If the attribute name has language tags
$matches = [];
if (preg_match('/^([a-zA-Z]+)(;([a-zA-Z-;]+))+/',$attribute,$matches)) {
$attribute = $matches[1];
// If the attribute doesnt exist we'll create it
$o = Arr::get($result,$attribute,Factory::create($attribute,[]));
$o->setLangTag($matches[3],$value);
} else {
$o = Factory::create($attribute,$value);
}
if (! $result->has($attribute)) {
// Set the rdn flag
if (preg_match('/^'.$attribute.'=/i',$this->dn))
$o->setRDN();
// Set required flag
$o->required_by(collect($this->getAttribute('objectclass')));
// Store our original value to know if this attribute has changed
if ($x=Arr::get($this->original,$attribute))
$o->oldValues($x);
$result->put($attribute,$o);
}
}
$sort = collect(config('ldap.attr_display_order',[]))->transform(function($item) { return strtolower($item); });
// Order the attributes
$result = $result->sortBy([function(Attribute $a,Attribute $b) use ($sort): int {
if ($a === $b)
return 0;
// Check if $a/$b are in the configuration to be sorted first, if so get it's key
$a_key = $sort->search($a->name_lc);
$b_key = $sort->search($b->name_lc);
// If the keys were not in the sort list, set the key to be the count of elements (ie: so it is last to be sorted)
if ($a_key === FALSE)
$a_key = $sort->count()+1;
if ($b_key === FALSE)
$b_key = $sort->count()+1;
// Case where neither $a, nor $b are in ldap.attr_display_order, $a_key = $b_key = one greater than num elements.
// So we sort them alphabetically
if ($a_key === $b_key)
return strcasecmp($a->name,$b->name);
// Case where at least one attribute or its friendly name is in $attrs_display_order
// return -1 if $a before $b in $attrs_display_order
return ($a_key < $b_key) ? -1 : 1;
} ]);
return $result;
}
/**
* Return a list of available attributes - as per the objectClass entry of the record
*
* @return Collection
*/
public function getAvailableAttributes(): Collection
{
$result = collect();
foreach ($this->objectclass as $oc)
$result = $result->merge(config('server')->schema('objectclasses',$oc)->attributes);
return $result;
}
/**
* Return a secure version of the DN
* @return string
*/
public function getDNSecure(): string
{
return Crypt::encryptString($this->getDn());
}
/**
* Return a list of LDAP internal attributes
*
* @return Collection
*/
public function getInternalAttributes(): Collection
{
return $this->objects->filter(function($item) {
return $item->is_internal;
});
}
/**
* Get an attribute as an object
*
* @param string $key
* @return Attribute|null
*/
public function getObject(string $key): Attribute|null
{
return $this->objects->get($this->normalizeAttributeKey($key));
}
public function getObjects(): Collection
{
// In case we havent built our objects yet (because they werent available while determining the schema DN)
if ((! $this->objects->count()) && $this->attributes)
$this->objects = $this->getAttributesAsObjects($this->attributes);
return $this->objects;
}
/**
* Return a list of attributes without any values
*
* @return Collection
*/
public function getMissingAttributes(): Collection
{
return $this->getAvailableAttributes()->diff($this->getVisibleAttributes());
}
/**
* Return this list of user attributes
*
* @return Collection
*/
public function getVisibleAttributes(): Collection
{
return $this->objects->filter(function($item) {
return ! $item->is_internal;
});
}
public function hasAttribute(int|string $key): bool
{
return $this->objects->has($key);
}
/**
* Export this record
*
* @param string $method
* @param string $scope
* @return string
* @throws \Exception
*/
public function export(string $method,string $scope): string
{
// @todo To implement
switch ($scope) {
case 'base':
case 'one':
case 'sub':
break;
default:
throw new \Exception('Export scope unknown:'.$scope);
}
switch ($method) {
case 'ldif':
return new LDIF(collect($this));
default:
throw new \Exception('Export method not implemented:'.$method);
}
}
/**
* Return an icon for a DN based on objectClass
*
* @return string
*/
public function icon(): string
{
$objectclasses = array_map('strtolower',$this->objectclass);
// Return icon based upon objectClass value
if (in_array('person',$objectclasses) ||
in_array('organizationalperson',$objectclasses) ||
in_array('inetorgperson',$objectclasses) ||
in_array('account',$objectclasses) ||
in_array('posixaccount',$objectclasses))
return 'fas fa-user';
elseif (in_array('organization',$objectclasses))
return 'fas fa-university';
elseif (in_array('organizationalunit',$objectclasses))
return 'fas fa-object-group';
elseif (in_array('posixgroup',$objectclasses) ||
in_array('groupofnames',$objectclasses) ||
in_array('groupofuniquenames',$objectclasses) ||
in_array('group',$objectclasses))
return 'fas fa-users';
elseif (in_array('dcobject',$objectclasses) ||
in_array('domainrelatedobject',$objectclasses) ||
in_array('domain',$objectclasses) ||
in_array('builtindomain',$objectclasses))
return 'fas fa-network-wired';
elseif (in_array('alias',$objectclasses))
return 'fas fa-theater-masks';
elseif (in_array('country',$objectclasses))
return sprintf('flag %s',strtolower(Arr::get($this->c,0)));
elseif (in_array('device',$objectclasses))
return 'fas fa-mobile-alt';
elseif (in_array('document',$objectclasses))
return 'fas fa-file-alt';
elseif (in_array('iphost',$objectclasses))
return 'fas fa-wifi';
elseif (in_array('room',$objectclasses))
return 'fas fa-door-open';
elseif (in_array('server',$objectclasses))
return 'fas fa-server';
elseif (in_array('openldaprootdse',$objectclasses))
return 'fas fa-info';
// Default
return 'fa-fw fas fa-cog';
}
/**
* Dont convert our $this->attributes to $this->objects when creating a new Entry::class
*
* @return $this
*/
public function noObjectAttributes(): static
{
$this->noObjectAttributes = TRUE;
return $this;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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('ldap.login.objectclass')) {
return count(array_intersect($user->objectclass,$x));
// Otherwise allow the user to login
} else {
return TRUE;
}
}
}

View File

@ -1,29 +0,0 @@
<?php
namespace App\Ldap;
use Laravel\Passport\HasApiTokens;
use LdapRecord\Models\OpenLDAP\User as Model;
use App\Ldap\Rules\LoginObjectclassRule;
class User extends Model
{
use HasApiTokens;
/**
* The object classes of the LDAP model.
*
* @note We set this to an empty array so that any objectclass can login
* @see LoginObjectclassRule::class
*/
public static array $objectClasses = [
];
/* METHODS */
public function getDn(): string
{
return $this->exists ? parent::getDn() : 'Anonymous';
}
}

View File

@ -1,44 +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.
*
* @return void
*/
public function register()
{
// Add a new option available to be set in the configuration:
DomainConfiguration::extend('name', $default = null);
// Use our LdapUserRepository to support multiple baseDN querying
LdapRecord::locateUsersUsing(LdapUserRepository::class);
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
$this->loadViewsFrom(__DIR__.'/../../resources/themes/architect/views/','architect');
// Enable pluck on collections to work on private values
Collection::macro('ppluck', function ($attr) {
return $this->map(function (object $item) use ($attr) {
return $item->{$attr};
})->values();
});
}
}

View File

@ -1,27 +0,0 @@
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The model to policy mappings for the application.
*
* @var array<class-string, class-string>
*/
protected $policies = [
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
//
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Broadcast::routes();
require base_path('routes/channels.php');
}
}

View File

@ -1,30 +0,0 @@
<?php
namespace App\Providers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [];
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
parent::boot();
//
}
}

View File

@ -1,52 +0,0 @@
<?php
namespace App\Providers;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
/**
* The path to the "home" route for your application.
*
* Typically, users are redirected here after authentication.
*
* @var string
*/
public const HOME = '/';
/**
* Define your route model bindings, pattern filters, and other route configuration.
*
* @return void
*/
public function boot()
{
$this->configureRateLimiting();
$this->routes(function () {
Route::middleware('api')
->prefix('api')
->group(base_path('routes/api.php'));
Route::middleware('web')
->group(base_path('routes/web.php'));
});
}
/**
* Configure the rate limiters for the application.
*
* @return void
*/
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
}
}

View File

@ -1,23 +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
{
if (! parent::isDirty())
return TRUE;
foreach ($this->values->diff($this->oldValues) as $key => $value)
if (md5(Arr::get($this->oldValues,$key)) !== $value)
return TRUE;
return FALSE;
}
}

View File

@ -1,38 +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;
/**
* Create a new component instance.
*
* @return void
*/
public function __construct(LDAPAttribute $o,bool $edit,bool $old=FALSE,bool $new=FALSE)
{
$this->o = $o;
$this->edit = $edit;
$this->old = $old;
$this->new = $new;
}
/**
* Get the view / contents that represent the component.
*
* @return \Illuminate\Contracts\View\View|\Closure|string
*/
public function render()
{
return $this->o->render($this->edit,$this->old,$this->new);
}
}

View File

@ -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);
}
}

View File

@ -1,13 +0,0 @@
<?php
use Illuminate\Support\Arr;
function login_attr_description(): string
{
return Arr::get(config('ldap.login.attr'),login_attr_name());
}
function login_attr_name(): string
{
return key(config('ldap.login.attr'));
}

151
application/bootstrap.php Normal file
View File

@ -0,0 +1,151 @@
<?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
'ldap' => MODPATH.'ldap', // LDAP Database Interaction (Depends on DATABASE which must be after this)
'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.');
}
?>

View File

@ -0,0 +1,106 @@
<?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
* @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 'theme/'.Kohana::$config->load('config')->theme;
}
public static function version() {
// @todo Work out our versioning
return 'TBA';
}
}
?>

View File

@ -0,0 +1,4 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
class Controller_Template extends PLA_Controller_Template {}
?>

View File

@ -0,0 +1,46 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides the default template controller for rendering pages.
*
* @package OSB
* @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;
}
}
?>

View File

@ -0,0 +1,15 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class overrides Kohana's Cookie
*
* @package PLA/Modifications
* @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';
}
?>

View File

@ -0,0 +1,116 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides the default template controller for rendering pages.
*
* @package PLA
* @category Controllers
* @author Deon George
* @copyright (c) phpLDAPadmin Development Team
* @license http://dev.phpldapadmin.org/license.html
*/
abstract class PLA_Controller_Template extends Kohana_Controller_Template {
// @var object meta object information as per [meta]
private $meta;
public function __construct(Request $request, Response $response) {
$this->template = Config::theme().'/page';
return parent::__construct($request,$response);
}
public function before() {
// Do not template media files
if ($this->request->action() === 'media') {
$this->auto_render = FALSE;
return;
}
parent::before();
// For AJAX calls, we dont need to render the complete page.
if ($this->request->is_ajax()) {
$this->auto_render = FALSE;
return;
}
$this->template->content = '';
// Setup the page template
$this->meta = new Meta;
View::bind_global('meta',$this->meta);
}
public function after() {
if ($this->auto_render === TRUE) {
// Application Title
$this->meta->title = Kohana::$config->load('config')->appname;
// Language
// @todo
$this->meta->language = '';
// Description
$this->meta->description = sprintf('%s::%s',$this->request->controller(),$this->request->action());
// Control Line
// @todo
$this->template->control = '';
// System Messages line
// @todo
$this->template->sysmsg = '';
// Left Item
// @todo
$this->template->left = '';
$this->template->right = '';
$this->template->center = '';
if (! $this->response->body())
$this->response->body((string)Block::factory());
if (empty($this->template->content))
$this->template->content = $this->response->body();
// Footer
// @todo
$this->template->footer = '';
// Our default script(s)
foreach (array('file'=>array_reverse(array(
))) as $type => $datas) {
foreach ($datas as $data) {
Script::add(array(
'type'=>$type,
'data'=>$data,
),TRUE);
}
}
// For any ajax rendered actions, we'll need to capture the content and put it in the response
// @todo
} elseif ($this->request->is_ajax() && isset($this->template->content) && ! $this->response->body()) {
// @todo move this formatting to a view?
if ($s = $this->_sysmsg() AND (string)$s)
$this->response->body(sprintf('<table class="sysmsg"><tr><td>%s</td></tr></table>',$s));
// Since we are ajax, we should re-render the breadcrumb
Session::instance()->set('breadcrumb',(string)Breadcrumb::factory());
$this->response->bodyadd(Script::add(array('type'=>'stdin','data'=>'$().ready($("#ajCONTROL").load("'.URL::site('welcome/breadcrumb').'",null,function(x,s,r) {}));')));
// In case there any javascript for this render.
$this->response->bodyadd(Script::factory());
// Get the response body
$this->response->bodyadd(sprintf('<table class="content"><tr><td>%s</td></tr></table>',$this->template->content));
}
parent::after();
// Generate and check the ETag for this file
if (Kohana::$environment === Kohana::PRODUCTION)
$this->response->check_cache(NULL,$this->request);
}
}

View File

@ -0,0 +1,31 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class extends the core Kohana exception handling
*
* @package PLA
* @category Exceptions
* @author Deon George
* @copyright (c) phpLDAPadmin Development Team
* @license http://dev.phpldapadmin.org/license.html
*/
class PLA_Exception extends Kohana_Exception {
public function __construct($message, array $variables = NULL, $code = 0) {
parent::__construct($message,$variables,$code);
switch ($code) {
case '400':
SystemMessage::add('warn',$message);
Request::current()->redirect('login');
break;
}
echo debug::vars(array('m'=>$message,'v'=>$variables,'c'=>$code,'t'=>$this));die();
}
public function __toString() {
echo __METHOD__;die();
}
}
?>

View File

@ -0,0 +1,51 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class overrides Kohana's URL
*
* @package OSB/Modifications
* @category Classes
* @category Helpers
* @author Deon George
* @copyright (c) phpLDAPadmin Development Team
* @license http://dev.phpldapadmin.org/license.html
*/
class URL extends Kohana_URL {
// Our method paths for different functions
public static $method_directory = array(
'user'=>'',
);
/**
* Wrapper to provide a URL::site() link based on function
*/
public static function link($dir,$src,$site=FALSE) {
if (! $dir)
return $src;
if (! array_key_exists($dir,URL::$method_directory))
throw new Kohana_Exception('Unknown directory :dir for :src',array(':dir'=>$dir,':src'=>$src));
$x = URL::$method_directory[$dir].'/'.$src;
return $site ? URL::site($x) : $x;
}
/**
* Function to reveal the real directory for a URL
*/
public static function dir($dir) {
// Quick check if we can do something here
if (! in_array(strtolower($dir),URL::$method_directory))
return $dir;
// OK, we can, find it.
foreach (URL::$method_directory as $k=>$v)
if (strtolower($dir) == $v)
return ucfirst($k);
// If we get here, we didnt have anything.
return $dir;
}
}
?>

View File

@ -0,0 +1,22 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* OSB authentication configuration
*
* @package PLA
* @category Configuration
* @author Deon George
* @copyright (c) phpLDAPadmin Development Team
* @license http://dev.phpldapadmin.org/license.html
*/
return array(
'driver' => 'LDAP',
// 'hash_method' => '',
'hash_key' => '', // LDAP passwords should be cleartext
'lifetime' => 1209600,
// 'session_key' => 'auth_user',
// 'forced_key' => 'auth_forced',
'pwreset' => FALSE,
);
?>

View File

@ -0,0 +1,22 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* PLA Configuration - System Default Configurable Items.
*
* @package PLA
* @category Configuration
* @author Deon George
* @copyright (c) phpLDAPadmin Development Team
* @license http://dev.phpldapadmin.org/license.html
*/
return array(
'appname' => 'phpLDAPadmin - LDAP Administration', // Our application name, as shown in the title bar of pages
'method_security' => FALSE, // Enables Method Security. Setting to false means any method can be run without authentication
// Our mode level (PRODUCTION, STAGING, TESTING, DEVELOPMENT) - see [Kohana]
'mode' => Kohana::PRODUCTION,
// Our custom theme
'loginpage' => 'welcome',
'theme' => 'bootstrap',
);
?>

View File

@ -0,0 +1,23 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* PLA Configuration - Debug Settings
*
* @package PLA
* @category Configuration
* @author Deon George
* @copyright (c) 2013 phpLDAPadmin Development Team
* @license http://dev.phpldapadmin.org/license.html
*/
return array
(
'ajax'=>FALSE, // AJAX actions can only be run by ajax calls if set to FALSE
'etag'=>FALSE, // Force generating ETAGS
'checkout_notify'=>FALSE, // Test mode to test a particular checkout_notify item
'invoice'=>0, // Number of invoices to generate in a pass
'site'=>FALSE, // Glogal site debug
'show_inactive'=>FALSE, // Show Inactive Items
'task_sim'=>FALSE, // Simulate running tasks
);
?>

Some files were not shown because too many files have changed in this diff Show More