Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions vertx-auth-properties/src/main/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,10 @@ role.developer=do_actual_work

When describing roles a wildcard `*` can be used to indicate that the role has all permissions.

The provider is usually created from a file on the file system with
{@link io.vertx.ext.auth.properties.PropertyFileAuthentication#create(io.vertx.core.Vertx, java.lang.String)}.
When the content is not stored on the file system, e.g.: it is loaded from the classpath, a remote location or preprocessed
in memory, the provider can be created from a {@link io.vertx.core.buffer.Buffer} with
{@link io.vertx.ext.auth.properties.PropertyFileAuthentication#create(io.vertx.core.Vertx, io.vertx.core.buffer.Buffer)}.

The implementation currently assumes that user/password based authentication is used.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import io.vertx.codegen.annotations.VertxGen;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.ext.auth.authentication.AuthenticationProvider;
import io.vertx.ext.auth.properties.impl.PropertyFileAuthenticationImpl;

Expand All @@ -34,4 +35,17 @@ public interface PropertyFileAuthentication extends AuthenticationProvider {
static PropertyFileAuthentication create(Vertx vertx, String path) {
return new PropertyFileAuthenticationImpl(vertx, path);
}

/**
* Create a File authentication provider from the given property file content. Use this when the
* content is not stored on the file system, e.g.: loaded from the classpath, a remote location
* or preprocessed in memory.
*
* @param vertx the Vert.x instance
* @param buffer the property file content
* @return the authentication provider
*/
static PropertyFileAuthentication create(Vertx vertx, Buffer buffer) {
return new PropertyFileAuthenticationImpl(vertx, buffer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import io.vertx.codegen.annotations.VertxGen;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.ext.auth.authorization.AuthorizationProvider;
import io.vertx.ext.auth.properties.impl.PropertyFileAuthenticationImpl;

Expand All @@ -35,4 +36,17 @@ static PropertyFileAuthorization create(Vertx vertx, String path) {
return new PropertyFileAuthenticationImpl(vertx, path);
}

/**
* Create a File authorization provider from the given property file content. Use this when the
* content is not stored on the file system, e.g.: loaded from the classpath, a remote location
* or preprocessed in memory.
*
* @param vertx the Vert.x instance
* @param buffer the property file content
* @return the authorization provider
*/
static PropertyFileAuthorization create(Vertx vertx, Buffer buffer) {
return new PropertyFileAuthenticationImpl(vertx, buffer);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.internal.logging.Logger;
import io.vertx.core.internal.logging.LoggerFactory;
import io.vertx.ext.auth.authentication.CredentialValidationException;
Expand Down Expand Up @@ -72,9 +73,20 @@ private void addPermission(String permission) {
public PropertyFileAuthenticationImpl(Vertx vertx, String path) {
Objects.requireNonNull(vertx);
this.path = Objects.requireNonNull(path);
parse(vertx.fileSystem().readFileBlocking(path).toString(StandardCharsets.UTF_8));
}

public PropertyFileAuthenticationImpl(Vertx vertx, Buffer buffer) {
Objects.requireNonNull(vertx);
Objects.requireNonNull(buffer);
// there is no path to identify this provider
this.path = "properties";
parse(buffer.toString(StandardCharsets.UTF_8));
}

private void parse(String fileContent) {
final Map<String, Role> roles = new HashMap<>();

String fileContent = vertx.fileSystem().readFileBlocking(path).toString(StandardCharsets.UTF_8);
String[] lines = fileContent.split("\n");
for (String line : lines) {
if (line.length() == 0 || line.startsWith("#")) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/********************************************************************************
* Copyright (c) 2019 Stephane Bastian
*
* This program and the accompanying materials are made available under the 2
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors: 4
* Stephane Bastian - initial API and implementation
********************************************************************************/
package io.vertx.tests;

import io.vertx.core.buffer.Buffer;
import io.vertx.ext.auth.authentication.AuthenticationProvider;
import io.vertx.ext.auth.authentication.Credentials;
import io.vertx.ext.auth.authentication.UsernamePasswordCredentials;
import io.vertx.ext.auth.authorization.AuthorizationContext;
import io.vertx.ext.auth.authorization.AuthorizationProvider;
import io.vertx.ext.auth.authorization.PermissionBasedAuthorization;
import io.vertx.ext.auth.authorization.RoleBasedAuthorization;
import io.vertx.ext.auth.properties.PropertyFileAuthentication;
import io.vertx.ext.auth.properties.PropertyFileAuthorization;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.RunTestOnContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(VertxUnitRunner.class)
public class PropertyFileBufferAuthenticationTest {

private static final Buffer CONTENT = Buffer.buffer(
"user.tim=sausages,morris_dancer,developer\n" +
"role.morris_dancer=bang_sticks\n" +
"role.developer=do_actual_work\n");

@Rule
public final RunTestOnContext rule = new RunTestOnContext();

private AuthenticationProvider authn;
private AuthorizationProvider authz;

@Before
public void setUp() throws Exception {
authn = PropertyFileAuthentication.create(rule.vertx(), CONTENT);
authz = PropertyFileAuthorization.create(rule.vertx(), CONTENT);
}

@Test
public void testSimpleAuthenticate(TestContext should) {
final Async test = should.async();
Credentials authInfo = new UsernamePasswordCredentials("tim", "sausages");
authn.authenticate(authInfo)
.onFailure(should::fail)
.onSuccess(user -> {
should.assertNotNull(user);
should.assertEquals("tim", user.principal().getString("username"));
test.complete();
});
}

@Test
public void testSimpleAuthenticateFailWrongPassword(TestContext should) {
final Async test = should.async();
Credentials authInfo = new UsernamePasswordCredentials("tim", "wrongpassword");
authn.authenticate(authInfo)
.onSuccess(user -> should.fail("Not Expected"))
.onFailure(thr -> {
should.assertNotNull(thr);
test.complete();
});
}

@Test
public void testHasRoleAndPermission(TestContext should) {
final Async test = should.async();
Credentials authInfo = new UsernamePasswordCredentials("tim", "sausages");
authn.authenticate(authInfo)
.onFailure(should::fail)
.onSuccess(user -> authz.getAuthorizations(user)
.onFailure(should::fail)
.onSuccess(v -> {
should.assertTrue(
RoleBasedAuthorization.create("morris_dancer").match(AuthorizationContext.create(user)));
should.assertTrue(
PermissionBasedAuthorization.create("do_actual_work").match(AuthorizationContext.create(user)));
test.complete();
}));
}
}