log4j2 appender talking to sql server database - log4j2

This URL
https://logging.apache.org/log4j/log4j-2.0/manual/appenders.html
has this example:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="error">
<Appenders>
<JDBC name="databaseAppender" tableName="dbo.application_log">
<DataSource jndiName="java:/comp/env/jdbc/LoggingDataSource" />
<Column name="eventDate" isEventTimestamp="true" />
<Column name="level" pattern="%level" />
<Column name="logger" pattern="%logger" />
<Column name="message" pattern="%message" />
<Column name="exception" pattern="%ex{full}" />
</JDBC>
</Appenders>
<Loggers>
<Root level="warn">
<AppenderRef ref="databaseAppender"/>
</Root>
</Loggers>
</Configuration>
When I try to wire up to a sqlserver database......
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<JDBC name="SQLServerAppender" tableName="dbo.LogEntry">
<DataSource jndiName="jdbc:sqlserver://MyMachine\\MyInstance:1433;databaseName=LoggingDB;applicationName=myappname;integratedSecurity=true;" />
<Column name="EntryDateUtc" isEventTimestamp="true" />
<Column name="LOGGER" pattern="%C" />
<Column name="Level" pattern="%level" />
<Column name="Message" pattern="%m" />
<Column name="UserName" pattern="%x" />
<Column name="Priority" pattern="%p" />
<Column name="ElapsedMilliseconds" pattern="%r" />
<Column name="ThreadName" pattern="%t" />
<Column name="ThrowableMessage" pattern="%throwable " />
</JDBC>
I get errors like:
ERROR No ConnectionSource provided: connectionSource
ERROR Could not create plugin of type class org.apache.logging.log4j.core.appender.db.jdbc.JdbcAppender for element JDBC org.apache.logging.log4j.core.config.ConfigurationException: Arguments given for element JDBC are invalid: field 'connectionSource' has invalid value 'null'
How do I set up a connection string in the xml-configuration to talk to sqlserver?
Even though it doesn't match the documentation, I tried this:
<Appenders>
<JDBC name="SQLServerAppender" tableName="dbo.LogEntry">
<ConnectionSource jndiName="jdbc:sqlserver
It didn't work of course.
What is the magic syntax sugar?? #help
Thanks.

The value of DataSource jndiName is not a SQL Server connection string. If you want to use a SQL Server connection string then put in Log4J configuration put something like:
<appender name="SQLServer" class="org.apache.log4j.jdbc.JDBCAppender">
<param name="url" value="jdbc:sqlserver://MyMachine\\MyInstance:1433;databaseName=LoggingDB;applicationName=myappname;"/>
<param name="driver" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
<param name="user" value="user_id"/>
<param name="password" value="password"/>
<param name="sql" value="INSERT INTO LOG4J_TABLE VALUES('%x','%d','%C','%p','%m')"/>
<layout class="org.apache.log4j.PatternLayout"></layout>
</appender>
<logger name="log4j.rootLogger" additivity="false">
<level value="DEBUG"/>
<appender-ref ref="SQLServer"/>
</logger>
Which will write to a table that you must have created previously like:
CREATE TABLE LOG4J_TABLE (
User_Id VARCHAR(20) NOT NULL,
Date_Stamp DATETIME NOT NULL,
Logger VARCHAR(100) NOT NULL,
Level VARCHAR(10) NOT NULL,
Message VARCHAR(8000) NOT NULL
)
Note that you canĀ“t use integratedSecurity=true straightforward from Java but must specify the user + password.

I think the problem is that JNDI is not enabled.
This is my configuration log4j2.xml:
<bean id="dataSourceProxy" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg ref="dataSource"/>
</bean>
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceProxy"/>
</bean>
<bean id="jdbcTemplate" class="xx.com.xxxxx.framework.database.xxxxxJDBCTemplate">
<constructor-arg ref="dataSourceProxy"/>
</bean>
<bean id="DBSession" class="xx.com.xxxxx.framework.database.DBConnectionManager" lazy-init="true">
<constructor-arg type="java.lang.String" value="DBSession"/>
<constructor-arg ref="dataSourceProxy"/>
<constructor-arg ref="transactionManager"/>
<property name="template" ref="jdbcTemplate"/>
</bean>
It worked normally before we had upgraded log4j to log4j2. After upgrading, there was an error output from my weblogic console:
ERROR Could not create plugin of type class org.apache.logging.log4j.core.appender.db.jdbc.JdbcAppender for element JDBC org.apache.logging.log4j.core.config.ConfigurationException: Arguments given for element JDBC are invalid: field 'connectionSource' has invalid value 'null'
at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.injectFields(PluginBuilder.java:210)
at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.build(PluginBuilder.java:121)
at org.apache.logging.log4j.core.config.AbstractConfiguration.createPluginObject(AbstractConfiguration.java:1120)
....
According to Apache documentation ,
a Log4j JDBC Appender configured with a DataSource is disabled by default. You may try to add java option -Dlog4j2.enableJndiJdbc=true to use JNDI's java protocol.
When we added set JAVA_OPTIONS=-Dlog4j2.enableJndiJdbc=true into our weblogic, the error was gone.
I hope it is helpful for you.

Related

CAS attributes in wsfederation not being forwarded to CAS client

I am implementing an authentication system for a Tomcat web application that gets authenticated against ADFS using CAS. I am using unicon's CAS server with ADFS integration.
I have reached to the state where I can see that the required attributes reach the CAS server. But these attributes are not getting forwarded to the client. check the figure below:
In the above image, the attribute map is empty after authentication. Also, when the client application validates the ticket, the attribute map is empty. Ref picture below:
After getting authenticated, the attributes are visible in the logs, but they are not being loaded into attribute Map.
The deployerConfigContext.xml is as follows. The attributeRepository bean and allowed attributes property in serviceRegistryDao bean are the probably the main focus areas.
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<bean id="authenticationManager"
class="org.jasig.cas.authentication.AuthenticationManagerImpl">
<property name="authenticationMetaDataPopulators">
<list>
<bean class="net.unicon.cas.support.wsfederation.authentication.WsFederationAuthenticationMetaDataPopulator" />
</list>
</property>
<property name="credentialsToPrincipalResolvers">
<list>
<bean class="net.unicon.cas.support.wsfederation.authentication.principal.WsFederationCredentialsToPrincipalResolver">
<property name="configuration" ref="wsFedConfig" />
</bean>
<bean class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" >
<property name="attributeRepository" ref="attributeRepository" />
</bean>
<bean class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" />
</list>
</property>
<property name="authenticationHandlers">
<list>
<bean class="net.unicon.cas.support.wsfederation.authentication.handler.support.WsFederationAuthenticationHandler" />
<bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
p:httpClient-ref="httpClient" />
</list>
</property>
</bean>
<sec:user-service id="userDetailsService">
<sec:user name="##THIS SHOULD BE REPLACED##" password="notused" authorities="ROLE_ADMIN" />
</sec:user-service>
<bean id="attributeRepository"
class="org.jasig.services.persondir.support.StubPersonAttributeDao">
<property name="backingMap">
<map>
<entry key="emailaddress" value="upn" />
<!--<entry key="FirstName" value="username" />-->
<entry key="name" value="LastName" />
<entry key="costcent" value="costcent" />
<entry key="title" value="FirstName" />
</map>
</property>
</bean>
<bean
id="serviceRegistryDao"
class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl">
<property name="registeredServices">
<list>
<bean class="org.jasig.cas.services.RegexRegisteredService">
<property name="id" value="0" />
<property name="name" value="HTTP and IMAP" />
<property name="description" value="Allows HTTP(S) and IMAP(S) protocols" />
<property name="serviceId" value="^(https?|imaps?)://.*" />
<property name="evaluationOrder" value="10000001" />
<property name="allowedAttributes">
<list>
<value>upn</value>
<value>Department</value>
<value>costcent</value>
<value>LastName</value>
<value>FirstName</value>
<value>name</value>
<value>emailaddress</value>
<value>title</value>
<value>SAM-Account-Name</value>
</list>
</property>
</bean>
</list>
</property>
</bean>
<bean id="auditTrailManager" class="com.github.inspektr.audit.support.Slf4jLoggingAuditTrailManager" />
<bean id="healthCheckMonitor" class="org.jasig.cas.monitor.HealthCheckMonitor">
<property name="monitors">
<list>
<bean class="org.jasig.cas.monitor.MemoryMonitor"
p:freeMemoryWarnThreshold="10" />
<bean class="org.jasig.cas.monitor.SessionMonitor"
p:ticketRegistry-ref="ticketRegistry"
p:serviceTicketCountWarnThreshold="5000"
p:sessionCountWarnThreshold="100000" />
</list>
</property>
</bean>
</beans>
The rest of the at CAS server is same as in the sample implementation of unicon's CAS-server implementation here
I've tried a lot of combinations in the mentioned beans. Being new to Spring I could not understand how to load the credentials in the attributeMap. Kindly guide me in forwarding the attributes sent by CAS server during authentication to the client application.
It looks like the WsFederationCredentialsToPrincipalResolver only extracts the principal id from the collection of attributes received, and ignores other attributes. So you only get the identity attribute defined in your configuration. You could for the time being, connect that resolver to your attribute repository and have it consume and retrieve attributes from there.
Note that CAS 4.2 supports and fixes this behavior and has built-in support for the ADFS integration. Your other option would be to extend WsFederationCredentialsToPrincipalResolver and have it process attributes and stuff them into the final principal created there by overriding the appropriate method.

Spring Security with CAS and Forms login

I am writing an application that needs to use CAS authentication for employees, and a username/password form login (which validates against a database table) for customers.
The idea is the front page would have a link to send them to CAS for employees ("click here if you are an employee"), and below that username & password boxes for non-employees.
I have both of these working in separate test apps - based on the sample applications in Spring Security - but am not clear how to combine the two AuthenticationProviders into one.
Current config - mainly from the CAS config with Forms config added:
<?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:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!--Set up the url based security features-->
<security:http entry-point-ref="casEntryPoint" use-expressions="true" >
<security:custom-filter position="FORM_LOGIN_FILTER" ref="casFilter" />
<security:logout logout-success-url="${cas.sso}/logout" />
<security:access-denied-handler ref="accessDeniedHandler" />
<!-- Set up the form login -->
<security:form-login login-page="/login.jsp" authentication-failure- url="/login.jsp?login_error=1"/>
</security:http>
<!-- Specify a destinatation for 403 errors raised by the above URL patterns
this is performed as a 'forward' internally -->
<bean id="accessDeniedHandler"
class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage" value="/error/403.html"/>
</bean>
<!--Hook in the properties for the CAS-->
<bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<property name="service" value="${cas.service}"/>
<property name="sendRenew" value="true"/>
</bean>
<!--Set up the CAS filter-->
<bean id="casFilter"
class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureHandler" ref="accessFailureHandler" />
</bean>
<!-- Specify a destinatation for 401 errors raised by casFilter (and UserService) -->
<bean id='accessFailureHandler'
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/error/401.html" />
<property name="useForward" value='true' />
<property name="allowSessionCreation" value="false" />
</bean>
<!--Set up the entry point for CAS-->
<bean id="casEntryPoint"
class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<property name="loginUrl" value="${cas.sso}"/>
<property name="serviceProperties" ref="serviceProperties"/>
<property name="encodeServiceUrlWithSessionId" value="false"/>
</bean>
<!--Setup authentication managers-->
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthenticationProvider"/>
<security:authentication-provider ref="customAuthenticationProvider"/>
</security:authentication-manager>
<!-- Our own user details service, which hooks in to the database -->
<bean id='userService' class='test.security.UserDetailsServiceImpl' />
<!-- Enable annotations -->
<security:global-method-security pre-post-annotations="enabled"/>
<!-- Customise an auth provider with the local specifics-->
<bean id="casAuthenticationProvider"
class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<property name="userDetailsService" ref="userService"/>
<property name="serviceProperties" ref="serviceProperties"/>
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<constructor-arg index="0" value="${cas.sso}"/>
</bean>
</property>
<property name="key" value="testSSO"/>
</bean>
<!-- Custom auth provider that validates usernames&pwds against DB -->
<bean id="customAuthenticationProvider" class="test.security.CustomAuthenticationProvider" />
</beans>
This gives the error message:
Filter beans '<casFilter>' and '<org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#0>' have the same 'order' value.
How can I put both filters into the chain? Or do I need to write my own filter that (somehow?) knows which method is being chosen and then delegates the relevant specific filter?
I am new to Spring Security so is there a better way of doing this entirely?
Thanks!
casFilter must be placed at position=CAS_FILTER

Spring OAuth2 with implicit and password flows

I am trying to setup a project with Spring OAuth 2 using implicit, password and authorization flows.
The problem I have appears when I use the same token endpoint for implicit and the other two, password and authorization needs the basic authentication for client validation while implicit doesn't validate the client secret and I want to use a more clasical login/password authentication for user authorization.
So depending on the configuration one or two flows work.
Having 2 endpoints seems to be the easiest solution, but I can not find how to achieve that.
<?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:oauth="http://www.springframework.org/schema/security/oauth2"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<!--
<sec:http pattern="/external/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security" entry-point-ref="authenticationEntryPoint">
<sec:intercept-url pattern="/external/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<sec:anonymous enabled="false" />
<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
</sec:http>
-->
<sec:http pattern="/external/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<sec:intercept-url pattern="/external/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<sec:anonymous enabled="false" />
<sec:http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
</sec:http>
<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="blablabla" />
<property name="typeName" value="Basic" />
</bean>
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>
<bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<bean id="tokenStore" class="com.proton.oauthprovider.service.ProtOnTokenStore" />
<bean id="clientDetails" class="com.proton.oauthprovider.service.ProtOnClientDetailsService" />
<bean id="oauthCodeDetails" class="com.proton.oauthprovider.service.ProtOnAuthorizationCodeServices" />
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<bean id="userApprovalHandler" class="com.proton.oauthprovider.service.OAuthUserApprovalHandler">
<property name="autoApproveClients">
<set>
<!-- <value>rest-client</value> -->
</set>
</property>
<property name="tokenServices" ref="tokenServices" />
</bean>
<oauth:authorization-server client-details-service-ref="clientDetails"
token-services-ref="tokenServices"
user-approval-handler-ref="userApprovalHandler" authorization-endpoint-url="/external/oauth/authorize"
user-approval-page="forward:/external/oauth/confirm_access"
error-page="forward:/external/oauth/error"
token-endpoint-url="/external/oauth/token" >
<oauth:authorization-code authorization-code-services-ref="oauthCodeDetails"/>
<oauth:implicit/>
<oauth:refresh-token />
<oauth:password authentication-manager-ref="authenticationManager"/>
</oauth:authorization-server>
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
<!-- Override the default mappings for approval and error pages -->
<bean id="accessConfirmationController" class="com.proton.oauthprovider.controller.AccessConfirmationController">
<property name="clientDetailsService" ref="clientDetails" />
</bean>
</beans>
authenticationEntryPoint is the login form entry point, and the custom classes are more or less the same from sparklr and tonr just using DB backend for storing client and token data.
Ok I got everything wrong, implicit flow does not use token endpoint, it uses authorize one.
So the previous config is ok and I only needed to point implicit flow to /oauth/authorize/ and it works as expected.

How to handle an exception from MethodInvokingFactoryObject Spring.NET?

I have Spring.Configuration like that:
<objects xmlns="http://www.springframework.net">
<!-- Create SQLite connection -->
<object id="SqlConnection" type="System.Data.SQLite.SQLiteConnection, System.Data.SQLite">
<constructor-arg index="0" value="Data Source=MyDatabase.db"/>
</object>
<!-- Open connection -->
<object type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core">
<property name="TargetObject">
<ref local="SqlConnection" />
</property>
<property name="TargetMethod" value="Open"/>
</object>
</objects>
So how do I handle an exception from the method Open() if this throws since MethodInvokingFactoryObject is created and invoked?

Can Spring.Net log what it's doing as it constructors objects?

Is there anyway to get Spring.Net to log what it's doing as it constructs objects? Something on the order of
Constructing Object A
Constructing Object B
etc etc.....
I need to resolve a circular dependency within my application, and seeing the order in which Spring is creating the objects could be a huge help.
This can easily be done. Spring uses Common.Logging. You can grab logging output from Spring.Objects.Factory.* classes and look for ... Creating instance of Object 'your-id-here' ... messages.
Note that you have to log at DEBUG level, which means you'll see quite a lot of other information too.
The following app.config would log creation calls to the console, using log4net:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="common">
<section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
</sectionGroup>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<common>
<logging>
<factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Net">
<arg key="configType" value="INLINE" />
</factoryAdapter>
</logging>
</common>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger %ndc - %message%newline" />
</layout>
</appender>
<!-- this logger will catch creation messages -->
<logger name="Spring.Objects.Factory">
<level value="DEBUG" />
<appender-ref ref="ConsoleAppender" />
</logger>
</log4net>
</configuration>
So most of this is boiler-plate Common.Logging and log4net configuration, which is well documented on the Common.Logging website. If you want to append to a file or something else, see the log4net docs.

Resources