Slim app

parent f1d22bf7
...@@ -77,6 +77,10 @@ if ($bind) { ...@@ -77,6 +77,10 @@ if ($bind) {
$attribute = isset($data[$i][$attribute_name]) ? $data[$i][$attribute_name] : null; $attribute = isset($data[$i][$attribute_name]) ? $data[$i][$attribute_name] : null;
if ($attribute === null) if ($attribute === null)
continue; continue;
if (is_string($attribute)) {
echo "{$attribute_name}:{$attribute}", PHP_EOL;
continue;
}
for ($c = 0; $c < $attribute['count']; $c++) { for ($c = 0; $c < $attribute['count']; $c++) {
echo "{$attribute_name}:{$attribute[$c]}", PHP_EOL; echo "{$attribute_name}:{$attribute[$c]}", PHP_EOL;
} }
......
/vendor/
/logs/*
!/logs/README.md
Διαδικτυακή εφαρμογή SLIM για έλεγχο LDAP χρήστη
================================================
Η εφαρμογή ελέγχει με απλό τρόπο εάν ένα χρήστης υπάρχει στον
κατάλογο και επιστρέφει attributes του χρήστη εφόσον ζητηθούν.
# Οδηγίες
Αντιγράψτε το αρχείο `src\settings.php.dist` σε ένα νέο αρχείο `src\settings.php`
και τροποποιήστε αναλόγως.
```php
return [
'settings' => [
// ...
'ldap' => [
'connectionString' => 'ldap://my.ldap.server',
'domain' => null, // if AD, provide domain name for login
'baseSearchDN' => 'cn=Users,dc=YOURCOMPANY,dc=COM',
]
]
];
```
# Εφαρμογή επίδειξης
Η εφαρμογή έχει αναπτυχθεί με το SLIM framework και παρέχει τις παρακάτω
λειτουργίες. Οι σχετικές path parameters αποδίδονται στις μεθόδους.
Όπου χρησιμοποιούνται επιπλέον παράμετροι, μέσω query string ή post
body, αναφέρονται παρακάτω στις σχετικές παραγράφους περιγραφής των
λειτουργιών.
| HTTP Method | Resource routes | Επιστρεφόμενη τιμή | Μέθοδος που καλείται στο [Gr\Gov\Minedu\Osteam\Slim\App](src/osteam/App.php) |
|-------------+-----------------+--------------------+--------------------------------------------------------------------------------|
| GET | /check/{username}/{password}[/{domain}] | JSON μήνυμα | checkCredentials |
| GET | /get/{username}/{password}[/{domain}]?attributes=[comma separated list of attribute names] | JSON μήνυμα | getInformation |
Η εφαρμογή δημιουργήθηκε με σημείο εκκίνησης το Slim Framework 3 Skeleton Application.
Περισσότερες λεπτομέρειες στη [σελίδα του Slim-Skeleton](https://github.com/slimphp/Slim-Skeleton)
Στο φάκελο [src\osteam](src/osteam) βρίσκεται ο κύριος κώδικας της εφαρμογής και στο
αρχείο [src\routes.php](src/routes.php) καθορίζονται τα σχετικά routes.
**Σημαντικό!** Για να τρέξει η εφαρμογή είναι απαραίτητο μετά τη λήψη της
να εκτελέσετε την εντολή `composer install` μέσα στο ριζικό της φάκελο
(\slim-app).
## Gr\Gov\Minedu\Osteam\Slim\App
Η κλάση [Gr\Gov\Minedu\Osteam\Slim\App](src/osteam/App.php) παρέχει μεθόδους
διευκόλυνσης για τον προγραμματιστή ώστε να μπορεί εύκολα να καταναλώσει
λειτουργίες που αφορούν την πρωτοκόλληση εγγράφων. Οι βασικές λειτουργίες
περιγράφηκαν παραπάνω.
**Τεκμηρίωση παρέχεται εντός του [Gr\Gov\Minedu\Osteam\Slim\App](src/osteam/App.php)**
# Λειτουργίες
> Στα παρακάτω δείγματα κλήσης θεωρείται ότι η εφαρμογή είναι διαθέσιμη στη διεύθυνση: http://generic.local.dev/ldap/
## Επιτυχής έλεγχος στοιχείων
- `http://generic.local.dev/ldap/check/kotsos/kotsos`
Δείγμα απάντησης
```json
{
"result": "true"
}
```
## Ανεπιτυχής έλεγχος στοιχείων
- `http://generic.local.dev/ldap/check/kotsos/lauos/domain`
Δείγμα απάντησης
```json
{
"result": "false"
}
```
## Επιτυχής άντληση στοιχείων
Πρέπει να δοθεί η παράμετρος `attributes` στο query string:
- `attributes` λίστα ονομάτων πεδίων που ζητούνται, χωρισμένη με κόμματα
Παράδειγμα
- `http://generic.local.dev/ldap/get/kotsos/kotsos?attributes=sn,displayname,memberOf`
Δείγμα απάντησης
```json
{
"result": true,
"data": [
{
"sn": [
{
"value": "kotsou"
}
],
"displayname": [
{
"value": "kotsos kotsou"
}
],
"memberof": [
{
"value": "CN=Debugger Users,CN=Users,DC=pdecrete,DC=local"
},
{
"value": "CN=IIS_WPG,CN=Users,DC=pdecrete,DC=local"
}
]
}
]
}```
## Ανεπιτυχής άντληση στοιχείων
- `http://generic.local.dev/ldap/get/kotsos/denyparhei?attributes=sn,displayname,memberOf`
```json
{
"result": false,
"reason": "Λανθασμένα στοιχεία πρόσβασης ή λάθος κατά τη σύνδεση"
}
```
- `http://generic.local.dev/ldap/get/kotsos/kotsos`
```json
{
"result": false,
"reason": "Δεν έχετε καθορίσει λίστα ονομάτων πεδίων"
}
```
## Άλλες πιθανές απαντήσεις της εφαρμογής
Σε περίπτωση λάθους κατά την αναζήτηση θα επιστραφεί μήνυμα αντίστοιχο με το παρακάτω.
```json
{
"result": false,
"reason": "ΕΠΕΞΗΓΗΜΑΤΙΚΟ ΚΕΙΜΕΝΟ"
}
```
Δείγμα απάντησης στο `http://generic.local.dev/ldap/%CE%B1%CF%85%CF%84%CF%8C%20%CE%B4%CE%B5%CE%BD%20%CF%85%CF%80%CE%AC%CF%81%CF%87%CE%B5%CE%B9`
```json
{
"message": "Δεν αναγνωρίστηκε το αίτημα σας",
"in": "array (\n 'anythingelse' => 'αυτό δεν υπάρχει',\n)"
}
```
{
"name": "minedu-osteam/ldap-test-slim-app",
"description": "A Slim Framework application for testing ldap",
"keywords": ["rest", "minedu"],
"homepage": "https://git.minedu.gov.gr/spapad/samples",
"license": "EUPL",
"authors": [
{
"name": "MINEDU OPEN SOURCE TEAM",
"email": "osteam@minedu.gov.gr",
"homepage": "http://ostmgmt.minedu.gov.gr/"
},
{
"name": "Stavros Papadakis",
"email": "spapad@gmail.com"
}
],
"require": {
"php": ">=5.5.0",
"slim/slim": "^3.1",
"slim/php-view": "^2.0",
"monolog/monolog": "^1.17"
},
"require-dev": {
"phpunit/phpunit": ">=4.8 < 6.0"
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"start": "php -S 0.0.0.0:8080 -t public public/index.php",
"test": "phpunit"
}
}
This diff is collapsed.
Your Slim Framework application's log files will be written to this directory.
<phpunit bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="SampleApp">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>
\ No newline at end of file
RewriteEngine On
# Some hosts may require you to use the `RewriteBase` directive.
# If you need to use the `RewriteBase` directive, it should be the
# absolute physical path to the directory that contains this htaccess file.
#
# RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
<?php
if (PHP_SAPI == 'cli-server') {
// To help the built-in PHP dev server, check if the request was actually for
// something which should probably be served as a static file
$url = parse_url($_SERVER['REQUEST_URI']);
$file = __DIR__ . $url['path'];
if (is_file($file)) {
return false;
}
}
$autoloader = require __DIR__ . '/../vendor/autoload.php';
session_name('MineduOsteamApp');
session_start();
date_default_timezone_set('Europe/Athens');
// Instantiate the app
$settings = require __DIR__ . '/../src/settings.php';
$app = new \Slim\App($settings);
$container = $app->getContainer();
// Set up dependencies
require __DIR__ . '/../src/dependencies.php';
// Register middleware
require __DIR__ . '/../src/middleware.php';
//
// setup the app
//
$container['autoloader'] = $autoloader;
$autoloader->addPsr4('Gr\Gov\Minedu\Osteam\Slim\\', __DIR__ . '/../src/osteam');
$container['errorHandler'] = function ($c) {
return function ($request, $response, $exception) use ($c) {
return $c['response']->withJson([
'message' => 'Προέκυψε λάθος',
'in' => $exception->getMessage()
], intval($code = $exception->getCode()) > 0 ? $code : null
);
};
};
//
// end setup the app
//
// Register routes
require __DIR__ . '/../src/routes.php';
// Run app
$app->run();
<?php
// DIC configuration
$container = $app->getContainer();
// view renderer
$container['renderer'] = function ($c) {
$settings = $c->get('settings')['renderer'];
return new Slim\Views\PhpRenderer($settings['template_path']);
};
// monolog
$container['logger'] = function ($c) {
$settings = $c->get('settings')['logger'];
$logger = new Monolog\Logger($settings['name']);
$logger->pushProcessor(new Monolog\Processor\UidProcessor());
$logger->pushHandler(new Monolog\Handler\StreamHandler($settings['path'], $settings['level']));
return $logger;
};
<?php
// Application middleware
// e.g: $app->add(new \Slim\Csrf\Guard);
<?php
namespace Gr\Gov\Minedu\Osteam\Slim;
use Interop\Container\ContainerInterface;
use Slim\Http\Body;
class App
{
const APP_SETTINGS_KEY = 'ldap';
protected $ci = null;
protected $logger = null;
private $connectionString = '';
private $domain = '';
private $baseSearchDN = '';
public function __construct(ContainerInterface $ci)
{
$this->ci = $ci;
$settings = $this->ci->get('settings')[self::APP_SETTINGS_KEY];
$this->connectionString = (isset($settings['connectionString']) ? $settings['connectionString'] : '');
$this->domain = (isset($settings['domain']) ? $settings['domain'] : null);
$this->baseSearchDn = (isset($settings['baseSearchDN']) ? $settings['baseSearchDN'] : '');
if (($logger = $this->ci->get('logger')) != null) {
$this->logger = $logger;
}
}
/**
* Send a JSON formatted string as JSON response to the client.
*
* @param Response $res
* @param mixed $data The data
* @param int $status The HTTP status code.
* @return response
*/
public function withJsonReady($res, $data, $status = null)
{
$response = $res->withBody(new Body(fopen('php://temp', 'r+')));
$response->getBody()->write($data);
$jsonResponse = $response->withHeader('Content-Type', 'application/json;charset=utf-8');
if (isset($status)) {
return $jsonResponse->withStatus($status);
}
return $jsonResponse;
}
/**
* Send a text response string as text/plain response to the client.
*
* @param Response $res
* @param mixed $data The text
* @param int $status The HTTP status code.
* @return response
*/
public function withTextReady($res, $data, $status = null)
{
$response = $res->withBody(new Body(fopen('php://temp', 'r+')));
$response->getBody()->write($data);
$textResponse = $response->withHeader('Content-Type', 'text/plain');
if (isset($status)) {
return $textResponse->withStatus($status);
}
return $textResponse;
}
protected function log($msg)
{
if ($this->logger) {
$this->logger->info($msg);
}
}
/**
* Δοκιμή σύνδεσης
*/
protected function connectAndBind($username, $password, $domain = null)
{
$ldap = ldap_connect($this->connectionString);
if ($domain !== null) {
$this->domain = $domain;
}
$ldaprdn = ($this->domain ? "{$this->domain}\\" : '') . $username;
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
$bind = @ldap_bind($ldap, $ldaprdn, $password);
return ['connection' => $ldap, 'bind' => $bind];
}
/**
* Έλεγχος στοιχείων σύνδεσης.
*
* @param Psr\Http\Message\ServerRequestInterface $req
* @param Psr\Http\Message\ResponseInterface $res
* @param string[] $args Πίνακας με παραμέτρους από το call:
* 'username' Το όνομα χρήστη
* 'password' Ο κωδικός πρόσβασης
* 'domain' Domain name για σύνδεση (προεραιτική)
* @throws \Exception
* @return Response
*/
public function checkCredentials($req, $res, $args)
{
$this->logger->info("checkCredentials::{$args['username']}");
$connectbind = $this->connectAndBind($args['username'], $args['password'], (isset($args['domain']) ? $args['domain'] : null));
if ($connectbind['bind']) {
return $res->withJson(['result' => true]);
} else {
return $res->withJson(['result' => false], 404);
}
}
/**
* Έλεγχος στοιχείων σύνδεσης και παροχή πληροφοριών.
*
* @param Psr\Http\Message\ServerRequestInterface $req
* @param Psr\Http\Message\ResponseInterface $res
* @param string[] $args Πίνακας με παραμέτρους από το call:
* 'username' Το όνομα χρήστη
* 'password' Ο κωδικός πρόσβασης
* 'domain' Domain name για σύνδεση (προεραιτική)
* Επίσης στο query string αναμένεται παράμετρος `attributes`, λίστα
* ονομάτων πεδίων που ζητούνται, χωρισμένη με κόμματα (υποχρεωτική)
* @throws \Exception
* @return Response
*/
public function getInformation($req, $res, $args)
{
$this->logger->info("getInformation::{$args['username']}");
$connectbind = $this->connectAndBind($args['username'], $args['password'], (isset($args['domain']) ? $args['domain'] : null));
if (!$connectbind['bind']) {
return $res->withJson(['result' => false, 'reason' => 'Λανθασμένα στοιχεία πρόσβασης ή λάθος κατά τη σύνδεση'], 404);
}
$get_attributes_requested = $req->getQueryParam('attributes', null);
if ($get_attributes_requested === null) {
return $res->withJson(['result' => false, 'reason' => 'Δεν έχετε καθορίσει λίστα ονομάτων πεδίων'], 400);
}
$get_attributes = explode(',', $get_attributes_requested);
array_walk($get_attributes, function (&$v, $k) {
$v = mb_strtolower($v);
});
$filter = "(sAMAccountName={$args['username']})";
$result = @ldap_search($connectbind['connection'], $this->baseSearchDn, $filter, $get_attributes);
if ($result === false) {
return $res->withJson(['result' => false, 'reason' => ldap_error($connectbind['connection'])], 500);
} else {
$data = ldap_get_entries($connectbind['connection'], $result);
$return_data = [];
for ($i = 0; $i < $data['count']; $i++) {
$item = [];
foreach ($get_attributes as $attribute_name) {
$attribute = isset($data[$i][$attribute_name]) ? $data[$i][$attribute_name] : null;
if ($attribute === null)
continue;
if (is_string($attribute)) {
$item[$attribute_name] = ['value' => $attribute];
continue;
}
$item[$attribute_name] = [];
for ($c = 0; $c < $attribute['count']; $c++) {
$item[$attribute_name][] = ['value' => $attribute[$c]];
}
}
$return_data[] = $item;
}
return $res->withJson(['result' => true, 'data' => $return_data]);
}
}
public function setDebug($debug = true)
{
$this->client->setDebug($debug === true);
return;
}
}
<?php
$app->get('/check/{username}/{password}[/{domain}]', '\Gr\Gov\Minedu\Osteam\Slim\App:checkCredentials');
$app->get('/get/{username}/{password}[/{domain}]', '\Gr\Gov\Minedu\Osteam\Slim\App:getInformation');
$app->any('/[{anythingelse}]', function ($request, $response, $args) {
$this->logger->info("Void response, no action route was enabled");
return $response->withJson([
'message' => 'Δεν αναγνωρίστηκε το αίτημα σας',
'in' => var_export($args, true)
], 404
);
});
<?php
return [
'settings' => [
'displayErrorDetails' => true, // set to false in production
'addContentLengthHeader' => false, // Allow the web server to send the content-length header
// Renderer settings
'renderer' => [
'template_path' => __DIR__ . '/../templates/',
],
// Monolog settings
'logger' => [
'name' => 'slim-app',
'path' => __DIR__ . '/../logs/app.log',
'level' => \Monolog\Logger::DEBUG,
],
//
// app custom settings
'ldap' => [
'connectionString' => 'ldap://my.ldap.server',
'domain' => null, // if AD, provide domain name for login
'baseSearchDN' => 'cn=Users,dc=YOURCOMPANY,dc=COM',
]
],
];
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Slim 3</title>
<link href='//fonts.googleapis.com/css?family=Lato:300' rel='stylesheet' type='text/css'>
<style>
body {
margin: 50px 0 0 0;
padding: 0;
width: 100%;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
text-align: center;
color: #aaa;
font-size: 18px;
}
h1 {
color: #719e40;
letter-spacing: -3px;
font-family: 'Lato', sans-serif;
font-size: 100px;
font-weight: 200;
margin-bottom: 0;
}
</style>
</head>
<body>
<h1>Slim</h1>
<div>a microframework for PHP</div>
<?php if (isset($name)) : ?>
<h2>Hello <?= htmlspecialchars($name); ?>!</h2>
<?php else: ?>
<p>Try <a href="http://www.slimframework.com">SlimFramework</a></p>
<?php endif; ?>
</body>
</html>
<?php
namespace Tests\Functional;
use Slim\App;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Http\Environment;
/**
* This is an example class that shows how you could set up a method that
* runs the application. Note that it doesn't cover all use-cases and is
* tuned to the specifics of this skeleton app, so if your needs are
* different, you'll need to change it.
*/
class BaseTestCase extends \PHPUnit_Framework_TestCase
{
/**
* Use middleware when running application?
*
* @var bool
*/
protected $withMiddleware = true;
/**
* Process the application given a request method and URI
*
* @param string $requestMethod the request method (e.g. GET, POST, etc.)
* @param string $requestUri the request URI
* @param array|object|null $requestData the request data
* @return \Slim\Http\Response
*/
public function runApp($requestMethod, $requestUri, $requestData = null)
{
// Create a mock environment for testing with
$environment = Environment::mock(
[
'REQUEST_METHOD' => $requestMethod,
'REQUEST_URI' => $requestUri
]
);
// Set up a request object based on the environment
$request = Request::createFromEnvironment($environment);
// Add request data, if it exists
if (isset($requestData)) {
$request = $request->withParsedBody($requestData);
}
// Set up a response object
$response = new Response();
// Use the application settings
$settings = require __DIR__ . '/../../src/settings.php';
// Instantiate the application
$app = new App($settings);
// Set up dependencies
require __DIR__ . '/../../src/dependencies.php';
// Register middleware
if ($this->withMiddleware) {
require __DIR__ . '/../../src/middleware.php';
}
// Register routes
require __DIR__ . '/../../src/routes.php';
// Process the application
$response = $app->process($request, $response);
// Return the response
return $response;
}
}
<?php
namespace Tests\Functional;
class HomepageTest extends BaseTestCase
{
/**
* Test that the index route returns a rendered response containing the text 'SlimFramework' but not a greeting
*/
public function testGetHomepageWithoutName()
{
$response = $this->runApp('GET', '/');
$this->assertEquals(200, $response->getStatusCode());
$this->assertContains('SlimFramework', (string)$response->getBody());
$this->assertNotContains('Hello', (string)$response->getBody());
}
/**
* Test that the index route with optional name argument returns a rendered greeting
*/
public function testGetHomepageWithGreeting()
{
$response = $this->runApp('GET', '/name');
$this->assertEquals(200, $response->getStatusCode());
$this->assertContains('Hello name!', (string)$response->getBody());
}
/**
* Test that the index route won't accept a post request
*/
public function testPostHomepageNotAllowed()
{
$response = $this->runApp('POST', '/', ['test']);
$this->assertEquals(405, $response->getStatusCode());
$this->assertContains('Method not allowed', (string)$response->getBody());
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment