Checking certificate dates

Sometime, when we are working or doing some investigations in our spare time we need to check the dates a web certificate has, especially, the expiration date. Obviously, we can go to our browser, introduce the desired url and with a few clicks check the issued and expiration dates.

But, there is another way more simple, easier and, in case we need it, we can script.

echo | openssl s_client -servername www.google.co.uk -connect www.google.co.uk:443 2>/dev/null | openssl x509 -noout -dates

This simple command gives us the information we want.

I hope it is useful.

Checking certificate dates

Git top committers

It is very common nowadays for companies to have a big when not a huge amount of code in their repositories and, if we are lucky, all this code will be split across multiple projects and repositories. In addition to this, it is very common in this company environments that no one owns an specific project, people just work in their tasks and sometimes they change multiple projects. This environment produces a situation where when you have questions about an specific project you do not know exactly who is the best person to ask.

There is not a simple solution to solve that but, if you are using git as a version control system, one possible solution is to obtain the top committers of the project. We can do this easily with a very simple command:

git shortlog -s -n --all | head -3

This will show us the first three top committers for our project. But, we are developers, we are lazy and we like to automate and build scripts to cover more than the simple case. Then, we can build this script:

#!/bin/bash

print_help() {
    echo " Do you need help or knowledge about one of our projects?
    Who is the better person to ask about one of them?
    Here you can find it!

    TOPCOMMITTERS(1)

    NAME
        topcommitters - list top committers in the git projects

    SYNOPSIS
        topcommitters [OPTION]... [PROJECT]...

    DESCRIPTION
        List top committers in the git projects

        There are not mandatory arguments. By default top 5 committers in all projects are listed.

            -f, --folder
                location of the repositories. By default ~/sourcecode

            -c, --count
                number of committers listed. By default 5

            -p, --project
                single project to be listed. ie: deliveries-service

            -h, --help
                show this help message.

        Exit status:
            0 if OK,

    AUTHOR
        Written by fjavierm.

    REPORTING BUGS
        www.binarycoders.wordpress.com

    BSD                   1 July, 2018                        BSD
    "
}

show_multiple() {
    # Loop all sub-directories
    for f in $dir
    do
        show_single $f
    done
}

show_single() {
    f=$1

    # Only interested in directories
    [ -d "${f}" ] || return

    echo -en "\033[0;35m"
    echo -n "${f}"
    echo -en "\033[0m"

    # Check if directory is a git repository
    inside_git_repo="$f/$(git rev-parse --is-inside-work-tree 2>/dev/null)"
    if [ "$inside_git_repo" ];
    then
        cd $f

        basename=${PWD}
        dirlen=${#basename}

        # list top authors
        echo -en "\n"
        git shortlog -s -n --all -- . | head -${count}

        cd ../
    else
        echo -ne "\t\t\tNot a git repository"
    fi

    echo
}

POSITIONAL=()
while [[ $# -gt 0 ]]
do
key="$1"

case $key in
    -f|--folder)
    FOLDER="$2"
    shift # past argument
    shift # past value
    ;;
    -c|--count)
    COUNT="$2"
    shift # past argument
    shift # past value
    ;;
    -p|--project)
    PROJECT="$2"
    shift # past argument
    shift # past value
    ;;
    -h|--help)
    print_help
    exit 0
    ;;
    *) # unknown option
    POSITIONAL+=("$1") # save it in an array for later
    shift # past argument
    ;;
esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters

dir="$FOLDER"
count="$COUNT"

# No directory has been provided, use default
if [ -z "$dir" ]
then
    dir="${HOME}/sourcecode"
fi

# No count has been provided, use 5
if [ -z "$count" ]
then
    count="5"
fi

# Make sure directory ends with "/"
if [[ $dir != */ ]]
then
    dir="$dir/*"
else
    dir="$dir*"
fi

if [ -z "$PROJECT" ]
then
    show_multiple
else
    show_single $dir$PROJECT
fi

exit 0

Basically the script executes almost the same command we have seen at the beginning but it offers us a few more options.

We can list all the projects at once in our default folder ~/sourcecode:

./topcommitters.sh

We can see the help text:

./topcommitters.sh -h

We can specify where our projects are:

./topcommitters.sh -f ~/mycode

We can select where the projects are, which concrete project do we want and how many committers we want to see:

./topcommitters.sh -f ~/mycode -p ecommerce -c 3

One interesting feature is that, as you can notice, the command in the script is slightly different to the original command:

Original: git shortlog -s -n --all | head -3
Script: git shortlog -s -n --all -- . | head -${count}

This difference gives us the possibility to list committers in a folder inside the git repository even if that folder is not the repository folder. For example, imagine we have the next project structure:

big-project
-- .git
-- 3rd-party-apis
-- facebook
-- twitter
-- google
-- linkedin

Imagine that we want to obtain information about the facebook project. If we just execute “./topcommitters.sh -p big-project” we will obtain the top committers for the whole project and this is not what we want but, with the modification of the original command in the script, we are allowed to execute “./topcommitters.sh -p big-project/facebook” and obtain the exact information we want.

Git top committers

Java REST API + cURL + python3

During the next lines, I am going to implement a very simple REST API using JavaEE 7 and Java SE 8 technologies, maven as build tool and GlassFish as application server. I am going to test this API with cURL and, I am going to consume this API using python3 using the Requests module.

Java EE 7 REST API

First, we need to create a maven project in our favorite IDE and add our dependencies to the pom.xml file

...
<properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
 
<dependencies>
    <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>eclipselink</artifactId>
        <version>2.5.2</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
        <version>2.5.2</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax</groupId>
        <artifactId>javaee-api</artifactId>
        <version>7.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
 
<build>
    <finalName>apicrud</finalName>
</build>
...

Second, we are going to create a persistence.xml file. In this case, we are not interested in how to configure a DB, then, we are going to use the default datasource that GlassFish provides us as default “jdbc/__default“. With all of this in mind, the file should look like this:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
        http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="ApiCrudPU" transaction-type="JTA">
        <jta-data-source>jdbc/__default</jta-data-source>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="javax.persistence.schema-generation.database.action" value="create"/>
        </properties>
    </persistence-unit>
</persistence>

Now, we need to create our Entity. In this case, I have chosen a simple one, User with three attributes:

  • id: Autogenerated id to identify the user.
  • name: User’s name.
  • email: User’s email.

The result should be something like:

package com.wordpress.binarycoders.apicrud;
 
import java.io.Serializable;
import java.util.Objects;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
 
@Entity
@Table (name = "users")
@NamedQueries ({
    @NamedQuery (name = User.FIND_ALL, query = "SELECT u FROM User u")
})
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class User implements Serializable {
     
    private static final long serialVersionUID = 1L;
     
    public static final String FIND_ALL = "User.findAll";
     
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
     
    @Column(name = "name")
    private String name;
     
    @Column(name = "email")
    private String email;
 
    /* Getters, Setters, equals and hashCode methods */   
}

The next step, it is to implement the boundary or service layer. Being a simple example, we can skip this layer, but I like to implement everything for learning purposes. This layer is going to be very simple, only the necessary object to perform the operation in the DB and the basic operations to implement a CRUD. The result looks like:

package com.wordpress.binarycoders.apicrud;
 
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.validation.constraints.NotNull;
 
@Stateless
public class UserService {
     
    @PersistenceContext
    private EntityManager em;
     
    public void create(@NotNull User user) {
        this.em.persist(user);
    }
     
    public User findById(@NotNull Long id) {
        return this.em.find(User.class, id);
    }
     
    public List<User> findAll() {
        TypedQuery<User> typedQuery = this.em.createNamedQuery(User.FIND_ALL, User.class);
 
        return typedQuery.getResultList();
    }
     
    public void update(@NotNull User user) {
        this.em.merge(user);
    }
     
    public void delete(@NotNull Long id) {
        this.em.remove(this.findById(id));
    }
}

To do all of this available to the world, we need to implement our REST API, and for this, we need to configure the REST capabilities in our system, piece of cake with the last version of Java EE. We just need to create a class to enable these capabilities:

package com.wordpress.binarycoders.apicrud;
 
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
 
@ApplicationPath("/rs")
public class ApplicationConfig extends Application {
     
}

And, finally, we need to implement our REST layer with the four actions that are going to allow us to perform the CRUD operations:

package com.wordpress.binarycoders.apicrud;
 
import java.net.URI;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
 
@Path("/users")
@Produces ({ MediaType.APPLICATION_JSON })
@Consumes ({ MediaType.APPLICATION_JSON })
@Stateless
public class UserEndPoint {
     
    @EJB
    private UserService service;
     
    @Context
    private UriInfo uriInfo;
     
    @GET
    public Response findAll() {
        List<User> users = this.service.findAll();
         
        GenericEntity<List<User>> list = new GenericEntity<List<User>>(users) {};
         
        return Response.ok(list).build();
    }
     
    @GET
    @Path("/{id}")
    public Response findById(@PathParam("id") Long id) {
        User user = this.service.findById(id);
         
        if (user == null) {
            throw new NotFoundException();
        }
         
        return Response.ok(user).build();
    }
     
    @POST
    public Response create(User user) {
        this.service.create(user);
         
        URI uri = uriInfo.getAbsolutePathBuilder().path(String.valueOf(user.getId())).build();
         
        return Response.created(uri).build();
    }
     
    @PUT
    public Response update(User user) {
        this.service.update(user);
         
        return Response.ok().build();
    }
     
    @DELETE
    @Path("/{id}")
    public Response delete(@PathParam("id") Long id) {
        this.service.delete(id);
         
        return Response.noContent().build();
    }
}

I think that in this point, assuming that everyone reading this, it is a developer and this is not an entry level post, all of you should be capable to understand everything has been written in the above lines. If there are things that you do not know or you do not understand due to a lack of information or your knowledge is not enough (all of us have been there), look at the note at the end of this post.

cURL test

Now, obviously, we need to test if our REST API is working properly. For this, we can implement some solution based on test frameworks or manual code but, due to the simplicity of the object User, it is enough with the cURL tool. To test our API we are going to perform these operations:

  • Select all the users
  • Create a user
  • Select the created user
  • Update the user
  • Delete the user

These operations can be performed with the next commands:

curl -i -H "Content-Type: application/json" http://localhost:8080/ApiCRUD/rs/users
curl -i -H "Content-Type: application/json" -X POST -d '{"name":"john","email":"john@example.org"}' http://localhost:8080/ApiCRUD/rs/users
curl -i -H "Content-Type: application/json" http://localhost:8080/ApiCRUD/rs/users/1
curl -i -H "Content-Type: application/json" -X PUT -d '{"id”:1,”name":"John Doe","email":"john.doe@example.org"}' http://localhost:8080/ApiCRUD/rs/users
curl -i -H "Content-Type: application/json" -X DELETE http://localhost:8080/ApiCRUD/rs/users/1

Everything should look well, and the appropriate HTTP codes should be received.

Python3 client

The last step today, it is to implement a very very simple python3 client to consume our API. This is going to be a basic script to invoke the CRUD operations available in the API.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
 
import requests
import json
 
URL = 'http://localhost:8080/ApiCRUD/rs/users'
 
def getUsers():
    print('Get users...\n')
 
    response = requests.get(URL)
 
    assert response.status_code == 200
 
    users = response.json()
    print(json.dumps(users, indent = 4, separators = (',', ':')))
 
def getUser(location):
    print('Get single user ...\n')
 
    response = requests.get(location)
 
    assert response.status_code == 200
 
    users = response.json()
    print(json.dumps(users, indent = 4, separators = (',', ':')))
 
def createUser():
    print('Creating user...\n')
 
    headers = {'content-type': 'application/json'}
    user = {"name": "John Doe", "email": "john.doe@example.org"}
    response = requests.post(URL, data = json.dumps(user), headers = headers)
 
    assert response.status_code == 201
 
    print('Location: ' + response.headers['location'])
 
    return response.headers['location']
 
def updateUser(location):
    print('Updating user...')
 
    loc = location.split('/')
 
    headers = {'content-type': 'application/json'}
    user = {"id": loc[-1], "name": "Jane Doe", "email": "jane.doe@example.org"}
    response = requests.put(URL, data = json.dumps(user), headers = headers)
 
    assert response.status_code == 200
 
def deleteUser(location):
    print('Deleting user...')
 
    response = requests.delete(location)
 
    assert response.status_code == 204
 
    print('Checking delete operation')
    response = requests.get(location)
 
    assert response.status_code == 404
 
def main():
    getUsers()
    location = createUser()
    getUser(location)
    updateUser(location)
    getUser(location)
    deleteUser(location)
 
if __name__ == "__main__":
    main()

And that’s all. Now, we have a little REST AP, a python3 client and a basic knowledge about how to use cURL to test it.

See you.

You can find the code for the REST API available here.

Note: All the content in this post will be splitter and explained in future post, one for each one of the three big point explained in here: Java EE 7 REST API creation, cURL test and python3 REST client.

Java REST API + cURL + python3

Mac OS X + IntelliJ IDEA + JDK 8

I don´t understand exactly why when you try to install IntelliJ IDEA in a Windows System or a Linux System the requirements say something like “JDK 1.6 or higher“, but when you try to install it in a Mac OS X System, it says “Java 6“. I guess it is something related with Apple and Oracle licenses and support but I am not sure.

In any case, it is clear that Java / Scala developers want to install the IDE with the latests JDK version. Probably, there are a lot of solutions, but the one that I am going to explain has work for me and it is pretty simple:

  1. Download the latest version of IntelliJ IDEA (in my case, I have downloaded the community edition)
  2. Install it in your system. Copy to your  Applications folder.
  3. Click with the secondary button in the file IntelliJ IDEA 14 CE.app and select Show package content.
  4. Edit the file Contents/Info.plist.
  5. Find the section with the key JVMVersion.
  6. Replace 1.6* with 1.8* (1.7* if you are using JDK 7)

That’s all. Now you can run IntelliJ IDEA with your installed JDK without the need to install the JDK 6.

See you.

Update: In the IntelliJ IDEA supports page, there is a thread about all of this, you can find it here. Thanks to Trisha Gee for her quick answer through twitter.

Mac OS X + IntelliJ IDEA + JDK 8