Switched from InMemoryUsers- to JdbcUsersConnectionRepository with H2
[examples/facebook-app] / src / main / java / de / juplo / yourshouter / SocialConfig.java
1 package de.juplo.yourshouter;
2
3
4
5 import java.math.BigInteger;
6 import javax.inject.Inject;
7 import javax.sql.DataSource;
8 import org.apache.http.HttpRequestFactory;
9 import org.springframework.beans.factory.annotation.Value;
10 import org.springframework.context.annotation.Bean;
11 import org.springframework.context.annotation.Configuration;
12 import org.springframework.context.annotation.Scope;
13 import org.springframework.context.annotation.ScopedProxyMode;
14 import org.springframework.social.UserIdSource;
15 import org.springframework.core.env.Environment;
16 import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
17 import org.springframework.security.core.context.SecurityContext;
18 import org.springframework.security.crypto.encrypt.Encryptors;
19 import org.springframework.social.config.annotation.ConnectionFactoryConfigurer;
20 import org.springframework.social.config.annotation.EnableSocial;
21 import org.springframework.social.config.annotation.SocialConfigurerAdapter;
22 import org.springframework.social.connect.Connection;
23 import org.springframework.social.connect.ConnectionFactoryLocator;
24 import org.springframework.social.connect.ConnectionRepository;
25 import org.springframework.social.connect.ConnectionSignUp;
26 import org.springframework.social.connect.UsersConnectionRepository;
27 import org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository;
28 import org.springframework.social.connect.web.ConnectController;
29 import org.springframework.social.connect.web.ProviderSignInController;
30 import org.springframework.social.connect.web.SignInAdapter;
31 import org.springframework.social.facebook.api.Facebook;
32 import org.springframework.social.facebook.connect.FacebookConnectionFactory;
33 import org.springframework.social.facebook.web.CanvasSignInController;
34
35
36 /**
37  * Spring Social Configuration.
38  *
39  * @author Kai Moritz
40  */
41 @Configuration
42 @EnableSocial
43 public class SocialConfig extends SocialConfigurerAdapter
44 {
45   @Inject
46   DataSource dataSource;
47   @Inject
48   ConnectionSignUp connectionSignUp;
49   @Inject
50   SignInAdapter signInAdapter;
51
52   @Value("${facebook.app.secret}")
53   String secret;
54   @Value("${facebook.app.salt}")
55   String salt;
56
57
58   /**
59    * Add a {@link FacebookConnectionFactory} to the configuration.
60    * The factory is configured through the keys <code>facebook.app.id</code>
61    * and <code>facebook.app.secret</code>.
62    *
63    * @param config
64    * @param env 
65    */
66   @Override
67   public void addConnectionFactories(
68       ConnectionFactoryConfigurer config,
69       Environment env
70       )
71   {
72     config.addConnectionFactory(
73         new FacebookConnectionFactory(
74             env.getProperty("facebook.app.id"),
75             env.getProperty("facebook.app.secret")
76             )
77         );
78   }
79
80   /**
81    * {@inheritDoc}
82    *
83    * Configure an instance of {@link JdbcUsersConnection} as persistent
84    * store of user/connection-mappings.
85    * <p>
86    * The app-secret is reused as password for the encryption of the data.
87    * The salt can be changed in the <code>pom.xml</code>
88    * <p>
89    * This does only work, if you have the Java Crypto Extension (JCE) in
90    * full strength version, since Spring Security is using a 256-bit key.
91    *
92    * @see http://stackoverflow.com/a/17637354
93    */
94   @Override
95   public UsersConnectionRepository getUsersConnectionRepository(
96       ConnectionFactoryLocator connectionFactoryLocator
97       )
98   {
99     JdbcUsersConnectionRepository repository =
100         new JdbcUsersConnectionRepository(
101             dataSource,
102             connectionFactoryLocator,
103             Encryptors.text(
104                 secret,
105                 String.format("%08x", new BigInteger(1, salt.getBytes()))
106                 )
107             );
108     repository.setConnectionSignUp(connectionSignUp);
109     return repository;
110   }
111
112   /**
113    * Configure our new implementation of {@link UserIdSource}, that retrieves
114    * the current user from the {@link SecurityContext}.
115    *
116    * @return
117    *     An instance of {@link AnonymousUserIdSource}.
118    *
119    * @see {@link SecurityContextUserIdSource}
120    * @see {@link SecurityContext}
121    * @see {@link UserCookieInterceptor}
122    */
123   @Override
124   public UserIdSource getUserIdSource()
125   {
126     return new SpringSecurityContextUserIdSource();
127   }
128
129
130   /**
131    * Configuration of the controller, that handles the authorization against
132    * the Facebook-API, to connect a user to Facebook.
133    *
134    * At the moment, no special configuration is needed.
135    *
136    * @param factoryLocator
137    *     The {@link ConnectionFactoryLocator} will be injected by Spring.
138    * @param repository
139    *     The {@link ConnectionRepository} will be injected by Spring.
140    * @return
141    *     The configured controller.
142    */
143   @Bean
144   public ConnectController connectController(
145       ConnectionFactoryLocator factoryLocator,
146       ConnectionRepository repository
147       )
148   {
149     ConnectController controller =
150         new ConnectController(factoryLocator, repository);
151     return controller;
152   }
153
154   /**
155    * Configure the {@link ProviderSignInController} to use our implementation
156    * of {@link SignInAdapter} to sign in the user by storing the ID in the
157    * {@link SecurityContext} and the user-cookie.
158    *
159    * @param factoryLocator The {@link ConnectionFactoryLocator} will be injected by Spring.
160    * @param repository The {@link UserConnectionRepository} will be injected by Spring.
161    * @return The configured {@link ProviderSignInController}
162    */
163   @Bean
164   public ProviderSignInController signInController(
165       ConnectionFactoryLocator factoryLocator,
166       UsersConnectionRepository repository
167       )
168   {
169     ProviderSignInController controller =
170         new ProviderSignInController(factoryLocator, repository, signInAdapter);
171     return controller;
172   }
173
174   /**
175    * Configure the {@link CanvasSignInController} to enable sign-in through
176    * the <code>signed_request</code>, that Facebook sends to the canvas-page.
177    *
178    * @param factoryLocator The {@link ConnectionFactoryLocator} will be injected by Spring.
179    * @param repository The {@link UserConnectionRepository} will be injected by Spring.
180    * @param env The {@link Environment}, to read additional parameters from.
181    * @return The configured {@link CanvasSignInController}
182    */
183   @Bean
184   public CanvasSignInController canvasSignInController(
185       ConnectionFactoryLocator factoryLocator,
186       UsersConnectionRepository repository,
187       Environment env
188       )
189   {
190     return
191         new CanvasSignInController(
192             factoryLocator,
193             repository,
194             signInAdapter,
195             env.getProperty("facebook.app.id"),
196             env.getProperty("facebook.app.secret"),
197             env.getProperty("facebook.app.canvas")
198             );
199   }
200
201   /**
202    * Configure a scoped bean named <code>facebook</code>, that enables
203    * access to the Graph-API in the name of the current user.
204    *
205    * @param repository
206    *     The {@link ConnectionRepository} will be injected by Spring.
207    * @return
208    *     A {@Connection<Facebook>}, that represents the authorization of the
209    *     current user against the Graph-API, or <code>null</code>, if the
210    *     current user is not connected to the API.
211    */
212   @Bean
213   @Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
214   public Facebook facebook(ConnectionRepository repository)
215   {
216     Connection<Facebook> connection =
217         repository.findPrimaryConnection(Facebook.class);
218     return connection != null ? connection.getApi() : null;
219   }
220
221   /**
222    * Use the <code>HttpClient</code> from Apaches <code>HttpComponents</code>
223    * for HTTP-requests.
224    *
225    * We also configure shorter intervals for the connection timeout and the
226    * read timeout.
227    *
228    * @param env The {@link Environment}, to read additional parameters from.
229    * @return The alternative implementation of {@link HttpRequestFactory}.
230    */
231   @Bean
232   public HttpComponentsClientHttpRequestFactory requestFactory(Environment env)
233   {
234     HttpComponentsClientHttpRequestFactory factory =
235         new HttpComponentsClientHttpRequestFactory();
236     factory.setConnectTimeout(
237         Integer.parseInt(env.getProperty("httpclient.timeout.connection"))
238         );
239     factory.setReadTimeout(
240         Integer.parseInt(env.getProperty("httpclient.timeout.read"))
241         );
242     return factory;
243   }
244 }