diff --git a/src/internal/auth/auth.go b/src/internal/auth/auth.go
index b072cb1..0fefd82 100644
--- a/src/internal/auth/auth.go
+++ b/src/internal/auth/auth.go
@@ -30,6 +30,7 @@ type User interface {
GetHash() string
HasRole(string) bool
IsAdmin() bool
+ IsActivated() bool
}
// Authorizer handle sesssion
diff --git a/src/internal/auth/middleware.go b/src/internal/auth/middleware.go
index 758cfb9..1ae088f 100644
--- a/src/internal/auth/middleware.go
+++ b/src/internal/auth/middleware.go
@@ -74,7 +74,13 @@ func (m *MiddlewareRole) ServeHTTP(w http.ResponseWriter, r *http.Request, next
return
}
- m.log.Debug("user has the role, continuing")
+ if !user.IsActivated() {
+ // return unauthorized
+ http.Error(w, "User is not activated", http.StatusUnauthorized)
+ return
+ }
+
+ m.log.Debug("user has the role and is activated, continuing")
next(w, r)
}
diff --git a/src/internal/users/handlers.go b/src/internal/users/handlers.go
index 6adf924..d07223c 100644
--- a/src/internal/users/handlers.go
+++ b/src/internal/users/handlers.go
@@ -81,8 +81,9 @@ func LoginPOSTHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error
// Issued at
"iat": time.Now().Unix(),
// Private claims
- "username": user.GetName(),
- "isAdmin": user.IsAdmin(),
+ "username": user.GetName(),
+ "isAdmin": user.IsAdmin(),
+ "isActivated": user.IsActivated(),
})
// Sign the token
diff --git a/src/internal/users/users.go b/src/internal/users/users.go
index 880b0c4..d33e52d 100644
--- a/src/internal/users/users.go
+++ b/src/internal/users/users.go
@@ -251,3 +251,8 @@ func (u *User) HasRole(role string) bool {
func (u *User) IsAdmin() bool {
return u.HasRole(AdminRole)
}
+
+// IsActivated checks if a user is activated
+func (u *User) IsActivated() bool {
+ return u.Activated
+}
diff --git a/src/public/js/app.js b/src/public/js/app.js
index 0fafb56..2f53839 100644
--- a/src/public/js/app.js
+++ b/src/public/js/app.js
@@ -47,6 +47,7 @@ function mapStateToProps(state) {
return {
username: state.userStore.get("username"),
isAdmin: state.userStore.get("isAdmin"),
+ isActivated: state.userStore.get("isActivated"),
torrentCount: torrentCount,
alerts: state.alerts,
}
@@ -62,6 +63,7 @@ function Main(props) {
diff --git a/src/public/js/components/navbar.js b/src/public/js/components/navbar.js
index 5c44c5d..5c38db6 100644
--- a/src/public/js/components/navbar.js
+++ b/src/public/js/components/navbar.js
@@ -41,6 +41,9 @@ export default class AppNavBar extends React.PureComponent {
this.setState({ expanded: value });
}
render() {
+ const loggedAndActivated = (this.state.userLoggedIn && this.props.isActivated);
+ const displayShowsSearch = (this.state.displayShowsSearch && loggedAndActivated);
+ const displayMoviesSearch = (this.state.displayMoviesSearch && loggedAndActivated);
return (
@@ -51,20 +54,24 @@ export default class AppNavBar extends React.PureComponent {
- {this.state.userLoggedIn &&
+ {loggedAndActivated &&
}
- {this.state.userLoggedIn &&
+ {loggedAndActivated &&
}
- {this.state.userLoggedIn &&
+ {loggedAndActivated &&
}
- {this.state.userLoggedIn &&
+ {loggedAndActivated &&
}
-
- {(this.state.displayMoviesSearch && this.state.userLoggedIn) &&
+
+ {displayMoviesSearch &&
}
- {(this.state.displayShowsSearch && this.state.userLoggedIn) &&
+ {displayShowsSearch &&
Admin Panel
}
-
-
-
+ {props.isActivated &&
+
+
+
+ }
diff --git a/src/public/js/components/users/activation.js b/src/public/js/components/users/activation.js
new file mode 100644
index 0000000..a6f5288
--- /dev/null
+++ b/src/public/js/components/users/activation.js
@@ -0,0 +1,16 @@
+import React from "react"
+
+function UserActivation(props) {
+ return (
+
+
+
+
Waiting for activation
+
+ Hang tight! Your user will soon be activated by the administrators of this site.
+
+
+
+ );
+}
+export default UserActivation;
diff --git a/src/public/js/reducers/users.js b/src/public/js/reducers/users.js
index 572f90c..8e133e7 100644
--- a/src/public/js/reducers/users.js
+++ b/src/public/js/reducers/users.js
@@ -7,6 +7,7 @@ const defaultState = Map({
loading: false,
username: "",
isAdmin: false,
+ isActivated: false,
isLogged: false,
polochonToken: "",
polochonUrl: "",
@@ -46,6 +47,7 @@ function updateFromToken(state, token) {
userLoading: false,
isLogged: true,
isAdmin: decodedToken.isAdmin,
+ isActivated: decodedToken.isActivated,
username: decodedToken.username,
}))
}
diff --git a/src/public/js/routes.js b/src/public/js/routes.js
index 4bde093..163ce69 100644
--- a/src/public/js/routes.js
+++ b/src/public/js/routes.js
@@ -3,6 +3,7 @@ import ShowList from "./components/shows/list"
import ShowDetails from "./components/shows/details"
import UserLoginForm from "./components/users/login"
import UserEdit from "./components/users/edit"
+import UserActivation from "./components/users/activation"
import UserSignUp from "./components/users/signup"
import TorrentList from "./components/torrents/list"
import AdminView from "./components/admins/users"
@@ -42,15 +43,20 @@ function isLoggedIn() {
return false
}
+function isActivated() {
+ const state = store.getState();
+ return state.userStore.get("isActivated");
+}
+
var pollingTorrentsId;
const loginCheck = function(nextState, replace, next, f = null) {
const loggedIn = isLoggedIn();
if (!loggedIn) {
- replace("/users/login");
+ replace("/users/login");
+ } else if (!isActivated()) {
+ replace("/users/activation");
} else {
- if (f) {
- f();
- }
+ if (f) { f(); }
// Poll torrents once logged
if (!pollingTorrentsId) {
@@ -104,6 +110,20 @@ export default function getRoutes(App) {
});
},
},
+ {
+ path: "/users/activation",
+ component: UserActivation,
+ onEnter: function(nextState, replace, next) {
+ if (!isLoggedIn()) {
+ replace('/users/login');
+ }
+ if (isActivated()) {
+ // User is already activated, redirect him to the default route
+ replace(defaultRoute);
+ }
+ next();
+ },
+ },
{
path: "/users/logout",
onEnter: function(nextState, replace, next) {