1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package engine
5
6import (
7	"encoding/hex"
8	"fmt"
9
10	"github.com/keybase/client/go/libkb"
11	keybase1 "github.com/keybase/client/go/protocol/keybase1"
12)
13
14type SignupJoinEngine struct {
15	uv       keybase1.UserVersion
16	session  string
17	csrf     string
18	username libkb.NormalizedUsername
19	ppGen    libkb.PassphraseGeneration
20	libkb.Contextified
21}
22
23func NewSignupJoinEngine(g *libkb.GlobalContext) *SignupJoinEngine {
24	return &SignupJoinEngine{Contextified: libkb.NewContextified(g)}
25}
26
27func (s *SignupJoinEngine) Init() error {
28	return nil
29}
30
31func (s *SignupJoinEngine) CheckRegistered() (err error) {
32	s.G().Log.Debug("+ libkb.SignupJoinEngine::CheckRegistered")
33	if cr := s.G().Env.GetConfig(); cr == nil {
34		err = fmt.Errorf("No configuration file available")
35	} else if u := cr.GetUID(); u.Exists() {
36		err = libkb.AlreadyRegisteredError{UID: u}
37	}
38	s.G().Log.Debug("- libkb.SignupJoinEngine::CheckRegistered -> %s", libkb.ErrToOk(err))
39	return err
40}
41
42type SignupJoinEngineRunArg struct {
43	Username    string
44	Email       string
45	InviteCode  string
46	PWHash      []byte
47	PWSalt      []byte
48	RandomPW    bool
49	PDPKA5KID   keybase1.KID
50	SkipMail    bool
51	VerifyEmail bool
52	BotToken    keybase1.BotToken
53}
54
55func (s *SignupJoinEngine) Post(m libkb.MetaContext, arg SignupJoinEngineRunArg) (err error) {
56	var res *libkb.APIRes
57	var ppGenTmp int
58	postArgs := libkb.HTTPArgs{
59		"salt":          libkb.S{Val: hex.EncodeToString(arg.PWSalt)},
60		"pwh":           libkb.S{Val: hex.EncodeToString(arg.PWHash)},
61		"random_pw":     libkb.B{Val: arg.RandomPW},
62		"username":      libkb.S{Val: arg.Username},
63		"invitation_id": libkb.S{Val: arg.InviteCode},
64		"pwh_version":   libkb.I{Val: int(libkb.ClientTriplesecVersion)},
65		"skip_mail":     libkb.B{Val: arg.SkipMail},
66		"pdpka5_kid":    libkb.S{Val: arg.PDPKA5KID.String()},
67		"platform":      libkb.S{Val: libkb.GetPlatformString()},
68		"verify_email":  libkb.B{Val: arg.VerifyEmail},
69	}
70	if len(arg.Email) > 0 {
71		postArgs["email"] = libkb.S{Val: arg.Email}
72	} else {
73		postArgs["no_email"] = libkb.B{Val: true}
74	}
75	if arg.BotToken.Exists() {
76		postArgs["bot_token"] = libkb.S{Val: arg.BotToken.String()}
77	}
78	res, err = m.G().API.Post(m, libkb.APIArg{
79		Endpoint: "signup",
80		Args:     postArgs,
81	})
82	if err == nil {
83		s.username = libkb.NewNormalizedUsername(arg.Username)
84		libkb.GetUIDVoid(res.Body.AtKey("uid"), &s.uv.Uid, &err)
85		res.Body.AtKey("session").GetStringVoid(&s.session, &err)
86		res.Body.AtKey("csrf_token").GetStringVoid(&s.csrf, &err)
87		res.Body.AtPath("me.basics.passphrase_generation").GetIntVoid(&ppGenTmp, &err)
88	}
89	if err == nil {
90		err = libkb.CheckUIDAgainstUsername(s.uv.Uid, arg.Username)
91		s.ppGen = libkb.PassphraseGeneration(ppGenTmp)
92	}
93	return err
94}
95
96type SignupJoinEngineRunRes struct {
97	PassphraseOk bool
98	PostOk       bool
99	WriteOk      bool
100	UV           keybase1.UserVersion
101	User         *libkb.User
102	Err          error
103	PpGen        libkb.PassphraseGeneration
104}
105
106func (r SignupJoinEngineRunRes) Error() string {
107	return r.Err.Error()
108}
109
110func (s *SignupJoinEngine) Run(m libkb.MetaContext, arg SignupJoinEngineRunArg) (res SignupJoinEngineRunRes) {
111	res.PassphraseOk = true
112
113	if res.Err = s.Post(m, arg); res.Err != nil {
114		return
115	}
116	res.PostOk = true
117	if res.Err = s.WriteOut(m, arg); res.Err != nil {
118		return
119	}
120	res.WriteOk = true
121	res.UV = s.uv
122	res.PpGen = s.ppGen
123	return
124}
125
126func (s *SignupJoinEngine) WriteOut(m libkb.MetaContext, arg SignupJoinEngineRunArg) error {
127	lctx := m.LoginContext()
128	if err := lctx.CreateLoginSessionWithSalt(s.username.String(), arg.PWSalt); err != nil {
129		return err
130	}
131	var nilDeviceID keybase1.DeviceID
132	if err := lctx.SaveState(s.session, s.csrf, s.username, s.uv, nilDeviceID); err != nil {
133		return err
134	}
135	if arg.BotToken.Exists() {
136		m.Debug("SignupJoinEngine#WriteOut: not saving config since in bot mode")
137		return nil
138	}
139	// Switching to a new user is an operation on the GlobalContext, and will atomically
140	// update the config file and alter the current ActiveDevice. So farm out to over there.
141	return m.SwitchUserNewConfig(s.uv.Uid, s.username, arg.PWSalt, nilDeviceID)
142}
143
144func (s *SignupJoinEngine) PostInviteRequest(m libkb.MetaContext, arg libkb.InviteRequestArg) error {
145	return libkb.PostInviteRequest(m, arg)
146}
147