func bindUser(l *ldap.Conn, userDN, passwd string) error { log.Trace("Binding with userDN: %s", userDN) err := l.Bind(userDN, passwd) if err != nil { log.Debug("LDAP auth. failed for %s, reason: %v", userDN, err) return err } log.Trace("Bound successfully with userDN: %s", userDN) return err }
// testPatch checks if patch can be merged to base repository without conflit. // FIXME: make a mechanism to clean up stable local copies. func (pr *PullRequest) testPatch() (err error) { if pr.BaseRepo == nil { pr.BaseRepo, err = GetRepositoryByID(pr.BaseRepoID) if err != nil { return fmt.Errorf("GetRepositoryByID: %v", err) } } patchPath, err := pr.BaseRepo.PatchPath(pr.Index) if err != nil { return fmt.Errorf("BaseRepo.PatchPath: %v", err) } // Fast fail if patch does not exist, this assumes data is cruppted. if !com.IsFile(patchPath) { log.Trace("PullRequest[%d].testPatch: ignored cruppted data", pr.ID) return nil } log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath) if err := pr.BaseRepo.UpdateLocalCopy(); err != nil { return fmt.Errorf("UpdateLocalCopy: %v", err) } // Checkout base branch. _, stderr, err := process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(), fmt.Sprintf("PullRequest.Merge (git checkout): %v", pr.BaseRepo.ID), "git", "checkout", pr.BaseBranch) if err != nil { return fmt.Errorf("git checkout: %s", stderr) } pr.Status = PULL_REQUEST_STATUS_CHECKING _, stderr, err = process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(), fmt.Sprintf("testPatch (git apply --check): %d", pr.BaseRepo.ID), "git", "apply", "--check", patchPath) if err != nil { for i := range patchConflicts { if strings.Contains(stderr, patchConflicts[i]) { log.Trace("PullRequest[%d].testPatch (apply): has conflit", pr.ID) fmt.Println(stderr) pr.Status = PULL_REQUEST_STATUS_CONFLICT return nil } } return fmt.Errorf("git apply --check: %v - %s", err, stderr) } return nil }
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) } } } }
// PushToBaseRepo pushes commits from branches of head repository to // corresponding branches of base repository. // FIXME: Only push branches that are actually updates? func (pr *PullRequest) PushToBaseRepo() (err error) { log.Trace("PushToBaseRepo[%d]: pushing commits to base repo 'refs/pull/%d/head'", pr.BaseRepoID, pr.Index) headRepoPath := pr.HeadRepo.RepoPath() headGitRepo, err := git.OpenRepository(headRepoPath) if err != nil { return fmt.Errorf("OpenRepository: %v", err) } tmpRemoteName := fmt.Sprintf("tmp-pull-%d", pr.ID) if err = headGitRepo.AddRemote(tmpRemoteName, pr.BaseRepo.RepoPath(), false); err != nil { return fmt.Errorf("headGitRepo.AddRemote: %v", err) } // Make sure to remove the remote even if the push fails defer headGitRepo.RemoveRemote(tmpRemoteName) headFile := fmt.Sprintf("refs/pull/%d/head", pr.Index) // Remove head in case there is a conflict. os.Remove(path.Join(pr.BaseRepo.RepoPath(), headFile)) if err = git.Push(headRepoPath, tmpRemoteName, fmt.Sprintf("%s:%s", pr.HeadBranch, headFile)); err != nil { return fmt.Errorf("Push: %v", err) } return nil }
// AddPublicKey adds new public key to database and authorized_keys file. func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) { log.Trace(content) if err := checkKeyContent(content); err != nil { return nil, err } // Key name of same user cannot be duplicated. has, err := x.Where("owner_id=? AND name=?", ownerID, name).Get(new(PublicKey)) if err != nil { return nil, err } else if has { return nil, ErrKeyNameAlreadyUsed{ownerID, name} } sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { return nil, err } key := &PublicKey{ OwnerID: ownerID, Name: name, Content: content, Mode: ACCESS_MODE_WRITE, Type: KEY_TYPE_USER, } if err = addKey(sess, key); err != nil { return nil, fmt.Errorf("addKey: %v", err) } return key, sess.Commit() }
func transferRepoAction(e Engine, actUser, oldOwner, newOwner *User, repo *Repository) (err error) { if err = notifyWatchers(e, &Action{ ActUserID: actUser.Id, ActUserName: actUser.Name, ActEmail: actUser.Email, OpType: ACTION_TRANSFER_REPO, RepoID: repo.ID, RepoUserName: newOwner.Name, RepoName: repo.Name, IsPrivate: repo.IsPrivate, Content: path.Join(oldOwner.LowerName, repo.LowerName), }); err != nil { return fmt.Errorf("notify watchers '%d/%d': %v", actUser.Id, repo.ID, err) } // Remove watch for organization. if repo.Owner.IsOrganization() { if err = watchRepo(e, repo.Owner.Id, repo.ID, false); err != nil { return fmt.Errorf("watch repository: %v", err) } } log.Trace("action.transferRepoAction: %s/%s", actUser.Name, repo.Name) 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) }
// Cell2Int64 converts a xorm.Cell type to int64, // and handles possible irregular cases. func Cell2Int64(val xorm.Cell) int64 { switch (*val).(type) { case []uint8: log.Trace("Cell2Int64 ([]uint8): %v", *val) return com.StrTo(string((*val).([]uint8))).MustInt64() } return (*val).(int64) }
// 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() } }
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 }
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() } }
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 newRepoAction(e Engine, u *User, repo *Repository) (err error) { if err = notifyWatchers(e, &Action{ ActUserID: u.Id, ActUserName: u.Name, ActEmail: u.Email, OpType: ACTION_CREATE_REPO, RepoID: repo.ID, RepoUserName: repo.Owner.Name, RepoName: repo.Name, IsPrivate: repo.IsPrivate, }); err != nil { return fmt.Errorf("notify watchers '%d/%d': %v", u.Id, repo.ID, err) } log.Trace("action.newRepoAction: %s/%s", u.Name, repo.Name) return err }
func renameRepoAction(e Engine, actUser *User, oldRepoName string, repo *Repository) (err error) { if err = notifyWatchers(e, &Action{ ActUserID: actUser.Id, ActUserName: actUser.Name, ActEmail: actUser.Email, OpType: ACTION_RENAME_REPO, RepoID: repo.ID, RepoUserName: repo.Owner.Name, RepoName: repo.Name, IsPrivate: repo.IsPrivate, Content: oldRepoName, }); err != nil { return fmt.Errorf("notify watchers: %v", err) } log.Trace("action.renameRepoAction: %s/%s", actUser.Name, repo.Name) return nil }
// UpdatePatch generates and saves a new patch. func (pr *PullRequest) UpdatePatch() (err error) { if err = pr.GetHeadRepo(); err != nil { return fmt.Errorf("GetHeadRepo: %v", err) } else if pr.HeadRepo == nil { log.Trace("PullRequest[%d].UpdatePatch: ignored cruppted data", pr.ID) return nil } if err = pr.GetBaseRepo(); err != nil { return fmt.Errorf("GetBaseRepo: %v", err) } headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) if err != nil { return fmt.Errorf("OpenRepository: %v", err) } // Add a temporary remote. tmpRemote := com.ToStr(time.Now().UnixNano()) if err = headGitRepo.AddRemote(tmpRemote, RepoPath(pr.BaseRepo.MustOwner().Name, pr.BaseRepo.Name), true); err != nil { return fmt.Errorf("AddRemote: %v", err) } defer func() { headGitRepo.RemoveRemote(tmpRemote) }() remoteBranch := "remotes/" + tmpRemote + "/" + pr.BaseBranch pr.MergeBase, err = headGitRepo.GetMergeBase(remoteBranch, pr.HeadBranch) if err != nil { return fmt.Errorf("GetMergeBase: %v", err) } else if err = pr.Update(); err != nil { return fmt.Errorf("Update: %v", err) } patch, err := headGitRepo.GetPatch(pr.MergeBase, pr.HeadBranch) if err != nil { return fmt.Errorf("GetPatch: %v", err) } if err = pr.BaseRepo.SavePatch(pr.Index, patch); err != nil { return fmt.Errorf("BaseRepo.SavePatch: %v", err) } return nil }
// 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() } }
// CheckPublicKeyString checks if the given public key string is recognized by SSH. // // The function returns the actual public key line on success. func CheckPublicKeyString(content string) (_ string, err error) { if setting.SSH.Disabled { return "", errors.New("SSH is disabled") } content, err = parseKeyString(content) if err != nil { return "", err } content = strings.TrimRight(content, "\n\r") if strings.ContainsAny(content, "\n\r") { return "", errors.New("only a single line with a single key please") } // remove any unnecessary whitespace now content = strings.TrimSpace(content) var ( keyType string length int ) if setting.SSH.StartBuiltinServer { keyType, length, err = SSHNativeParsePublicKey(content) } else { keyType, length, err = SSHKeyGenParsePublicKey(content) } if err != nil { return "", fmt.Errorf("ParsePublicKey: %v", err) } log.Trace("Key info [native: %v]: %s-%d", setting.SSH.StartBuiltinServer, keyType, length) if !setting.SSH.MinimumKeySizeCheck { return content, nil } if minLen, found := setting.SSH.MinimumKeySizes[keyType]; found && length >= minLen { return content, nil } else if found && length < minLen { return "", fmt.Errorf("Key length is not enough: got %d, needs %d", length, minLen) } return "", fmt.Errorf("Key type is not allowed: %s", keyType) }
// AutoSignIn reads cookie and try to auto-login. func AutoSignIn(ctx *Context) (bool, error) { if !models.HasEngine { return false, nil } uname := ctx.GetCookie(setting.CookieUserName) if len(uname) == 0 { return false, nil } isSucceed := false defer func() { if !isSucceed { log.Trace("auto-login cookie cleared: %s", uname) ctx.SetCookie(setting.CookieUserName, "", -1, setting.AppSubUrl) ctx.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubUrl) } }() u, err := models.GetUserByName(uname) if err != nil { if !models.IsErrUserNotExist(err) { return false, fmt.Errorf("GetUserByName: %v", err) } return false, nil } if val, _ := ctx.GetSuperSecureCookie( base.EncodeMD5(u.Rands+u.Passwd), setting.CookieRememberName); val != u.Name { return false, nil } isSucceed = true ctx.Session.Set("uid", u.Id) ctx.Session.Set("uname", u.Name) return true, nil }
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) } }
func Toggle(options *ToggleOptions) macaron.Handler { return func(ctx *Context) { // Cannot view any page before installation. if !setting.InstallLock { ctx.Redirect(setting.AppSubUrl + "/install") return } // Checking non-logged users landing page. if !ctx.IsSigned && ctx.Req.RequestURI == "/" && setting.LandingPageUrl != setting.LANDING_PAGE_HOME { ctx.Redirect(setting.AppSubUrl + string(setting.LandingPageUrl)) return } // Redirect to dashboard if user tries to visit any non-login page. if options.SignOutRequire && ctx.IsSigned && ctx.Req.RequestURI != "/" { ctx.Redirect(setting.AppSubUrl + "/") return } if !options.SignOutRequire && !options.DisableCsrf && ctx.Req.Method == "POST" && !auth.IsAPIPath(ctx.Req.URL.Path) { csrf.Validate(ctx.Context, ctx.csrf) if ctx.Written() { return } } if options.SignInRequire { if !ctx.IsSigned { // Restrict API calls with error message. if auth.IsAPIPath(ctx.Req.URL.Path) { ctx.APIError(403, "", "Only signed in user is allowed to call APIs.") return } ctx.SetCookie("redirect_to", url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl) ctx.Redirect(setting.AppSubUrl + "/user/login") return } else if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { ctx.Data["Title"] = ctx.Tr("auth.active_your_account") ctx.HTML(200, "user/auth/activate") return } } // Try auto-signin when not signed in. if !options.SignOutRequire && !ctx.IsSigned && !auth.IsAPIPath(ctx.Req.URL.Path) { succeed, err := AutoSignIn(ctx) if err != nil { ctx.Handle(500, "AutoSignIn", err) return } else if succeed { log.Trace("Auto-login succeed: %s", ctx.Session.Get("uname")) ctx.Redirect(setting.AppSubUrl + ctx.Req.RequestURI) return } } if options.AdminRequire { if !ctx.User.IsAdmin { ctx.Error(403) return } ctx.Data["PageIsAdmin"] = true } } }
// 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 }