jueves, 26 de octubre de 2017

Deshabilitar Cortana

Equipo\HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Windows Search\AllowCortana

Si no existe “Windows Search” hay que crearlo.

AllowCortana es un DWORD con valor 0

viernes, 15 de septiembre de 2017

Cambiar resolución añadiendo modo de video (con xrandr) en Linux

Vamos a establecer un modo de 1680x1050.

Lo primero calculamos la la Modeline con la utilizada cvt (VESA Coordinated Video Timing)

$cvt 1680 1050
# 1680x1050 59.95 Hz (CVT 1.76MA) hsync: 65.29 kHz; pclk: 146.25 MHz
Modeline "1680x1050_60.00"  146.25  1680 1784 1960 2240  1050 1053 1059 1089 -hsync +vsync

Luego utilizamos el xrandr para poner nuestro modo deseado

$ xrandr --newmode "1680x1050_60.00"  146.25  1680 1784 1960 2240  1050 1053 1059 1089 -hsync +vsync
$ xrandr --output VGA1 --mode  "1680x1050_60.00"
$ xrandr --output VGA1 --mode "1680x1050_60.00"

Hay que sustituir el VGA1 por el output que corresponda en cada caso, si se hace un xrandr sin nada ya nos lo muestra así como los modos actuales que tiene soportados.

miércoles, 13 de septiembre de 2017

Bashware

Quedaos con la palabra que podría comprometer a millones de equipos con Windows 10
El WSL, el subsistema para Linux que nos permite la ejecución de algunas cosas de Linux en Windows (como la bash) de forma nativa, tiene un pequeño gran problema.

La mayoría de software de seguridad y antivirus no está diseñado para lidiar con procesos Linux en sistemas Windows, es por ello que un malware que se ejecute en dicho subsistema, a día de hoy, campará a sus anchas...

Podéis ver la demo de la gente de Check Point en este vídeo:

Para más info, un buen punto de partida es este


martes, 12 de septiembre de 2017

Seguridad bluetooth comprometida

Da igual de quién seas, de Windows, de Linux, de Android, de iOS o de lo que quieras, hay fallos de seguridad que nos afectan a todos.

Miles de dispositivos con conectividad bluetooth pueden ser atacados, en algún caso incluso sin que tengas que hacer nada.
Mantén tu dispositivo actualizado siempre con los últimos parches de seguridad proporcionados por el fabricante (y aún así, algún día te la colaran).

Para muestra un botón..


viernes, 8 de septiembre de 2017

Localizar puerto al que estamos conectado vía CDP con WinDump

Si el equipo al que estamos conectado es un Cisco con CDP habilitado, simplemente usando el WinDump (en el caso de Windows) podemos localizarlo fácilmente.

Esto es sumamente útil para localizar las tomas en los paneles de parcheo, luego sólo hay que seguir el cable. Si todo está bien documentado esto no sirve de nada, pero como en muchas instalaciones no es el caso, pues se puede aprovechar.

También si tenéis un equipo viejo de cisco que soporte CDP os puede servir poniéndolo temporalmente en el armario, en cualquier instalación para encontrar a que boca del panel va cada cable.
  1. Primero vemos como se llama nuestro Intefaz para el WinDump
windump -D
1.\Device\NPF_{C6ACD745-CF9B-4366-AAAF-DCFCD2108F11} (Microsoft)
2.\Device\NPF_{44EE031D-814E-4657-B0E8-7276996469F6} (TAP-Windows Adapter V9)
3.\Device\NPF_{290F16FC-3584-4FB5-A172-DAA2272A3491} (Intel(R) Ethernet Connection I219-V)
4.\Device\NPF_{D33776EB-C80E-44AE-B3E2-0EC0EA7D938E} (Microsoft)

  1. Luego ejecutamos el siguiente comando y esperamos respuesta


windump -nn -v -i \Device\NPF_{290F16FC-3584-4FB5-A172-DAA2272A3491} -s 1500 -c 1 "ether[20:2] == 0x2000"
windump: listening on \Device\NPF_{290F16FC-3584-4FB5-A172-DAA2272A3491}
12:30:11.644124 CDPv2, ttl: 180s, checksum: 692 (unverified), length 385
        Device-ID (0x01), length: 15 bytes: 'nombredemiequipo.loquesea.com'
        Version String (0x05), length: 182 bytes:
          Cisco IOS Software, C3560 Software (C3560-IPBASE-M), Version 12.2(35)SE5, RELEASE SOFTWARE (fc1)
          Copyright (c) 1986-2007 by Cisco Systems, Inc.
          Compiled Thu 19-Jul-07 18:15 by nachen
        Platform (0x06), length: 20 bytes: 'cisco WS-C3560G-48PS'
        Address (0x02), length: 13 bytes: IPv4 (1) 1.1.1.1
        Port-ID (0x03), length: 18 bytes: 'GigabitEthernet0/2'
        Capability (0x04), length: 4 bytes: (0x00000029): Router, L2 Switch, IGMP snooping
        Protocol-Hello option (0x08), length: 32 bytes:
        VTP Management Domain (0x09), length: 4 bytes: 'test'
        Native VLAN ID (0x0a), length: 2 bytes: 12
        Duplex (0x0b), length: 1 byte: full
        ATA-186 VoIP VLAN request (0x0e), length: 3 bytes: app 1, vlan 22
        AVVID trust bitmap (0x12), length: 1 byte: 0x00
        AVVID untrusted ports CoS (0x13), length: 1 byte: 0x00
        Management Addresses (0x16), length: 13 bytes: IPv4 (1) 2.2.2.2
        unknown field type (0x1a), length: 12 bytes:
          0x0000:  0000 0001 0000 0000 ffff ffff
1 packets captured
689 packets received by filter
0 packets dropped by kernel


lunes, 28 de agosto de 2017

Empezar a trabajar con .Net Core en Linux Ubuntu

Dado que .Net Core es multiplaforma, vamos a ver cómo podríamos realizar nuestro típico "hola mundo" usando .Net Core en Ubuntu.

Añadimos las claves de Microsoft a nuestro repositorio de claves, para ello hacemos:

wget -qO - https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -

Añadimos el repositorio, en este caso para una Ubuntu 17.04
sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-zesty-prod zesty main" > /etc/apt/sources.list.d/dotnetdev.list'

Instalamos el .Net
sudo apt-get update
sudo apt-get install dotnet-sdk-2.0.0

Creamos nuestra primera nueva (new) aplicación de tipo consola (console) en C#  (por defecto). Se puede obtener ayuda de comando mediante dotnet hew help:
dotnet new console -o miholamundo

El -o miholamundo especifica el directorio de salida de la aplicación. Si vamos a este directorio podemos ver que nos ha generado lo siguiente:
fran$ ls
miholamundo.csproj  obj  Program.cs

Donde miholamundo.csproj es el fichero de configuración de nuestro proyecto, al uso:
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>

</Project>
y el Program.cs, es nuestro primer programa:
using System;

namespace miholamundo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

Para ejecutar basta con que hagamos un donet run dentro del directorio:
fran$ dotnet run
Hello World!

Llegados a este punto, puedo utilizar cualquier editor para modificar el fichero, añadir otros al proyecto, etc., pero voy a aprovechar para instalar en mi entorno el Visual Studio Core, evidentemente también desde paquetes, para ello, antes de nada añadimos el repositorio, actualizamos e instalamos
sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list'
sudo apt-get update
sudo apt-get install code

Una vez instalado basta con ejecutar code y ya está.

viernes, 25 de agosto de 2017

Utilizar .Net Core MVC con MySQL sin Entity Framework y con consultas SQL simples

Hola dejo aquí el cómo podemos utilizar el .Net Core MVC pero pasando del Entity Framework para las conexiones a la DB y teniendo así un poco más de flexibilidad, que aunque no es quizás, tan puritano como algunos querrían, suele dar más versatilidad, y, en algunos proyectos suele ser más efectivo; sobre todo si tenemos programadores que saben de bases de datos y de SQL.

Premisas:

  • Tengo un servidor MySql instalado en local con un esquema test1 y una tabla data, que contienen dos campos id (entero sin signo, autoincremental) y dato (varchar de 45), usuario test1 con clave test1.
  • Voy a utilizar Visual Studio Community 2017 para todo el proceso, ya que está lo aprovechamos.

Creamos proyecto .Net Core MVC





Añadimos paquete Nuget del conector MySQL 

Botón derecho encima de la solución -> Administrar paquetes NuGet para la solución
Pestaña examinar Buscar Mysql

Presionar Instalar

Leemos y aceptamos si estamos de acuerdo con lo que pregunta (deberíamos):



Et voilà

Nota importante: la versión estable instalada en el momento de hacer este documento no funcionó bien y tuve que marcar la casilla de "Incluir versiones preliminares" y actualizar a la 6.10.3rc, posiblemente cuando leáis esto ya la versión que esté subida como estable será buena, por lo tanto he preferido mantener el artículo con la instalación de estable.



Añadimos el ConnectionString al fichero de configuración:

"ConnectionStrings": {
    "DefaultConnection": "server=localhost;port=3306;database=test1;user=test1;password=test1"
  }

Modelo de datos

Vamos a crear el modelo de datos, para ello creamos una nueva clase dentro de la carpeta Models que defina nuestro modelo de datos
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace test1.Models
{
    public class Dato
    {
        private string _dato;
        public int id { get; set; }
        public string dato
        {
            get { return _dato; }
            set
            {
                if (value.Length < 45)
                    _dato = value.Substring(0, 45);
                else
                    _dato = value;
            }
        }
      
    }
}


Nota importante: en el código vemos que el dato del id es tipo int, mientras que en la base de datos es uint, esto lo he puesto así, porque para el paso siguiente lo vamos a necesitar así, sino falla el asistente, pero luego hay que acordarse de cambiarlo de nuevo a uint porque no estamos usando EF.

Controlador, contexto y vistas


Vamos a añadir el controlador tal y como si estuviésemos usando Entity Framework, porque esto nos va a ahorrar escribir unas cuantas líneas.

Por lo tanto vamos a presionar con el botón derecho encima de la carpeta Controllers y darle a la opción Agregar -> Controlador y seleccionamos "Controlador MVC con ... Entity Framework"

Pulsamos "Agregar"
Completamos los datos, la clase modelo está clara en este caso es nuestra clase Dato, al seleccionarla nos pone un nombre de controlador, en mi caso lo he modificado para que se llame DatoControler   en clase de contexto pulsamos el "+" 
Elegimos el nombre de nuestro contexto de datos, en este caso le dejo el que está y pulsamos Agregar
Volvemos a pulsar Agregar

Bien si todo va bien, vemos que tendríamos, según el log de salida:
info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0]
      User profile is available. Using 'C:\Users\francisco.counago\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
Added DbContext : '\Data\test1Context.cs'
info: Microsoft.EntityFrameworkCore.Infrastructure[100403]
      Entity Framework Core 2.0.0-rtm-26452 initialized 'test1Context' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: None
Added Controller : '\Controllers\DatoController.cs'.
Added View : \Views\Dato\Create.cshtml
Added View : \Views\Dato\Edit.cshtml
Added View : \Views\Dato\Details.cshtml
Added View : \Views\Dato\Delete.cshtml
Added View : \Views\Dato\Index.cshtml


Es decir el esqueleto de todo lo que necesitamos. 

Nota: cambiar el int a uint de la clase dato, porque ya podemos hacerlo.

Además si abrimos el Startup.cs veremos que ha añadido una línea para registrar nuestro contexto

¿Cuál es el problema?, nosotros no vamos a usar un contexto al uso por lo tanto debemos cambiar, algunas cosas, primero, nuestro contexto "text1Context" no viene de una clase DbContext, por lo tanto vamos a eliminar está herencia, cambiamos:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

namespace test1.Models
{
    public class test1Context : DbContext
    {
        public test1Context (DbContextOptions options)
            : base(options)
        {
        }

        public DbSet Dato { get; set; }
    }
}

Por esto, esqueleto básico de nuestro Contexto

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace test1.Models
{
    public class test1Context
    {
        public test1Context (string pConexion)            
        {
        }        
    }
}

Por lo tanto como tampoco nos podemos añadir como un services.AddDbContext sino como un nuevo servicio, para ello cambiamos en el Startup.cs:
esto

public void ConfigureServices(IServiceCollection services)
{
            services.AddMvc();

            services.AddDbContext(options =>
                    options.UseSqlServer(Configuration.GetConnectionString("test1Context")));
          
}

por esto
 public void ConfigureServices(IServiceCollection services)
 {
            services.AddMvc();
            services.Add(new ServiceDescriptor(typeof(test1Context), new test1Context(Configuration.GetConnectionString("DefaultConnection"))));            
 }

Con esto nos hemos desecho del DbContext del EF, realmente todo esto puede hacerse a mano sin el asistente del EF, pero he preferido hacerlo así, para que se vea que generamos lo mismo que con el EF.
Si queremos aprovechar lo máximo las vistas generadas para el EF, sólo tenemos que hacer métodos dentro de nuestro contexto que llamaremos desde el controlador.

No me voy a parar en todos los detalles (se puede ver en el código), básicamente lo que voy a hacer es cambiar este controlador

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using test1.Models;

namespace test1.Controllers
{
    public class DatoController : Controller
    {
        private readonly test1Context _context;

        public DatoController(test1Context context)
        {
            _context = context;
        }

        // GET: Dato
        public async Task Index()
        {
            return View(await _context.Dato.ToListAsync());
        }

        // GET: Dato/Details/5
        public async Task Details(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var dato = await _context.Dato
                .SingleOrDefaultAsync(m => m.id == id);
            if (dato == null)
            {
                return NotFound();
            }

            return View(dato);
        }

        // GET: Dato/Create
        public IActionResult Create()
        {
            return View();
        }

        // POST: Dato/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task Create([Bind("id,dato")] Dato dato)
        {
            if (ModelState.IsValid)
            {
                _context.Add(dato);
                await _context.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            }
            return View(dato);
        }

        // GET: Dato/Edit/5
        public async Task Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var dato = await _context.Dato.SingleOrDefaultAsync(m => m.id == id);
            if (dato == null)
            {
                return NotFound();
            }
            return View(dato);
        }

        // POST: Dato/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task Edit(int id, [Bind("id,dato")] Dato dato)
        {
            if (id != dato.id)
            {
                return NotFound();
            }

            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(dato);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!DatoExists(dato.id))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction(nameof(Index));
            }
            return View(dato);
        }

        // GET: Dato/Delete/5
        public async Task Delete(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var dato = await _context.Dato
                .SingleOrDefaultAsync(m =>; m.id == id);
            if (dato == null)
            {
                return NotFound();
            }

            return View(dato);
        }

        // POST: Dato/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task DeleteConfirmed(int id)
        {
            var dato = await _context.Dato.SingleOrDefaultAsync(m => m.id == id);
            _context.Dato.Remove(dato);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }

        private bool DatoExists(int id)
        {
            return _context.Dato.Any(e => e.id == id);
        }
    }
}


por este
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using test1.Models;

namespace test1.Controllers
{
    public class DatoController : Controller
    {
        private readonly test1Context _context;

        public DatoController(test1Context context)
        {
            _context = context;
        }

        // GET: Dato
        public  IActionResult Index()
        {
            return View(_context.Listado());
        }

        //// GET: Dato/Details/5
        public IActionResult Details(uint? id)
        {
            if (id == null)
            {
                return NotFound();
            }
            Dato datoAux = _context.dame(id.Value);
            if (datoAux == null)
            {
                return NotFound();
            }
            return View(datoAux);
        }

        // GET: Dato/Create
        public IActionResult Create()
        {
            return View();
        }

        // POST: Dato/Create
        [HttpPost]
        public IActionResult Create(String dato)
        {
            if(dato==null)
            {
                return RedirectToAction(nameof(Create));
            }
            String strDato = dato;
            if(_context.Add(strDato))
            {
                return RedirectToAction(nameof(Index));
            }
            return View(new Dato { dato = strDato });
        }

        //// GET: Dato/Edit/5
        public IActionResult Edit(uint? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            Dato datoAux = _context.dame(id.Value);
            if (datoAux == null)
          {
               return NotFound();
          }
            return View(datoAux);
        }

        //// POST: Dato/Edit/5
        [HttpPost]    
        public  IActionResult Edit(uint? id, string dato)
        {
            if (id ==null)
            {
                return NotFound();
            }
            uint uiId = id.Value;
            if (dato == null)
            {
                return RedirectToAction(nameof(Edit), new { id =uiId });
            }
            String strDato = dato;
            if (_context.Update(uiId,strDato))
            {
                return RedirectToAction(nameof(Index));
            }
            return View(new Dato { dato = strDato, id=uiId });
        }

        //// GET: Dato/Delete/5
        public IActionResult Delete(uint? id)
        {
            if (id == null)
            {
                return NotFound();
            }
            Dato datoAux = _context.dame(id.Value);
            if (datoAux == null)
            {
                return NotFound();
            }
            return View(datoAux);
        }

        //// POST: Dato/Delete/5
        [HttpPost, ActionName("Delete")]

        public IActionResult DeleteConfirmed(uint? id)
        {
            if (id == null)
            {
                return NotFound();
            }
            uint uiId = id.Value;
            if (_context.Delete(uiId))
            {
                return RedirectToAction(nameof(Index));
            }
            return RedirectToAction(nameof(Delete),new { id = uiId });
        }       
    }
}




y añadir a nuestro contexto las funciones necesarias, quedando de esta forma





using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MySql.Data.MySqlClient;


namespace test1.Models
{
    public class test1Context
    {
        private string strCnnString;
        public test1Context (string pstrConexion)            
        {
            strCnnString = pstrConexion;        
        }
        private List hazConsulta(string pstrConsulta)
        {
            List lstDato = new List();
            using (MySqlConnection cnn = new MySqlConnection(strCnnString))
            {
                cnn.Open();
                using (MySqlDataReader drAux = (new MySqlCommand(pstrConsulta, cnn).ExecuteReader()))
                {
                    while (drAux.Read())
                    {
                        lstDato.Add(new Dato()
                        {
                            id = drAux.GetUInt32("id"),
                            dato = drAux.GetString("dato")
                        });
                    }
                }
            }
            return lstDato;
        }
        private int ejecutaSQL(string pstrConsulta)
        {
            int iFilas = 0;
            using (MySqlConnection cnn = new MySqlConnection(strCnnString))
            {
                cnn.Open();
                iFilas = (new MySqlCommand(pstrConsulta, cnn).ExecuteNonQuery());
            }
            return iFilas;
        }
        public bool  Add(string pDato)
        {
  
            if (pDato==null || pDato.Trim().Length < 1)
                return false;
            try
            {
                return (ejecutaSQL($"insert into dato (dato) values (\"{pDato}\")") > 0) ? true : false;                
            }catch
            {
                return false;
            }
        }
        public bool Update(uint puiId,string pDato)
        {

            if (pDato == null || pDato.Trim().Length < 1)
                return false;
            try
            {
                return (ejecutaSQL($"update dato set dato= \"{pDato}\" where id={puiId}") > 0) ? true : false;
            }
            catch
            {
                return false;
            }
        }
        public bool Delete(uint puiId)
        {
            
            try
            {
                return (ejecutaSQL($"delete from dato where id={puiId}") > 0) ? true : false;
            }
            catch
            {
                return false;
            }
        }
        public List Listado()
        {

            return hazConsulta("select * from dato");
        }
        public Dato dame(uint puiId)
        {
            try
            {
                return hazConsulta($"select * from dato where id={puiId}")[0];
            }
            catch
            {
                return null;
            }              
        }


    }
}




Con esto podríamos acceder perfectamente a todas las funcionalidades "autogeneradas" pero sin EF, teniendo de esta forma mayor control sobre lo que queremos hacer, ojo, mayor control implica mayor responsabilidad a la hora de controlar los datos que si lo hacemos con el EF y un mayor control de errores, pero estas cosas están fuera del ámbito de este artículo. Podemos darle a Ejecutar en IISExpress y vemos nuestro resultado a través de URL/dato







Como podemos concluir, podemos prescindir del EF para hacer las cosas y aprovechar ciertas ventajas que nos proporciona el Visual Studio como si estuviésemos usándolo. 



viernes, 18 de agosto de 2017

Intro a la programación web para programadores de escritorio

Hola, este artículo va dirigido a una persona en concreto a la que no tengo tiempo de explicarle ciertas cosas como querría, así que lo voy poniendo por aquí a ratos y, de paso, lo dejo por si le es interesante a alguien.

Cosas a tener en cuenta:

  • Tal y como he dicho, esto es una introducción a conceptos básicos, que programadores habituales de entorno no web no tienen claros  y que suponen un cambio de paradigma para ellos. 
  • No es un documento hipertécnico, pero a lo mejor tampoco para principiantes, está expresado con palabras que creo que cualquier programador puede entender pero, en algunos casos, puedo haberme permitido alguna licencia técnica y haber omitido algo o haber dicho algo que no  es exactamente así (si eres consciente de eso, posiblemente este documento no sea para ti).


Primera cosa, la programación web, no es como la programación de escritorio, parece una perogrullada pero no lo es, veamos, bajo mi punto de vista la mayor diferencia para que se tenga en cuenta:

  • En la programación de escritorio uno escribe una aplicación y se ejecuta en una máquina, bajo unas determinadas condiciones y un determinada forma de programar, por ejemplo, hacemos una aplicación  en C# tipo cliente - servidor que se conecta con una base de datos PostgreSQL. Por lo tanto esta aplicación se compila a un código que interprete una máquina virtual o a nativo y se ejecuta en un equipo. Un concepto interesante, depende como programemos, que nos simplificará bastante las explicaciones en el futuro es el de tener parte de la lógica en el lado de la base de datos (no voy a entrar en discusiones de si es bueno o malo), eso implicaría que entendemos el concepto de que algo se ejecute en un servidor fuera de nuestra aplicación y nos devuelva unos resultados (por ejemplo un procedimiento almacenado).
  • En una aplicación Web, uno escribe una aplicación pero su mentalidad tiene que cambiar con respecto a donde se ejecuta, porque esta aplicación posiblemente tenga una parte que se ejecuta en un servidor (como el procedimiento almacenado) y otra parte que se ejecuta en un navegador del equipo del usuario (del cliente), y he aquí una gran diferencia. Obviando complementos (tipo flash, silverlight, etc,), el navegador web sólo entiende dos cosas HTML y Javascript.
Otra gran diferencia a tener en cuenta es que todas las comunicaciones entre la parte que se ejecuta en el servidor y la parte cliente se realizan sobre protocolo HTTP y por tanto de forma stateless, nos quedamos con la palabra pero no voy a profundizar. Lo que quiere decir esto básicamente es que cada vez que cargamos una página web el servidor nos devuelve de nuevo la información sin tener en cuenta "variables de estado", esto se subsana utilizando técnicas para conservar el estado de los datos que nos interesa en el servidor, por ejemplo mediante un identificador de sesión. ¿Qué demonios quiere decir esto?, que la información tiene que almacenarse en algún sitio, por lo tanto o la vamos guardando toda del lado del cliente y la pasamos al servidor de cada vez para que haga lo que tenga que hacer, o la guardamos del lado del servidor y le pasamos, por ejemplo, alguna información con un identificador de sesión para que el la realice y nos devuelva los datos que necesitemos, siempre manteniendo el identificador de sesión. Hay más formas de hacer esto, pero como se puede ver la cosa se complica un poco con respecto a la programación de escritorio.

Cuando hablamos de que pasamos algo al servidor o él nos lo pasa esto se realiza sobre protocolo HTTP y se puede realizar (hay más pero aquí solo nos interesan estas inicialmente) mediante lo que se llama un POST o mediante un GET. Hay algunas diferencias importantes entre estos métodos, que aquí se explican perfectamente, pero básicamente el GET va en la URL visible de longitud máxima 2048 caracteres ASCII y el POST se envía sin reflejarse en la URL y no tiene restricciones, cae de cajón que el POST es más seguro que el GET. Aunque en la página de w3schools pone que el POST es de tamaño ilimitado, normalmente este tamaño es limitado por el servidor para evitar "problemas", con lo cual hay que tener en cuenta el tamaño del POST configurado en el servidor si queremos "subir" cosas grandes desde el cliente.

Bien explicado todo esto ya tenemos lo básico para saber como funciona la programación web. Resumiendo:
  • Hay dos sitios donde se pueden ejecutar las cosas (cliente) Navegador web y servidor (equipo que nos da los resultados)
  • Además del lenguaje en el que realizamos la parte gorda de operaciones vamos a necesitar como mínimo conocimientos de HTML, Javascritpt y hoy en día también CSS
  • Dependiendo que tecnología usemos deberemos conocer más o menos de esto, por ejemplo si usamos ASP.NET WebForms, se  supone que no tenemos que conocer mucho porque hará todo por nosotros, sin embargo si usamos ASP.NET MVC u otras cosas vamos a tener que meternos en otras capas.
De todas formas la plataforma de desarrollo o las herramientas son importantes pero lo más importante es el programador. Como dijo un día  Scott Guthrie: “Great developers using bad tools/frameworks can make great apps. Bad developers using great tools/frameworks can make bad apps.”

Por lo tanto para empezar se puede empezar con algo que nos permita una transición ligera tipo WebForms para luego si tal lanzarnos a MVC o a Linx con PHP o a lo que queramos.

Vamos a aclarar algunos conceptos a mayores:

  • HTTP: Protocolo que sirve para comunicarnos con el servidor, le mandamos cosas en texto y nos devuelve cosas en texto que interpreta el navegador (más info poner HTTP en buscador)
  • HTML: lenguaje de marcado, esto es un texto con etiquetas que le dicen al navegador lo que tiene que hacer.
  • JavaScript: Lenguaje de script que ejecutamos en el navegador 
  • CSS: Hojas de estilo en cascada (decir esto y nada es lo mismo). Es texto que le dice al navegador como tiene que decorar los distintos elementos que vemos.
(Ojo! el HTML y el Javascript son texto que interpreta el navegador aquí no se compila nada)
  • Servidor web: este término se utiliza muchas veces para designar la máquina donde se ejecuta la aplicación a la que se denota por el mismo nombre "servidor web". Por lo tanto por el contexto sabemos a qué nos referimos. El servidor web es la aplicación sobre la cual se ejecuta nuestro código del lado del servidor, por ejemplo un servidor Apache, o un servidor IIS. 
  • Navegador Web: es la aplicación que ejecuta nuestro código del lado del cliente.

Por lo tanto la programación web para un programador tradicional es un galimatías de cuidado, ya que le obliga a preocuparse de cosas que antes eran obvias y tenía un RAD maravilloso para colocar elementos que ahora (para hacerlo rápido) va a tener que picar en HTML, a esos elementos va a tener que asignarle colorines y cosas por el estilo mediante CSS y aún para mayor complicación va a tener que hacer cosas, como comprobaciones de datos o cargar algo dinámicamente en Javascript para que todo funcione de forma correcta del lado del cliente. Y después de todo esto tendrá que hacer lo que siempre le ha gustado que es programar.

Realmente no parece tan complicado... jajajaja. Pongamos un ejemplo comparativo: queremos que al seleccionar algo en un combo nos cargue otro combo con otra cosa.

Procedimiento en programación de escritorio con herramienta  RAD:
  1. Diseñar formulario donde incluye combos (arrastrar colocar y poner colorines)
  2. Asignar al evento que corresponda la función (cuando el combo cambié o al perder el foco o lo que sea ejecutar TaloCualFunción)
  3. TaoCualFunción, se encarga de cargar el combo 2 a partid del cambio del 1.
Procedimiento en programación web (dos variantes la bonita y la fea).
Parte común:
  1. Diseño el formulario HTML y le digo que el FORM me mande los datos por POST.
Opción fea:

  1. Diseño función TaloCualFunción que cuando me llega un POST recojo el valor seleccionado del combo 1 llamo a la función TaloCualFunción que rellena el combo 2 y reescribo el HTML con el combo 2 cargado con los datos que correspondan y lo devuelvo
Esta opción es fea, porque implica que la página se va a recargar, en un caso real tendríamos que asignar un código de operación en una variable oculta o comparar el valor del combo o algo así para saber que lo que queremos hacer es rellenar el combo 2, además si el usuario ya ha introducido datos en el formulario a parte de los datos del combo habría que pasar todos los datos (cosa que ya hace la etiqueta FORM) pero habría que recogerlos del otro lado y volver a regenerar el HTML con ellos cargados

Opción bonita (agárrate que vienen curvas):
  1. En el evento onchange del elemento combobox del HTML asignamos la función TaloCualFuncion de javascript para que cuando cambie el combo 1 cargue los datos en el combo2
  2. Hacemos la función javascript que haga el proceso
Y ahora veamos como se pude hacer esto del punto 3 en javascript, bien la función está en el cliente y los datos del combo 2 (en una base de datos posiblemente) en el servidor. Pues bien, podemos usar a nuestro amigo AJAX, que queda muy bonito el nombre, pero que tiene tela (para más info ver el link). Que vamos a hacer con el AJAX, bien lo que hacemos es desde javascript hacer una petición HTTP al servidor (con lo cual el servidor tiene que tener algún método programado (webapi, para que vayan sonando las cosas) que responda a él) y decirle que nos de los datos. El servidor nos los da, y luego en tiempo real modificamos el HTML de la página (añadimos elementos al combo) para modificar el combo 2.

En resumen, un follón.

Para todo esto, hoy en día hay cosas que se pueden usar que te simplifican la vida como JQuery, aunque no tengo claro a veces si compensa el meter tremenda librería para rellenar un combo, pero lo dicho antes, lo importante es el resultado no las herramientas. Por suerte, los entornos de desarrollo se van adaptando al medio y cosas como el VisualStudio o el Eclipse nos facilitan mucho la vida.

Bajo mi punto de vista lo más importante es ser ordenado, evidentemente no me refiero a tener la mesa ordenada, sino a que dado que tenemos tantas "capas de cebolla" para hacer una aplicación web es importante tenerlas bien separadas, escojamos el lenguaje que escojamos o las herramientas que queramos, observemos que básicamente hay dos capas importantes:

  • Lado del servidor (C#, Java, PHP, Python, Perl, puff lo que queráis casi)
  • Lado del cliente (HTML, Javascript y si queremos por ponerlo aparte CSS)
Teniendo en cuenta estas cosas es importante, ojo, siempre bajo mi punto de vista,  tanto la organización estructural del código fuente (muchas de las herramientas ya nos lo estructuran en directorios apropiados), como el evitar la mezcla de capas entre sí. Que quiero decir con esto, bien, se puede hacer una página en PHP o ASP.NET mezclando el HTML y la programación en la misma página incluso con el Javascript y las CSS y tener unos resultados perfectos, pero la mantenibilidad de esto es de carácter dudoso, por lo tanto yo separaría perfectamente estas capas. Y... ¿cómo lo hacemos? De la siguiente forma:

  • El HTML se utiliza como plantilla con Etiquetas que se reemplazan desde la parte de programación, esto es lo que se lleva haciendo toda la vida y lo que siguen haciendo los lenguajes actuales, pero poniéndonoslo más fácil. Este HTML incluirá las referencias a las CSS y código Javascript que utilice, que, deberán estar en ficheros separados
  • La programación que controla todo, incluso la generación del HTML debe tener sólo eso código de control, que lea una plantilla y la rellene de datos o que haga lo que quiera con ella, en la medida de lo posible (no siempre es posible) debe evitar el uso de HTML, y en caso de usarlo, por ejemplo para generar elementos de una lista, se debe hacer con etiquetas HTML simples, a ser posible sin utilizar clases ni estilos ni javascript, intentando escribir esto en el fichero que corresponda de alguna forma. Por ejemplo en el caso de los elementos de la lista puede definirse su formato desde un CSS externo que incluya la plantilla HTML  donde insertamos los elementos de la lista.
Trabajar de esta forma, evidentemente, nos va a suponer un poco más de tiempo (no tanto hoy en día porque la mayoría de los entornos de desarrollo nos permiten hacerlo fácilmente), pero merece la pena, porque, aunque empecemos un proyecto de forma individual, puede que el día de mañana seamos un equipo trabajando en la misma aplicación y esto nos permitirá, por ejemplo, que un diseñador muy bueno que sepa mucho de HTML no tenga que preocuparse de andar esquivando el código mezclado con el mismo, o de encontrar el HTML dentro de un código que no entiende.

Profundizaré un poco más ahora en la parte de eso que comenté antes de los estados, relacionándolo con cómo se ejecuta la aplicación. Cuando una aplicación de escritorio se ejecuta, se encuentra en un determinado estado a partir del cual se sigue ejecutando, dependiendo de factores condicionales como  por ejemplo los eventos del sistema. En una aplicación web la ejecución en local con Javascript y HTML funciona más o menos parecido, pero cuando pasamos todas esa información para que el servidor haga algo, ahí la cosa cambia, desde el punto de vista, que la aplicación siempre se ejecuta desde el inicio, esto es, en cada petición pasa por el punto de partida, y es ahí donde tenemos que determinar (en base a los datos que recibimos) cual es el estado en el que estamos y por donde seguimos. Realmente, teniendo claro este concepto no hay ningún problema y la adaptación es sencilla.

Con esta idiosincrasia de ejecución, la cosa no cambia tanto como creemos, por ejemplo, en el caso del rendimiento de una aplicación, en modo escritorio esta dependía (vamos a obviar la programación que será igual de buena o mala en ambos casos dependiendo del programador) de la máquina donde se ejecutase y el rendimiento del servidor de la base de datos. Ahora es lo mismo, pero un poco más complicado, por los siguientes factores:

  • La parte cliente no la hacemos nosotros, la interpreta un navegador, con lo cual va a depender del rendimiento de esté además de nuestra programación.
  • La parte del servidor suele tener dependencias de otros servicios o servidores, es decir tenemos trozos de programa que se ejecutan en un servidor (máquina), pero las peticiones suelen ser cursadas por un servidor web (programa) que muchas veces determina como se ejecuta nuestro código. Normalmente de que este servidor funcione bien se encarga la gente de sistemas, pero es un punto más a tener en cuenta. 
  • Por supuesto nuestro amigo el servidor de la base de datos va a seguir estando, que en casos de aplicaciones pequeñas a lo mejor hasta se ejecuta la base de datos y la aplicación web en el mismo host físico
  • Otra consideración es la escalabilidad, que realmente tampoco cambia tanto. Normalmente en las aplicaciones de escritorio (cliente-servidor) tenemos una escalabilidad vertical en el lado del cliente (si no llega se aumenta el hardware) y una escalabilidad vertical y horizontal en el lado de la base de datos (se puede crecer en hardware o añadir nodos si la base de datos soporta el hardware). En el caso web, es lo mismo, pero hay que tener en cuenta cómo y donde guardamos las cosas ¿por qué?, muy simple, por la escalabilidad horizontal de los servidores web (obviamos la base de datos, porque es lo mismo que en el otro caso), por lo tanto si guardamos algo en un disco duro local de un servidor, debemos tener en cuenta que puede que en la próxima petición que se lanzará automáticamente (sin depender de la aplicación) a otro servidor no lo tendremos, es por ello que debemos tener un poco más en cuenta como hacer ciertas cosas (para muchas nos darán solución nuestros amigos de sistemas, otras tendremos que ingeniárnoslas nosotros)







miércoles, 2 de agosto de 2017

Cambiar nombres a dispositivos en Windows (FriendlyName)

Esto parece algo totalmente innecesario hasta que te hace falta, por ejemplo, el típico software empeñado en codificar su licencia con un hardware específico y crearnos una dependencia hardware que no nos interesa, ya que luego puede haber problemas en caso de que falle un componente, por ejemplo la tarjeta de red y su MAC.

Como un ejemplo vale más que mil palabras, vamos a renombrar el dispositivo Loopback de Windows previamente instalado y llamarle "Mi tarjetita de red".

Pasos

  1. Abrir el administrador de dispositivos (devmgmt.msc)
  2. Buscar el dispositivo que queremos y darle a propiedades
  3. Ir a detalles
  4. Buscar la propiedad "Clave del controlador" y copiarla
  5. Ejecutar el editor de registro
  6. Ir a HKEY_LOCAL_MACHINE -> SYSTEM -> ControlSet001 -> Enum
  7. Buscar la clave que tenemos copiada en el portapapeles Edición->Buscar o Ctrl+F
  8. Cuando encontremos el driver que buscamos podemos cambiarle la clave FriendlyName a la que queramos
  9. Cerramos el registro y listo.
Tened en cuenta que, en algunos casos, podría ser necesario reiniciar el equipo para que los cambios tengan efecto.

A continuación dejo un vídeo con todo el proceso:




Instalar Loopback adaptarer en windows 10

Muchas veces nos interesa tener un adaptador de red local del equipo para hacer nuestras pruebas de programación, cambiarle la MAC y cosas así.

Para no complicarnos mucho la vida, podemos usar el LoopBack de MS.

Instalarlo es tan sencillo como realizar los siguientes pasos:


  1. Ir al administrador de dispositivos
  2. Acción -> Agregar hardware heredado
  3. Siguiente
  4. Instalar hardware seleccionado manualmente de una lista (avanzado)
  5. Seleccionar Adaptadores de red
  6. Siguiente
  7. Seleccionamos fabricante Microsoft
  8. Adaptador de Bucle Invertido KM-TEST de Microsoft
  9. Siguiente y listo
Como un vídeo es mejor que cualquier explicación aquí os lo dejo:


Para cambiar la dirección MAC de la tarjeta tenemos que ir a las propiedades del driver y en "opciones avanzadas" en "Dirección de red" se pone la que queramos, ojo tiene que pertenecer al rango de direcciones MAC administradas localmente unicast, esto eso es: que tenga el segundo bit menos significativo del primer byte más significativo a 1 y el primero a 0, ya que sino sería multicast, con lo cual nos quedan los rangos:
  • x2-xx-xx-xx-xx-xx
  • x6-xx-xx-xx-xx-xx
  • xA-xx-xx-xx-xx-xx
  • xE-xx-xx-xx-xx-xx
Con un vídeo también se ve mejor, en el se verá que las direcciones que no coinciden con estos rangos no nos las va a admitir y nos las dejará con la generada por defecto:



martes, 28 de marzo de 2017

Lanzar RComander

Por primera vez,  desde el prompt de R como siempre

library(Rcmdr)

Luego si lo cerramos, en esa misma sesión, podemos volver a lanzarlo con

Commander()

Actualizar R desde el propio R (Windows)

Existe un paquete {installr} que nos permite realizar una actualización desde el propio R.

Basta con ejecutar

       

       if(!require(installr)) {
install.packages("installr"); require(installr)}
updateR()

       
 
Para más información podemos ver el post original en inglés: Updating R from R (on Windows) – using the {installr} package

Ojo, no quita la versión vieja. Se puede hacer también desde el paquete, pero yo prefiero hacerlo luego a mano.


L2TP Ipsec Windows to Mikrotik eror 789

 Add this to registry REG ADD HKLM\SYSTEM\CurrentControlSet\Services\PolicyAgent /v AssumeUDPEncapsulationContextOnSendRule /t REG_DWORD /d...