How to authenticate Dropwizard admin portal, so as to restrict normal users from accessing it?
Please help
In your config, you can set adminUsername and adminPassword under http like so:
http:
adminUsername: user1234
adminPassword: pass5678
For DW 0.7 my approach would be:
public class AdminConstraintSecurityHandler extends ConstraintSecurityHandler {
private static final String ADMIN_ROLE = "admin";
public AdminConstraintSecurityHandler(final String userName, final String password) {
final Constraint constraint = new Constraint(Constraint.__BASIC_AUTH, ADMIN_ROLE);
constraint.setAuthenticate(true);
constraint.setRoles(new String[]{ADMIN_ROLE});
final ConstraintMapping cm = new ConstraintMapping();
cm.setConstraint(constraint);
cm.setPathSpec("/*");
setAuthenticator(new BasicAuthenticator());
addConstraintMapping(cm);
setLoginService(new AdminMappedLoginService(userName, password, ADMIN_ROLE));
}
}
public class AdminMappedLoginService extends MappedLoginService {
public AdminMappedLoginService(final String userName, final String password, final String role) {
putUser(userName, new Password(password), new String[]{role});
}
#Override
public String getName() {
return "Hello";
}
#Override
protected UserIdentity loadUser(final String username) {
return null;
}
#Override
protected void loadUsers() throws IOException {
}
}
and using them in the way:
environment.admin().setSecurityHandler(new AdminConstraintSecurityHandler(...))
Newer Jetty versions do not have MappedLoginService, so #Kamil's answer no longer works. I have modified their answer to get it working as of Dropwizard 1.2.2:
public class AdminConstraintSecurityHandler extends ConstraintSecurityHandler {
private static final String ADMIN_ROLE = "admin";
public AdminConstraintSecurityHandler(final String userName, final String password) {
final Constraint constraint = new Constraint(Constraint.__BASIC_AUTH, ADMIN_ROLE);
constraint.setAuthenticate(true);
constraint.setRoles(new String[]{ADMIN_ROLE});
final ConstraintMapping cm = new ConstraintMapping();
cm.setConstraint(constraint);
cm.setPathSpec("/*");
setAuthenticator(new BasicAuthenticator());
addConstraintMapping(cm);
setLoginService(new AdminLoginService(userName, password));
}
public class AdminLoginService extends AbstractLoginService {
private final UserPrincipal adminPrincipal;
private final String adminUserName;
public AdminLoginService(final String userName, final String password) {
this.adminUserName = Objects.requireNonNull(userName);
this.adminPrincipal = new UserPrincipal(userName, new Password(Objects.requireNonNull(password)));
}
#Override
protected String[] loadRoleInfo(final UserPrincipal principal) {
if (adminUserName.equals(principal.getName())) {
return new String[]{ADMIN_ROLE};
}
return new String[0];
}
#Override
protected UserPrincipal loadUserInfo(final String userName) {
return adminUserName.equals(userName) ? adminPrincipal : null;
}
}
}
Related
I'm using springboot and rabbitmq to receive a message.
The first consumer i created works, declared as below:
#Component
public class UserConsumer {
#Autowired
private RabbitTemplate template;
#RabbitListener(queues = MessagingConfig.CONSUME_QUEUE)
public void consumeMessageFromQueue(MassTransitRequest userRequest) {
...
}
}
I then needed a second consumer so i duplicated the above and called it another name:
#Component
public class PackConsumer {
#Autowired
private RabbitTemplate template;
#RabbitListener(queues = MessagingConfig.CONSUME_QUEUE_CREATE_PACK)
public void consumeMessageFromQueue(MassTransitRequest fileRequest) {
...
}
}
Everything works locally on my machine, however when i deploy it the new queue does not process messages because there is no consumer connected to it. The UserConsumer continues to work.
Is there something else i should be doing in order to connect to the new queue at the same time as the original?
During my learning i did add a "MessagingConfig" class as below, however i believe it relates to sending messages and not receiving them or an alternative configuration:
#Configuration
public class MessagingConfig {
public static final String CONSUME_QUEUE = "merge-document-request";
public static final String CONSUME_EXCHANGE = "merge-document-request";
public static final String CONSUME_ROUTING_KEY = "";
public static final String PUBLISH_QUEUE = "merge-document-response";
public static final String PUBLISH_EXCHANGE = "merge-document-response";
public static final String PUBLISH_ROUTING_KEY = "";
public static final String CONSUME_QUEUE_CREATE_PACK = "create-pack-request";
public static final String CONSUME_EXCHANGE_CREATE_PACK = "create-pack-request";
public static final String CONSUME_ROUTING_KEY_CREATE_PACK = "";
public static final String PUBLISH_QUEUE_CREATE_PACK = "create-pack-response";
public static final String PUBLISH_EXCHANGE_CREATE_PACK = "create-pack-response";
public static final String PUBLISH_ROUTING_KEY_CREATE_PACK = "";
#Bean
public Queue queue() {
return new Queue(CONSUME_QUEUE);
}
#Bean
public TopicExchange exchange() {
return new TopicExchange(CONSUME_EXCHANGE);
}
#Bean
public Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(CONSUME_ROUTING_KEY);
}
#Bean
public MessageConverter converter() {
return new Jackson2JsonMessageConverter();
}
#Bean
public AmqpTemplate template(ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(converter());
return rabbitTemplate;
}
}
Thanks in advance
public class MyApplication extends Application<MyConfiguration> {
final static Logger LOG = Logger.getLogger(MyApplication.class);
public static void main(final String[] args) throws Exception {
new MyApplication().run(args);
}
#Override
public String getName() {
return "PFed";
}
#Override
public void initialize(final Bootstrap<MyConfiguration> bootstrap) {
// TODO: application initialization
bootstrap.addBundle(new DBIExceptionsBundle());
}
#Override
public void run(final MyConfiguration configuration,
final Environment environment) {
// TODO: implement application
final DBIFactory factory = new DBIFactory();
final DBI jdbi = factory.build(environment, configuration.getDataSourceFactory(), "postgresql");
UserDAO userDAO = jdbi.onDemand(UserDAO.class);
userDAO.findNameById(1);
UserResource userResource = new UserResource(new UserService(userDAO));
environment.jersey().register(userResource);
}
I get the the following error at findNameById.
java.lang.NoSuchMethodError: java.lang.Object.findNameById(I)Ljava/lang/String;
at org.skife.jdbi.v2.sqlobject.CloseInternalDoNotUseThisClass$$EnhancerByCGLIB$$a0e63670.CGLIB$findNameById$5()
}
public interface UserDAO {
#SqlQuery("select userId from user where id = :email")
User isEmailAndUsernameUnique(#Bind("email") String email);
#SqlQuery("select name from something where id = :id")
String findNameById(#Bind("id") int id);
}
I'd like to not require my clients to provide content_type application/json but just default to it. I got this working.
I also tried to combine with another example to implement a custom isFatal(Throwable t) from ConditionalRejectingErrorHandler. I can get my custom error handler to fire, but then it seems to require the content_type property again. I can't figure out how to get them both to work at the same time.
Any ideas?
The below successfully works to not require content_type
EDIT: The below code does not work as I thought. An old message in the queue with the property content_type application/json must have been pulled in
#EnableRabbit
#Configuration
public class ExampleRabbitConfigurer implements
RabbitListenerConfigurer {
#Value("${spring.rabbitmq.host:'localhost'}")
private String host;
#Value("${spring.rabbitmq.port:5672}")
private int port;
#Value("${spring.rabbitmq.username}")
private String username;
#Value("${spring.rabbitmq.password}")
private String password;
#Autowired
private MappingJackson2MessageConverter mappingJackson2MessageConverter;
#Autowired
private DefaultMessageHandlerMethodFactory messageHandlerMethodFactory;
#Bean
public MappingJackson2MessageConverter mappingJackson2MessageConverter() {
return new MappingJackson2MessageConverter();
}
#Bean
public DefaultMessageHandlerMethodFactory messageHandlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setMessageConverter(mappingJackson2MessageConverter);
return factory;
}
#Override
public void configureRabbitListeners(final RabbitListenerEndpointRegistrar registrar) {
registrar.setMessageHandlerMethodFactory(messageHandlerMethodFactory);
}
The below here works to override isFatal() in ConditionalRejectingErrorHandler. The SimpleRabbitListenerContainerFactory.setMessageConverter() seems like it should serve the same purpose as DefaultMessageHandlerMethodFactory.setMessageConverter(). Obviously this is not the case.
#EnableRabbit
#Configuration
public class ExampleRabbitConfigurer {
#Value("${spring.rabbitmq.host:'localhost'}")
private String host;
#Value("${spring.rabbitmq.port:5672}")
private int port;
#Value("${spring.rabbitmq.username}")
private String username;
#Value("${spring.rabbitmq.password}")
private String password;
#Autowired
ConnectionFactory connectionFactory;
#Autowired
Jackson2JsonMessageConverter jackson2JsonConverter;
#Autowired
ErrorHandler amqpErrorHandlingExceptionStrategy;
#Bean
public Jackson2JsonMessageConverter jackson2JsonConverter() {
return new Jackson2JsonMessageConverter();
}
#Bean
public ErrorHandler amqpErrorHandlingExceptionStrategy() {
return new ConditionalRejectingErrorHandler(new AmqpErrorHandlingExceptionStrategy());
}
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(jackson2JsonConverter);
factory.setErrorHandler(amqpErrorHandlingExceptionStrategy);
return factory;
}
public static class AmqpErrorHandlingExceptionStrategy extends ConditionalRejectingErrorHandler.DefaultExceptionStrategy {
private final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(getClass());
#Override
public boolean isFatal(Throwable t) {
if (t instanceof ListenerExecutionFailedException) {
ListenerExecutionFailedException lefe = (ListenerExecutionFailedException) t;
LOGGER.error("Failed to process inbound message from queue "
+ lefe.getFailedMessage().getMessageProperties().getConsumerQueue()
+ "; failed message: " + lefe.getFailedMessage(), t);
}
return super.isFatal(t);
}
}
Use an "after receive" MessagePostProcessor to add the contentType header to the inbound message.
Starting with version 2.0, you can add the MPP to the container factory.
For earlier versions you can reconfigure...
#SpringBootApplication
public class So47424449Application {
public static void main(String[] args) {
SpringApplication.run(So47424449Application.class, args);
}
#Bean
public ApplicationRunner runner(RabbitListenerEndpointRegistry registry, RabbitTemplate template) {
return args -> {
SimpleMessageListenerContainer container =
(SimpleMessageListenerContainer) registry.getListenerContainer("myListener");
container.setAfterReceivePostProcessors(m -> {
m.getMessageProperties().setContentType("application/json");
return m;
});
container.start();
// send a message with no content type
template.setMessageConverter(new SimpleMessageConverter());
template.convertAndSend("foo", "{\"bar\":\"baz\"}", m -> {
m.getMessageProperties().setContentType(null);
return m;
});
template.convertAndSend("foo", "{\"bar\":\"qux\"}", m -> {
m.getMessageProperties().setContentType(null);
return m;
});
};
}
#Bean
public Jackson2JsonMessageConverter converter() {
return new Jackson2JsonMessageConverter();
}
#RabbitListener(id = "myListener", queues = "foo", autoStartup = "false")
public void listen(Foo foo) {
System.out.println(foo);
if (foo.bar.equals("qux")) {
throw new MessageConversionException("test");
}
}
public static class Foo {
public String bar;
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
#Override
public String toString() {
return "Foo [bar=" + this.bar + "]";
}
}
}
As you can see, since it modifies the source message, the modified header is available in the error handler...
2017-11-22 09:39:26.615 WARN 97368 --- [cTaskExecutor-1] ingErrorHandler$DefaultExceptionStrategy : Fatal message conversion error; message rejected; it will be dropped or routed to a dead letter exchange, if so configured: (Body:'{"bar":"qux"}' MessageProperties [headers={}, contentType=application/json, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, redelivered=false, receivedExchange=, receivedRoutingKey=foo, deliveryTag=2, consumerTag=amq.ctag-re1kcxKV14L_nl186stM0w, consumerQueue=foo]), contentType=application/json, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, redelivered=false, receivedExchange=, receivedRoutingKey=foo, deliveryTag=2, consumerTag=amq.ctag-re1kcxKV14L_nl186stM0w, consumerQueue=foo])
I had created 4 classes for the restlet. However, When I hit on the browser, http://localhost:8182/firstSteps/hello, it returns me UserName = userName, Password = password. Which class should I change in order to get the intended url such as http://localhost:8080/restletTest?p1=abc&p2=def??
package firstStep;
import org.restlet.Component;
import org.restlet.data.Protocol;
public class Mainone {
public static void main(String[] args) throws Exception {
// Create a new Component.
Component component = new Component();
// Add a new HTTP server listening on port 8182.
component.getServers().add(Protocol.HTTP, 8182);
// Attach the sample application.
component.getDefaultHost().attach("/firstSteps", new FirstStepsApplication());
// Start the component.
component.start();
}}
package firstStep;
import org.restlet.Application;
import org.restlet.Restlet;
import org.restlet.routing.Router;
public class FirstStepsApplication extends Application{
public Restlet createInboundRoot(){
Router router = new Router(getContext());
router.attach("/hello",FirstServerResource.class);
return router;
}}
package firstStep;
import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;
public class FirstServerResource extends ServerResource {
Contact contact = new Contact("userName","Password");
//Contact contactTwo = contact.retrieve();
// #Get
// public Contact retrieve() {
// return contact;
// }
#Get
public String toString() {
return contact.toString();
}
}
package firstStep;
public class Contact {
private String userName;
private String password;
//Constructor
public Contact(String userName,String password){
this.userName = userName;
this.password = password;
}
public Contact retrieve(){
System.out.println("Contact retrieve():"+this.userName+"|"+this.password);
return this;
}
public String toString(){
return "Username:\t"+this.userName+"\nPassword:\t"+this.password;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}}
When you hit http://localhost:8182/firstSteps/hello
the #Get toString()-method of contact is called and the output
UserName: userName
Password: Password
is correct.
So The value is returned from the server correctly.
What else do you want to do?
can i access ApplicationResource.properties file keys from Action Class in Struts 2
and update the values of the key ?
I don't think you can update the values of those keys directly, that would kind of defeat the purpose of it being (static) resources.
You can however use placeholders.
ApplicationResources.properties
property.key=Hi {0}, there's a problem with {1}
MyAction.java
public ActionForward execute(ActionMapping mapping,
ActionForm form,
javax.servlet.ServletRequest request,
javax.servlet.ServletResponse response)
throws java.lang.Exception {
MessageResources msgResource = getResources(request);
String msg = msgResource.getMessage("property.key", "Sankar", "updating values in the resources.");
}
Yes its possible.
Lets say if you have a property error.login in applicationResources.properties file.
eg : error.login= Invalid Username/Password. Please try again.
then in the Action class you can access it like this : getText("error.login")
Complete example:
applicationResources.properties
error.login= Invalid Username/Password
LoginAction.java
package net.sumitknath.struts2;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport {
private String username;
private String password;
public String execute() {
if (this.username.equals("admin") && this.password.equals("admin123")) {
return "success";
} else {
addActionError(getText("error.login"));
return "error";
}
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}