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
Original file line number Diff line number Diff line change
Expand Up @@ -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))

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
* `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

Expand Down
7 changes: 7 additions & 0 deletions cmd/walkhub/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down Expand Up @@ -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"),
Expand Down
8 changes: 8 additions & 0 deletions config.json.sample.full
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,16 @@
"contentpages": "",
"frontpagecomponent": "",
"menuitems": "",
"pwauth": true,
"google": {
"id": "",
"secret": ""
},
"smtp": {
"addr": "",
"identity": "",
"username": "",
"password": "",
"host": ""
}
}
6 changes: 1 addition & 5 deletions config.json.sample.minimal
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,5 @@
"db": "",
"secret": "",
"cookiesecret": "",
"baseurl": "http://localhost:8080/",
"google": {
"id": "",
"secret": ""
}
"baseurl": "http://localhost:8080/"
}
2 changes: 1 addition & 1 deletion js/actions/auth_provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class AuthProviderActions {
"receivedAuthProvider",
"fetchingAuthProviderFailed",

"loadAuthProvider"
"loadAuthProvider",
);
}
}
Expand Down
18 changes: 9 additions & 9 deletions js/components/user.js → js/actions/message.js
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;
52 changes: 51 additions & 1 deletion js/actions/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"
);
}
}
Expand Down
184 changes: 182 additions & 2 deletions js/components/connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -47,6 +225,8 @@ class Connect extends React.Component {
<div className="row">
{providers}
</div>
<hr />
{signupWrapper}
</section>
);
}
Expand Down
Loading

0 comments on commit 9a406c0

Please sign in to comment.