Skip to content
This repository has been archived by the owner on Aug 24, 2020. It is now read-only.

Commit

Permalink
Adds password authentication and user profile support.
Browse files Browse the repository at this point in the history
  • Loading branch information
tamasd committed Apr 22, 2016
1 parent 31bc633 commit 9a406c0
Show file tree
Hide file tree
Showing 26 changed files with 1,452 additions and 105 deletions.
12 changes: 9 additions & 3 deletions README.md
Expand Up @@ -10,7 +10,7 @@ Check the [landing page](http://pronovix.com/walkhub) for more information.
## Dependencies ## Dependencies


* Go 1.5+ * Go 1.5+
* PostgreSQL 9.4+ (older versions might work too) * PostgreSQL 9.5
* NPM * NPM
* Node.js 4.x ([5.x does not work](https://github.com/Pronovix/walkhub-service/issues/12)) * Node.js 4.x ([5.x does not work](https://github.com/Pronovix/walkhub-service/issues/12))


Expand All @@ -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 * `secret`: 32 bytes long random byte sequence, encoded with hex encoding
* `cookiesecret`: 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. * `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 ### Optional values


Expand Down
7 changes: 7 additions & 0 deletions cmd/walkhub/main.go
Expand Up @@ -54,6 +54,7 @@ func main() {
cfg.Set("gzip", false) cfg.Set("gzip", false)
cfg.Set("CookiePrefix", "WALKHUB") cfg.Set("CookiePrefix", "WALKHUB")
cfg.RegisterAlias("db", "PGConnectString") cfg.RegisterAlias("db", "PGConnectString")
cfg.SetDefault("pwauth", true)


level := log.LOG_USER level := log.LOG_USER
if cfg.GetBool("trace") { if cfg.GetBool("trace") {
Expand Down Expand Up @@ -89,10 +90,16 @@ func main() {
s.HTTPOrigin = httpOrigin s.HTTPOrigin = httpOrigin
s.RedirectAll = cfg.GetBool("redirectall") s.RedirectAll = cfg.GetBool("redirectall")
s.EnforceDomains = cfg.GetBool("enforcedomains") s.EnforceDomains = cfg.GetBool("enforcedomains")
s.PWAuth = cfg.GetBool("pwauth")
if cp := cfg.GetString("contentpages"); cp != "" { if cp := cfg.GetString("contentpages"); cp != "" {
s.CustomPaths = loadContentPages(cp) s.CustomPaths = loadContentPages(cp)
whlogger.Verbose().Println("custom paths", s.CustomPaths) 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{ s.AuthCreds.Google = auth.OAuthCredentials{
ID: cfg.GetString("google.id"), ID: cfg.GetString("google.id"),
Secret: cfg.GetString("google.secret"), Secret: cfg.GetString("google.secret"),
Expand Down
8 changes: 8 additions & 0 deletions config.json.sample.full
Expand Up @@ -13,8 +13,16 @@
"contentpages": "", "contentpages": "",
"frontpagecomponent": "", "frontpagecomponent": "",
"menuitems": "", "menuitems": "",
"pwauth": true,
"google": { "google": {
"id": "", "id": "",
"secret": "" "secret": ""
},
"smtp": {
"addr": "",
"identity": "",
"username": "",
"password": "",
"host": ""
} }
} }
6 changes: 1 addition & 5 deletions config.json.sample.minimal
Expand Up @@ -2,9 +2,5 @@
"db": "", "db": "",
"secret": "", "secret": "",
"cookiesecret": "", "cookiesecret": "",
"baseurl": "http://localhost:8080/", "baseurl": "http://localhost:8080/"
"google": {
"id": "",
"secret": ""
}
} }
2 changes: 1 addition & 1 deletion js/actions/auth_provider.js
Expand Up @@ -25,7 +25,7 @@ class AuthProviderActions {
"receivedAuthProvider", "receivedAuthProvider",
"fetchingAuthProviderFailed", "fetchingAuthProviderFailed",


"loadAuthProvider" "loadAuthProvider",
); );
} }
} }
Expand Down
18 changes: 9 additions & 9 deletions js/components/user.js → js/actions/message.js
@@ -1,5 +1,5 @@
// Walkhub // Walkhub
// Copyright (C) 2015 Pronovix // Copyright (C) 2016 Pronovix
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as // it under the terms of the GNU Affero General Public License as
Expand All @@ -14,16 +14,16 @@
// You should have received a copy of the GNU Affero General Public License // 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/>. // 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 { @createActions(flux)

class MessageActions {
render() { constructor() {
return ( this.generateActions(
<h1> User </h1> "flashMessage"
); );
} }

} }


export default User; export default MessageActions;
52 changes: 51 additions & 1 deletion js/actions/user.js
Expand Up @@ -25,7 +25,57 @@ class UserActions {
"receivedUser", "receivedUser",
"fetchingUserFailed", "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"
); );
} }
} }
Expand Down
184 changes: 182 additions & 2 deletions js/components/connect.js
Expand Up @@ -17,26 +17,204 @@
import React from "react"; import React from "react";
import {csrfToken} from "util"; import {csrfToken} from "util";
import {t} from "t"; import {t} from "t";
import {noop, TextField, Button, ButtonSet, ButtonSetButton, Form} from "form";


import logo from "images/walkhub-official-logo.jpg" import logo from "images/walkhub-official-logo.jpg"


class Connect extends React.Component { class Connect extends React.Component {


static defaultProps = { 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() { render() {
const providers = this.props.providers.map((provider) => { const providers = this.props.providers.map((provider) => {
const url = `/api/auth/${provider.id}/connect?token=${csrfToken}`; const url = `/api/auth/${provider.id}/connect?token=${csrfToken}`;
return ( 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> <a href={url} className="btn btn-primary btn-block">{t("Log in with @label", {"@label": provider.label})}</a>
</div> </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 ( return (
<section className="wh-connect"> <section className="wh-connect">
{signinWrapper}
<div className="row"> <div className="row">
<div className="col-xs-12 text-center"> <div className="col-xs-12 text-center">
<p><img src={logo} width="300" height="300" /></p> <p><img src={logo} width="300" height="300" /></p>
Expand All @@ -47,6 +225,8 @@ class Connect extends React.Component {
<div className="row"> <div className="row">
{providers} {providers}
</div> </div>
<hr />
{signupWrapper}
</section> </section>
); );
} }
Expand Down

0 comments on commit 9a406c0

Please sign in to comment.