Update readme a bit and add SSO support
This commit is contained in:
		
							
								
								
									
										11
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								README.md
									
									
									
									
									
								
							@@ -1,11 +1,9 @@
 | 
				
			|||||||
# gomuks
 | 
					# gomuks
 | 
				
			||||||

 | 
					
 | 
				
			||||||
[](LICENSE)
 | 
					[](LICENSE)<!--
 | 
				
			||||||
[](https://github.com/tulir/gomuks/releases)
 | 
					[](https://github.com/tulir/gomuks/releases)-->
 | 
				
			||||||
[](https://travis-ci.org/tulir/gomuks)
 | 
					 | 
				
			||||||
[](https://mau.dev/tulir/gomuks/pipelines)
 | 
					[](https://mau.dev/tulir/gomuks/pipelines)
 | 
				
			||||||
[](https://codeclimate.com/github/tulir/gomuks)
 | 
					[](https://codeclimate.com/github/tulir/gomuks)
 | 
				
			||||||
[](https://codeclimate.com/github/tulir/gomuks)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -18,9 +16,8 @@ Matrix room: [#gomuks:maunium.net](https://matrix.to/#/#gomuks:maunium.net)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Installation
 | 
					## Installation
 | 
				
			||||||
Once the client becomes actually usable, I'll start making GitHub releases with
 | 
					Once the client becomes actually usable, I'll start making GitHub releases with
 | 
				
			||||||
precompiled executables. For now, you can either download
 | 
					precompiled executables. For now, you can either download a CI build from
 | 
				
			||||||
a CI build from [GitLab CI](https://mau.dev/tulir/gomuks/pipelines)
 | 
					[GitLab CI](https://mau.dev/tulir/gomuks/pipelines) or compile from source:
 | 
				
			||||||
or compile from source:
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
0. Install [Go](https://golang.org/) 1.12 or higher
 | 
					0. Install [Go](https://golang.org/) 1.12 or higher
 | 
				
			||||||
1. Clone the repo: `git clone https://github.com/tulir/gomuks.git && cd gomuks`
 | 
					1. Clone the repo: `git clone https://github.com/tulir/gomuks.git && cd gomuks`
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							@@ -19,7 +19,7 @@ require (
 | 
				
			|||||||
	golang.org/x/net v0.0.0-20200202094626-16171245cfb2
 | 
						golang.org/x/net v0.0.0-20200202094626-16171245cfb2
 | 
				
			||||||
	gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2
 | 
						gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2
 | 
				
			||||||
	gopkg.in/yaml.v2 v2.2.8
 | 
						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/mauview v0.0.0-20200218183549-88ecb1321176
 | 
				
			||||||
	maunium.net/go/tcell v1.1.2-0.20200218183045-87c4a25c5b09
 | 
						maunium.net/go/tcell v1.1.2-0.20200218183045-87c4a25c5b09
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								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=
 | 
					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 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.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 h1:KoTm7ASEzFIZ1SvPWuWYzpkeA+wiR1fuUu4l7TCHcE0=
 | 
				
			||||||
maunium.net/go/mauview v0.0.0-20200218183549-88ecb1321176/go.mod h1:jwg3Ow7akzsCX3q38pZAfmEC5gGN8gXwMyyjy/yZVMg=
 | 
					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=
 | 
					maunium.net/go/tcell v1.1.2-0.20200218183045-87c4a25c5b09 h1:hu+R+0nodoZPS19WGyYiw/d63+/NQS/R3Duw3d9HqAU=
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@ package matrix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
@@ -33,6 +34,7 @@ import (
 | 
				
			|||||||
	dbg "runtime/debug"
 | 
						dbg "runtime/debug"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"maunium.net/go/gomuks/lib/open"
 | 
				
			||||||
	"maunium.net/go/gomuks/matrix/event"
 | 
						"maunium.net/go/gomuks/matrix/event"
 | 
				
			||||||
	"maunium.net/go/mautrix"
 | 
						"maunium.net/go/mautrix"
 | 
				
			||||||
	"maunium.net/go/mautrix/format"
 | 
						"maunium.net/go/mautrix/format"
 | 
				
			||||||
@@ -131,8 +133,7 @@ func (c *Container) Initialized() bool {
 | 
				
			|||||||
	return c.client != nil
 | 
						return c.client != nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Login sends a password login request with the given username and password.
 | 
					func (c *Container) PasswordLogin(user, password string) error {
 | 
				
			||||||
func (c *Container) Login(user, password string) error {
 | 
					 | 
				
			||||||
	resp, err := c.client.Login(&mautrix.ReqLogin{
 | 
						resp, err := c.client.Login(&mautrix.ReqLogin{
 | 
				
			||||||
		Type: "m.login.password",
 | 
							Type: "m.login.password",
 | 
				
			||||||
		Identifier: mautrix.UserIdentifier{
 | 
							Identifier: mautrix.UserIdentifier{
 | 
				
			||||||
@@ -145,14 +146,95 @@ func (c *Container) Login(user, password string) error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						c.finishLogin(resp)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Container) finishLogin(resp *mautrix.RespLogin) {
 | 
				
			||||||
	c.client.SetCredentials(resp.UserID, resp.AccessToken)
 | 
						c.client.SetCredentials(resp.UserID, resp.AccessToken)
 | 
				
			||||||
	c.config.UserID = resp.UserID
 | 
						c.config.UserID = resp.UserID
 | 
				
			||||||
	c.config.AccessToken = resp.AccessToken
 | 
						c.config.AccessToken = resp.AccessToken
 | 
				
			||||||
	c.config.Save()
 | 
						c.config.Save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	go c.Start()
 | 
						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.
 | 
					// Logout revokes the access token, stops the syncer and calls the OnLogout() method of the UI.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,6 +44,8 @@ type LoginView struct {
 | 
				
			|||||||
	loginButton *mauview.Button
 | 
						loginButton *mauview.Button
 | 
				
			||||||
	quitButton  *mauview.Button
 | 
						quitButton  *mauview.Button
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loading bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	matrix ifc.MatrixContainer
 | 
						matrix ifc.MatrixContainer
 | 
				
			||||||
	config *config.Config
 | 
						config *config.Config
 | 
				
			||||||
	parent *GomuksUI
 | 
						parent *GomuksUI
 | 
				
			||||||
@@ -112,11 +114,7 @@ func (view *LoginView) Error(err string) {
 | 
				
			|||||||
	view.parent.Render()
 | 
						view.parent.Render()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (view *LoginView) Login() {
 | 
					func (view *LoginView) actuallyLogin(hs, mxid, password string) {
 | 
				
			||||||
	hs := view.homeserver.GetText()
 | 
					 | 
				
			||||||
	mxid := view.username.GetText()
 | 
					 | 
				
			||||||
	password := view.password.GetText()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	debug.Printf("Logging into %s as %s...", hs, mxid)
 | 
						debug.Printf("Logging into %s as %s...", hs, mxid)
 | 
				
			||||||
	view.config.HS = hs
 | 
						view.config.HS = hs
 | 
				
			||||||
	err := view.matrix.InitClient()
 | 
						err := view.matrix.InitClient()
 | 
				
			||||||
@@ -130,8 +128,23 @@ func (view *LoginView) Login() {
 | 
				
			|||||||
				view.Error(httpErr.Message)
 | 
									view.Error(httpErr.Message)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			view.Error("Failed to connect to server.")
 | 
								view.Error(err.Error())
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		debug.Print("Login error:", err)
 | 
							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)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user