func addHeadRepoTasks(prs []*PullRequest) { for _, pr := range prs { log.Trace("addHeadRepoTasks[%d]: composing new test task", pr.ID) if err := pr.UpdatePatch(); err != nil { log.Error(4, "UpdatePatch: %v", err) continue } else if err := pr.PushToBaseRepo(); err != nil { log.Error(4, "PushToBaseRepo: %v", err) continue } pr.AddToTaskQueue() } }
// GetRepositoryAccesses finds all repositories with their access mode where a user has access but does not own. func (u *User) GetRepositoryAccesses() (map[*Repository]AccessMode, error) { accesses := make([]*Access, 0, 10) if err := x.Find(&accesses, &Access{UserID: u.Id}); err != nil { return nil, err } repos := make(map[*Repository]AccessMode, len(accesses)) for _, access := range accesses { repo, err := GetRepositoryByID(access.RepoID) if err != nil { if IsErrRepoNotExist(err) { log.Error(4, "GetRepositoryByID: %v", err) continue } return nil, err } if err = repo.GetOwner(); err != nil { return nil, err } else if repo.OwnerID == u.Id { continue } repos[repo] = access.Mode } return repos, nil }
// saveAuthorizedKeyFile writes SSH key content to authorized_keys file. func saveAuthorizedKeyFile(keys ...*PublicKey) error { sshOpLocker.Lock() defer sshOpLocker.Unlock() fpath := filepath.Join(setting.SSH.RootPath, "authorized_keys") f, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) if err != nil { return err } defer f.Close() fi, err := f.Stat() if err != nil { return err } // FIXME: following command does not support in Windows. if !setting.IsWindows { // .ssh directory should have mode 700, and authorized_keys file should have mode 600. if fi.Mode().Perm() > 0600 { log.Error(4, "authorized_keys file has unusual permission flags: %s - setting to -rw-------", fi.Mode().Perm().String()) if err = f.Chmod(0600); err != nil { return err } } } for _, key := range keys { if _, err = f.WriteString(key.GetAuthorizedString()); err != nil { return err } } return nil }
// Listen starts a SSH server listens on given port. func Listen(port int) { config := &ssh.ServerConfig{ PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { pkey, err := models.SearchPublicKeyByContent(strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key)))) if err != nil { log.Error(3, "SearchPublicKeyByContent: %v", err) return nil, err } return &ssh.Permissions{Extensions: map[string]string{"key-id": com.ToStr(pkey.ID)}}, nil }, } keyPath := filepath.Join(setting.AppDataPath, "ssh/gogs.rsa") if !com.IsExist(keyPath) { os.MkdirAll(filepath.Dir(keyPath), os.ModePerm) _, stderr, err := com.ExecCmd("ssh-keygen", "-f", keyPath, "-t", "rsa", "-N", "") if err != nil { panic(fmt.Sprintf("Fail to generate private key: %v - %s", err, stderr)) } log.Trace("SSH: New private key is generateed: %s", keyPath) } privateBytes, err := ioutil.ReadFile(keyPath) if err != nil { panic("SSH: Fail to load private key") } private, err := ssh.ParsePrivateKey(privateBytes) if err != nil { panic("SSH: Fail to parse private key") } config.AddHostKey(private) go listen(config, port) }
// AddToTaskQueue adds itself to pull request test task queue. func (pr *PullRequest) AddToTaskQueue() { go PullRequestQueue.AddFunc(pr.ID, func() { pr.Status = PULL_REQUEST_STATUS_CHECKING if err := pr.UpdateCols("status"); err != nil { log.Error(5, "AddToTaskQueue.UpdateCols[%d].(add to queue): %v", pr.ID, err) } }) }
func (a *Action) GetIssueContent() string { index := com.StrTo(a.GetIssueInfos()[0]).MustInt64() issue, err := GetIssueByIndex(a.RepoID, index) if err != nil { log.Error(4, "GetIssueByIndex: %v", err) return "500 when get issue" } return issue.Content }
// TestPullRequests checks and tests untested patches of pull requests. // TODO: test more pull requests at same time. func TestPullRequests() { prs := make([]*PullRequest, 0, 10) x.Iterate(PullRequest{ Status: PULL_REQUEST_STATUS_CHECKING, }, func(idx int, bean interface{}) error { pr := bean.(*PullRequest) if err := pr.GetBaseRepo(); err != nil { log.Error(3, "GetBaseRepo: %v", err) return nil } if err := pr.testPatch(); err != nil { log.Error(3, "testPatch: %v", err) return nil } prs = append(prs, pr) return nil }) // Update pull request status. for _, pr := range prs { pr.checkAndUpdateStatus() } // Start listening on new test requests. for prID := range PullRequestQueue.Queue() { log.Trace("TestPullRequests[%v]: processing test task", prID) PullRequestQueue.Remove(prID) pr, err := GetPullRequestByID(com.StrTo(prID).MustInt64()) if err != nil { log.Error(4, "GetPullRequestByID[%d]: %v", prID, err) continue } else if err = pr.testPatch(); err != nil { log.Error(4, "testPatch[%d]: %v", pr.ID, err) continue } pr.checkAndUpdateStatus() } }
// AddTestPullRequestTask adds new test tasks by given head/base repository and head/base branch, // and generate new patch for testing as needed. func AddTestPullRequestTask(repoID int64, branch string) { log.Trace("AddTestPullRequestTask[head_repo_id: %d, head_branch: %s]: finding pull requests", repoID, branch) prs, err := GetUnmergedPullRequestsByHeadInfo(repoID, branch) if err != nil { log.Error(4, "Find pull requests[head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err) return } addHeadRepoTasks(prs) log.Trace("AddTestPullRequestTask[base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch) prs, err = GetUnmergedPullRequestsByBaseInfo(repoID, branch) if err != nil { log.Error(4, "Find pull requests[base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err) return } for _, pr := range prs { pr.AddToTaskQueue() } }
// checkAndUpdateStatus checks if pull request is possible to levaing checking status, // and set to be either conflict or mergeable. func (pr *PullRequest) checkAndUpdateStatus() { // Status is not changed to conflict means mergeable. if pr.Status == PULL_REQUEST_STATUS_CHECKING { pr.Status = PULL_REQUEST_STATUS_MERGEABLE } // Make sure there is no waiting test to process before levaing the checking status. if !PullRequestQueue.Exist(pr.ID) { if err := pr.UpdateCols("status"); err != nil { log.Error(4, "Update[%d]: %v", pr.ID, err) } } }
func listen(config *ssh.ServerConfig, port int) { listener, err := net.Listen("tcp", "0.0.0.0:"+com.ToStr(port)) if err != nil { panic(err) } for { // Once a ServerConfig has been configured, connections can be accepted. conn, err := listener.Accept() if err != nil { log.Error(3, "SSH: Error accepting incoming connection: %v", err) continue } // Before use, a handshake must be performed on the incoming net.Conn. // It must be handled in a separate goroutine, // otherwise one user could easily block entire loop. // For example, user could be asked to trust server key fingerprint and hangs. go func() { log.Trace("SSH: Handshaking for %s", conn.RemoteAddr()) sConn, chans, reqs, err := ssh.NewServerConn(conn, config) if err != nil { if err == io.EOF { log.Warn("SSH: Handshaking was terminated: %v", err) } else { log.Error(3, "SSH: Error on handshaking: %v", err) } return } log.Trace("SSH: Connection from %s (%s)", sConn.RemoteAddr(), sConn.ClientVersion()) // The incoming Request channel must be serviced. go ssh.DiscardRequests(reqs) go handleServerConn(sConn.Permissions.Extensions["key-id"], chans) }() } }
func processMailQueue() { sender := &Sender{} for { select { case msg := <-mailQueue: log.Trace("New e-mail sending request %s: %s", msg.GetHeader("To"), msg.Info) if err := gomail.Send(sender, msg.Message); err != nil { log.Error(4, "Fail to send e-mails %s: %s - %v", msg.GetHeader("To"), msg.Info, err) } else { log.Trace("E-mails sent %s: %s", msg.GetHeader("To"), msg.Info) } } } }
// AvatarLink tries to match user in database with e-mail // in order to show custom avatar, and falls back to general avatar link. func (push *PushCommits) AvatarLink(email string) string { _, ok := push.avatars[email] if !ok { u, err := GetUserByEmail(email) if err != nil { push.avatars[email] = base.AvatarLink(email) if !IsErrUserNotExist(err) { log.Error(4, "GetUserByEmail: %v", err) } } else { push.avatars[email] = u.AvatarLink() } } return push.avatars[email] }
func (ls *Source) findUserDN(l *ldap.Conn, name string) (string, bool) { log.Trace("Search for LDAP user: %s", name) if ls.BindDN != "" && ls.BindPassword != "" { err := l.Bind(ls.BindDN, ls.BindPassword) if err != nil { log.Debug("Failed to bind as BindDN[%s]: %v", ls.BindDN, err) return "", false } log.Trace("Bound as BindDN %s", ls.BindDN) } else { log.Trace("Proceeding with anonymous LDAP search.") } // A search for the user. userFilter, ok := ls.sanitizedUserQuery(name) if !ok { return "", false } log.Trace("Searching for DN using filter %s and base %s", userFilter, ls.UserBase) search := ldap.NewSearchRequest( ls.UserBase, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter, []string{}, nil) // Ensure we found a user sr, err := l.Search(search) if err != nil || len(sr.Entries) < 1 { log.Debug("Failed search using filter[%s]: %v", userFilter, err) return "", false } else if len(sr.Entries) > 1 { log.Debug("Filter '%s' returned more than one user.", userFilter) return "", false } userDN := sr.Entries[0].DN if userDN == "" { log.Error(4, "LDAP search was successful, but found no DN!") return "", false } return userDN, true }
// Exec starts executing a command in given path, it records its process and timeout. func ExecDir(timeout time.Duration, dir, desc, cmdName string, args ...string) (string, string, error) { if timeout == -1 { timeout = DEFAULT } bufOut := new(bytes.Buffer) bufErr := new(bytes.Buffer) cmd := exec.Command(cmdName, args...) cmd.Dir = dir cmd.Stdout = bufOut cmd.Stderr = bufErr if err := cmd.Start(); err != nil { return "", err.Error(), err } pid := Add(desc, cmd) done := make(chan error) go func() { done <- cmd.Wait() }() var err error select { case <-time.After(timeout): if errKill := Kill(pid); errKill != nil { log.Error(4, "Exec(%d:%s): %v", pid, desc, errKill) } <-done return "", ErrExecTimeout.Error(), ErrExecTimeout case err = <-done: } Remove(pid) return bufOut.String(), bufErr.String(), err }
func handleServerConn(keyID string, chans <-chan ssh.NewChannel) { for newChan := range chans { if newChan.ChannelType() != "session" { newChan.Reject(ssh.UnknownChannelType, "unknown channel type") continue } ch, reqs, err := newChan.Accept() if err != nil { log.Error(3, "Error accepting channel: %v", err) continue } go func(in <-chan *ssh.Request) { defer ch.Close() for req := range in { payload := cleanCommand(string(req.Payload)) switch req.Type { case "env": args := strings.Split(strings.Replace(payload, "\x00", "", -1), "\v") if len(args) != 2 { log.Warn("SSH: Invalid env arguments: '%#v'", args) continue } args[0] = strings.TrimLeft(args[0], "\x04") _, _, err := com.ExecCmdBytes("env", args[0]+"="+args[1]) if err != nil { log.Error(3, "env: %v", err) return } case "exec": cmdName := strings.TrimLeft(payload, "'()") os.Setenv("SSH_ORIGINAL_COMMAND", cmdName) log.Trace("SSH: Payload: %v", cmdName) args := []string{"serv", "key-" + keyID, "--config=" + setting.CustomConf} log.Trace("SSH: Arguments: %v", args) cmd := exec.Command(setting.AppPath, args...) stdout, err := cmd.StdoutPipe() if err != nil { log.Error(3, "SSH: StdoutPipe: %v", err) return } stderr, err := cmd.StderrPipe() if err != nil { log.Error(3, "SSH: StderrPipe: %v", err) return } input, err := cmd.StdinPipe() if err != nil { log.Error(3, "SSH: StdinPipe: %v", err) return } // FIXME: check timeout if err = cmd.Start(); err != nil { log.Error(3, "SSH: Start: %v", err) return } req.Reply(true, nil) go io.Copy(input, ch) io.Copy(ch, stdout) io.Copy(ch.Stderr(), stderr) if err = cmd.Wait(); err != nil { log.Error(3, "SSH: Wait: %v", err) return } ch.SendRequest("exit-status", false, []byte{0, 0, 0, 0}) return default: } } }(reqs) } }
// searchEntry : search an LDAP source if an entry (name, passwd) is valid and in the specific filter func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, string, string, string, bool, bool) { l, err := ldapDial(ls) if err != nil { log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err) ls.Enabled = false return "", "", "", "", false, false } defer l.Close() var userDN string if directBind { log.Trace("LDAP will bind directly via UserDN template: %s", ls.UserDN) var ok bool userDN, ok = ls.sanitizedUserDN(name) if !ok { return "", "", "", "", false, false } } else { log.Trace("LDAP will use BindDN.") var found bool userDN, found = ls.findUserDN(l, name) if !found { return "", "", "", "", false, false } } if directBind || !ls.AttributesInBind { // binds user (checking password) before looking-up attributes in user context err = bindUser(l, userDN, passwd) if err != nil { return "", "", "", "", false, false } } userFilter, ok := ls.sanitizedUserQuery(name) if !ok { return "", "", "", "", false, false } log.Trace("Fetching attributes '%v', '%v', '%v', '%v' with filter %s and base %s", ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, userFilter, userDN) search := ldap.NewSearchRequest( userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter, []string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail}, nil) sr, err := l.Search(search) if err != nil { log.Error(4, "LDAP Search failed unexpectedly! (%v)", err) return "", "", "", "", false, false } else if len(sr.Entries) < 1 { if directBind { log.Error(4, "User filter inhibited user login.") } else { log.Error(4, "LDAP Search failed unexpectedly! (0 entries)") } return "", "", "", "", false, false } username_attr := sr.Entries[0].GetAttributeValue(ls.AttributeUsername) name_attr := sr.Entries[0].GetAttributeValue(ls.AttributeName) sn_attr := sr.Entries[0].GetAttributeValue(ls.AttributeSurname) mail_attr := sr.Entries[0].GetAttributeValue(ls.AttributeMail) admin_attr := false if len(ls.AdminFilter) > 0 { log.Trace("Checking admin with filter %s and base %s", ls.AdminFilter, userDN) search = ldap.NewSearchRequest( userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, ls.AdminFilter, []string{ls.AttributeName}, nil) sr, err = l.Search(search) if err != nil { log.Error(4, "LDAP Admin Search failed unexpectedly! (%v)", err) } else if len(sr.Entries) < 1 { log.Error(4, "LDAP Admin Search failed") } else { admin_attr = true } } if !directBind && ls.AttributesInBind { // binds user (checking password) after looking-up attributes in BindDN context err = bindUser(l, userDN, passwd) if err != nil { return "", "", "", "", false, false } } return username_attr, name_attr, sn_attr, mail_attr, admin_attr, true }
// CommitRepoAction adds new action for committing repository. func CommitRepoAction( userID, repoUserID int64, userName, actEmail string, repoID int64, repoUserName, repoName string, refFullName string, commit *PushCommits, oldCommitID string, newCommitID string) error { u, err := GetUserByID(userID) if err != nil { return fmt.Errorf("GetUserByID: %v", err) } repo, err := GetRepositoryByName(repoUserID, repoName) if err != nil { return fmt.Errorf("GetRepositoryByName: %v", err) } else if err = repo.GetOwner(); err != nil { return fmt.Errorf("GetOwner: %v", err) } // Change repository bare status and update last updated time. repo.IsBare = false if err = UpdateRepository(repo, false); err != nil { return fmt.Errorf("UpdateRepository: %v", err) } isNewBranch := false opType := ACTION_COMMIT_REPO // Check it's tag push or branch. if strings.HasPrefix(refFullName, "refs/tags/") { opType = ACTION_PUSH_TAG commit = &PushCommits{} } else { // if not the first commit, set the compareUrl if !strings.HasPrefix(oldCommitID, "0000000") { commit.CompareUrl = repo.ComposeCompareURL(oldCommitID, newCommitID) } else { isNewBranch = true } if err = updateIssuesCommit(u, repo, repoUserName, repoName, commit.Commits); err != nil { log.Error(4, "updateIssuesCommit: %v", err) } } if len(commit.Commits) > setting.FeedMaxCommitNum { commit.Commits = commit.Commits[:setting.FeedMaxCommitNum] } bs, err := json.Marshal(commit) if err != nil { return fmt.Errorf("Marshal: %v", err) } refName := git.RefEndName(refFullName) if err = NotifyWatchers(&Action{ ActUserID: u.Id, ActUserName: userName, ActEmail: actEmail, OpType: opType, Content: string(bs), RepoID: repo.ID, RepoUserName: repoUserName, RepoName: repoName, RefName: refName, IsPrivate: repo.IsPrivate, }); err != nil { return fmt.Errorf("NotifyWatchers: %v", err) } payloadRepo := repo.ComposePayload() pusher_email, pusher_name := "", "" pusher, err := GetUserByName(userName) if err == nil { pusher_email = pusher.Email pusher_name = pusher.DisplayName() } payloadSender := &api.PayloadUser{ UserName: pusher.Name, ID: pusher.Id, AvatarUrl: pusher.AvatarLink(), } switch opType { case ACTION_COMMIT_REPO: // Push p := &api.PushPayload{ Ref: refFullName, Before: oldCommitID, After: newCommitID, CompareUrl: setting.AppUrl + commit.CompareUrl, Commits: commit.ToApiPayloadCommits(repo.FullRepoLink()), Repo: payloadRepo, Pusher: &api.PayloadAuthor{ Name: pusher_name, Email: pusher_email, UserName: userName, }, Sender: payloadSender, } if err = PrepareWebhooks(repo, HOOK_EVENT_PUSH, p); err != nil { return fmt.Errorf("PrepareWebhooks: %v", err) } if isNewBranch { return PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{ Ref: refName, RefType: "branch", Repo: payloadRepo, Sender: payloadSender, }) } case ACTION_PUSH_TAG: // Create return PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{ Ref: refName, RefType: "tag", Repo: payloadRepo, Sender: payloadSender, }) } return nil }