Skip to content

Commit 0a37535

Browse files
authored
First pass at making user authentication optional via configuration option. (#91)
* First pass at making user authentication optional via configuration option * Cleanup, bump SpringBoot version * codestyle violation fixes * fix changelog wording
1 parent d9f3125 commit 0a37535

File tree

12 files changed

+206
-57
lines changed

12 files changed

+206
-57
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
55
## 2.0.0 (UNRELEASED)
66

77
- Added new Stream consumer management page at /configuration/stream
8-
- Updated SpringBoot framework from 1.5.x to 2.0.4
8+
- Added ability to disable user authentication, allowing for anonymous users to access the web service.
9+
- Updated SpringBoot framework from 1.5.x to 2.0.5.
910

1011
### Breaking Changes
1112

README.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,23 @@ By default config.yml will look similar to:
4949
server:
5050
port: 8080
5151

52-
security:
53-
require-ssl: false
54-
5552
## Various App Configs
5653
app:
54+
## Should be unique to your installation.
55+
## This key will be used for symmetric encryption of JKS/TrustStore secrets if you configure any SSL enabled Kafka clusters.
5756
key: "SuperSecretKey"
57+
58+
## Defines a prefix prepended to the Id of all consumers.
59+
consumerIdPrefix: "KafkaWebViewConsumer"
60+
61+
## Require SSL
62+
requireSsl: false
63+
64+
## User authentication options
65+
user:
66+
## Require user authentication
67+
## Setting to false will disable login requirement.
68+
enabled: true
5869
```
5970
6071
### Starting the service
@@ -78,6 +89,8 @@ Point your browser at `http://localhost:8080` follow the [Logging in for the fir
7889

7990
## Logging in for the first time
8091

92+
**NOTE** If you've disabled user authentication in your configuration, no login will be required.
93+
8194
On first start up a default Administrator user will be created for you. Login using `[email protected]` with password `admin`
8295

8396
**NOTE** After logging in you should create your own Administrator user and remove the default account.

kafka-webview-ui/src/assembly/distribution/config.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,10 @@ app:
1111
consumerIdPrefix: "KafkaWebViewConsumer"
1212

1313
## Require SSL
14-
require-ssl: false
14+
requireSsl: false
15+
16+
## User authentication options
17+
user:
18+
## Require user authentication
19+
## Setting to false will disable login requirement.
20+
enabled: true

kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/configuration/AppProperties.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,16 @@ public class AppProperties {
4848
@Value("${app.consumerIdPrefix}")
4949
private String consumerIdPrefix;
5050

51-
@Value("${app.require_ssl:false}")
51+
@Value("${app.requireSsl:false}")
5252
private boolean requireSsl = false;
5353

54+
/**
55+
* Flag read from configuratio file to determine if the app should
56+
* enforce User authentication.
57+
*/
58+
@Value("${app.user.enabled:true}")
59+
private boolean userAuthEnabled = true;
60+
5461
public String getName() {
5562
return name;
5663
}
@@ -75,6 +82,10 @@ public boolean isRequireSsl() {
7582
return requireSsl;
7683
}
7784

85+
public boolean isUserAuthEnabled() {
86+
return userAuthEnabled;
87+
}
88+
7889
@Override
7990
public String toString() {
8091
return "AppProperties{"
@@ -84,6 +95,7 @@ public String toString() {
8495
+ ", maxConcurrentWebSocketConsumers=" + maxConcurrentWebSocketConsumers
8596
+ ", consumerIdPrefix='" + consumerIdPrefix + '\''
8697
+ ", requireSsl='" + requireSsl + '\''
98+
+ ", userAuthEnabled='" + userAuthEnabled + '\''
8799
+ '}';
88100
}
89101
}

kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/configuration/SecurityConfig.java

Lines changed: 84 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@
2424

2525
package org.sourcelab.kafka.webview.ui.configuration;
2626

27+
import org.sourcelab.kafka.webview.ui.manager.user.AnonymousUserDetailsService;
28+
import org.sourcelab.kafka.webview.ui.manager.user.CustomUserDetails;
2729
import org.sourcelab.kafka.webview.ui.manager.user.CustomUserDetailsService;
2830
import org.sourcelab.kafka.webview.ui.repository.UserRepository;
2931
import org.springframework.beans.factory.annotation.Autowired;
30-
import org.springframework.beans.factory.annotation.Value;
3132
import org.springframework.context.annotation.Bean;
3233
import org.springframework.context.annotation.Configuration;
3334
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@@ -39,6 +40,8 @@
3940
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
4041
import org.springframework.web.context.request.RequestContextListener;
4142

43+
import java.util.ArrayList;
44+
4245
/**
4346
* Manages Security Configuration.
4447
*/
@@ -49,12 +52,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
4952
@Autowired
5053
private UserRepository userRepository;
5154

52-
/**
53-
* Allows for requiring all requests over SSL.
54-
* If not defined in the config under the key security.require_ssl, we default to false.
55-
*/
56-
@Value("${app.require_ssl:false}")
57-
private boolean isRequireSsl;
55+
@Autowired
56+
private AppProperties appProperties;
5857

5958
private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
6059

@@ -65,43 +64,21 @@ public PasswordEncoder getPasswordEncoder() {
6564

6665
@Override
6766
protected void configure(final HttpSecurity http) throws Exception {
68-
http
69-
// CSRF Enabled
70-
.csrf().and()
7167

72-
.authorizeRequests()
73-
// Paths to static resources are available to anyone
74-
.antMatchers("/register/**", "/login/**", "/vendors/**", "/css/**", "/js/**", "/img/**")
75-
.permitAll()
76-
// Users can edit their own profile
77-
.antMatchers("/configuration/user/edit/**", "/configuration/user/update")
78-
.fullyAuthenticated()
79-
// But other Configuration paths require ADMIN role.
80-
.antMatchers("/configuration/**")
81-
.hasRole("ADMIN")
82-
// All other requests must be authenticated
83-
.anyRequest()
84-
.fullyAuthenticated()
85-
.and()
86-
87-
// Define how you login
88-
.formLogin()
89-
.loginPage("/login")
90-
.usernameParameter("email")
91-
.passwordParameter("password")
92-
.failureUrl("/login?error=true")
93-
.defaultSuccessUrl("/")
94-
.permitAll()
95-
.and()
68+
// CSRF Enabled
69+
http
70+
.csrf();
9671

97-
// And how you logout
98-
.logout()
99-
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
100-
.logoutSuccessUrl("/login")
101-
.permitAll();
72+
// If user auth is enabled
73+
if (appProperties.isUserAuthEnabled()) {
74+
// Set it up.
75+
enableUserAuth(http);
76+
} else {
77+
disableUserAuth(http);
78+
}
10279

10380
// If require SSL is enabled
104-
if (isRequireSsl) {
81+
if (appProperties.isRequireSsl()) {
10582
// Ensure its enabled.
10683
http
10784
.requiresChannel()
@@ -112,10 +89,73 @@ protected void configure(final HttpSecurity http) throws Exception {
11289

11390
@Override
11491
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
115-
auth
116-
// Define our custom user details service.
117-
.userDetailsService(new CustomUserDetailsService(userRepository))
118-
.passwordEncoder(getPasswordEncoder());
92+
if (appProperties.isUserAuthEnabled()) {
93+
auth
94+
// Define our custom user details service.
95+
.userDetailsService(new CustomUserDetailsService(userRepository))
96+
.passwordEncoder(getPasswordEncoder());
97+
} else {
98+
auth
99+
// Define our custom user details service.
100+
.userDetailsService(new AnonymousUserDetailsService());
101+
}
102+
}
103+
104+
/**
105+
* Sets up HttpSecurity for standard local user authentication.
106+
*/
107+
private void enableUserAuth(final HttpSecurity http) throws Exception {
108+
http
109+
.authorizeRequests()
110+
// Paths to static resources are available to anyone
111+
.antMatchers("/register/**", "/login/**", "/vendors/**", "/css/**", "/js/**", "/img/**")
112+
.permitAll()
113+
// Users can edit their own profile
114+
.antMatchers("/configuration/user/edit/**", "/configuration/user/update")
115+
.fullyAuthenticated()
116+
// But other Configuration paths require ADMIN role.
117+
.antMatchers("/configuration/**")
118+
.hasRole("ADMIN")
119+
// All other requests must be authenticated
120+
.anyRequest()
121+
.fullyAuthenticated()
122+
.and()
123+
124+
// Define how you login
125+
.formLogin()
126+
.loginPage("/login")
127+
.usernameParameter("email")
128+
.passwordParameter("password")
129+
.failureUrl("/login?error=true")
130+
.defaultSuccessUrl("/")
131+
.permitAll()
132+
.and()
133+
134+
// And how you logout
135+
.logout()
136+
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
137+
.logoutSuccessUrl("/login")
138+
.permitAll();
139+
}
140+
141+
/**
142+
* Sets up HttpSecurity for standard local user authentication.
143+
*/
144+
private void disableUserAuth(final HttpSecurity http) throws Exception {
145+
// Define the "User" that anonymous web clients will assume.
146+
final CustomUserDetails customUserDetails = AnonymousUserDetailsService.getDefaultAnonymousUser();
147+
148+
http
149+
// All requests should require authorization as anonymous
150+
.authorizeRequests()
151+
.anyRequest()
152+
.anonymous()
153+
.and()
154+
// And the user provider should always return our anonymous user instance
155+
// with admin credentials.
156+
.anonymous()
157+
.principal(customUserDetails)
158+
.authorities(new ArrayList<>(customUserDetails.getAuthorities()));
119159
}
120160

121161
@Bean

kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/controller/BaseController.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
package org.sourcelab.kafka.webview.ui.controller;
2626

27+
import org.sourcelab.kafka.webview.ui.configuration.AppProperties;
2728
import org.sourcelab.kafka.webview.ui.manager.user.CustomUserDetails;
2829
import org.sourcelab.kafka.webview.ui.model.Cluster;
2930
import org.sourcelab.kafka.webview.ui.model.View;
@@ -51,14 +52,21 @@ public abstract class BaseController {
5152
@Autowired
5253
private ViewRepository viewRepository;
5354

55+
@Autowired
56+
private AppProperties appProperties;
57+
5458
/**
5559
* Determine if the current user is logged in or not.
5660
* @return True if so, false if not.
5761
*/
5862
protected boolean isLoggedIn() {
5963
// For now bypass auth
60-
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
61-
if (auth == null || auth instanceof AnonymousAuthenticationToken) {
64+
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
65+
if (auth == null) {
66+
return false;
67+
}
68+
69+
if (auth instanceof AnonymousAuthenticationToken && appProperties.isUserAuthEnabled()) {
6270
return false;
6371
}
6472
return true;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* MIT License
3+
*
4+
* Copyright (c) 2017, 2018 SourceLab.org (https://github.com/Crim/kafka-webview/)
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
package org.sourcelab.kafka.webview.ui.manager.user;
26+
27+
import org.sourcelab.kafka.webview.ui.model.User;
28+
import org.sourcelab.kafka.webview.ui.model.UserRole;
29+
import org.springframework.security.core.userdetails.UserDetails;
30+
import org.springframework.security.core.userdetails.UserDetailsService;
31+
import org.springframework.security.core.userdetails.UsernameNotFoundException;
32+
33+
/**
34+
* Simple implementation that should only be used when real user authentication has been disabled.
35+
*/
36+
public class AnonymousUserDetailsService implements UserDetailsService {
37+
private static CustomUserDetails defaultUserDetails;
38+
39+
{
40+
// Setup a mock user.
41+
final User anonymousUser = new User();
42+
anonymousUser.setId(0);
43+
anonymousUser.setDisplayName("Anonymous User");
44+
anonymousUser.setEmail("Anonymous User");
45+
anonymousUser.setRole(UserRole.ROLE_ADMIN);
46+
anonymousUser.setActive(true);
47+
48+
defaultUserDetails = new CustomUserDetails(anonymousUser);
49+
}
50+
51+
@Override
52+
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
53+
return getDefaultAnonymousUser();
54+
}
55+
56+
/**
57+
* @return Default user when not using real user authentication.
58+
*/
59+
public static CustomUserDetails getDefaultAnonymousUser() {
60+
return defaultUserDetails;
61+
}
62+
}

kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/manager/user/CustomUserDetailsService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,15 @@
2727
import org.sourcelab.kafka.webview.ui.model.User;
2828
import org.sourcelab.kafka.webview.ui.repository.UserRepository;
2929
import org.springframework.security.core.userdetails.UserDetails;
30+
import org.springframework.security.core.userdetails.UserDetailsService;
3031
import org.springframework.security.core.userdetails.UsernameNotFoundException;
3132
import org.springframework.stereotype.Service;
3233

3334
/**
3435
* Custom User Details Service. Create Custom User Details implementation.
3536
*/
3637
@Service
37-
public class CustomUserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
38+
public class CustomUserDetailsService implements UserDetailsService {
3839
private final UserRepository userRepository;
3940

4041
public CustomUserDetailsService(final UserRepository userRepository) {

kafka-webview-ui/src/main/resources/config/base.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,6 @@ app:
4343
key: "SuperSecretKey"
4444
maxConcurrentWebSocketConsumers: 64
4545
consumerIdPrefix: "KafkaWebViewConsumer"
46-
require-ssl: true
46+
requireSsl: true
47+
user:
48+
enabled: true

kafka-webview-ui/src/main/resources/config/dev.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ spring:
1515
show-sql: true
1616

1717
app:
18-
require-ssl: false
18+
requireSsl: false
19+
user:
20+
enabled: true

0 commit comments

Comments
 (0)