import time
import webbrowser
import win32clipboard
from urllib.parse import urlparse
print("##########################")
print("# Listening to clipboard #")
print("##########################")
prevData = ''
# Clear clipboard
try:
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.CloseClipboard()
except:
print('Error!')
# Listen to clipboard paste event
while(True):
win32clipboard.OpenClipboard()
try:
data = win32clipboard.GetClipboardData()
except:
data = ''
win32clipboard.CloseClipboard()
if(data):
print("[DEBUG] {data} " + data)
print("[DEBUG] {prevData} " + prevData)
if(prevData != data):
prevData = data
parsedUrl = urlparse(data)
# check regex
if(parsedUrl.scheme and parsedUrl.netloc):
print("[OPEN URL] " + data)
webbrowser.open(data, new=2)
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.CloseClipboard()
time.sleep(1)
Config TypeScript and create aliases in Webpack Encore from Symfony
In the following article we will learn how to configure and create aliases in Webpack Encore from Symfony to be able to use TypeScript in our projects. We will also learn how to configure Webpack aliases that point to directories in our project in order to import our TypeScript classes easily without having to use relative paths.
The first thing we have to do is to install the tsconfig-paths-webpack-plugin package with NPM:
npm i --save tsconfig-paths-webpack-pluginOnce installed, we are going to create the tsconfig.json file (in our case we are going to do it inside the /assets folder that is where we have all the assets of our project) with the following content.
{
"compilerOptions": {
"sourceMap": true,
// Last ECMAScript version.
"target": "esnext",
// Search in node_modules for non-relative import.
"moduleResolution": "node",
// Enables strict mode for settings such as strictNullChecks & noImplicitAny.
"strict": true,
"jsx": "react",
"isolatedModules": true,
"esModuleInterop": true,
"baseUrl": "./ts/",
"paths": {
"@ts/*": ["*"]
}
}
}Important properties to consider:
- baseUrl: the relative path where our TypeScript files will be.
- paths: the paths that will have the aliases that we will define later in Webpack.
Now we need to add the plugin to the Webpack Encore configuration. So let’s edit the webpack.config.js file located in the root of our Symfony project.
In the following example we will create several aliases, so we will need to have the Path plugin installed.
const Encore = require('@symfony/webpack-encore');
const path = require('path');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const ROOT_PATH = path.resolve(__dirname, './');
const ASSETS_PATH = ROOT_PATH + '/assets';
const ALIASES = {
'@assets' : ASSETS_PATH,
'@ts' : ASSETS_PATH + '/ts',
'@styles' : ASSETS_PATH + '/styles'
};
Encore
.setOutputPath('public/build/')
.setPublicPath('/build')
// Here we tell Webpack that we want to have these aliases available
.addAliases(ALIASES)
// Here there would be more configuration details that we are going to omit
// ...
;Now, at the end of our webpack.config.js file we have to add the following code:
var config = Encore.getWebpackConfig()
config.resolve.plugins = [
new TsconfigPathsPlugin({
configFile: './assets/tsconfig.json',
extensions: ['.jsx', '.json', '.ts', '.tsx']
})
]
module.exports = configThere we indicate where our configuration file tsconfig.json is located and the extensions we want to accept.
Once this is done, we can use the aliases defined in Webpack as follows:
import {BaseController} from "@ts/Controller/BaseController"
import {ImportedClass} from "@ts/ImportedClass"
class MyFirstController extends BaseController {
execute(): void {
let importedClass = new ImportedClass()
importedClass.execute()
}
}
export {MyFirstController}And that would be all.
Any questions, see you in the comments 😉
Using multiple SSH keys for same host in Ubuntu
Any developer has at least one SSH key, mostly used for login through SSH to remote machines or working with GIT repositories.
When we try to connect through SSH to another machine or pulling from a GIT repository, our system is going to use the first SSH key we created by default.
So, what happens if we want to use different SSH Keys for same server or same repository? How we deal with this trouble?.
We have to make a config file in our SSH folder and specify to the system which key has to use given host.
Let’s see an example where we have two different repositories hosted in Github.com and we want to use different SSH keys for each repository.
First we have to create our ~/.ssh/config file:
Host project_1
HostName github.com
IdentityFile ~/ssh/dev_key
Host project_2
HostName github.com
IdentityFile ~/ssh/deploy_key
For example, it is common having multiple SSH keys in our production server, where each SSH key have access to specific repositories.
Now in our production server, if we want to pull changes for our project_1 we have to do run this command:
git pull git@project_1:user/project.git
For project_2, will be the same command with the host in the repository url modified:
git pull git@project_2:user/project.git
Analyzing the config file, Host it’s the alias for our hostname, in our case github.com, and in the IdentityFile we link to our SSH key path.
That’s all, easy and powerful.
Creating alias for Linux command line
Command-line alias is a quick and useful tool for those repetitive commands which we are continuously executing in our terminal. We can transform large and painful commands into a one word length alias.
Let’s see how to make it:
First of all, we have to open a new terminal in our favorite Linux distribution.
Now edit the .bashrc file which is located in our home directory. To do this we have to type the next command in the terminal:
sudo nano ~/.bashrc
Then nano editor (you can use your favorite text editor) will be open and we can add a new alias (one per line).
When defining a new alias it must have the following structure:
alias ALIAS_NAME='OUR_ALIAS_COMMAND'
For example, we are going to add this new alias:
# Download all servers backups alias remote_backups='python /var/www/scripts/remote_backups.py'
After edit our .bashrc file, we have to save the file changes.
Last step is loading the .bashrc file into the current command prompt executing the following command:
source ~/.bashrc
Now we are ready to execute our aliases in the terminal.
# Alias execution example (in a terminal) $ remote_backups
Como crear un acceso único para varias aplicaciones – Introducción
Cuando se gestionan varias aplicaciones para un mismo grupo de usuarios (como suele pasar en la mayoría de las empresas), es un verdadero fastidio tener que identificarse en cada una de las distintas aplicaciones, más aún cuando en cada una los criterios de acceso son completamente distintos: en uno se identifica con email, en otro con un nombre de usuario, diferentes contraseñas, etcétera.
Para solventar este problema, la mejor opción es crear un único acceso y una vez el usuario se haya identificado, podrá acceder a todas las aplicaciones a las que tenga acceso.
Para quien todavía no entiende de lo que estamos hablando, el ejemplo más conocido es Google. Una vez que te identificas con tu cuenta de Google, tienes acceso a todos sus sitios y aplicaciones: Gmail, Youtube, Maps, Analytics, etcétera.
Además, lo mejor de todo esto, es que al tener nuestros usuarios centralizados en una sóla base de datos, tendremos una mejor gestión y monitorización sobre todos los usuarios del sistema, evitando también los fallos de seguridad que puedan existir en los procesos de autentificación en alguna de nuestras aplicaciones.
En esta serie de artículos os vamos a mostrar como crear este sistema de acceso único desde cero. Pero en el artículo actual, sólo vamos a hacer una introducción sobre qué tecnologías y metodologías vamos a utilizar para crear este sistema.
Como nuestro sistema será una API REST, podremos integrarlo con cualquier tipo de aplicación: web, móvil, escritorio, etcétera.
¿Qué podrá hacer nuestro sistema?
Nuestro sistema gestionará lo siguiente:
- Usuarios
- Crear / Editar / Eliminar
- Habilitar y deshabilitar cuentas de usuario.
- Búsqueda de usuarios
- Recuperar contraseñas
- Validación contra servidor LDAP (Active Directory)
- Roles de usuarios (API)
- Administrador
- Usuario genérico
- Aplicaciones
- Roles de los usuarios en las aplicaciones
- Datos adicionales de los usuarios necesarios para las aplicaciones
- Si por ejemplo en una de nuestras aplicaciones necesitamos guardar y conocer el «Centro de trabajo» del usuario actual, este dato lo podemos guardar en la base de datos de nuestra aplicación y sincronizarlo con nuestra API. Así, si modificamos este dato en cualquier usuario desde el sistema, la API sincronizará los cambios en la base de datos de la aplicación y viceversa.
¿Y cómo va a funcionar todo esto?
La forma rápida de explicarlo sería así:
Todas nuestras aplicaciones preguntarán en cada petición a nuestro sistema de acceso único si el usuario es válido (está autentificado) y tiene permisos de acceso a dicha aplicación.
Representación gráfica (click para ampliar)
Y ahora vamos a entrar un poco más en detalle:
Para poder crear un login único en todas las aplicaciones se utiliza un proceso de autentificación llamado JWT (Javascript web tokens).
¿Cómo funciona el proceso de autentificación?
Los usuarios se identifican a través de un Front-End en el que aparece un formulario de login.
Cuando se envía el formulario, el Front-End hace una petición a la API para comprobar si las credenciales proporcionadas por el usuario son correctas.
En el caso de que las credenciales sean correctas, se crea una cookie en el navegador que contiene el JWT (en el caso de las aplicaciones web), la cual es válida para todos los subdominios de nuestro dominio *.albertolabs.com.
¿Cómo saben las aplicaciones si mi cookie es válida?
Las aplicaciones que están integradas, comprueban en cada petición si el JWT que contiene la cookie es válido preguntando a la API.
Para que el JWT sea válido, se deben cumplir ciertas condiciones previamente definidas:
- Que el JWT se haya generado desde el servidor / RSA Public / Private keys.
- El usuario tiene que estar habilitado (tanto en la bbdd de la API).
- El usuario debe tener acceso (un rol asignado) a la aplicación a la que se accede.
Si el JWT es válido y se cumple lo mencionado anteriormente, podemos acceder a la aplicación, de no ser válido, nos enviará al Front-End para que nos identifiquemos.
¿Qué información contiene el JWT?
El JWT contiene la información básica del usuario que proporciona desde la tabla Users en la base de datos de la API.
También incluye el listado de aplicaciones a las que el usuario tiene acceso junto con su rol correspondiente.
Ejemplo del contenido de un JWT:
{
"id": 13,
"type": "api",
"username": "albertolabs",
"email": "info[at]albertolabs[dot]com",
"role": [
"ROLE_SUPERADMIN"
],
"name": "Alberto",
"surnames": "~",
"avatar": null,
"apps": {
"intranet": {
"role": "ROLE_SUPERADMIN"
},
"issues_portal": {
"role": "ROLE_SUPERADMIN"
}
},
"exp": 1495034731,
"iat": 1495002331
}
En el próximo artículo comenzaremos con la parte técnica y con el desarrollo de nuestra API.
Cómo crear un contenedor Docker con PHP y Nginx
La forma más sencilla para crear contenedores de Docker que contengan el servidor web Nginx y PHP instalado junto con las extensiones que necesitemos y otros servicios comunes en el desarrollo de aplicaciones web, es utilizando el generador de contenedores PhpDocker.io.
Al entrar en la página, nos muestra una breve descripción del servicio:
PhpDocker.io es una herramienta que te ayuda a construir el entorno de desarrollo típico de PHP en un contenedor de Docker con unos pocos clicks. Soporta los servicios más comunes (MySQL/MariaDB, Redis, Elasticsearch …), y más que están por llegar. Soporta PHP 7.1, además de la versión 7.0 y la 5.6.
Para generar nuestro contenedor pinchamos en el enlace Generator.
En el primer bloque, seleccionamos la configuración básica de nuestro contenedor:
- Project name: El nombre de nuestro proyecto.
- Base port: El puerto donde se ejecutará nuestro contenedor.
- Application type: El tipo de aplicación de PHP (Genérica, Symfony, Phalcon 3, Silex). En esta opción, si por ejemplo elegimos Symfony, nos configurará Nginx para que funcione con una aplicación de Symfony (vinculando el archivo app.php como controlador frontal, etc).
- Max upload size (MB): Límite de tamaño por archivo para las subidas al servidor.
En el segundo bloque, debemos especificar la versión que queremos de PHP y las extensiones que necesitemos.
Más abajo, el generador nos permite añadir otros servicios a nuestro contenedor múltiple. Estos servicios son MySQL, MariaDB, Postgres, Elasticsearch, Memcached, Redis y Mailhog.
Y por último, pinchamos en el botón Generate project archive, lo que nos generará un archivo .zip que incluye lo siguiente:
- docker-compose.yml
- phpdocker: En esta carpeta se incluyen los servicios que hemos añadido junto con sus archivos de configuración.
- Readme.htnl: Un archivo HTML donde nos indica cómo ejecutar nuestro contenedor.
- Readme.md
He creado un repositorio en Github donde podéis ver como tengo configurado un proyecto con Symfony utilizando Nginx y PHP-FPM 7.1.
Además, dentro del contenedor de PHP-FPM he instalado las extensiones MySQL y LDAP de PHP junto con GIT y Composer.
Para cualquier duda o sugerencia podéis dejar un comentario en la entrada.
Obtener el tiempo transcurrido de una fecha con PHP
Cuando queremos mostrar fechas con PHP, lo normal es mostrar el valor formateándolo como queramos, por ejemplo: d-m-Y H:i:s nos devolverá la fecha 14-05-2017 18:43:22.
Pero si queremos mostrar el tiempo transcurrido desde la fecha hasta el momento actual, como lo hacen las redes sociales en los timelines, podemos utilizar el siguiente método de PHP:
function getElapsedTime($datetime)
{
if( empty($datetime) )
{
return;
}
// check datetime var type
$strTime = ( is_object($datetime) ) ? $datetime->format('Y-m-d H:i:s') : $datetime;
$time = strtotime($strTime);
$time = time() - $time;
$time = ($time<1)? 1 : $time;
$tokens = array (
31536000 => 'año',
2592000 => 'mes',
604800 => 'semana',
86400 => 'día',
3600 => 'hora',
60 => 'minuto',
1 => 'segundo'
);
foreach ($tokens as $unit => $text)
{
if ($time < $unit) continue;
$numberOfUnits = floor($time / $unit);
$plural = ($unit == 2592000) ? 'es' : 's';
return $numberOfUnits . ' ' . $text . ( ($numberOfUnits > 1) ? $plural : '' );
}
}
A éste método le podemos pasar un objeto tipo Datetime o una fecha como String.
Si ejecutamos el siguiente código introduciéndole una fecha anterior a la actual como parámetro, nos devolverá algo parecido a esto:
// Ejecutamos la función ...
echo 'Publicado hace: ' . getElapsedTime('2017-05-14 19:00:00');
// Resultado => Publicado hace 1 hora.
Sincronizar los archivos de un proyecto automáticamente en Sublime Text utilizando un directorio compartido con Samba
Actualmente, para mis desarrollos web trabajo sobre Windows 10 con Sublime Text y un directorio compartido con Samba que conecta con un servidor Ubuntu en local, donde tengo instaladas todas mis herramientas necesarias: PHP 7, MySQL/MariaDB, Composer, GIT, Bower, etcétera.
Cuando te encuentras en Windows y añades tu directorio como proyecto en Sublime Text, a la hora de generar nuevos archivos o directorios desde fuera del editor, ya bien sea directamente desde el explorador de Windows en un directorio de tu proyecto, o utilizando algún generador como la consola de Symfony, nos damos cuenta rápidamente de que estos nuevos archivos que han sido generados no aparecen en nuestro proyecto de Sublime Text, por lo menos hasta que volvemos a reiniciar Sublime Text, algo que es un verdadero rollo ya que Sublime Text a veces tarda demasiado en arrancar si tenemos varios proyectos en nuestro menú izquierdo.
En el foro oficial de Sublime Text, nos enteramos de que los directorios compartidos con Samba no envían por defecto el evento de notificación ReadDirectoryChangesW de la Win32API, por lo que a Sublime Text no le llega la notificación de que algún archivo ha cambiado.
Para solucionar este problema, debemos añadir el siguiente valor a nuestro archivo smb.conf.
En una distribución de Ubuntu, habría que seguir los siguientes pasos:
- Abre un terminal
- Edita el archivo de configuración
sudo nano /etc/samba/smb.conf
- Añade la siguiente línea debajo de la línea que pone [global]
change notify = yes
- Guarda los cambios y cierra el archivo
- Reinicia los servicios de Samba
sudo service smbd restart sudo service nmbd restart
- Elimina tu proyecto en Sublime Text y vuelve a añadirlo.
- Voila. Tus proyectos ya se sincronizan en tu editor favorito.
Custom login with LDAP in Symfony
Last months I were working on a new Symfony application where the users needed to be authenticated against a Windows Active Directory.
Our custom login authentication process will do this:
- User sign in through a login form
- We connect to our LDAP server and check if user credentials are correct.
- If credentials are correct, we check if the user exists on our database user table.
- If the user exists, we update the last login field, else we create the new user on database user table.
- We log the user into our Symfony application.
First step is to create a service class for authenticating users with LDAP:
<?php
// src/Services/Utils/Ldap.php
namespace Services\Utils;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class Ldap
{
private $em, $request, $container;
private $strLdapServer, $strLdapDN;
private $objLdapBind, $strLdapFilter, $strLdapDC, $objLdapConnection;
private $arrLoginResult, $strUserEmail, $strUserPasswd;
public function __construct(ContainerInterface $container, Request $request)
{
$this->request = $request;
$this->container = $container;
// LDAP CONFIG
$this->strLdapServer = "192.168.1.2";
$this->strLdapDN = "DomainName";
$this->strLdapDC = "dc=DcName,dc=local";
// init vars
$this->objLdapBind = false;
$this->objLdapConnection = false;
}
// Load LDAP config
private function loadLdapConfig()
{
$this->strLdapFilter = "(sAMAccountName=" . $this->strUserEmail . ")";
$this->strLdapServer = "ldap://" . $this->strLdapServer;
$this->strLdapDN = $this->strLdapDN . "\\" . $this->strUserEmail;
}
// Connects to LDAP server
private function connectToLdapServer()
{
$this->objLdapConnection = ldap_connect($this->strLdapServer);
ldap_set_option($this->objLdapConnection, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($this->objLdapConnection, LDAP_OPT_REFERRALS, 0);
$this->objLdapBind = @ldap_bind($this->objLdapConnection, $this->strLdapDN, $this->strUserPasswd);
}
// Get username and password form login form
private function getLdapUsernameAndPassword()
{
$this->strUserEmail = $this->request->request->get('email');
$this->strUserPasswd = $this->request->request->get('password');
if( ! empty($this->strUserEmail) && ! empty($this->strUserPasswd))
{
$this->arrLoginResult['USER_EMAIL'] = $this->strUserEmail;
$this->arrLoginResult['PASSWORD'] = $this->strUserPasswd;
// get only username, deleting all data after @
if(preg_match('/@/', $this->strUserEmail))
{
$arrUserData = explode("@", $this->strUserEmail);
$this->strUserEmail = $arrUserData[0];
}
}
else
{
$this->arrLoginResult['ERROR'] = "EMPTY_CREDENTIALS";
}
}
// check ldap login with username and password
public function checkLdapLogin()
{
$this->arrLoginResult = array(
'LOGIN' => 'ERROR',
'ERROR' => 'INIT',
'USER_EMAIL' => NULL,
'PASSWORD' => NULL,
'USERNAME' => NULL
);
// get username and password
$this->getLdapUsernameAndPassword();
if( ! empty($this->strUserEmail) && ! empty($this->strUserPasswd) )
{
// load LDAP config
$this->loadLdapConfig();
// connect to server
$this->connectToLdapServer();
// check connection result
if($this->objLdapBind)
{
// login ok
$this->arrLoginResult['LOGIN'] = "OK";
// get ldap response
$result = ldap_search($this->objLdapConnection, $this->strLdapDC, $this->strLdapFilter);
// sort ldap results
ldap_sort($this->objLdapConnection, $result, "sn");
// get user info
$info = ldap_get_entries($this->objLdapConnection, $result);
// get user info
$this->arrLoginResult['USERNAME'] = ! empty($info[0]['name'][0]) ? $info[0]['name'][0] : NULL;
// close ldap connection
@ldap_close($this->objLdapConnection);
// login user
$objUserServ = $this->container->get('userManager');
$objUserServ->loginAction($this->strUserEmail);
}
else
{
$this->arrLoginResult['ERROR'] = 'INVALID_CREDENTIALS';
}
}
return json_encode($this->arrLoginResult);
}
}
Our Ldap service checks autenthication using the user email and password entered in the login form against our Windows Active Directory.
With the Ldap service created, we need to include it into the app/config/Services.yml file.
# Learn more about services, parameters and containers at
# http://symfony.com/doc/current/book/service_container.html
services:
ldap:
class: Services\Utils\Ldap
arguments: ["@service_container", "@request"]
scope: request
Now, we are going to create our login controller:
<?php
// src/UserBundle/Controller/LoginController.php
namespace UserBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class LoginController extends Controller
{
public function indexAction(Request $request)
{
$arrViewData = array('USER_EMAIL' => NULL, 'PASSWORD' => NULL, 'ERROR' => NULL);
// Checks if the login form has been submitted
if($request->getMethod() == 'POST')
{
// load Ldap service
$objLdapServ = $this->get('ldap');
// check Ldap login
$arrLoginResult = $objLdapServ->checkLdapLogin();
// Ldap login result
$arrViewData = json_decode($arrLoginResult, TRUE);
// check Ldap login result
if($arrViewData['LOGIN'] == "OK")
{
// user logged ok, then we redirect to the home page
$router = $this->get('router');
$url = $router->generate('home');
return $this->redirect($url);
}
}
return $this->render('UserBundle:Login:login.html.twig', $arrViewData);
}
}
And the login view:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body class="login-page">
<div class="container">
<div class="login-box">
{% if ERROR == 'INVALID_CREDENTIALS' %}
<div class="row">
<div class="alert alert-danger text-center">
<strong>Error, wrong credentials.</strong>
</div>
</div>
{% endif %}
<div class="login-box-body">
<form class="form-signin" action="?checkLogin" method="post">
<div class="form-group has-feedback">
<input name="email" type="text" class="form-control" placeholder="User or email" value="{{ USER_EMAIL }}" autofocus />
<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<input type="password" class="form-control" placeholder="Password" name="password" value="{{ PASSWORD }}" />
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<div class="row">
<div class="col-xs-12">
<button type="submit" class="btn btn-primary btn-block btn-flat">Login</button>
</div>
</div>
</form>
</div>
<!-- /.login-box-body -->
</div>
</div><!-- /container -->
</body>
</html>
After that, we are going to create a ‘UserManager‘ service class for managing our application users. In this service we handle the user session, create new users, etc.
<?php
// src/Services/User/UserManager.php
namespace Services\User;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Doctrine\ORM\EntityManager;
use UserBundle\Entity\User;
class UserManager
{
private $container, $em, $request, $session, $user;
public function __construct(EntityManager $em, ContainerInterface $container, Request $request, Session $session)
{
$this->em = $em;
$this->request = $request;
$this->container = $container;
$this->session = $session;
}
// checks if user exists when login form has been submitted
public function loginAction($strUsername)
{
if( ! $this->checkUserExists($strUsername) )
{
// create new user
$this->createUser($strUsername);
}
$this->createLoginSession();
}
// get user data from database
public function getUser($strUsername)
{
return $this->em->getRepository('UserBundle:User')->findOneBy( array('user' => $strUsername) );
}
// Check if a user exists on database
public function checkUserExists($strUsername)
{
$this->user = $this->getUser($strUsername);
return ( ! empty($this->user)) ? true : false;
}
// Create new user on database
public function createUser($strUsername)
{
$boolResult = false;
$objCurrentDatetime = new \Datetime();
try
{
$objUser = new User();
$objUser->setUser($strUsername);
$objUser->setCreationDate($objCurrentDatetime);
$objUser->setLastLoginDate($objCurrentDatetime);
// save data
$this->em->persist($objUser);
$this->em->flush();
// result data
$boolResult = true;
// user obj
$this->user = $objUser;
}
catch(Exception $ex)
{
echo $ex->getMessage();
}
return $boolResult;
}
// creates login session
public function createLoginSession()
{
$objToken = new UsernamePasswordToken($this->user, null, 'main', $this->user->getRoles() ) ;
// update user last login
$this->user->setLastLoginDate( new \Datetime() );
$this->em->persist($this->user);
$this->em->flush();
// save token
$objTokenStorage = $this->container->get("security.token_storage")->setToken($objToken);
$this->session->set('_security_main', serialize($objToken));
}
// logout a user
public function logOutUser()
{
$this->container->get('security.context')->setToken(null);
$this->container->get('request')->getSession()->invalidate();
$url = $router->generate('oportunidades');
return $this->redirect($url);
}
}
Add UserManager service into app/config/services.yml
userManager:
class: Services\User\UserManager
arguments: ["@doctrine.orm.entity_manager", "@service_container", "@request", "@session"]
scope: request
We need to configure our app/config/security.yml file:
# To get started with security, check out the documentation:
# http://symfony.com/doc/current/book/security.html
security:
# http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
providers:
db_provider:
entity:
class: UserBundle:User
property: user
#main:
#id: securityProvider
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/login$
security: false
#anonymous: ~
#http_basic:
# realm: "Secured Demo Area"
main:
anonymous: ~
form_login:
login_path: login
check_path: login
# activate different ways to authenticate
# http_basic: ~
# http://symfony.com/doc/current/book/security.html#a-configuring-how-your-users-will-authenticate
# form_login: ~
# http://symfony.com/doc/current/cookbook/security/form_login_setup.html
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
access_control:
- { path: ^/, roles: ROLE_USER }
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
Our UserEntity should be something like this:
UserBundle\Entity\User:
type: entity
table: null
repositoryClass: UserBundle\Repository\UserRepository
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
user:
type: string
length: 255
unique: true
role:
type: string
nullable: true
column: role
lastLoginDate:
type: datetime
nullable: true
column: last_login_date
creationDate:
type: datetime
nullable: true
column: creation_date
deletionDate:
type: datetime
nullable: true
column: deletion_date
lifecycleCallbacks: { }
Our entity has a field named ‘role’ where we can save the user role, but you will need to implement a method to save and load the role when user logs into the application. This topic could be interesting for a new post.
How to disable WordPress plugin updates
Sometimes we make code changes in our installed plugins to fix problems, add new features or customize the plugin. Obviously, if we update any modified plugin to the latest version, we are going to lost all the changes we have made.
Maybe, we have another administrator user as webmaster of our website and he doesn’t know this plugin update problem.
To avoid this, we are going to disable a list of indicated plugins.
First, add a new function at the end of our active theme functions.php file, which it is located in ‘/wp-content/themes/template_name/functions.php‘.
function disable_plugin_updates( $value )
{
unset( $value->response['advanced-access-manager/aam.php'] );
unset( $value->response['css3_web_pricing_tables_grids/css3_web_pricing_tables_grids.php'] );
unset( $value->response['google-sitemap-generator/sitemap.php'] );
unset( $value->response['simple-share-buttons-adder/simple-share-buttons-adder.php'] );
unset( $value->response['simply-exclude/simplyexclude.php'] );
unset( $value->response['visual-form-builder/visual-form-builder.php'] );
unset( $value->response['wordpress-seo/wp-seo.php'] );
unset( $value->response['wp-memory-db-indicator/wp-memory-db-indicator.php'] );
unset( $value->response['wp-migrate-db/wp-migrate-db.php'] );
unset( $value->response['wp-rss-multi-importer/wp-rss-multi-importer.php'] );
unset( $value->response['wp-super-cache/wp-cache.php'] );
return $value;
}
Each line is a plugin to being disabled.
We must define the plugin path name ‘advanced-access-manager‘ and the main plugin file, in this case ‘aam.php‘.
unset( $value->response['advanced-access-manager/aam.php'] );
Then, after our function, we add the next code:
add_filter( 'site_transient_update_plugins', 'disable_plugin_updates' );
Save the file and the indicated plugins will not shown more update messages.


