--- /dev/null
+package org.argeo;
+
+/** Framework agnostic interface for log notifications */
+public interface ArgeoLogListener {
+ /**
+ * Appends a log
+ *
+ * @param username
+ * authentified user, null for anonymous
+ * @param level
+ * INFO, DEBUG, WARN, etc. (logging framework specific)
+ * @param category
+ * hierarchy (logging framework specific)
+ * @param thread
+ * name of the thread which logged this message
+ * @param msg
+ * any object as long as its toString() method returns the
+ * message
+ * @param the
+ * exception in log4j ThrowableStrRep format
+ */
+ public void appendLog(String username, Long timestamp, String level,
+ String category, String thread, Object msg, String[] exception);
+}
/**
* JCR types, see
* http://www.day.com/maven/javax.jcr/javadocs/jcr-2.0/index.html
- * ?javax/jcr/Property.html
+ * ?javax/jcr/PropertyType.html
*/
private Integer type;
org.argeo.security.dao.os,\
org.argeo.security.equinox,\
-org.argeo.security.ui.initialPerspective=org.argeo.osgi.ui.explorer.perspective
-
+#org.argeo.security.ui.initialPerspective=org.argeo.osgi.ui.explorer.perspective
+org.argeo.security.ui.initialPerspective==org.argeo.security.ui.userHomePerspective
#argeo.node.repo.uri=http://localhost:7070/org.argeo.jcr.webapp/remoting/node
log4j.configuration=file:../../log4j.properties
log4j.logger.org.apache.directory.server=ERROR
log4j.logger.org.apache.jackrabbit.core.query.lucene=ERROR
-log4j.logger.org.springframework.security.context=DEBUG
+#log4j.logger.org.springframework.security.context=DEBUG
## Appenders
# console is set to be a ConsoleAppender.
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+ <!-- Log4j appender singleton -->
+ <bean id="secureAppender" class="org.argeo.security.log4j.SecureLogger"
+ init-method="init" destroy-method="destroy">
+ <property name="listeners" ref="logListeners" />
+ <property name="configuration">
+ <value><![CDATA[
+log4j.rootLogger=WARN, console
+
+## Levels
+log4j.logger.org.argeo=DEBUG
+log4j.logger.org.argeo.jackrabbit.remote.ExtendedDispatcherServlet=WARN
+log4j.logger.org.argeo.server.webextender.TomcatDeployer=WARN
+
+log4j.logger.org.apache.catalina=INFO
+log4j.logger.org.apache.coyote=INFO
+log4j.logger.org.apache.directory.server=ERROR
+log4j.logger.org.apache.jackrabbit.core.query.lucene=ERROR
+log4j.logger.org.apache.jackrabbit.core.config=ERROR
+
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern=%d{yyMMdd HH:mm:ss} %-5p %m [%t] %c%n
+ ]]></value>
+ </property>
+ </bean>
+</beans>
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"\r
osgi:default-timeout="30000">\r
\r
+ <!-- REFERENCES -->\r
<reference id="userDetailsManager"\r
interface="org.springframework.security.userdetails.UserDetailsManager"\r
cardinality="0..1" />\r
+\r
+ <list id="logListeners" interface="org.argeo.ArgeoLogListener"\r
+ cardinality="0..N" />\r
</beans:beans>
\ No newline at end of file
name="Profile"
restorable="true">
</view>
+ <view
+ class="org.argeo.security.ui.views.LogView"
+ id="org.argeo.security.ui.logView"
+ name="Log"
+ restorable="true">
+ </view>
</extension>
<extension
point="org.eclipse.ui.perspectives">
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.argeo.commons.security</groupId>
<Require-Bundle>org.eclipse.ui;resolution:=optional,org.eclipse.rap.ui;resolution:=optional,org.eclipse.core.runtime</Require-Bundle>
<Import-Package>
org.argeo.eclipse.spring,
+ org.apache.log4j;resolution:=optional,
*
</Import-Package>
<Export-Package>
<groupId>org.slf4j</groupId>
<artifactId>com.springsource.slf4j.org.apache.commons.logging</artifactId>
</dependency>
+
</dependencies>
</project>
package org.argeo.security.ui;
+import org.argeo.security.ui.views.LogView;
import org.argeo.security.ui.views.UserProfile;
import org.eclipse.ui.IFolderLayout;
import org.eclipse.ui.IPageLayout;
0.30f, editorArea);
left.addView(UserProfile.ID);
// left.addView(RolesView.ID);
+
+ IFolderLayout bottom = layout.createFolder("bottom",
+ IPageLayout.BOTTOM, 0.30f, editorArea);
+ bottom.addView(LogView.ID);
}
}
--- /dev/null
+package org.argeo.security.ui.views;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+
+import org.argeo.ArgeoLogListener;
+import org.eclipse.jface.viewers.ILazyContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+
+/** A content provider maintaing an array of lines */
+class LogContentProvider implements ILazyContentProvider, ArgeoLogListener {
+ private DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
+
+ private final TableViewer viewer;
+ private List<String> lines;
+
+ public LogContentProvider(TableViewer viewer) {
+ this.viewer = viewer;
+ }
+
+ public synchronized void dispose() {
+ lines = null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public synchronized void inputChanged(Viewer viewer, Object oldInput,
+ Object newInput) {
+ lines = (List<String>) newInput;
+ if (lines == null)
+ return;
+ this.viewer.setItemCount(lines.size());
+ }
+
+ public void updateElement(int index) {
+ viewer.replace(lines.get(index), index);
+ }
+
+ public synchronized void appendLog(String username, Long timestamp,
+ String level, String category, String thread, Object msg,
+ String[] exception) {
+ // check if valid
+ if (lines == null)
+ return;
+
+ String message = msg.toString();
+ StringBuffer buf = new StringBuffer("");
+ buf.append(dateFormat.format(new Date(timestamp))).append(" ");
+ buf.append(level).append(" ");
+ int count = 0;
+ String lastLine = null;
+ for (String line : message.split("\n")) {
+ if (count == 0)
+ lastLine = buf + line;
+ else
+ lastLine = line;
+ lines.add(lastLine);
+ count++;
+ }
+
+ if (exception != null) {
+ for (String ste : exception) {
+ lastLine = ste;
+ lines.add(lastLine);
+ }
+ }
+ final Object lastElement = lastLine;
+ viewer.getTable().getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ viewer.setItemCount(lines.size());
+ if (lastElement != null)
+ viewer.reveal(lastElement);
+ }
+ });
+ }
+ // private class LogLine {
+ // private String message;
+ // }
+}
\ No newline at end of file
--- /dev/null
+package org.argeo.security.ui.views;
+
+import java.util.ArrayList;
+
+import org.argeo.ArgeoLogListener;
+import org.argeo.security.ui.SecurityUiPlugin;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.part.ViewPart;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Display log lines with a virtual table. Register and unregisters a
+ * {@link ArgeoLogListener} via OSGi services.
+ */
+public class LogView extends ViewPart {
+ public static String ID = SecurityUiPlugin.PLUGIN_ID + ".logView";
+
+ private TableViewer viewer;
+
+ private LogContentProvider logContentProvider;
+
+ private ServiceRegistration serviceRegistration;
+
+ @Override
+ public void createPartControl(Composite parent) {
+ viewer = new TableViewer(parent, SWT.VIRTUAL);
+ viewer.setLabelProvider(new LabelProvider());
+ logContentProvider = new LogContentProvider(viewer);
+ serviceRegistration = getBundleContext().registerService(
+ ArgeoLogListener.class.getName(), logContentProvider, null);
+ viewer.setContentProvider(logContentProvider);
+ viewer.setUseHashlookup(true);
+ viewer.setInput(new ArrayList<String>());
+ }
+
+ @Override
+ public void setFocus() {
+ viewer.getTable().setFocus();
+ }
+
+ @Override
+ public void dispose() {
+ if (serviceRegistration != null)
+ serviceRegistration.unregister();
+ }
+
+ private BundleContext getBundleContext() {
+ return SecurityUiPlugin.getDefault().getBundle().getBundleContext();
+ }
+}
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
-import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.part.ViewPart;
import org.springframework.security.Authentication;
<artifactId>com.springsource.slf4j.org.apache.commons.logging</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.log4j</groupId>
+ <artifactId>com.springsource.org.apache.log4j</artifactId>
+ <optional>true</optional>
+ </dependency>
+
<!-- TEST -->
<dependency>
<groupId>org.junit</groupId>
this(null);
}
+ /** @return the name, or null if not yet logged */
public String getName() {
+ Subject subject = Subject.getSubject(AccessController.getContext());
+ if (subject == null)
+ return null;
return getUser().getName();
}
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
+import javax.jcr.security.Privilege;
import org.argeo.ArgeoException;
import org.argeo.jcr.JcrUtils;
private Repository repository;
private String securityWorkspace = "security";
private Session securitySession;
+ private Session nodeSession;
public void init() {
try {
securitySession = repository.login(securityWorkspace);
+ nodeSession = repository.login();
} catch (RepositoryException e) {
throw new ArgeoException("Cannot initialize", e);
}
public void destroy() {
JcrUtils.logoutQuietly(securitySession);
+ JcrUtils.logoutQuietly(nodeSession);
}
public Authentication authenticate(Authentication authentication)
String username = System.getProperty("user.name");
Node userProfile = JcrUtils.createUserProfileIfNeeded(
securitySession, username);
-
JcrUserDetails.checkAccountStatus(userProfile);
+
+ // each user should have a writable area in the default workspace of
+ // the node
+ Node userNodeHome = JcrUtils.createUserHomeIfNeeded(nodeSession,
+ username);
+ JcrUtils.addPrivilege(nodeSession, userNodeHome.getPath(),
+ username, Privilege.JCR_ALL);
+ if (nodeSession.hasPendingChanges())
+ nodeSession.save();
+
// user details
JcrUserDetails userDetails = new JcrUserDetails(userProfile, authen
.getCredentials().toString(), getBaseAuthorities());
--- /dev/null
+/*
+ * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
+ *
+ * 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.argeo.security.log4j;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.spi.LoggingEvent;
+import org.argeo.ArgeoException;
+import org.argeo.ArgeoLogListener;
+import org.springframework.security.Authentication;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken;
+
+/** Not meant to be used directly in standard log4j config */
+public class SecureLogger {
+ private List<ArgeoLogListener> listeners;
+
+ private Boolean disabled = false;
+
+ private String level = null;
+
+ private Level log4jLevel = null;
+ // private Layout layout;
+
+ private Properties configuration;
+
+ private AppenderImpl appender;
+
+ /** Marker to prevent stack overflow */
+ private ThreadLocal<Boolean> dispatching = new ThreadLocal<Boolean>() {
+
+ @Override
+ protected Boolean initialValue() {
+ return false;
+ }
+ };
+
+ public void init() {
+ try {
+ // if (layout != null)
+ // setLayout(layout);
+ // else
+ // setLayout(new PatternLayout(pattern));
+ appender = new AppenderImpl();
+
+ if (configuration != null)
+ PropertyConfigurator.configure(configuration);
+
+ Logger.getRootLogger().addAppender(appender);
+ } catch (Exception e) {
+ throw new ArgeoException("Cannot initialize log4j");
+ }
+ }
+
+ public void destroy() throws Exception {
+ Logger.getRootLogger().removeAppender(appender);
+ }
+
+ // public void setLayout(Layout layout) {
+ // this.layout = layout;
+ // }
+
+ /** For development purpose, since using regular logging is not easy here */
+ static void stdOut(Object obj) {
+ System.out.println(obj);
+ }
+
+ // public void setPattern(String pattern) {
+ // this.pattern = pattern;
+ // }
+
+ public void setDisabled(Boolean disabled) {
+ this.disabled = disabled;
+ }
+
+ public void setLevel(String level) {
+ this.level = level;
+ }
+
+ public void setListeners(List<ArgeoLogListener> listeners) {
+ this.listeners = listeners;
+ }
+
+ public void setConfiguration(Properties configuration) {
+ this.configuration = configuration;
+ }
+
+ private class AppenderImpl extends AppenderSkeleton {
+ public boolean requiresLayout() {
+ return false;
+ }
+
+ public void close() {
+ }
+
+ @Override
+ protected void append(LoggingEvent event) {
+ if (disabled)
+ return;
+
+ if (dispatching.get())
+ return;
+
+ if (level != null && !level.trim().equals("")) {
+ if (log4jLevel == null || !log4jLevel.toString().equals(level))
+ try {
+ log4jLevel = Level.toLevel(level);
+ } catch (Exception e) {
+ System.err
+ .println("Log4j level could not be set for level '"
+ + level + "', resetting it to null.");
+ e.printStackTrace();
+ level = null;
+ }
+
+ if (log4jLevel != null
+ && !event.getLevel().isGreaterOrEqual(log4jLevel)) {
+ return;
+ }
+ }
+
+ try {
+ Thread currentThread = Thread.currentThread();
+ String username = null;
+
+ // find username
+ SecurityContext securityContext = SecurityContextHolder
+ .getContext();
+ if (securityContext != null) {
+ Authentication authentication = securityContext
+ .getAuthentication();
+ if (authentication != null) {
+ if (authentication instanceof AnonymousAuthenticationToken) {
+ username = null;
+ } else {
+ username = authentication.getName();
+ }
+ }
+ }
+
+ // Spring OSGi safe
+ Iterator<ArgeoLogListener> it = listeners.iterator();
+ while (it.hasNext()) {
+ ArgeoLogListener logListener = it.next();
+ logListener.appendLog(username, event.getTimeStamp(), event
+ .getLevel().toString(), event.getLoggerName(),
+ currentThread.getName(), event.getMessage(), event.getThrowableStrRep());
+ }
+ } catch (Exception e) {
+ stdOut("Cannot process logging event");
+ e.printStackTrace();
+ }
+ }
+
+ }
+}
</bean>
<bean id="addRemoteRepository" class="org.argeo.jcr.ui.explorer.commands.AddRemoteRepository">
- <property name="repositoryFactory" ref="repositoryRegister" />
+ <property name="repositoryFactory" ref="repositoryFactory" />
<property name="bundleContext" ref="bundleContext" />
<property name="keyring" ref="jcrKeyring" />
</bean>
\r
<reference id="nodeRepository" interface="javax.jcr.Repository"\r
filter="(argeo.jcr.repository.alias=node)" />\r
+ <reference id="repositoryFactory" interface="javax.jcr.RepositoryFactory" />\r
\r
<reference id="defaultCallbackHandler" interface="javax.security.auth.callback.CallbackHandler" />\r
\r
else if (parameters.containsKey(JcrUtils.REPOSITORY_URI))
uri = parameters.get(JcrUtils.REPOSITORY_URI).toString();
- if (uri == null)
+ if (uri != null)
repository = createRemoteRepository(uri);
if (parameters.containsKey(JCR_REPOSITORY_ALIAS)) {
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.argeo.ArgeoException;
+import org.argeo.util.security.SimplePrincipal;
/** Utility methods to simplify common JCR operations. */
public class JcrUtils implements ArgeoJcrConstants {
/*
* SECURITY
*/
+
+ /**
+ * Convenience method for adding a single privilege to a principal (user or
+ * role), typically jcr:all
+ */
+ public static void addPrivilege(Session session, String path,
+ String principal, String privilege) throws RepositoryException {
+ List<Privilege> privileges = new ArrayList<Privilege>();
+ privileges.add(session.getAccessControlManager().privilegeFromName(
+ privilege));
+ addPrivileges(session, path, new SimplePrincipal(principal), privileges);
+ }
+
/**
* Add privileges on a path to a {@link Principal}. The path must already
* exist.
private Node contentNode;
private ByteArrayOutputStream out;
private CsvWriter csvWriter;
+ private final List<TabularColumn> columns;
/** Creates a table node */
public JcrTabularWriter(Node tableNode, List<TabularColumn> columns,
String contentNodeType) {
try {
+ this.columns = columns;
for (TabularColumn column : columns) {
String normalized = JcrUtils.replaceInvalidChars(column
.getName());