Permalink
Browse files

Adds password authentication and user profile support.

  • Loading branch information...
tamasd committed Apr 22, 2016
1 parent 31bc633 commit 9a406c05c46d9c4f713769bb778f55a0a9a0a6ff
View
@@ -10,7 +10,7 @@ Check the [landing page](http://pronovix.com/walkhub) for more information.
## Dependencies
* Go 1.5+
* PostgreSQL 9.4+ (older versions might work too)
* PostgreSQL 9.5
* NPM
* Node.js 4.x ([5.x does not work](https://github.com/Pronovix/walkhub-service/issues/12))
@@ -25,9 +25,15 @@ Check the [landing page](http://pronovix.com/walkhub) for more information.
* `secret`: 32 bytes long random byte sequence, encoded with hex encoding
* `cookiesecret`: 32 bytes long random byte sequence, encoded with hex encoding
* `baseurl`: the url where WalkHub will be. A default is set in the example config files.
* `google`: OAuth2 tokens for Google
Currently only OAuth2 through Google is supported as an authentication mechanism, but other OAuth2 providers and password authentication (with 2FA support) are coming soon.
At least one of the following authentication providers are mandatory:
* `google`: OAuth2 tokens for Google (see config.json.sample.full)
* `pwauth`: password authentication
If password authentication is enabled, then SMTP credentials are mandatory:
* `smtp`: SMTP credentials (see config.json.sample.full)
### Optional values
View
@@ -54,6 +54,7 @@ func main() {
cfg.Set("gzip", false)
cfg.Set("CookiePrefix", "WALKHUB")
cfg.RegisterAlias("db", "PGConnectString")
cfg.SetDefault("pwauth", true)
level := log.LOG_USER
if cfg.GetBool("trace") {
@@ -89,10 +90,16 @@ func main() {
s.HTTPOrigin = httpOrigin
s.RedirectAll = cfg.GetBool("redirectall")
s.EnforceDomains = cfg.GetBool("enforcedomains")
s.PWAuth = cfg.GetBool("pwauth")
if cp := cfg.GetString("contentpages"); cp != "" {
s.CustomPaths = loadContentPages(cp)
whlogger.Verbose().Println("custom paths", s.CustomPaths)
}
s.AuthCreds.SMTP.Addr = cfg.GetString("smtp.addr")
s.AuthCreds.SMTP.Identity = cfg.GetString("smtp.identity")
s.AuthCreds.SMTP.Username = cfg.GetString("smtp.username")
s.AuthCreds.SMTP.Password = cfg.GetString("smtp.password")
s.AuthCreds.SMTP.Host = cfg.GetString("smtp.host")
s.AuthCreds.Google = auth.OAuthCredentials{
ID: cfg.GetString("google.id"),
Secret: cfg.GetString("google.secret"),
View
@@ -13,8 +13,16 @@
"contentpages": "",
"frontpagecomponent": "",
"menuitems": "",
"pwauth": true,
"google": {
"id": "",
"secret": ""
},
"smtp": {
"addr": "",
"identity": "",
"username": "",
"password": "",
"host": ""
}
}
@@ -2,9 +2,5 @@
"db": "",
"secret": "",
"cookiesecret": "",
"baseurl": "http://localhost:8080/",
"google": {
"id": "",
"secret": ""
}
"baseurl": "http://localhost:8080/"
}
@@ -25,7 +25,7 @@ class AuthProviderActions {
"receivedAuthProvider",
"fetchingAuthProviderFailed",
"loadAuthProvider"
"loadAuthProvider",
);
}
}
@@ -1,5 +1,5 @@
// Walkhub
// Copyright (C) 2015 Pronovix
// Copyright (C) 2016 Pronovix
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
@@ -14,16 +14,16 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import flux from "control";
import {createActions} from 'alt/utils/decorators';
class User extends React.Component {
render() {
return (
<h1> User </h1>
@createActions(flux)
class MessageActions {
constructor() {
this.generateActions(
"flashMessage"
);
}
}
export default User;
export default MessageActions;
View
@@ -25,7 +25,57 @@ class UserActions {
"receivedUser",
"fetchingUserFailed",
"loadUser"
"updatingUser",
"updatedUser",
"updatingUserFailed",
"registeringUser",
"registeredUser",
"registeringUserFailed",
"logginginUser",
"loggedinUser",
"logginginUserFailed",
"logginginSecondFactorUser",
"loggedinSecondFactorUser",
"loggingInSecondFactorUserFailed",
"loadingHas2fa",
"loadedHas2fa",
"loadingHas2faFailed",
"adding2fa",
"added2fa",
"adding2faFailed",
"disabling2fa",
"disabled2fa",
"disabling2faFailed",
"changingPassword",
"changedPassword",
"changingPasswordFailed",
"requestingLostPassword",
"requestedLostPassword",
"requestingLostPasswordFailed",
"loadingUsersAuthProvider",
"receivedUsersAuthProvider",
"fetchingUsersAuthProviderFailed",
"loadUser",
"updateUser",
"registerUser",
"loginUser",
"loginSecondFactorUser",
"load2fa",
"add2fa",
"disable2fa",
"changePassword",
"lostPassword",
"loadUsersAuthProvider"
);
}
}
View
@@ -17,26 +17,204 @@
import React from "react";
import {csrfToken} from "util";
import {t} from "t";
import {noop, TextField, Button, ButtonSet, ButtonSetButton, Form} from "form";
import logo from "images/walkhub-official-logo.jpg"
class Connect extends React.Component {
static defaultProps = {
providers: []
providers: [],
password: true,
signin: false,
signin2fa: false,
signinMail: "",
signinPassword: "",
signin2faToken: "",
signupMail: "",
signupPassword: "",
signupPasswordConfirm: "",
lostPassword: false,
lostPasswordMail: "",
signinMailChange: noop,
signinPasswordChange: noop,
signinTokenChange: noop,
signupMailChange: noop,
signupPasswordChange: noop,
signupPasswordConfirmChange: noop,
signinClick: noop,
signinSubmit: noop,
signin2faSubmit: noop,
signupSubmit: noop,
lostPasswordClick: noop,
lostPasswordSubmit: noop,
};
render() {
const providers = this.props.providers.map((provider) => {
const url = `/api/auth/${provider.id}/connect?token=${csrfToken}`;
return (
<div key={provider.id} className={"col-xs-4 col-md-offset-4 provider-"+provider.id}>
<div key={provider.id} className={"col-xs-12 col-md-4 col-md-offset-4 provider-"+provider.id}>
<a href={url} className="btn btn-primary btn-block">{t("Log in with @label", {"@label": provider.label})}</a>
</div>
);
});
const signinButton = (this.props.password && !this.props.signin) ? (
<a className="btn btn-default" onClick={this.props.signinClick}>{t("Sign in")}</a>
) : null;
let signinForm = null;
const lostPasswordButton = this.props.lostPassword ? null : (
<ButtonSetButton
onClick={this.props.lostPasswordClick}
className="btn-default"
id="signinLostPassword"
>
{t("Lost password")}
</ButtonSetButton>
);
if (this.props.password && this.props.signin) {
if (this.props.signin2fa) {
signinForm = (
<Form onSubmit={this.props.signin2faSubmit}>
<TextField
id="token"
label={t("Token")}
value={this.props.signin2faToken}
onChange={this.props.signinTokenChange}
placeholder="123456"
attributes={{autocomplete: "off"}}
/>
<ButtonSet>
<ButtonSetButton
type="submit"
className="btn-success"
onClick={() => {}}
>
{t("Continue")}
</ButtonSetButton>
</ButtonSet>
</Form>
);
} else {
signinForm = (
<form onSubmit={this.props.signinSubmit} name="signin-form" className="text-left">
<TextField
id="identifier"
type="email"
label={t("email")}
value={this.props.signinMail}
onChange={this.props.signinMailChange}
placeholder="mail@example.com"
/>
<TextField
id="password"
value={this.props.signinPassword}
onChange={this.props.signinPasswordChange}
type="password"
label={t("password")}
placeholder="********"
/>
<ButtonSet>
<ButtonSetButton
type="submit"
className="btn-default"
onClick={() => {}}
id="signinSubmit"
>
{t("Sign in")}
</ButtonSetButton>
{lostPasswordButton}
</ButtonSet>
</form>
);
}
}
const lostPasswordForm = this.props.lostPassword ? (
<form onSubmit={this.props.lostPasswordSubmit} name="lostpassword-form">
<TextField
id="email"
type="email"
label={t("email")}
value={this.props.lostPasswordMail}
onChange={this.props.lostPasswordMailChange}
placeholder="mail@example.com"
/>
<Button
type="submit"
className="btn-default"
onClick={() => {}}
id="lostpasswordSubmit"
>
{t("Request a one-time login link")}
</Button>
</form>
) : null;
const signinWrapper = this.props.password ? (
<div className="row">
<div className="col-xs-12 col-md-5 col-md-offset-7 text-right connect-signin">
{signinButton}
{signinForm}
{lostPasswordForm}
</div>
</div>
) : null;
const signupForm = (this.props.password) ? (
<form onSubmit={this.props.signupSubmit} name="signup-form">
<TextField
id="mail"
type="email"
label={t("email")}
value={this.props.signupMail}
onChange={this.props.signupMailChange}
placeholder="mail@example.com"
/>
<TextField
id="password"
type="password"
label={t("password")}
value={this.props.signupPassword}
onChange={this.props.signupPasswordChange}
placeholder="********"
/>
<TextField
id="password_confirm"
type="password"
label={t("password confirm")}
value={this.props.signupPasswordConfirm}
onChange={this.props.signupPasswordConfirmChange}
placeholder="********"
/>
<Button
type="submit"
className="btn-success"
onClick={() => {}}
id="signupSubmit"
>
{t("Sign up for free")}
</Button>
</form>
) : null;
const signupWrapper = this.props.password ? (
<div className="row">
<div className="col-xs-12 col-md-6 col-md-offset-3 connect-signup">
{signupForm}
</div>
</div>
): null;
return (
<section className="wh-connect">
{signinWrapper}
<div className="row">
<div className="col-xs-12 text-center">
<p><img src={logo} width="300" height="300" /></p>
@@ -47,6 +225,8 @@ class Connect extends React.Component {
<div className="row">
{providers}
</div>
<hr />
{signupWrapper}
</section>
);
}
Oops, something went wrong.

0 comments on commit 9a406c0

Please sign in to comment.