Skip to content

Commit

Permalink
Specific permissions registry jmix-projects/jmix-security#99
Browse files Browse the repository at this point in the history
  • Loading branch information
gorbunkov committed Sep 22, 2021
1 parent b607c69 commit a2b1668
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2021 Haulmont.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 io.jmix.core.security;

import io.jmix.core.accesscontext.SpecificOperationAccessContext;

import java.util.List;

/**
* Provides an information about specific policies defined in the application. In order to be returned by the service,
* the specific policy must have a corresponding {@link SpecificOperationAccessContext}. E.g. for the {@code
* ui.loginToUi} policy the following class should exist:
*
* <pre>
* public class UiLoginToUiContext extends SpecificOperationAccessContext {
*
* public static final String NAME = "ui.loginToUi";
*
* public UiLoginToUiContext() {
* super(NAME);
* }
* }
* </pre>
*/
public interface SpecificPolicyInfoRegistry {

/**
* Returns an information about specific policies defined in the application
*/
List<SpecificPolicyInfo> getSpecificPolicyInfos();

/**
* Class stores an information about specific security policy that may be used in the application
*/
class SpecificPolicyInfo {

private final String name;

public SpecificPolicyInfo(String name) {
this.name = name;
}

public String getName() {
return name;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2021 Haulmont.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 io.jmix.core.security.impl;

import io.jmix.core.accesscontext.SpecificOperationAccessContext;
import io.jmix.core.impl.scanning.ClasspathScanCandidateDetector;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

@Component("core_SpecificOperationAccessContextDetector")
public class SpecificOperationAccessContextDetector implements ClasspathScanCandidateDetector {

@Override
public boolean isCandidate(MetadataReader metadataReader) {
return SpecificOperationAccessContext.class.getName().equals(metadataReader.getClassMetadata().getSuperClassName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright 2021 Haulmont.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 io.jmix.core.security.impl;

import io.jmix.core.accesscontext.SpecificOperationAccessContext;
import io.jmix.core.impl.scanning.JmixModulesClasspathScanner;
import io.jmix.core.security.SpecificPolicyInfoRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Component("core_SpecificPolicyInfoRegistry")
public class SpecificPolicyInfoRegistryImpl implements SpecificPolicyInfoRegistry {

@Autowired
protected JmixModulesClasspathScanner jmixModulesClasspathScanner;

protected List<SpecificPolicyInfo> specificPolicyInfoList = new ArrayList<>();

protected volatile boolean initialized = false;

protected static final Logger log = LoggerFactory.getLogger(SpecificPolicyInfoRegistryImpl.class);

protected ReadWriteLock lock = new ReentrantReadWriteLock();

protected void checkInitialized() {
if (!initialized) {
lock.readLock().unlock();
lock.writeLock().lock();
try {
if (!initialized) {
log.info("Initializing specific policy infos list");
init();
initialized = true;
}
} finally {
lock.readLock().lock();
lock.writeLock().unlock();
}
}
}

protected void init() {
specificPolicyInfoList.clear();
Set<String> classNames = jmixModulesClasspathScanner.getClassNames(SpecificOperationAccessContextDetector.class);
for (String className : classNames) {
try {
Class<?> aClass = Class.forName(className);
SpecificOperationAccessContext newInstance = (SpecificOperationAccessContext) aClass.getDeclaredConstructor().newInstance();
specificPolicyInfoList.add(new SpecificPolicyInfo(newInstance.getName()));
} catch (Exception e) {
log.error("Cannot instantiate an instance of {}", className, e);
}
}
}

@Override
public List<SpecificPolicyInfo> getSpecificPolicyInfos() {
lock.readLock().lock();
try {
checkInitialized();
return Collections.unmodifiableList(specificPolicyInfoList);
} finally {
lock.readLock().unlock();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2021 Haulmont.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 specific_policy_info_registry

import io.jmix.core.CoreConfiguration
import io.jmix.core.security.SpecificPolicyInfoRegistry
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.ContextConfiguration
import spock.lang.Specification
import test_support.base.TestBaseConfiguration
import test_support.base.accesscontext.TestSpecificOperationAccessContext

@ContextConfiguration(classes = [CoreConfiguration, TestBaseConfiguration])
class SpecificPolicyInfoRegistryTest extends Specification {

@Autowired
SpecificPolicyInfoRegistry specificPolicyInfoRegistry

def "specific policy info from TestSpecificOperationAccessContext is returned"() {

when:
def specificPolicyDefinitions = specificPolicyInfoRegistry.getSpecificPolicyInfos()

then:

specificPolicyDefinitions.size() > 0
specificPolicyDefinitions.find {it.name == TestSpecificOperationAccessContext.NAME} != null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2021 Haulmont.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 test_support.base.accesscontext;

import io.jmix.core.accesscontext.SpecificOperationAccessContext;

public class TestSpecificOperationAccessContext extends SpecificOperationAccessContext {

public static final String NAME = "test.policy";

public TestSpecificOperationAccessContext() {
super(NAME);
}
}

0 comments on commit a2b1668

Please sign in to comment.