Java tutorial
/* * Copyright 2015-2018 Emmanuel Keller / QWAZR * <p> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.qwazr.library.realm.table; import com.fasterxml.jackson.annotation.JsonIgnore; import com.qwazr.database.TableServiceInterface; import com.qwazr.database.model.ColumnDefinition; import com.qwazr.database.store.KeyStore; import com.qwazr.library.AbstractLibrary; import com.qwazr.utils.LoggerUtils; import io.undertow.security.idm.Account; import io.undertow.security.idm.Credential; import io.undertow.security.idm.IdentityManager; import io.undertow.security.idm.PasswordCredential; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.SystemUtils; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import java.security.Principal; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; public class TableRealmConnector extends AbstractLibrary implements IdentityManager { private static final Logger LOGGER = LoggerUtils.getLogger(TableRealmConnector.class); public final String table_name = null; public final String login_column = null; public final String password_column = null; public final String roles_column = null; @JsonIgnore private volatile TableServiceInterface tableService; @JsonIgnore private volatile Set<String> columns; public void load() { tableService = libraryManager.getInstancesSupplier().getInstance(TableServiceInterface.class); final Set<String> tables = tableService.list(); if (!tables.contains(table_name)) { tableService.createTable(table_name, SystemUtils.IS_OS_WINDOWS ? KeyStore.Impl.lmdb : KeyStore.Impl.leveldb); tableService.setColumn(table_name, login_column, new ColumnDefinition(ColumnDefinition.Type.STRING, ColumnDefinition.Mode.INDEXED)); tableService.setColumn(table_name, password_column, new ColumnDefinition(ColumnDefinition.Type.STRING, ColumnDefinition.Mode.STORED)); tableService.setColumn(table_name, roles_column, new ColumnDefinition(ColumnDefinition.Type.STRING, ColumnDefinition.Mode.STORED)); } columns = new HashSet<>(); columns.add(password_column); columns.add(roles_column); } @Override public Account verify(final Account account) { return account; } @Override public Account verify(final String id, final Credential credential) { // This realm only support one type of credential if (!(credential instanceof PasswordCredential)) throw new RuntimeException("Unsupported credential type: " + credential.getClass().getName()); PasswordCredential passwordCredential = (PasswordCredential) credential; // We request the database final Map<String, ?> row; try { row = tableService.getRow(table_name, id, columns); if (row == null) return null; } catch (WebApplicationException e) { if (e.getResponse().getStatusInfo().getFamily() == Response.Status.Family.CLIENT_ERROR) return authenticationFailure("Unknown user: " + id); throw e; } Object password = row.get(password_column); if (password == null) return null; if (password instanceof String[]) { String[] passwordArray = (String[]) password; if (passwordArray.length == 0) return null; password = passwordArray[0]; } // The password is stored hashed final String passwd = new String(passwordCredential.getPassword()); String digest = DigestUtils.sha256Hex(passwd); if (!digest.equals(password)) return authenticationFailure("Wrong password: " + id + " " + digest + '/' + passwd + '/' + password); //We retrieve the roles final Object object = row.get(roles_column); final LinkedHashSet<String> roles = new LinkedHashSet<>(); if (object instanceof String[]) { for (Object o : (String[]) object) roles.add(o.toString()); } else roles.add(object.toString()); return new Account() { @Override public Principal getPrincipal() { return () -> id; } @Override public Set<String> getRoles() { return roles; } }; } private Account authenticationFailure(final String msg) { LOGGER.warning(msg); try { Thread.sleep(2000); } catch (InterruptedException e) { LOGGER.log(Level.WARNING, e, e::getMessage); } return null; } @Override public Account verify(final Credential credential) { return null; } }