== JDBC Auth Provider implementation

We provide an implementation of `link:../../yardoc/VertxAuthCommon/AuthProvider.html[AuthProvider]` which uses the Vert.x `link:../../yardoc/VertxJdbc/JDBCClient.html[JDBCClient]`
to perform authentication and authorisation against any JDBC compliant database.

To use this project,
add the following dependency to the _dependencies_ section of your build descriptor:

* Maven (in your `pom.xml`):

[source,xml,subs="+attributes"]
----
<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-auth-jdbc</artifactId>
  <version>3.4.2</version>
</dependency>
----

* Gradle (in your `build.gradle` file):

[source,groovy,subs="+attributes"]
----
compile 'io.vertx:vertx-auth-jdbc:3.4.2'
----

To create an instance you first need an instance of `link:../../yardoc/VertxJdbc/JDBCClient.html[JDBCClient]`. To learn how to create one
of those please consult the documentation for the JDBC client.

Once you've got one of those you can create a `link:../../yardoc/VertxAuthJdbc/JDBCAuth.html[JDBCAuth]` instance as follows:

[source,ruby]
----
require 'vertx-jdbc/jdbc_client'
require 'vertx-auth-jdbc/jdbc_auth'

jdbcClient = VertxJdbc::JDBCClient.create_shared(vertx, jdbcClientConfig)

authProvider = VertxAuthJdbc::JDBCAuth.create(vertx, jdbcClient)

----

Once you've got your instance you can authenticate and authorise with it just like any `link:../../yardoc/VertxAuthCommon/AuthProvider.html[AuthProvider]`.

The out of the box config assumes certain queries for authentication and authorisation, these can easily be changed
with the operations `link:../../yardoc/VertxAuthJdbc/JDBCAuth.html#set_authentication_query-instance_method[setAuthenticationQuery]`,
`link:../../yardoc/VertxAuthJdbc/JDBCAuth.html#set_permissions_query-instance_method[setPermissionsQuery]` and
`link:../../yardoc/VertxAuthJdbc/JDBCAuth.html#set_roles_query-instance_method[setRolesQuery]`, if you want to use them with a different
database schema.

The default implementation assumes that the password is stored in the database as a SHA-512 hash after being
concatenated with a salt. It also assumes the salt is stored in the table too.

The basic data definition for the storage should look like this:

[source,sql]
----
--
-- Take this script with a grain of salt and adapt it to your RDBMS
--
CREATE TABLE `user` (
  `username` VARCHAR(255) NOT NULL,
  `password` VARCHAR(255) NOT NULL,
  `password_salt` VARCHAR(255) NOT NULL
);

CREATE TABLE `user_roles` (
  `username` VARCHAR(255) NOT NULL,
  `role` VARCHAR(255) NOT NULL
);

CREATE TABLE `roles_perms` (
  `role` VARCHAR(255) NOT NULL,
  `perm` VARCHAR(255) NOT NULL
);

ALTER TABLE user ADD CONSTRAINT `pk_username` PRIMARY KEY (username);
ALTER TABLE user_roles ADD CONSTRAINT `pk_user_roles` PRIMARY KEY (username, role);
ALTER TABLE roles_perms ADD CONSTRAINT `pk_roles_perms` PRIMARY KEY (role);

ALTER TABLE user_roles ADD CONSTRAINT fk_username FOREIGN KEY (username) REFERENCES user(username);
ALTER TABLE user_roles ADD CONSTRAINT fk_roles FOREIGN KEY (role) REFERENCES roles_perms(role);

----

If you want to override this behaviour you can do so by providing an alternative hash strategy and setting it with
`link:../../yardoc/VertxAuthJdbc/JDBCAuth.html#set_hash_strategy-instance_method[setHashStrategy]`.

WARNING: It is advised to always store your passwords as hashes in your database tables which have been created
with a salt which should be stored in the row too. A strong hashing algorithm should be used. It is strongly advised
never to store your passwords as plain text.

== Hashing passwords

Like any application there will be a time where you need to store new users into the database. Has you have learn
passwords are not stored in plain text but hashed according to the hashing strategy. The same strategy is required
to hash new password before storing it to the database. Doing it is a 3 step task.

1. Generate a salt string
2. Hash the password given the salt string
3. Store it to the database

[source,ruby]
----

salt = auth.generate_salt()
hash = auth.compute_hash("sausages", salt)
# save to the database
conn.update_with_params("INSERT INTO user VALUES (?, ?, ?)", [
  "tim",
  hash,
  salt
]) { |res_err,res|
  if (res_err == nil)
    # success!
  end
}

----

WARNING: Hashing user password with salt can be not enough, this approach his good enough for avoiding rainbow tables
attacks or precomputed table attacks but if the attacker gets the database it will be easier to setup a brute force
attack. This kind of attack is slower but all required information is given: the hash and the salt.

To make the hash attack more complex the default strategy allows you to provide an application level list of nonces
to be used in the computation. This list should not be stored in the database since it add an extra variable to the
computation that is unknown, making the brute force attack as potentially the only way to crack the hash. You might
want to refresh the nonces now and then so you should add and never remove entries to the list, for example:

[source,ruby]
----
auth.set_nonces([
  "random_hash_1",
  "random_hash_1"
])

----

In order to decode there is no change required to the code, however to generate a new user you must specify which
nonce (by it's index) you want to use. If you look at the previous example, the usage is quite similar:

1. Generate a salt string
2. Hash the password given the salt string and choosen nonce
3. Store it to the database

[source,ruby]
----

auth.set_nonces([
  "random_hash_1",
  "random_hash_1"
])

salt = auth.generate_salt()
# we will pick the second nonce
hash = auth.compute_hash("sausages", salt, 1)
# save to the database
conn.update_with_params("INSERT INTO user VALUES (?, ?, ?)", [
  "tim",
  hash,
  salt
]) { |res_err,res|
  if (res_err == nil)
    # success!
  end
}

----

== Authentication

When authenticating using this implementation, it assumes `username` and `password` fields are present in the
authentication info:

[source,ruby]
----

authInfo = {
  'username' => "tim",
  'password' => "sausages"
}

authProvider.authenticate(authInfo) { |res_err,res|
  if (res_err == nil)
    user = res
  else
    # Failed!
  end
}

----

== Authorisation - Permission-Role Model

Although Vert.x auth itself does not mandate any specific model of permissions (they are just opaque strings), this
implementation assumes a familiar user/role/permission model, where a user can have zero or more roles and a role
can have zero or more permissions.

If validating if a user has a particular permission simply pass the permission into.
`link:../../yardoc/VertxAuthCommon/User.html#is_authorised-instance_method[isAuthorised]` as follows:

[source,ruby]
----

user.is_authorised("commit_code") { |res_err,res|
  if (res_err == nil)
    hasPermission = res
  else
    # Failed to
  end
}


----

If validating that a user has a particular _role_ then you should prefix the argument with the role prefix.

[source,ruby]
----

user.is_authorised("role:manager") { |res_err,res|
  if (res_err == nil)
    hasRole = res
  else
    # Failed to
  end
}


----

The default role prefix is `role:`. You can change this with `link:../../yardoc/VertxAuthJdbc/JDBCAuth.html#set_role_prefix-instance_method[setRolePrefix]`.
<a href="mailto:julien@julienviet.com">Julien Viet</a><a href="http://tfox.org">Tim Fox</a>