<commit_before>package setup

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/user"
	"path"
	"strconv"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/paralin/skiff-core/config"
)

// UserSetup sets up a container.
type UserSetup struct {
	config *config.ConfigUser
	waiter ContainerWaiter
	create bool

	wg  sync.WaitGroup
	err error
}

// NewUserSetup creates a new UserSetup.
func NewUserSetup(config *config.ConfigUser, waiter ContainerWaiter, createUsers bool) *UserSetup {
	return &UserSetup{config: config, waiter: waiter, create: createUsers}
}

// Execute starts the user setup.
func (cs *UserSetup) Execute() (execError error) {
	cs.wg.Add(1)
	defer func() {
		cs.err = execError
		cs.wg.Done()
	}()

	// check if we are root
	if os.Geteuid() != 0 {
		return fmt.Errorf("Not running as root, cannot setup user %s", cs.config.Name())
	}

	conf := cs.config
	if conf.Container == "" {
		return fmt.Errorf("User %s must have container specified.", conf.Name())
	}

	conf.Container = ensureSlashPrefix(conf.Container)
	if !cs.waiter.CheckHasContainer(conf.Container) {
		return fmt.Errorf("User %s: no such container: %s", conf.Name(), conf.Container)
	}

	euser, eusererr := user.Lookup(conf.Name())
	if eusererr != nil {
		if _, ok := eusererr.(user.UnknownUserError); !ok {
			return eusererr
		}
	}

	le := log.WithField("user", conf.Name())
	shellPath, err := pathToSkiffCore()
	if err != nil {
		return err
	}
	if euser == nil {
		if !cs.create {
			return fmt.Errorf("User %s: not found, and create-users is not enabled.", conf.Name())
		}

		// attempt to create the user
		le.Debug("Creating user")
		err = execCmd(
			"adduser",
			"-G",
			"docker",
			"-D",
			//"-c",
			// fmt.Sprintf("Skiff-Core user %s", cs.config.Name()),
			// "-m",
			"-s",
			shellPath,
			cs.config.Name(),
		)
		if err != nil {
			return err
		}
		euser, err = user.Lookup(cs.config.Name())
		if err != nil {
			return err
		}
	} else {
		// Set the shell for the user
		le.WithField("path", shellPath).Debug("Setting shell")
		if err := execCmd("chsh", "-s", shellPath, cs.config.Name()); err != nil {
			return err
		}

		// Add to the Docker group
		/*
			le.WithField("path", shellPath).Debug("Adding to Docker group")
			if err := execCmd("chsh", "-s", shellPath, cs.config.Name()); err != nil {
				return err
			}
		*/
	}

	// Set password
	if cs.config.Auth == nil || cs.config.Auth.Password == "" {
		le.Debug("Disabling password login")
		if err := execCmd("passwd", "-d", cs.config.Name()); err != nil {
			return err
		}
	} else {
		le.Debug("Setting password")
		passwd := strings.Replace(cs.config.Auth.Password, "\n", "", -1)
		passwordSet := strings.NewReader(fmt.Sprintf("%s\<PASSWORD>", passwd, passwd))
		cmd := exec.Command("passwd", cs.config.Name())
		cmd.Stdin = passwordSet
		if err := cmd.Run(); err != nil {
			return err
		}
	}

	le.Debug("Setting up SSH keys")
	sshDir := path.Join(euser.HomeDir, ".ssh")
	authorizedKeysPath := path.Join(sshDir, "authorized_keys")
	if _, err := os.Stat(sshDir); os.IsNotExist(err) {
		if err := os.Mkdir(sshDir, 0700); err != nil {
			return err
		}
	}
	if err := os.Chmod(sshDir, 0700); err != nil {
		return err
	}
	uid, err := strconv.Atoi(euser.Uid)
	if err != nil {
		return err
	}
	gid, err := strconv.Atoi(euser.Gid)
	if err != nil {
		return err
	}
	if err := os.Chown(sshDir, uid, gid); err != nil {
		return err
	}

	f, err := os.OpenFile(authorizedKeysPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)<commit_msg>fix: create home dir if not exist<commit_after>package setup

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/user"
	"path"
	"strconv"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/paralin/skiff-core/config"
)

// UserSetup sets up a container.
type UserSetup struct {
	config *config.ConfigUser
	waiter ContainerWaiter
	create bool

	wg  sync.WaitGroup
	err error
}

// NewUserSetup creates a new UserSetup.
func NewUserSetup(config *config.ConfigUser, waiter ContainerWaiter, createUsers bool) *UserSetup {
	return &UserSetup{config: config, waiter: waiter, create: createUsers}
}

// Execute starts the user setup.
func (cs *UserSetup) Execute() (execError error) {
	cs.wg.Add(1)
	defer func() {
		cs.err = execError
		cs.wg.Done()
	}()

	// check if we are root
	if os.Geteuid() != 0 {
		return fmt.Errorf("Not running as root, cannot setup user %s", cs.config.Name())
	}

	conf := cs.config
	if conf.Container == "" {
		return fmt.Errorf("User %s must have container specified.", conf.Name())
	}

	conf.Container = ensureSlashPrefix(conf.Container)
	if !cs.waiter.CheckHasContainer(conf.Container) {
		return fmt.Errorf("User %s: no such container: %s", conf.Name(), conf.Container)
	}

	euser, eusererr := user.Lookup(conf.Name())
	if eusererr != nil {
		if _, ok := eusererr.(user.UnknownUserError); !ok {
			return eusererr
		}
	}

	le := log.WithField("user", conf.Name())
	shellPath, err := pathToSkiffCore()
	if err != nil {
		return err
	}
	if euser == nil {
		if !cs.create {
			return fmt.Errorf("User %s: not found, and create-users is not enabled.", conf.Name())
		}

		// attempt to create the user
		le.Debug("Creating user")
		err = execCmd(
			"adduser",
			"-G",
			"docker",
			"-D",
			//"-c",
			// fmt.Sprintf("Skiff-Core user %s", cs.config.Name()),
			// "-m",
			"-s",
			shellPath,
			cs.config.Name(),
		)
		if err != nil {
			return err
		}
		euser, err = user.Lookup(cs.config.Name())
		if err != nil {
			return err
		}
	} else {
		// Set the shell for the user
		le.WithField("path", shellPath).Debug("Setting shell")
		if err := execCmd("chsh", "-s", shellPath, cs.config.Name()); err != nil {
			return err
		}

		// Add to the Docker group
		/*
			le.WithField("path", shellPath).Debug("Adding to Docker group")
			if err := execCmd("chsh", "-s", shellPath, cs.config.Name()); err != nil {
				return err
			}
		*/
	}

	// Set password
	if cs.config.Auth == nil || cs.config.Auth.Password == "" {
		le.Debug("Disabling password login")
		if err := execCmd("passwd", "-d", cs.config.Name()); err != nil {
			return err
		}
	} else {
		le.Debug("Setting password")
		passwd := strings.Replace(cs.config.Auth.Password, "\n", "", -1)
		passwordSet := strings.NewReader(fmt.Sprintf("%s\<PASSWORD>", passwd, passwd))
		cmd := exec.Command("passwd", cs.config.Name())
		cmd.Stdin = passwordSet
		if err := cmd.Run(); err != nil {
			return err
		}
	}

	le.Debug("Setting up SSH keys")
	sshDir := path.Join(euser.HomeDir, ".ssh")
	authorizedKeysPath := path.Join(sshDir, "authorized_keys")
	if _, err := os.Stat(sshDir); os.IsNotExist(err) {
		if err := os.MkdirAll(sshDir, 0700); err != nil {
			return err
		}
	}
	if err := os.Chmod(sshDir, 0700); err != nil {
		return err
	}
	uid, err := strconv.Atoi(euser.Uid)
	if err != nil {
		return err
	}
	gid, err := strconv.Atoi(euser.Gid)
	if err != nil {
		return err
	}
	if err := os.Chown(sshDir, uid, gid); err != nil {
		return err
	}

	f, err := os.OpenFile(authorizedKeysPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
{'fraction_non_alphanumeric': 0.16840153999714816, 'fraction_numerical': 0.004277769855981748, 'mean_word_length': 3.300429184549356, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1635918', 'n_tokens_mistral': 2841, 'n_tokens_neox': 2577, 'n_words': 663}