I'm trying to integrate the apache superset with LDAP. with the configuration that I will provide below I can log in superset web with LDAP user but at the same time I can't log in with local users, such as admin, which was created during installation. What is the problem?
Also, I'm trying to split permission roles with "AUTH_ROLES_MAPPING" but with no luck yet. How can I do that?
from flask_appbuilder.security.manager import AUTH_OID
from flask_appbuilder.security.manager import AUTH_REMOTE_USER
from flask_appbuilder.security.manager import AUTH_DB
from flask_appbuilder.security.manager import AUTH_LDAP
#AUTH_TYPE = AUTH_LDAP
#AUTH_USER_REGISTRATION = True
#AUTH_USER_REGISTRATION_ROLE = "Public"
#AUTH_LDAP_SERVER = "ldap://10.10.0.50"
#AUTH_LDAP_USE_TLS = False
#AUTH_LDAP_BIND_USER = "CN=Administrator,CN=Users,DC=gru,DC=lab"
#AUTH_LDAP_BIND_PASSWORD = "password"
#AUTH_LDAP_SEARCH = "DC=gru,DC=lab"
#AUTH_LDAP_GROUP_FIELS="CN=linuxadmins,DC=gru,DC=lab"
#AUTH_LDAP_SEARCH = "DC=gru,DC=lab"
#AUTH_LDAP_UID_FIELD = "sAMAccountName"
#AUTH_LDAP_FIRSTNAME_FIELD = "givenName"
#AUTH_LDAP_LASTNAME_FIELD = "sn"
UPDATE -- I've tried "AUTH_TYPE = 1|2", also "AUTH_TYPE = AUTH_DB, AUTH_LDAP" . unfortunately no result.
To use both - LDAP and DB users - you should implement custom Superset security manager which will solve the problem.
At first, you should create new file, e.g. my_security_manager.py. Put these lines into it:
from superset.security import SupersetSecurityManager
class MySecurityManager(SupersetSecurityManager):
def __init__(self, appbuilder):
super(MySecurityManager, self).__init__(appbuilder)
Secondly, you should let Superset know that you want to use your brand new security manager. To do so, add these lines to your Superset configuration file (superset_config.py):
from my_security_manager import MySecurityManager
CUSTOM_SECURITY_MANAGER = MySecurityManager
Then you can override auth_user_db(username, password) method of your class with your custom authentication logic.
Here is additional information on the topic.
Related
I'm trying to leverage testcontainers to test Kafka locally in some automated unit tests. I'm having trouble testing authorization.
My goal is to test
(1) if there are no ACLs in this test container that no KafkaProducer should be allowed to write to it (currently, even with no ACLs created as long as a producer is configured correctly, it can send to the topic - I thought setting the kafka env variable of allow.everyone.if.no.acl.found to false would do the trick - but doesn't seem to be the case)
(2) to test if the KafkaProducer is not using the correct sasl.jaas.config (i.e incorrect apiKey and pasword) that it gets denied access to write to the test topic, even if an ACL is setup for all principals.
Below is my code. I can get it to "work" but testing the above two scenarios I haven't been able to figure out. I think I might not be actually creating ACLs, as when I add a line after I create the ACLs (adminClient.describeAcls(AclBindingFilter.ANY).values().get(); I get a No Authorizer is configured on the broker error) -> looking at posts similar to this I thought this implies that no ACL binding had actually been created.
import org.testcontainers.containers.KafkaContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.utility.DockerImageName;
import java.util.ArrayList;
import java.util.List;
import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.KafkaAdminClient;
import org.apache.kafka.clients.admin.NewTopic;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.acl.AccessControlEntry;
import org.apache.kafka.common.acl.AclBinding;
import org.apache.kafka.common.acl.AclOperation;
import org.apache.kafka.common.acl.AclPermissionType;
import org.apache.kafka.common.resource.PatternType;
import org.apache.kafka.common.resource.ResourcePattern;
import org.apache.kafka.common.resource.ResourceType;
import org.apache.kafka.common.serialization.StringSerializer;
String topicName = "this-is-a-topic";
String confluentVersion = "5.5.1";
network = Network.newNetwork();
String jaasTemplate = "org.apache.kafka.common.security.plain.PlainLoginModule required %s=\"%s\" %s=\"%s\";";
String jaasConfig = String.format(jaasTemplate, "username", "apiKey", "password", "apiPassword");
kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:" + confluentVersion))
.withNetwork(network)
.withEnv("KAFKA_AUTO_CREATE_TOPICS_ENABLE", "false")
.withEnv("KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND", "false")
.withEnv("KAFKA_SUPER_USERS", "User:OnlySuperUser")
.withEnv("KAFKA_SASL_MECHANISM", "PLAIN")
.withEnv("KAFKA_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM", "http")
.withEnv("KAFKA_SASL_JAAS_CONFIG", jaasConfig);
kafka.start();
schemaRegistryContainer = new SchemaRegistryContainer(confluentVersion).withKafka(kafka);
schemaRegistryContainer.start();
Properties properties = new Properties();
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafka.getBootstrapServers());
properties.put("input.topic.name", topicName);
properties.put("input.topic.partitions", "1");
adminClient = KafkaAdminClient.create(properties);
AclBinding ACL = new AclBinding(new ResourcePattern(ResourceType.TOPIC, topicName, PatternType.LITERAL),
new AccessControlEntry( "User:*", "*", AclOperation.WRITE, AclPermissionType.ALLOW));
var acls = adminClient.createAcls(List.of(ACL)).values();
List<NewTopic> topics = new ArrayList<>();
topics.add(
new NewTopic(topicName,
Integer.parseInt(properties.getProperty("input.topic.partitions")),
Short.parseShort(properties.getProperty("input.topic.replication.factor")))
);
adminClient.createTopics(topics);
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServer);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put("input.topic.name", topicName);
props.put("security.protocol", "PLAINTEXT");
props.put("input.topic.partitions", "1");
props.put("input.topic.replication.factor", "1");
props.put("metadata.fetch.timeout.ms", "10000");
props.put("sasl.jaas.config", jaasConfig);
producer = new KafkaProducer<>(props);
String key = "testContainers";
String value = "AreAwesome";
ProducerRecord<String, String> record = new ProducerRecord<>(
(String) props.get("input.topic.name"), key, value);
try {
RecordMetadata o = (RecordMetadata) producer.send(record).get();
System.out.println(o.toString());
} catch (Exception e) {
e.printStackTrace();
}
If the current user role = admin then show all the records in the table. If not, then limit the rows by created user.
I can get the user name if I define a function in the View Class, but need it before the list is constructed. See source code below.
from flask_appbuilder.models.sqla.filters import FilterEqualFunction
from app import appbuilder, db
from app.models import Language
from wtforms import validators, TextField
from flask import g
from flask_appbuilder.security.sqla.models import User
def get_user():
return g.user
class LanguageView(ModelView):
datamodel = SQLAInterface(Language)
list_columns = ["id", "name"]
base_order = ("name", "asc")
page_size = 50
#This is the part that does not work - unable to import app Error: Working outside of application context
#If the user role is admin show all, if not filter only to the specific user
if g.user.roles != "admin":
base_filters = [['created_by', FilterEqualFunction, get_user]]
This is the error I'm getting:
Was unable to import app Error: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.
In this case it is better to create two different ModelViews, one for users with base_filters = [['created_by', FilterEqualFunction, get_user]] and the second for admins only without any filtering.
And do not forget to specify correct permissions for both.
In my case, I created new filter FilterStartsWithFunction by coping FilterStartsWith(You can find the source code in Flask-Appbuilder pack easily. ). see the codes
from flask_appbuilder.models.sqla.filters import get_field_setup_query
class FilterStartsWithFunction(BaseFilter):
name = "Filter view with a function"
arg_name = "eqf"
def apply(self, query, func):
query, field = get_field_setup_query(query, self.model, self.column_name)
return query.filter(field.ilike(func() + "%"))
def get_user():
if 'Admin' in [r.name for r in g.user.roles]:
return ''
else:
return g.user.username
...
...
base_filters = [['created_by',FilterStartsWithFunction,get_user]]
When running the tests i get outputted following error:
user = self.request.user
AttributeError: 'WSGIRequest' object has no attribute 'user'
I have tried switching from MIDDLEWARE to MIDDLEWARE_CLASSES and vice versa. Currently, I'm running Django 2.1.
Also, I have tried switching middleware positions and it didn't help.
settings.py
MIDDLEWARE = (
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.auth.middleware.SessionAuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware", # to serve static files
)
test_views.py
from django.test import (TestCase,
RequestFactory
)
from mixer.backend.django import mixer
from .. import views
from accounts.models import User
class TestHomeView(TestCase):
def test_anonymous(self):
req = RequestFactory().get("/")
resp = views.HomeView.as_view()(req)
self.assertEquals(resp.status_code, 200,
"Should be callable by anyone")
def test_auth_user(self):
req = RequestFactory().get("/")
req.user = mixer.blend("accounts.User")
resp = views.HomeView.as_view()(req)
self.assertTrue("dashboard" in resp.url,
"Should redirect authenticated user to /dashboard/")
error output
user = self.request.user
AttributeError: 'WSGIRequest' object has no attribute 'user'
In the first test test_anonymous, you are not actually setting the user. As per documentation https://docs.djangoproject.com/en/2.1/topics/testing/advanced/#the-request-factory, the middleware are not executed if you use RequestFactory and you have to set the user explicitly. So if you want to test again AnonymousUser, you should set request.user = AnonymousUser(). If you don't do this, the request object will not have the user attribute, and thus the error.
my resource is in this format "testing/101/getCustomer/99"
Here I need to change "101" and "99" part dynamically by groovy so that I can run for multiple values in same test case. I looked into the ReadyAPI's built in feature, but it was not that helpful.
I also found this link, but it changed the resource in the entire project. The solution I am looking for is in test case level. As my each test case will have different url.
https://community.smartbear.com/t5/SoapUI-Open-Source/How-to-set-the-resource-path-at-run-time-while...
Any help will be appreciated.
Here is what I have tried so far
import com.eviware.soapui.impl.rest.RestResource
import java.io.*;
def project = testRunner.testCase.testSuite.getProject()
String restServiceName = "Resource name" (From the Rest Request Properties)
List<RestResource> ops = project.getInterfaces()[restServiceName].getOperationList()
log.info("ops ::"+ops);
log.info("ops size ::"+ops.size());
for (RestResource restResource : ops) {
String pathStr = restResource.getFullPath();
log.info("pathStr first-->"+restResource.getFullPath());
restResource.setPath("testing/101/getCustomer/99");
if (pathStr.contains("101"))
{
restResource.setPath("testing/101/getCustomer/99");
restResource.setPath(pathStr.replace("testing/101/getCustomer/99", "testing/50/getCustomer/99"));
}
}
you could use a testCase level property
first set the the value of res by groovy like below
def x=101
def y=99
def res="testing/$x/getCustomer/$y"
testRunner.testCase.setPropertyValue("resourc",res)
Now the testcase level property is set. It can be used as below wherever you want
${#TestCase#res}
I'm am using Zend Framework.
I need to put multiple mail configurations in application.ini for Zend_Mail (using Zend_Application_Resource_Mail). Is it possible to do this using the standard classes in Zend Framework or do I need to create my own class?
I am using the latest stable version of Zend Framework.
Thanks for the answers
It does not appear to be possible to set multiple configurations for Zend_Mail with Zend_Application_Resource_Mail.
You could add the various configurations to application.ini but you will have to write your own class/functions to make the desired configuration active.
The things that are set by Zend_Application_Resource_Mail that you will have to override are Zend_Mail::setDefaultTransport($newTransport);, Zend_Mail::setDefaultReplyTo($email);, and Zend_Mail::setDefaultFrom($email);.
I tested something and found an easy thing you can do.
Set up your different configurations like this in application.ini:
mail_config.mail_test.transport.type = smtp
mail_config.mail_test.transport.host = "smtp.example.com"
mail_config.mail_test.transport.auth = login
mail_config.mail_test.transport.username = myUsername
mail_config.mail_test.transport.password = myPassword
mail_config.mail_test.defaultFrom.email = john#example.com
mail_config.mail_test.defaultFrom.name = "John Doe"
mail_config.mail_test.defaultReplyTo.email = Jane#example.com
mail_config.mail_test.defaultReplyTo.name = "Jane Doe"
Note how we are setting up options under mail_config. This will be the set of options to apply. mail_test is an example configuration. You can have multiple by setting mail_config.mail_test2, mail_config.corporate_mail, or mail_config.production etc.
Next, create an empty class that extends from Zend_Application_Resource_Mail. Preferably, it should be named and placed so it can be autoloaded.
The class:
<?php
class Application_Service_MailSettings extends Zend_Application_Resource_Mail { }
Now, here is how to override the default mail configuration easily with something else.
This example assumes you are in a controller:
// get the bootstrap, so we can get mail_config options
$bootstrap = $this->getInvokeArg('bootstrap');
$options = $bootstrap->getOption('mail_config');
// initialize the resource loader with the options from mail_config.mail_test
$mailSettings = new Application_Service_MailSettings($options['mail_test']);
$mailSettings->init(); // call init() so the settings are applied
// now the default transport, from, and reply to are set using mail_config.mail_test options.
// to use a different set of options, just do
// $mailSettings = new Application_Service_MailSettings($options['other_config');
This should accomplish what you want with very little new code.