All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.genesys.blocks.security.serialization.CurrentPermissionsWriter Maven / Gradle / Ivy

/*
 * Copyright 2018 Global Crop Diversity Trust
 *
 * 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 org.genesys.blocks.security.serialization;

import org.genesys.blocks.security.SecurityContextUtil;
import org.genesys.blocks.security.model.AclAwareModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

import com.fasterxml.jackson.annotation.JsonIgnoreType;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter;
import com.fasterxml.jackson.databind.util.Annotations;

/**
 * The CurrentPermissionsWriter is applied to {@link AclAwareModel} and it
 * instructs Jackson to include {@link Permissions} for current SID for every
 * ACL aware entity.
 * 
 * The serialization of {@code Permissions} as {@code "_permissions"} property is enabled
 * with the @JsonAppend annotation on AclAwareModel:
 * 
 * 
 *  @JsonAppend(props = { @JsonAppend.Prop(name="_permissions", value = CurrentPermissionsWriter.class, type=CurrentPermissions.class) })
 *  public interface AclAwareModel...
 * 
* * To be able to access the current permissions of the current SID, this code * requires that an instance of * org.genesys.blocks.util.CurrentApplicationContext is * registered in the Spring application context. *

* Writer can be disabled with {@code @JsonView(CurrentPermissionsWriter.NoPermissions.class)} * (or a {@code JsonView} extending it): * *

 *  static interface RootNoPermissions extends JsonViews.Root, CurrentPermissionsWriter.NoPermissions { }
 * 
* * or by mix-in that ignores {@link Permissions} type: * *
 *  {@code @JsonIgnoreType}
 *  public class MyMixInForIgnoreType {}
 *  ...
 *  mapper.addMixIn(Permissions.class, MyMixInForIgnoreType.class);
 * 
*/ public class CurrentPermissionsWriter extends VirtualBeanPropertyWriter { /** The Constant LOG. */ private static final Logger LOG = LoggerFactory.getLogger(CurrentPermissionsWriter.class); /** * Use this JsonView to exclude permission checks! */ public static interface NoPermissions { } /** The Constant serialVersionUID. */ private static final long serialVersionUID = 1L; /** * Writer can be disabled with @JsonView(CurrentPermissionsWriter.NoPermissions.class) * or by mix-in that ignores {@link Permissions}.class: * *
	 *  {@code @JsonIgnoreType}
	 *  public class MyMixInForIgnoreType {}
	 *  ...
	 *  mapper.addMixIn(Permissions.class, MyMixInForIgnoreType.class);
	 * 
*/ private boolean enabled = true; private static final Permissions NO_PERMISSIONS = new Permissions().grantNone(); /** * Instantiates a new current permissions writer. */ public CurrentPermissionsWriter() { LOG.trace("CurrentPermissionsWriter"); } /** * Instantiates a new current permissions writer. * * @param propDef the prop def * @param annotations the annotations * @param type the type */ public CurrentPermissionsWriter(BeanPropertyDefinition propDef, Annotations annotations, JavaType type) { super(propDef, annotations, type); LOG.trace("CurrentPermissionsWriter {} {}", propDef, type); } /* * (non-Javadoc) * @see * com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter#value(java.lang. * Object, com.fasterxml.jackson.core.JsonGenerator, * com.fasterxml.jackson.databind.SerializerProvider) */ @Override protected Object value(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception { if (!enabled) { // We are not enabled return null; } if (bean == null || !(bean instanceof AclAwareModel)) { // Skip nulls return null; } AclAwareModel aclAwareModel = (AclAwareModel) bean; if (aclAwareModel.getId() == null) { // Don't write permissions for non-persisted objects return null; } Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null) { return NO_PERMISSIONS; } Permissions perms = new Permissions(); try { perms.isPublic = SecurityContextUtil.anyoneHasPermission(aclAwareModel, "READ"); } catch (Throwable e) { LOG.warn("Could not read public permissions {}", e.getMessage(), e); perms.isPublic = false; } if (SecurityContextUtil.hasRole("ADMINISTRATOR")) { perms.grantAll(); } else { try { perms.create = SecurityContextUtil.hasPermission(authentication, aclAwareModel, BasePermission.CREATE); perms.read = SecurityContextUtil.hasPermission(authentication, aclAwareModel, BasePermission.READ); perms.write = SecurityContextUtil.hasPermission(authentication, aclAwareModel, BasePermission.WRITE); perms.delete = SecurityContextUtil.hasPermission(authentication, aclAwareModel, BasePermission.DELETE); perms.manage = SecurityContextUtil.hasPermission(authentication, aclAwareModel, BasePermission.ADMINISTRATION); } catch (Throwable e) { LOG.warn("Could not read current permissions {}", e.getMessage(), e); } } return perms; } /* * (non-Javadoc) * @see * com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter#withConfig(com. * fasterxml.jackson.databind.cfg.MapperConfig, * com.fasterxml.jackson.databind.introspect.AnnotatedClass, * com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition, * com.fasterxml.jackson.databind.JavaType) */ @Override public VirtualBeanPropertyWriter withConfig(MapperConfig config, AnnotatedClass declaringClass, BeanPropertyDefinition propDef, JavaType type) { var writer = new CurrentPermissionsWriter(propDef, declaringClass.getAnnotations(), type); var permissionsMixin = config.findMixInClassFor(Permissions.class); if (permissionsMixin != null) { var isIgnoredType = permissionsMixin.getAnnotation(JsonIgnoreType.class); LOG.warn("Mixin for {} has @JsonIgnoreType={}", Permissions.class, isIgnoredType); if (isIgnoredType != null) { LOG.debug("Permissions.class is @JsonIgnoreType({})", isIgnoredType.value()); writer.enabled = ! isIgnoredType.value(); // Enable or disable checks } } var activeView = config.getActiveView(); LOG.trace("Active JsonView {}", activeView); if (activeView != null && NoPermissions.class.isAssignableFrom(activeView)) { LOG.debug("Not computing permissions for @JsonView(CurrentPermissionWriter.NoPermissions.class)"); writer.enabled = false; } return writer; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy