001/* 002 * Licensed to DuraSpace under one or more contributor license agreements. 003 * See the NOTICE file distributed with this work for additional information 004 * regarding copyright ownership. 005 * 006 * DuraSpace licenses this file to you under the Apache License, 007 * Version 2.0 (the "License"); you may not use this file except in 008 * compliance with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.fcrepo.auth.common; 019 020import static org.modeshape.jcr.ModeShapePermissions.READ; 021import static org.modeshape.jcr.ModeShapePermissions.REGISTER_NAMESPACE; 022import static org.modeshape.jcr.ModeShapePermissions.REGISTER_TYPE; 023import static org.modeshape.jcr.api.JcrConstants.JCR_CONTENT; 024 025import java.security.Principal; 026import java.util.Set; 027 028import org.modeshape.jcr.security.AdvancedAuthorizationProvider; 029import org.modeshape.jcr.security.SecurityContext; 030import org.modeshape.jcr.value.Path; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034import com.google.common.collect.Sets; 035 036/** 037 * The security context for Fedora servlet users. These users are not 038 * necessarily authenticated by the container, i.e. users may include the 039 * general public. This security context delegates all access decisions to the 040 * configured authorization delegate. 041 * 042 * @author Gregory Jansen 043 */ 044public class FedoraUserSecurityContext implements SecurityContext, 045 AdvancedAuthorizationProvider { 046 047 private static final Logger LOGGER = LoggerFactory 048 .getLogger(FedoraUserSecurityContext.class); 049 050 private Principal userPrincipal = null; 051 052 private FedoraAuthorizationDelegate fad = null; 053 054 private boolean loggedIn = true; 055 056 /** 057 * Constructs a new security context. 058 * 059 * @param userPrincipal the user principal associated with this security 060 * context 061 * @param fad the authorization delegate 062 */ 063 protected FedoraUserSecurityContext(final Principal userPrincipal, 064 final FedoraAuthorizationDelegate fad) { 065 this.fad = fad; 066 this.userPrincipal = userPrincipal; 067 068 if (this.fad == null) { 069 LOGGER.warn("This security context must have a FAD injected"); 070 throw new IllegalArgumentException( 071 "This security context must have a FAD injected"); 072 } 073 } 074 075 /** 076 * {@inheritDoc} 077 * 078 * @see org.modeshape.jcr.security.SecurityContext#isAnonymous() 079 */ 080 @Override 081 public boolean isAnonymous() { 082 return this.userPrincipal == null; 083 } 084 085 /** 086 * {@inheritDoc} 087 * 088 * @see SecurityContext#getUserName() 089 */ 090 @Override 091 public final String getUserName() { 092 return getEffectiveUserPrincipal().getName(); 093 } 094 095 /** 096 * {@inheritDoc} 097 * 098 * @see SecurityContext#hasRole(String) 099 */ 100 @Override 101 public final boolean hasRole(final String roleName) { 102 // Under this custom PEP regime, all users have modeshape read and write 103 // roles. 104 if ("read".equals(roleName)) { 105 return true; 106 } else if ("write".equals(roleName)) { 107 return true; 108 } else if ("admin".equals(roleName)) { 109 return true; 110 } 111 return false; 112 } 113 114 /** 115 * Get the user principal associated with this context. 116 * 117 * @return the user principal associated with this security context 118 */ 119 public Principal getEffectiveUserPrincipal() { 120 if (this.loggedIn && this.userPrincipal != null) { 121 return this.userPrincipal; 122 } 123 return fad.getEveryonePrincipal(); 124 } 125 126 /** 127 * {@inheritDoc} 128 * 129 * @see org.modeshape.jcr.security.SecurityContext#logout() 130 */ 131 @Override 132 public void logout() { 133 this.loggedIn = false; 134 } 135 136 /* 137 * (non-Javadoc) 138 * @see 139 * org.modeshape.jcr.security.AdvancedAuthorizationProvider#hasPermission 140 * (org.modeshape.jcr.security.AdvancedAuthorizationProvider.Context, 141 * org.modeshape.jcr.value.Path, java.lang.String[]) 142 */ 143 @Override 144 public boolean hasPermission(final Context context, final Path absPath, 145 final String... actions) { 146 if (!this.loggedIn) { 147 return false; 148 } 149 150 if (absPath == null) { 151 // this permission is required for login 152 if (actions.length == 1 && READ.equals(actions[0])) { 153 return true; 154 } 155 // The REGISTER_NAMESPACE action and the REGISTER_TYPE action don't include 156 // a path and are allowed for all users. The fedora 4 code base doesn't expose 157 // any endpoint that *JUST* registers a namespace or type, so the operations 158 // that perform these actions will have to be authorized in context (for instance 159 // setting a property). 160 final Set<String> filteredActions = Sets.newHashSet(actions); 161 filteredActions.remove(REGISTER_NAMESPACE); 162 filteredActions.remove(REGISTER_TYPE); 163 return filteredActions.isEmpty(); 164 } 165 166 // Trim jcr:content from paths, if necessary 167 final Path path; 168 if (null != absPath.getLastSegment() && absPath.getLastSegment().getString().equals(JCR_CONTENT)) { 169 path = absPath.subpath(0, absPath.size() - 1); 170 LOGGER.debug("..new path to be verified: {}", path); 171 } else { 172 path = absPath; 173 } 174 175 // delegate 176 if (fad != null) { 177 return fad.hasPermission(context.getSession(), path, actions); 178 } 179 return false; 180 } 181}