185 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package main
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"os/exec"
 | |
| 
 | |
| 	"git.tilde.town/tildetown/town/invites"
 | |
| 	"git.tilde.town/tildetown/town/stats"
 | |
| 	"github.com/charmbracelet/lipgloss"
 | |
| 
 | |
| 	_ "embed"
 | |
| )
 | |
| 
 | |
| // TODO move magic key machine to static page
 | |
| // TODO link to code of conduct as part of the form and ask if they agree
 | |
| 
 | |
| //go:embed welcome.txt
 | |
| var welcomeArt string
 | |
| 
 | |
| type newUserData struct {
 | |
| 	Username    string
 | |
| 	DisplayName string
 | |
| 	Email       string
 | |
| 	PubKey      string
 | |
| }
 | |
| 
 | |
| func defaultStyle() lipgloss.Style {
 | |
| 	return lipgloss.NewStyle().
 | |
| 		Foreground(lipgloss.AdaptiveColor{
 | |
| 			Light: "#7D19BD",
 | |
| 			Dark:  "#E0B0FF",
 | |
| 		})
 | |
| }
 | |
| 
 | |
| func _main() error {
 | |
| 	inviteDB, err := invites.ConnectDB()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	s := defaultStyle().SetString(welcomeArt)
 | |
| 	fmt.Println(s)
 | |
| 
 | |
| 	code, err := promptCode()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	invite, err := invites.Get(inviteDB, code)
 | |
| 	if err != nil {
 | |
| 		return fmt.Errorf("could not look up invite code: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	if invite.Used {
 | |
| 		return errors.New("that invite code has already been used.")
 | |
| 	}
 | |
| 
 | |
| 	s = s.SetString("thanks!! just gotta collect some information now and then your account will be ready.")
 | |
| 
 | |
| 	fmt.Println(s)
 | |
| 
 | |
| 	townData, err := stats.Stats()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	data := &newUserData{
 | |
| 		Email: invite.Email,
 | |
| 	}
 | |
| 
 | |
| 	a := &asker{
 | |
| 		UserData: data,
 | |
| 		Invite:   *invite,
 | |
| 		TownData: townData,
 | |
| 		Style:    defaultStyle(),
 | |
| 	}
 | |
| 
 | |
| 	if err = a.Ask(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	conf, err := confirmContinue()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if !conf {
 | |
| 		for !conf {
 | |
| 			if err = a.Ask(); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			if conf, err = confirmContinue(); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	s = s.SetString("cool, awesome, going to make your account now...")
 | |
| 	fmt.Println(s)
 | |
| 
 | |
| 	if err = createUser(*data); err != nil {
 | |
| 		s = s.SetString(fmt.Sprintf(`augh, I'm sorry. the account creation failed.
 | |
| Please email root@tilde.town and paste the following error:
 | |
| 
 | |
| %s
 | |
| 
 | |
| Your invite code has not been marked as used and you're welcome to try again,
 | |
| though if there is a system issue you might need to wait for word from an admin.`, err.Error()))
 | |
| 		fmt.Println(s)
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	s = s.SetString(fmt.Sprintf(`OK! your user account has been created.
 | |
| 
 | |
| welcome, ~%[1]s <3
 | |
| 
 | |
| This program is going to exit and you are now free to ssh to town as yourself:
 | |
| 
 | |
|   ssh %[1]s@tilde.town
 | |
| 
 | |
| if your public key isn't found by ssh, you'll need to explain to ssh how to find it with:
 | |
| 
 | |
|   ssh -i "replace with path to public key file" %[1]s@tilde.town
 | |
| 
 | |
| for help with ssh, see: https://tilde.town/wiki/getting-started/ssh.html
 | |
| 
 | |
| if you end up very stuck, you can email root@tilde.town .`, data.Username))
 | |
| 
 | |
| 	fmt.Println(s)
 | |
| 
 | |
| 	// TODO mark invite as used
 | |
| 	// TODO add user to town.db
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // TODO add logging like the signup tool has
 | |
| 
 | |
| func createUser(data newUserData) (err error) {
 | |
| 	cmd := exec.Command("sudo", "/usr/sbin/adduser", "--quiet", "--disabled-password", data.Username)
 | |
| 	if err = cmd.Run(); err != nil {
 | |
| 		return fmt.Errorf("adduser failed: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	cmd = exec.Command("sudo", "/usr/sbin/usermod", "-a", "-G", "town", data.Username)
 | |
| 	if err = cmd.Run(); err != nil {
 | |
| 		return fmt.Errorf("usermod failed: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	cmd = exec.Command("sudo", "--user", data.Username, "/town/bin/createkeyfile", data.Username)
 | |
| 	cmd.Stdin = bytes.NewBufferString(keyfileText(data))
 | |
| 	if err = cmd.Run(); err != nil {
 | |
| 		return fmt.Errorf("createkeyfile failed: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	cmd = exec.Command("sudo", "/town/bin/generate_welcome_present.sh", data.Username)
 | |
| 	if err = cmd.Run(); err != nil {
 | |
| 		// TODO log this. no reason to bail out.
 | |
| 	}
 | |
| 
 | |
| 	// TODO any alerts
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func keyfileText(data newUserData) string {
 | |
| 	header := `########## GREETINGS! ##########
 | |
| # Hi! This file was automatically generated by tilde.town when
 | |
| # your account was created. You can edit it if you want, but we
 | |
| # recommend adding stuff to ~/.ssh/authorized_keys instead.`
 | |
| 
 | |
| 	return fmt.Sprintf("%s\n%s", header, data.PubKey)
 | |
| }
 | |
| 
 | |
| func main() {
 | |
| 	// TODO friendlier error handling
 | |
| 	err := _main()
 | |
| 	if err != nil {
 | |
| 		fmt.Fprintln(os.Stderr, err)
 | |
| 		os.Exit(1)
 | |
| 	}
 | |
| }
 |