aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md11
-rw-r--r--go.mod2
-rw-r--r--go.sum1
-rw-r--r--matrix/matrix.go88
-rw-r--r--ui/view-login.go25
5 files changed, 110 insertions, 17 deletions
diff --git a/README.md b/README.md
index 9bad54b..0163523 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,9 @@
# gomuks
![Languages](https://img.shields.io/github/languages/top/tulir/gomuks.svg)
-[![License](https://img.shields.io/github/license/tulir/gomuks.svg)](LICENSE)
-[![Release](https://img.shields.io/github/release/tulir/gomuks/all.svg)](https://github.com/tulir/gomuks/releases)
-[![Build Status](https://travis-ci.org/tulir/gomuks.svg?branch=master)](https://travis-ci.org/tulir/gomuks)
+[![License](https://img.shields.io/github/license/tulir/gomuks.svg)](LICENSE)<!--
+[![Release](https://img.shields.io/github/release/tulir/gomuks/all.svg)](https://github.com/tulir/gomuks/releases)-->
[![GitLab CI](https://mau.dev/tulir/gomuks/badges/master/pipeline.svg)](https://mau.dev/tulir/gomuks/pipelines)
[![Maintainability](https://img.shields.io/codeclimate/maintainability/tulir/gomuks.svg)](https://codeclimate.com/github/tulir/gomuks)
-[![Coverage](https://img.shields.io/codeclimate/coverage/tulir/gomuks.svg)](https://codeclimate.com/github/tulir/gomuks)
![Chat Preview](chat-preview.png)
@@ -18,9 +16,8 @@ Matrix room: [#gomuks:maunium.net](https://matrix.to/#/#gomuks:maunium.net)
## Installation
Once the client becomes actually usable, I'll start making GitHub releases with
-precompiled executables. For now, you can either download
-a CI build from [GitLab CI](https://mau.dev/tulir/gomuks/pipelines)
-or compile from source:
+precompiled executables. For now, you can either download a CI build from
+[GitLab CI](https://mau.dev/tulir/gomuks/pipelines) or compile from source:
0. Install [Go](https://golang.org/) 1.12 or higher
1. Clone the repo: `git clone https://github.com/tulir/gomuks.git && cd gomuks`
diff --git a/go.mod b/go.mod
index 1fb4d6d..da9478b 100644
--- a/go.mod
+++ b/go.mod
@@ -19,7 +19,7 @@ require (
golang.org/x/net v0.0.0-20200202094626-16171245cfb2
gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2
gopkg.in/yaml.v2 v2.2.8
- maunium.net/go/mautrix v0.1.0-alpha.3.0.20200218183645-fea33ed88d03
+ maunium.net/go/mautrix v0.1.0-alpha.3.0.20200218191514-cb8e637f1c62
maunium.net/go/mauview v0.0.0-20200218183549-88ecb1321176
maunium.net/go/tcell v1.1.2-0.20200218183045-87c4a25c5b09
)
diff --git a/go.sum b/go.sum
index 2f1e223..4a235af 100644
--- a/go.sum
+++ b/go.sum
@@ -71,6 +71,7 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
maunium.net/go/mautrix v0.1.0-alpha.3.0.20200218183645-fea33ed88d03 h1:0fvOe9KeB/JAkMAzJTmj6mg1P9xGPAgFhJcCSxNe1Rk=
maunium.net/go/mautrix v0.1.0-alpha.3.0.20200218183645-fea33ed88d03/go.mod h1:g10T1fh2Q2HkJWycVs93eBXdWpqD67f1YVQhNxdIDr4=
+maunium.net/go/mautrix v0.1.0-alpha.3.0.20200218191514-cb8e637f1c62/go.mod h1:g10T1fh2Q2HkJWycVs93eBXdWpqD67f1YVQhNxdIDr4=
maunium.net/go/mauview v0.0.0-20200218183549-88ecb1321176 h1:KoTm7ASEzFIZ1SvPWuWYzpkeA+wiR1fuUu4l7TCHcE0=
maunium.net/go/mauview v0.0.0-20200218183549-88ecb1321176/go.mod h1:jwg3Ow7akzsCX3q38pZAfmEC5gGN8gXwMyyjy/yZVMg=
maunium.net/go/tcell v1.1.2-0.20200218183045-87c4a25c5b09 h1:hu+R+0nodoZPS19WGyYiw/d63+/NQS/R3Duw3d9HqAU=
diff --git a/matrix/matrix.go b/matrix/matrix.go
index ffd4d0c..ed50f95 100644
--- a/matrix/matrix.go
+++ b/matrix/matrix.go
@@ -18,6 +18,7 @@ package matrix
import (
"bytes"
+ "context"
"crypto/tls"
"encoding/json"
"fmt"
@@ -33,6 +34,7 @@ import (
dbg "runtime/debug"
"time"
+ "maunium.net/go/gomuks/lib/open"
"maunium.net/go/gomuks/matrix/event"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/format"
@@ -131,8 +133,7 @@ func (c *Container) Initialized() bool {
return c.client != nil
}
-// Login sends a password login request with the given username and password.
-func (c *Container) Login(user, password string) error {
+func (c *Container) PasswordLogin(user, password string) error {
resp, err := c.client.Login(&mautrix.ReqLogin{
Type: "m.login.password",
Identifier: mautrix.UserIdentifier{
@@ -145,14 +146,95 @@ func (c *Container) Login(user, password string) error {
if err != nil {
return err
}
+ c.finishLogin(resp)
+ return nil
+}
+
+func (c *Container) finishLogin(resp *mautrix.RespLogin) {
c.client.SetCredentials(resp.UserID, resp.AccessToken)
c.config.UserID = resp.UserID
c.config.AccessToken = resp.AccessToken
c.config.Save()
go c.Start()
+}
- return nil
+func respondHTML(w http.ResponseWriter, status int, message string) {
+ w.Header().Add("Content-Type", "text/html")
+ w.WriteHeader(status)
+ _, _ = w.Write([]byte(fmt.Sprintf(`<!DOCTYPE html>
+<html>
+<head>
+ <title>gomuks single-sign on</title>
+ <meta charset="utf-8"/>
+</head>
+<body>
+ <center>
+ <h2>%s</h2>
+ </center>
+</body>
+</html>`, message)))
+}
+
+func (c *Container) SingleSignOn() error {
+ loginURL := c.client.BuildURLWithQuery([]string{"login", "sso", "redirect"}, map[string]string{
+ "redirectUrl": "http://localhost:29325",
+ })
+ err := open.Open(loginURL)
+ if err != nil {
+ return err
+ }
+ errChan := make(chan error, 1)
+ server := &http.Server{Addr: ":29325"}
+ server.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ loginToken := r.URL.Query().Get("loginToken")
+ if len(loginToken) == 0 {
+ respondHTML(w, http.StatusBadRequest, "Missing loginToken parameter")
+ return
+ }
+ resp, err := c.client.Login(&mautrix.ReqLogin{
+ Type: "m.login.token",
+ Token: loginToken,
+ InitialDeviceDisplayName: "gomuks",
+ })
+ if err != nil {
+ respondHTML(w, http.StatusForbidden, err.Error())
+ errChan <- err
+ return
+ }
+ respondHTML(w, http.StatusOK, fmt.Sprintf("Successfully logged in as %s", resp.UserID))
+ c.finishLogin(resp)
+ go func() {
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ err = server.Shutdown(ctx)
+ if err != nil {
+ debug.Printf("Failed to shut down SSO server: %v\n", err)
+ }
+ errChan <- err
+ }()
+ })
+ err = server.ListenAndServe()
+ if err != nil {
+ return err
+ }
+ err = <- errChan
+ return err
+}
+
+// Login sends a password login request with the given username and password.
+func (c *Container) Login(user, password string) error {
+ resp, err := c.client.GetLoginFlows()
+ if err != nil {
+ return err
+ }
+ if len(resp.Flows) == 1 && resp.Flows[0].Type == "m.login.password" {
+ return c.PasswordLogin(user, password)
+ } else if len(resp.Flows) == 2 && resp.Flows[0].Type == "m.login.sso" && resp.Flows[1].Type == "m.login.token" {
+ return c.SingleSignOn()
+ } else {
+ return fmt.Errorf("no supported login flows")
+ }
}
// Logout revokes the access token, stops the syncer and calls the OnLogout() method of the UI.
diff --git a/ui/view-login.go b/ui/view-login.go
index a65a77c..023aaad 100644
--- a/ui/view-login.go
+++ b/ui/view-login.go
@@ -44,6 +44,8 @@ type LoginView struct {
loginButton *mauview.Button
quitButton *mauview.Button
+ loading bool
+
matrix ifc.MatrixContainer
config *config.Config
parent *GomuksUI
@@ -112,11 +114,7 @@ func (view *LoginView) Error(err string) {
view.parent.Render()
}
-func (view *LoginView) Login() {
- hs := view.homeserver.GetText()
- mxid := view.username.GetText()
- password := view.password.GetText()
-
+func (view *LoginView) actuallyLogin(hs, mxid, password string) {
debug.Printf("Logging into %s as %s...", hs, mxid)
view.config.HS = hs
err := view.matrix.InitClient()
@@ -130,8 +128,23 @@ func (view *LoginView) Login() {
view.Error(httpErr.Message)
}
} else {
- view.Error("Failed to connect to server.")
+ view.Error(err.Error())
}
debug.Print("Login error:", err)
}
+ view.loading = false
+ view.loginButton.SetText("Login")
+}
+
+func (view *LoginView) Login() {
+ if view.loading {
+ return
+ }
+ hs := view.homeserver.GetText()
+ mxid := view.username.GetText()
+ password := view.password.GetText()
+
+ view.loading = true
+ view.loginButton.SetText("Logging in...")
+ go view.actuallyLogin(hs, mxid, password)
}