And some documentation.
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
# Configure your db driver and server_version in config/packages/doctrine.yaml
DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name
###< doctrine/doctrine-bundle ###
+
+###> symfony/swiftmailer-bundle ###
+# For Gmail as a transport, use: "gmail://username:password@localhost"
+# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode="
+# Delivery is disabled by default via "null://localhost"
+MAILER_URL=null://localhost
+###< symfony/swiftmailer-bundle ###
--- /dev/null
+* Installation:
+---------------
+
+ * Symfony:
+
+We suppose the Symfony framework is already installed with the composer.
+
+$ git clone https://git.piment-noir.org/Project_proches_de_moi-server.git /path/to/apache_document_root
+$ cd /path/to/apache_document_root
+$ composer install
+$ cp .env.dist .env
+$ vi .env
+ edit the line
+ DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name
+ to make it match your database installation
+$ php/console bin/console doctrine:database:create
+
+ * Apache:
+
+Virtual localhost configuration:
+
+<VirtualHost *:80>
+ ServerName domain.tld
+
+ DocumentRoot /path/to/apache_document_root/public
+ <Directory /path/to/apache_document_root/public>
+ AllowOverride None
+ Order Allow,Deny
+ Allow from All
+
+ <IfModule mod_rewrite.c>
+ Options -MultiViews
+ RewriteEngine On
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteRule ^(.*)$ index.php [QSA,L]
+ </IfModule>
+ </Directory>
+
+ # uncomment the following lines if you install assets as symlinks
+ # or run into problems when compiling LESS/Sass/CoffeeScript assets
+ # <Directory /path/to/apache_document_root>
+ # Options FollowSymlinks
+ # </Directory>
+
+ # optionally disable the RewriteEngine for the asset directories
+ # which will allow apache to simply reply with a 404 when files are
+ # not found instead of passing the request into the full symfony stack
+ <Directory /path/to/apache_document_root/public/bundles>
+ <IfModule mod_rewrite.c>
+ RewriteEngine Off
+ </IfModule>
+ </Directory>
+ ErrorLog ${APACHE_LOG_DIR}/domain.tld_error.log
+ CustomLog ${APACHE_LOG_DIR}/domain.tld_access.log combined
+
+ # optionally set the value of the environment variables used in the application
+ #SetEnv APP_ENV prod
+ #SetEnv APP_SECRET <app-secret-id>
+ #SetEnv DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name"
+</VirtualHost>
+
+ * REST resources:
+
+Open the tests/curl.txt file for an overview.
"symfony/lts": "^4@dev",
"symfony/security-bundle": "^4.1",
"symfony/serializer": "^4.1",
+ "symfony/swiftmailer-bundle": "^3.2",
"symfony/translation": "^4.1",
"symfony/twig-bundle": "^4.1",
"symfony/yaml": "^4.1"
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "content-hash": "9f3d3e3ec394c973255663edcef38cdf",
+ "content-hash": "cb8518852a624cb243ef9a1ff48e8ba5",
"packages": [
{
"name": "doctrine/annotations",
],
"time": "2018-02-27T07:30:56+00:00"
},
+ {
+ "name": "egulias/email-validator",
+ "version": "2.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/egulias/EmailValidator.git",
+ "reference": "8790f594151ca6a2010c6218e09d96df67173ad3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/8790f594151ca6a2010c6218e09d96df67173ad3",
+ "reference": "8790f594151ca6a2010c6218e09d96df67173ad3",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/lexer": "^1.0.1",
+ "php": ">= 5.5"
+ },
+ "require-dev": {
+ "dominicsayers/isemail": "dev-master",
+ "phpunit/phpunit": "^4.8.35||^5.7||^6.0",
+ "satooshi/php-coveralls": "^1.0.1"
+ },
+ "suggest": {
+ "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Egulias\\EmailValidator\\": "EmailValidator"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Eduardo Gulias Davis"
+ }
+ ],
+ "description": "A library for validating emails against several RFCs",
+ "homepage": "https://github.com/egulias/EmailValidator",
+ "keywords": [
+ "email",
+ "emailvalidation",
+ "emailvalidator",
+ "validation",
+ "validator"
+ ],
+ "time": "2018-04-10T10:11:19+00:00"
+ },
{
"name": "friendsofsymfony/rest-bundle",
"version": "2.3.1",
},
{
"name": "sensio/framework-extra-bundle",
- "version": "v5.1.6",
+ "version": "v5.2.0",
"source": {
"type": "git",
"url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git",
- "reference": "bf4940572e43af679aaa13be98f3446a1c237bd8"
+ "reference": "50e8b7292425957b8fd66887504430c89bcbd83c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/bf4940572e43af679aaa13be98f3446a1c237bd8",
- "reference": "bf4940572e43af679aaa13be98f3446a1c237bd8",
+ "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/50e8b7292425957b8fd66887504430c89bcbd83c",
+ "reference": "50e8b7292425957b8fd66887504430c89bcbd83c",
"shasum": ""
},
"require": {
"doctrine/common": "^2.2",
"symfony/config": "^3.3|^4.0",
"symfony/dependency-injection": "^3.3|^4.0",
- "symfony/framework-bundle": "^3.3|^4.0",
+ "symfony/framework-bundle": "^3.4|^4.0",
"symfony/http-kernel": "^3.3|^4.0"
},
"require-dev": {
"symfony/dom-crawler": "^3.3|^4.0",
"symfony/expression-language": "^3.3|^4.0",
"symfony/finder": "^3.3|^4.0",
+ "symfony/monolog-bridge": "^3.0|^4.0",
+ "symfony/monolog-bundle": "^3.2",
"symfony/phpunit-bridge": "^3.3|^4.0",
"symfony/psr-http-message-bridge": "^0.3",
"symfony/security-bundle": "^3.3|^4.0",
"type": "symfony-bundle",
"extra": {
"branch-alias": {
- "dev-master": "5.1.x-dev"
+ "dev-master": "5.2.x-dev"
}
},
"autoload": {
"annotations",
"controllers"
],
- "time": "2018-02-14T08:40:54+00:00"
+ "time": "2018-05-12T09:37:42+00:00"
+ },
+ {
+ "name": "swiftmailer/swiftmailer",
+ "version": "v6.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/swiftmailer/swiftmailer.git",
+ "reference": "412333372fb6c8ffb65496a2bbd7321af75733fc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/412333372fb6c8ffb65496a2bbd7321af75733fc",
+ "reference": "412333372fb6c8ffb65496a2bbd7321af75733fc",
+ "shasum": ""
+ },
+ "require": {
+ "egulias/email-validator": "~2.0",
+ "php": ">=7.0.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "~0.9.1",
+ "symfony/phpunit-bridge": "~3.3@dev"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "lib/swift_required.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Chris Corbyn"
+ },
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ }
+ ],
+ "description": "Swiftmailer, free feature-rich PHP mailer",
+ "homepage": "http://swiftmailer.symfony.com",
+ "keywords": [
+ "email",
+ "mail",
+ "mailer"
+ ],
+ "time": "2017-09-30T22:39:41+00:00"
},
{
"name": "symfony/cache",
"homepage": "https://symfony.com",
"time": "2018-06-22T08:59:39+00:00"
},
+ {
+ "name": "symfony/swiftmailer-bundle",
+ "version": "v3.2.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/swiftmailer-bundle.git",
+ "reference": "f1ba0552a9cd4df0191a58845fbd5541cf9eda2d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/swiftmailer-bundle/zipball/f1ba0552a9cd4df0191a58845fbd5541cf9eda2d",
+ "reference": "f1ba0552a9cd4df0191a58845fbd5541cf9eda2d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0",
+ "swiftmailer/swiftmailer": "^6.0.1",
+ "symfony/config": "~2.8|~3.3|~4.0",
+ "symfony/dependency-injection": "~2.7|~3.3|~4.0",
+ "symfony/http-kernel": "~2.7|~3.3|~4.0"
+ },
+ "require-dev": {
+ "symfony/console": "~2.7|~3.3|~4.0",
+ "symfony/framework-bundle": "~2.7|~3.3|~4.0",
+ "symfony/phpunit-bridge": "~3.3|~4.0",
+ "symfony/yaml": "~2.7|~3.3|~4.0"
+ },
+ "suggest": {
+ "psr/log": "Allows logging"
+ },
+ "type": "symfony-bundle",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.2-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Bundle\\SwiftmailerBundle\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Symfony Community",
+ "homepage": "http://symfony.com/contributors"
+ },
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ }
+ ],
+ "description": "Symfony SwiftmailerBundle",
+ "homepage": "http://symfony.com",
+ "time": "2018-04-03T16:29:41+00:00"
+ },
{
"name": "symfony/templating",
"version": "v4.1.1",
Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
+ Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true],
];
--- /dev/null
+# See https://symfony.com/doc/current/email/dev_environment.html
+swiftmailer:
+ # send all emails to a specific address
+ #delivery_addresses: ['me@example.com']
--- /dev/null
+swiftmailer:
+ url: '%env(MAILER_URL)%'
+ spool: { type: 'memory' }
--- /dev/null
+swiftmailer:
+ disable_delivery: true
#index:
# path: /
# controller: App\Controller\DefaultController::index
-person:
- type: rest
- resource: App\Controller\PersonController
$person = new Person();
$person->setFirstname($request->get('firstname'));
$person->setLastName($request->get('lastname'));
+ //TODO: email creation should normally have a verification step
$person->setEmail($request->get('email'));
$person->setPassword($request->get('password'));
$person->setOnline(false);
$person->setFirstName($request->get('firstname'));
$person->setLastName($request->get('lastname'));
+ //TODO: email update should normally have a verification step
$person->setEmail($request->get('email'));
$em->merge($person);
return $this->view($person, Response::HTTP_CREATED, ['Location' => $this->generateUrl('show_person', ['id' => $person->getId(), UrlGeneratorInterface::ABSOLUTE_URL])]);
}
+ /**
+ * @Rest\Post(
+ * path = "/api/person/authenticate",
+ * name = "authenticate_person"
+ * )
+ * @Rest\View(StatusCode = Response::HTTP_ACCEPTED)
+ */
+ public function authenticatePersonAction(Request $request)
+ {
+ $em = $this->getDoctrine()->getManager();
+ $person = $em->getRepository('App:Person')->findOneBy(['email' => $request->get('email')]);
+
+ if (empty($person)) {
+ return $this->PersonNotFound();
+ }
+
+ if ($request->get('password') != $person->getPassword()) {
+ return $this->PersonWrongPassword();
+ } else {
+ return $this->view($person, Response::HTTP_ACCEPTED, ['Location' => $this->generateUrl('show_person', ['id' => $person->getId(), UrlGeneratorInterface::ABSOLUTE_URL])]);
+ }
+ }
+
/**
* @Rest\Get("/api/person/{id}/localisations")
* @Rest\View()
return $localisations;
}
+ /**
+ * @Rest\Get(
+ * path = "/api/person/{id}/localisations/fuzzy/{distance}",
+ * name = "person_localisations_fuzzy",
+ * requirements = {"id"="\d+", "distance"="\d+"}
+ * )
+ * @Rest\View()
+ */
+ public function getLocalisationsFuzzyAction(Request $request)
+ {
+ //TODO: Check that the authenticated user is allowed to see the localisation
+ $em = $this->getDoctrine()->getManager();
+ $localisations = $em->getRepository('App:Localisation')->findBy(['person' => $request->get('id')]);
+
+ if (empty($localisations)) {
+ return $this->PersonLocalisationsNotFound();
+ }
+
+ if (!$this->chk_distance($request->get('distance'), 200, 500)) {
+ return $this->PersonLocalisationFuzzyWrongDistance();
+ }
+
+ $fuzzy_localisations = array_map(function($item) use ($request) { return $this->randomizeLocation($item, $request->get('distance'), 200, 500); }, $localisations);
+
+ return $fuzzy_localisations;
+ }
+
+ private function getLastLocalisation($em, $id) {
+ $query = $em->createQuery("SELECT l1 FROM App\Entity\Localisation l1 WHERE l1.person = :person and l1.timestamp = (SELECT MAX(l2.timestamp) FROM App\Entity\Localisation l2 WHERE l2.person = l1.person)");
+ $query->setParameter('person', $id);
+ return $query->getResult()[0];
+ }
+
/**
* @Rest\Get("/api/person/{id}/localisation")
* @Rest\View()
//TODO: Check that the authenticated user is allowed to see the localisation
$em = $this->getDoctrine()->getManager();
- $query = $em->createQuery("SELECT l1 FROM App\Entity\Localisation l1 WHERE l1.person = :person and l1.timestamp = (SELECT MAX(l2.timestamp) FROM App\Entity\Localisation l2 WHERE l2.person = l1.person)");
- $query->setParameter('person', $request->get('id'));
- $localisation = $query->getResult();
+ $localisation = $this->getLastLocalisation($em, $request->get('id'));
if (empty($localisation)) {
return $this->PersonLocalisationNotFound();
return $localisation;
}
+ private function chk_distance($distance, $min, $max) {
+ if ($distance >= $min && $distance <= $max) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private function randomizeLocation($localisation, $distance, $min, $max) {
+ // Generate random float in [0, 1[, [0, 1)
+ $u = rand(0, getrandmax() - 1) / getrandmax();
+ $v = rand(0, getrandmax() - 1) / getrandmax();
+
+ if ($this->chk_distance($distance, $min, $max)) {
+ $r = $distance / 111300;
+ } else {
+ return $this->PersonLocalisationFuzzyWrongDistance();
+ }
+
+ $w = $r * sqrt($u);
+ $t = 2 * pi() * $v;
+
+ $x = $w * cos($t);
+ $lng_off = $x / cos(deg2rad($localisation->getLatitude()));
+ $lat_off = $w * sin($t);
+
+ $fuzzy_localisation = new Localisation();
+ $fuzzy_localisation->setTimestamp($localisation->getTimestamp());
+ $fuzzy_localisation->setLatitude($localisation->getLatitude() + $lat_off);
+ $fuzzy_localisation->setLongitude($localisation->getLongitude() + $lng_off);
+ return $fuzzy_localisation;
+ }
+
+ /**
+ * @Rest\Get(
+ * path = "/api/person/{id}/localisation/fuzzy/{distance}",
+ * name = "person_localisation_fuzzy",
+ * requirements = {"id"="\d+", "distance"="\d+"}
+ * )
+ * @Rest\View()
+ */
+ public function getLocalisationFuzzyAction(Request $request)
+ {
+ //TODO: Check that the authenticated user is allowed to see the localisation
+ $em = $this->getDoctrine()->getManager();
+
+ $localisation = $this->getLastLocalisation($em, $request->get('id'));
+
+ if (empty($localisation)) {
+ return $this->PersonLocalisationNotFound();
+ }
+
+ return $this->randomizeLocation($localisation, $request->get('distance'), 200, 500);
+ }
+
/**
* @Rest\Post("/api/person/{id}/localisation")
* @Rest\View(StatusCode = Response::HTTP_CREATED)
return $person->getFriends();
}
+ /**
+ * @Rest\Get(
+ * path = "/api/person/{id}/friendswithme",
+ * name = "show_person_friends_with_me",
+ * requirements = {"id"="\d+"}
+ * )
+ * @Rest\View()
+ */
+ public function showPersonFriendsWithMe(Request $request)
+ {
+ $em = $this->getDoctrine()->getManager();
+ $person = $em->getRepository('App:Person')->find($request->get('id'));
+
+ if (empty($person)) {
+ return $this->PersonNotFound();
+ }
+
+ return $person->getFriendsWithMe();
+ }
+
/**
* @Rest\Get(
* path = "/api/persons",
return View::create(['message' => 'Person localisations not found'], Response::HTTP_NOT_FOUND);
}
+ private function PersonWrongPassword() {
+ return View::create(['message' => 'Supplied password do not match'], Response::HTTP_UNAUTHORIZED);
+ }
+ private function PersonLocalisationFuzzyWrongDistance() {
+ return View::create(['message' => 'Distance range do not match'], Response::HTTP_NOT_ACCEPTABLE);
+ }
+
}
/**
* @ORM\Id
* @ORM\Column(type="bigint")
- * @ORM\GeneratedValue
+ * @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
"doctrine/orm": {
"version": "v2.6.1"
},
+ "egulias/email-validator": {
+ "version": "2.1.4"
+ },
"friendsofsymfony/rest-bundle": {
"version": "2.2",
"recipe": {
"ref": "aaddfdf43cdecd4cf91f992052d76c2cadc04543"
}
},
+ "swiftmailer/swiftmailer": {
+ "version": "v6.0.2"
+ },
"symfony/cache": {
"version": "v4.1.0"
},
"symfony/serializer": {
"version": "v4.1.1"
},
+ "symfony/swiftmailer-bundle": {
+ "version": "2.5",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "2.5",
+ "ref": "3db029c03e452b4a23f7fc45cec7c922c2247eb8"
+ }
+ },
"symfony/templating": {
"version": "v4.1.1"
},
curl --request DELETE http://localhost:8000/api/person/1
* Update a user with id 1:
-curl --request PUT http://localhost:8000/api/person/1 --data "{ \"firstname\": \"Jamesson\", \"lastname\": \"Elbow\", \"email\": \"james@elbow.com\" }" --header "Content-Type: application/json"
+curl --request PUT http://localhost:8000/api/person/1 --data "{ \"firstname\": \"Jamesson\", \"lastname\": \"Elbow\", \"email\": \"jamesson@elbow.com\" }" --header "Content-Type: application/json"
+
+* Authenticate a user with email and password:
+curl --request POST http://localhost:8000/api/person/authenticate --data "{ \"email\": \"jamesson@elbow.com\", \"password\": \"FD3CC7719AB1776B81D70B65170D8A51119E127488C106622E0D2E680C1B3FFF\"}" --header "Content-Type: application/json"
* Update/add a user with id 1 localisation:
curl --request POST http://localhost:8000/api/person/1/localisation --data "{ \"timestamp\": \"$(date --iso-8601=seconds)\", \"latitude\": \"43.23\", \"longitude\": \"5.43\" }" --header "Content-Type: application/json"
* Show a user with id 1 friends:
curl --request GET http://localhost:8000/api/person/1/friends
+* Show a user with id 1 friends with me:
+curl --request GET http://localhost:8000/api/person/1/friendswithme
+
* Show a user with id 1 localisation:
curl --request GET http://localhost:8000/api/person/1/localisation
+* Show a user with id 1 fuzzy localisation at a distance of 300 meters:
+curl --request GET http://localhost:8000/api/person/1/localisation/fuzzy/300
+
* Show a user with id 1 localisations:
curl --request GET http://localhost:8000/api/person/1/localisations
+
+* Show a user with id 1 fuzzy localisations at a distance of 300 meters:
+curl --request GET http://localhost:8000/api/person/1/localisations/fuzzy/300