I have an HTTP Security API which can be configured for security, authentication and authorization like this
SecurityConfigurationBuilder configurationBuilder = new SecurityConfigurationBuilder();
HttpSecurityBuilder builder = configurationBuilder.http();
builder
.pathGroup("REST Service Group A") //returns PathConfigurationBuilder
.inbound() //returns InboundConfigurationBuilder
.authc() //returns AuthenticationConfigurationBuilder
.form() //returns FormAuthenticationConfigurationBuilder
.loginPage("/loginA.html")
.errorPage("/errorA.html")
.authz()
.allowedRoles("Role A")
.pathGroup("REST Service Group B")
.inbound()
.authc()
.form()
.loginPage("/loginB.html")
.errorPage("/errorB.html")
.authz() //returns AuthorizationConfigurationBuilder
.allowedRoles("Role B")
.path("/rest/a/*", "REST Service Group A") //returns PathConfigurationBuilder
.path("/rest/b/*", "REST Service Group B");
HttpSecurityConfiguration configuration = builder.build().getHttpSecurityConfiguration();
Note: Each config method returns an object of a class. I have mentioned only a few of them.
Similarly, we can also configure in this way,
SecurityConfigurationBuilder configurationBuilder = new SecurityConfigurationBuilder();
HttpSecurityBuilder builder = configurationBuilder.http();
builder
.allPaths()
.inbound()
.authc()
.form()
.loginPage("/login.html")
.errorPage("/error.html")
.authz()
.allowedRoles("Role A", "Role B")
.allowedGroups("Group A", "Group B")
.allowedRealms("Realm A", "Realm B")
.expression("#{identity.isLoggedIn()}");
HttpSecurityConfiguration configuration = builder.build().getHttpSecurityConfiguration();
I am now required to provide similar configuration using annotations on enum fields. For eg.
//Declaration of #annotation_A depends on #annotation_B means that annotation_B can only be declared if annotation_A is declared.
//Also, declaration of annotation_A must precede annotation_B
#PicketlinkHttpSecurity
public interface Security {
static enum Secure {
//optional
#Http
//Any one of these,optional
#Permissive
#Restrictive
//Any one of these, optional
#AllPaths
#Path(pathName = "Path URL Pattern")
#Path(pathName = "Path URL Pattern", pathGroup = "Path Group")
#PathGroup(pathGroup = "Path Group")
//optional, declaration depends on (#allPaths, #path, #pathGroup) whichever is declared
#Logout
//optional, declaration depends on (#allPaths, #path, #pathGroup) whichever is declared
#Inbound
//optional, declarion depends on #inbound
#Methods(methods = {"method_one", "method_two"}, ...)
//optional, declaration depends on #inbound
#Headers(headerName = "name", headerValues={"value1", "value2", ...}, requestedWith = "request_header")
//optional, declaration depends on (#allPaths, #path, #pathGroup) whichever is delclared
#Outbound
//optional, declaration depends on #outbound
#RedirectTo(redirect = "redirectPath")
//optional, declaration depends on #inbound/#outbound
#Authc
//Any one of form,basic,digest,x509,token(//optional and declaration depends on #authc)
#Form(restoreOriginalRequest = "yes_OR_no", loginPage="loginPage", errorPage="errorPage")
#Basic(realmName="realmName")
#Digest(realmName="realmName")
#X509(subjectRegex="")
#Token
//optional, declaration depends on #inbound/#outbound
#Authz
//optional, any one or many of these, declaration depends on #authz
#AllowedRoles(Roles={"Role_A", "Role_B", ...})
#AllowedGroups(Goups={"Goup_A", "Goup_B", ...})
#AllowedRealms(Realms={"Realm_A", "Realm_B", ...})
#Expressions(expressions= {"expression1", "expression2", ...})
SecurityConfigiration_One,
//Other Security Configuration Annotations Decorated Enum Fields
}
}
Now, I have a class SecurityConfigExtension which tries to read this enum class and build up SecurityConfigurationBuilder. An incomplete piece of code from this class is
public class SecurityConfigExtension implements Extension {
private transient final Logger log = Logger.getLogger(SecurityConfigExtension.class);
SecurityConfigurationBuilder builder = new SecurityConfigurationBuilder();
HttpSecurityBuilder httpSecurityBuilder = new HttpSecurityBuilder(builder);
PathConfigurationBuilder pathConfigurationBuilder;
public <T> void processAnnotatedType(#Observes ProcessAnnotatedType<T> event) {
AnnotatedType<T> tp = event.getAnnotatedType();
if (tp.isAnnotationPresent(PicketlinkHttpSecurity.class)) {
if (!tp.getJavaClass().isInterface()) {
log.warn("ViewConfig annotation should only be applied to interfaces, and [" + tp.getJavaClass()
+ "] is not an interface.");
} else {
for (Class<?> clazz : tp.getJavaClass().getClasses()) {
for (Field enumm : clazz.getFields()) {
for (Annotation a : enumm.getAnnotations()) {
if (a.annotationType() == Http.class) {
this.httpSecurityBuilder = this.builder.http();
} else if (a.annotationType() == Permissive.class) {
this.httpSecurityBuilder = this.httpSecurityBuilder.permissive();
} else if (a.annotationType() == Restrictive.class) {
this.httpSecurityBuilder = this.httpSecurityBuilder.restrictive();
} else if (a.annotationType() == AllPaths.class) {
this.pathConfigurationBuilder = this.httpSecurityBuilder.allPaths();
} else if (a.annotationType() == Path.class) {
Path path = (Path) a;
String pathName = path.pathName();
String pathGroup = path.pathGroup();
if (pathName != null && !pathName.isEmpty() && pathGroup != null && !pathGroup.isEmpty()) {
this.pathConfigurationBuilder = this.httpSecurityBuilder.path(pathName, pathGroup);
} else {
this.pathConfigurationBuilder = this.httpSecurityBuilder.path(pathName);
}
}
}
}
}
}
}
}
}
My problem is what is the best way of building configuration similar to above illustrations. I am using reflections to read the value of parameters passed to annotations. But as per the current code, it looks quite heavy and poor because after applying a config, i have to initialize it with an object of same type as the returning object, so, that next config can be applied and the process continues.Is there any better approach ??
Related
I have written a code which fetched the S3 objects from AWS s3 using S3 sdk and stores the same in our DB, the only problem is the task is repeated for three different services, the only thing is changed is the instance of service class.
I have copy and pasted code in each service layer just to changes the instance for an instance.
The task is repeated for service classes VehicleImageService, MsilLayoutService and NonMsilLayoutService, every layer is having its own repository.
I am trying to identify a way to accomplish the same by placing that snippet in one place and on an runtime using Reflection API I wish to pass the correct instance and invoke the method, but I want to achieve the same using best industry practices and pattern. I.e. I want to refactor into generic methods for other services, so instance can be passed at runtime.
So kindly assist me on the same.
public void persistImageDetails() {
log.info("MsilVehicleLayoutServiceImpl::persistImageDetails::START");
String bucketKey = null; //common param
String modelCode = null;//common param
List<S3Object> objList = new ArrayList<>(); //common param
String bucketName = s3BucketDetails.getBucketName();//common param
String bucketPath = s3BucketDetails.getBucketPrefix();//common param
try {
//the layoutRepository object can be MSILRepository,NonMSILRepository and VehilceImageRepository
List<ModelCode> modelCodes = layoutRepository.findDistinctAllBy(); // this line need to take care of
List<String> modelCodePresent = modelCodes.stream().map(ModelCode::getModelCode)
.collect(Collectors.toList());
List<CommonPrefix> allKeysInDesiredBucket = listAllKeysInsideBucket(bucketName, bucketPath);//common param
synchDB(modelCodePresent, allKeysInDesiredBucket);
if (null != allKeysInDesiredBucket && !allKeysInDesiredBucket.isEmpty()) {
for (CommonPrefix commonPrefix : allKeysInDesiredBucket) {
bucketKey = commonPrefix.prefix();
modelCode = new File(bucketKey).getName();
if (modelCodePresent.contains(modelCode)) {
log.info("skipping iteration for {} model code", modelCode);
continue;
}
objList = s3Service.getBucketObjects(bucketName, bucketKey);
if (null != objList && !objList.isEmpty()) {
for (S3Object object : AppUtil.skipFirst(objList)) {
saveLayout(bucketName, modelCode, object);
}
}
}
}
log.info("MSIL Vehicle Layout entries has been successfully saved");
} catch (Exception e) {
log.error("Error occured", e);
e.printStackTrace();
}
log.info("MsilVehicleLayoutServiceImpl::persistImageDetails::END");
}
private void saveLayout(String bucketName, String modelCode, S3Object object) {
log.info("Inside saveLayout::Start preparing entity to persist");
String resourceUri = null;
MsilVehicleLayout vehicleLayout = new MsilVehicleLayout();// this can be MsilVehicleLayout. NonMsilVehicleLayout, VehicleImage
vehicleLayout.setFileName(FilenameUtils.removeExtension(FilenameUtils.getName(object.key())));
vehicleLayout.setModelCode(modelCode);
vehicleLayout.setS3BucketKey(object.key());
resourceUri = getS3ObjectURI(bucketName, object.key());
vehicleLayout.setS3ObjectUri(resourceUri);
vehicleLayout.setS3PresignedUri(null);
vehicleLayout.setS3PresignedExpDate(null);
layoutRepository.save(vehicleLayout); //the layoutRepository object can be MSILRepository,NonMSILRepository and VehilceImageRepository
log.info("Exiting saveLayout::End entity saved");
}
I have a JSP page wherein user has to enter some custom URL. I want to pass that custom url in #WebInitParam in my servlet
#WebServlet(name = "oauthCustomURL", initParams = {
#WebInitParam(name = "clientId", value = "123"),
#WebInitParam(name = "key", value = "***"),
#WebInitParam(name = "environment", value = "customUrl"),
}) //in value I want to pass the value entered by user
#WebInitParam's are used for the configuration of servlets implemented by third-party libraries. Usually, these libraries use methods getInitParameterNames() and getInitParameter() of abstract class GenericServlet (but you should check in library code for it).
For the dynamic setting of the #WebInitParam you can override those methods in your servlet implementation. Below is an example of how to do it.
#WebServlet(urlPatterns = "/abc/*")
public class DynamicInitParamServlet extends SomeCustomLibraryHttpServlet {
private static final String WEB_INIT_PARAM_NAME = "some.param.name";
private Integer webInitParamValue = null;
#Override
public void init(ServletConfig config) throws ServletException {
// calculate init param value dynamically,
// TODO: implement your own code
webInitParamValue = 2 * 3;
// call custom library servlet init after init parameter value is set
super.init(config);
}
#Override
public Enumeration<String> getInitParameterNames() {
if (webInitParamValue != null) {
final Set<String> initParameterNames = new HashSet<>(Collections.list(super.getInitParameterNames()));
initParameterNames.add(WEB_INIT_PARAM_NAME);
return Collections.enumeration(initParameterNames);
} else {
return super.getInitParameterNames();
}
}
#Override
public String getInitParameter(String name) {
if (WEB_INIT_PARAM_NAME.compareTo(name) == 0 && webInitParamValue != null) {
return "" + webInitParamValue;
} else {
return super.getInitParameter(name);
}
}
}
I have some code that works properly on spring boot prior to 2 and I find it hard to convert it to work with spring boot 2.
Can somebody assist?
public static MutablePropertySources buildPropertySources(String propertyFile, String profile)
{
try
{
Properties properties = new Properties();
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
// load common properties
PropertySource<?> applicationYamlPropertySource = loader.load("properties", new ClassPathResource(propertyFile), null);
Map<String, Object> source = ((MapPropertySource) applicationYamlPropertySource).getSource();
properties.putAll(source);
// load profile properties
if (null != profile)
{
applicationYamlPropertySource = loader.load("properties", new ClassPathResource(propertyFile), profile);
if (null != applicationYamlPropertySource)
{
source = ((MapPropertySource) applicationYamlPropertySource).getSource();
properties.putAll(source);
}
}
propertySources = new MutablePropertySources();
propertySources.addLast(new PropertiesPropertySource("apis", properties));
}
catch (Exception e)
{
log.error("{} file cannot be found.", propertyFile);
return null;
}
}
public static <T> void handleConfigurationProperties(T bean, MutablePropertySources propertySources) throws BindException
{
ConfigurationProperties configurationProperties = bean.getClass().getAnnotation(ConfigurationProperties.class);
if (null != configurationProperties && null != propertySources)
{
String prefix = configurationProperties.prefix();
String value = configurationProperties.value();
if (null == value || value.isEmpty())
{
value = prefix;
}
PropertiesConfigurationFactory<?> configurationFactory = new PropertiesConfigurationFactory<>(bean);
configurationFactory.setPropertySources(propertySources);
configurationFactory.setTargetName(value);
configurationFactory.bindPropertiesToTarget();
}
}
PropertiesConfigurationFactory doesnt exist anymore and the YamlPropertySourceLoader load method no longer accepts 3 parameters.
(the response is not the same either, when I have tried invoking the new method the response objects were wrapped instead of giving me the direct strings/integers etc...)
The PropertiesConfigurationFactory should be replaced with Binder class.
Binder class
Sample code:-
ConfigurationPropertySource source = new MapConfigurationPropertySource(
loadProperties(resource));
Binder binder = new Binder(source);
return binder.bind("initializr", InitializrProperties.class).get();
We were also using PropertiesConfigurationFactory to bind a POJO to a
prefix of the Environment. In 2.0, a brand new Binder API was
introduced that is more flexible and easier to use. Our binding that
took 10 lines of code could be reduced to 3 simple lines.
YamlPropertySourceLoader:-
Yes, this class has been changed in version 2. It doesn't accept the third parameter profile anymore. The method signature has been changed to return List<PropertySource<?>> rather than PropertySource<?>. If you are expecting single source, please get the first occurrence from the list.
Load the resource into one or more property sources. Implementations
may either return a list containing a single source, or in the case of
a multi-document format such as yaml a source for each document in the
resource.
Since there is no accepted answer yet, i post my full solution, which builds upon the answer from #nationquest:
private ConfigClass loadConfiguration(String path){
MutablePropertySources sources = new MutablePropertySources();
Resource res = new FileSystemResource(path);
PropertiesFactoryBean propFactory = new PropertiesFactoryBean();
propFactory.setLocation(res);
propFactory.setSingleton(false);
// resolve potential references to local environment variables
Properties properties = null;
try {
properties = propFactory.getObject();
for(String p : properties.stringPropertyNames()){
properties.setProperty(p, env.resolvePlaceholders(properties.getProperty(p)));
}
} catch (IOException e) {
e.printStackTrace();
}
sources.addLast(new PropertiesPropertySource("prefix", properties));
ConfigurationPropertySource propertySource = new MapConfigurationPropertySource(properties);
return new Binder(propertySource).bind("prefix", ConfigClass.class).get();
}
The last three lines are the relevant part here.
dirty code but this is how i solved it
https://github.com/mamaorha/easy-wire/tree/master/src/main/java/co/il/nmh/easy/wire/core/utils/properties
I am trying to add a unique validation on a field while adding a product on Broadleaf. Currently we have added a 'SKU' field while adding product from admin screen. I have used the following annotation to validate:
#AdminPresentationMergeOverride(name = "userSku", mergeEntries = #AdminPresentationMergeEntry(propertyType = PropertyType.AdminPresentation.VALIDATIONCONFIGURATIONS, validationConfigurations = {
#ValidationConfiguration(validationImplementation = "blUniqueValueValidator", configurationItems = {
#ConfigurationItem(itemName = "otherField", itemValue = "userSku") }) })
It works perfect when we try to add a new product.
But the problem is, If I try to update any product to change any field, it gives the same validation error
Looks like that doesn't work quite right, can you open an issue in https://github.com/BroadleafCommerce/Issues?
You can also write your own uniqueness validates that does not run into the same ID problem like so:
#Component
public class MyUniqueValueValidator implements PropertyValidator {
protected static final Log LOG = LogFactory.getLog(UniqueValueValidator.class);
#Override
public PropertyValidationResult validate(Entity entity,
Serializable instance,
Map<String, FieldMetadata> entityFieldMetadata,
Map<String, String> validationConfiguration,
BasicFieldMetadata propertyMetadata,
String propertyName,
String value) {
String instanceClassName = instance.getClass().getName();
DynamicEntityDao dynamicEntityDao = getDynamicEntityDao(instanceClassName);
List<Long> responseIds = dynamicEntityDao.readOtherEntitiesWithPropertyValue(instance, propertyName, value);
String message = validationConfiguration.get(ConfigurationItem.ERROR_MESSAGE);
if (message == null) {
message = entity.getType()[0] + " with this value for attribute " +
propertyName + " already exists. This attribute's value must be unique.";
}
boolean onlyInCurrentEntity = CollectionUtils.isEmpty(responseIds)
|| (responseIds.size() == 1 && responseIds.get(0).equals(getDynamicEntityDao(instanceClassName).getIdentifier(instance)));
return new PropertyValidationResult(onlyInCurrentEntity, message);
}
protected DynamicEntityDao getDynamicEntityDao(String className) {
return PersistenceManagerFactory.getPersistenceManager(className).getDynamicEntityDao();
}
}
And then use the validator by passing in the bean ID to the validationImplementation:
#AdminPresentationMergeOverride(name = "userSku", mergeEntries = #AdminPresentationMergeEntry(propertyType = PropertyType.AdminPresentation.VALIDATIONCONFIGURATIONS, validationConfigurations = {
#ValidationConfiguration(validationImplementation = "myUniqueValidator", configurationItems = {
#ConfigurationItem(itemName = "otherField", itemValue = "userSku") }) })
I am learning Elasticsearch and I have started a new project. Now I wonder where I should add the initial code for creating the mappings etc. Would you create an external script which holds the different cURL commands and then run that, or have for example a own package in the Java project where you have the configuration code and then run it when you need to? Which approach is the most appropriate and why?
Mapping that I want to try with XContentBuilder
{
"tweet" : {
"properties" : {
"message" : {
"type" : "string",
"store" : "yes",
"index" : "analyzed",
"null_value" : "na"
}
}
}
}
I like to have it in java:
public void putMappingFromString(String index, String type, String mapping) {
IndicesAdminClient iac = getClient().admin().indices();
PutMappingRequestBuilder pmrb = new PutMappingRequestBuilder(iac);
pmrb.setIndices(index);
pmrb.setType(type);
pmrb.setSource(mapping);
ListenableActionFuture<PutMappingResponse> laf = pmrb.execute();
PutMappingResponse pmr = laf.actionGet();
pmr.getAcknowledged();
}
You can also get the mapping for an index from the cluster state (indirectly):
public String getMapping(String index, String type) throws EsuException {
ClusterState cs = getClient().admin().cluster().prepareState().setFilterIndices(index).execute().actionGet().getState();
IndexMetaData imd = cs.getMetaData().index(index);
if (imd == null) {
throw new EsuIndexDoesNotExistException(index);
}
MappingMetaData mmd = imd.mapping(type);
if (mmd == null) {
throw new EsuTypeDoesNotExistException(index, type);
}
String mapping = "";
try {
mapping = mmd.source().string();
} catch (IOException e) {
mapping = "{ \"" + e.toString() + "\"}";
}
return mapping;
}
This allows for versioning your mappings along with your source code if you store your mappings as a resource on your class path