Adding cmd line options

This commit is contained in:
=Corey Hulen
2015-09-04 11:59:10 -07:00
parent 05d95d80a8
commit 58d0d9afd2
11 changed files with 473 additions and 97 deletions

View File

@@ -285,7 +285,7 @@ func (c *Context) HasPermissionsToChannel(sc store.StoreChannel, where string) b
}
func (c *Context) IsSystemAdmin() bool {
if strings.Contains(c.Session.Roles, model.ROLE_SYSTEM_ADMIN) && IsPrivateIpAddress(c.IpAddress) {
if model.IsInRole(c.Session.Roles, model.ROLE_SYSTEM_ADMIN) && IsPrivateIpAddress(c.IpAddress) {
return true
}
return false
@@ -297,7 +297,7 @@ func (c *Context) IsTeamAdmin(userId string) bool {
return false
} else {
user := uresult.Data.(*model.User)
return strings.Contains(c.Session.Roles, model.ROLE_ADMIN) && user.TeamId == c.Session.TeamId
return model.IsInRole(c.Session.Roles, model.ROLE_ADMIN) && user.TeamId == c.Session.TeamId
}
}

View File

@@ -716,7 +716,7 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if post.UserId != c.Session.UserId && !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) {
if post.UserId != c.Session.UserId && !model.IsInRole(c.Session.Roles, model.ROLE_ADMIN) {
c.Err = model.NewAppError("deletePost", "You do not have the appropriate permissions", "")
c.Err.StatusCode = http.StatusForbidden
return

View File

@@ -239,47 +239,55 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
}
func createTeam(c *Context, w http.ResponseWriter, r *http.Request) {
team := model.TeamFromJson(r.Body)
rteam := CreateTeam(c, team)
if c.Err != nil {
return
}
w.Write([]byte(rteam.ToJson()))
}
func CreateTeam(c *Context, team *model.Team) *model.Team {
if utils.Cfg.ServiceSettings.DisableEmailSignUp {
c.Err = model.NewAppError("createTeam", "Team sign-up with email is disabled.", "")
c.Err.StatusCode = http.StatusNotImplemented
return
return nil
}
team := model.TeamFromJson(r.Body)
if team == nil {
c.SetInvalidParam("createTeam", "team")
return
return nil
}
if !isTreamCreationAllowed(c, team.Email) {
return
return nil
}
if utils.Cfg.ServiceSettings.Mode != utils.MODE_DEV {
c.Err = model.NewAppError("createTeam", "The mode does not allow network creation without a valid invite", "")
return
c.Err = model.NewAppError("CreateTeam", "The mode does not allow network creation without a valid invite", "")
return nil
}
if result := <-Srv.Store.Team().Save(team); result.Err != nil {
c.Err = result.Err
return
return nil
} else {
rteam := result.Data.(*model.Team)
if _, err := CreateDefaultChannels(c, rteam.Id); err != nil {
c.Err = err
return
return nil
}
if rteam.AllowValet {
CreateValet(c, rteam)
if c.Err != nil {
return
return nil
}
}
w.Write([]byte(rteam.ToJson()))
return rteam
}
}
@@ -467,7 +475,7 @@ func InviteMembers(c *Context, team *model.Team, user *model.User, invites []str
sender := user.GetDisplayName()
senderRole := ""
if strings.Contains(user.Roles, model.ROLE_ADMIN) || strings.Contains(user.Roles, model.ROLE_SYSTEM_ADMIN) {
if model.IsInRole(user.Roles, model.ROLE_ADMIN) || model.IsInRole(user.Roles, model.ROLE_SYSTEM_ADMIN) {
senderRole = "administrator"
} else {
senderRole = "member"
@@ -526,7 +534,7 @@ func updateTeamDisplayName(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) {
if !model.IsInRole(c.Session.Roles, model.ROLE_ADMIN) {
c.Err = model.NewAppError("updateTeamDisplayName", "You do not have the appropriate permissions", "userId="+c.Session.UserId)
c.Err.StatusCode = http.StatusForbidden
return
@@ -566,7 +574,7 @@ func updateValetFeature(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) {
if !model.IsInRole(c.Session.Roles, model.ROLE_ADMIN) {
c.Err = model.NewAppError("updateValetFeature", "You do not have the appropriate permissions", "userId="+c.Session.UserId)
c.Err.StatusCode = http.StatusForbidden
return

View File

@@ -925,7 +925,16 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) {
}
new_roles := props["new_roles"]
// no check since we allow the clearing of Roles
if model.IsValidRoles(new_roles) {
c.SetInvalidParam("updateRoles", "new_roles")
return
}
if model.IsInRole(new_roles, model.ROLE_SYSTEM_ADMIN) {
c.Err = model.NewAppError("updateRoles", "The system_admin role can only be set from the command line", "")
c.Err.StatusCode = http.StatusForbidden
return
}
var user *model.User
if result := <-Srv.Store.User().Get(user_id); result.Err != nil {
@@ -939,43 +948,15 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) && !c.IsSystemAdmin() {
if !model.IsInRole(c.Session.Roles, model.ROLE_ADMIN) && !c.IsSystemAdmin() {
c.Err = model.NewAppError("updateRoles", "You do not have the appropriate permissions", "userId="+user_id)
c.Err.StatusCode = http.StatusForbidden
return
}
// make sure there is at least 1 other active admin
if strings.Contains(user.Roles, model.ROLE_ADMIN) && !strings.Contains(new_roles, model.ROLE_ADMIN) {
if result := <-Srv.Store.User().GetProfiles(user.TeamId); result.Err != nil {
c.Err = result.Err
return
} else {
activeAdmins := -1
profileUsers := result.Data.(map[string]*model.User)
for _, profileUser := range profileUsers {
if profileUser.DeleteAt == 0 && strings.Contains(profileUser.Roles, model.ROLE_ADMIN) {
activeAdmins = activeAdmins + 1
}
}
if activeAdmins <= 0 {
c.Err = model.NewAppError("updateRoles", "There must be at least one active admin", "userId="+user_id)
return
}
}
}
user.Roles = new_roles
var ruser *model.User
if result := <-Srv.Store.User().Update(user, true); result.Err != nil {
c.Err = result.Err
ruser := UpdateRoles(c, user, new_roles)
if c.Err != nil {
return
} else {
c.LogAuditWithUserId(user.Id, "roles="+new_roles)
ruser = result.Data.([2]*model.User)[0]
}
uchan := Srv.Store.Session().UpdateRoles(user.Id, new_roles)
@@ -1002,6 +983,42 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(ruser.ToJson()))
}
func UpdateRoles(c *Context, user *model.User, roles string) *model.User {
// make sure there is at least 1 other active admin
if model.IsInRole(user.Roles, model.ROLE_ADMIN) && !model.IsInRole(roles, model.ROLE_ADMIN) {
if result := <-Srv.Store.User().GetProfiles(user.TeamId); result.Err != nil {
c.Err = result.Err
return nil
} else {
activeAdmins := -1
profileUsers := result.Data.(map[string]*model.User)
for _, profileUser := range profileUsers {
if profileUser.DeleteAt == 0 && model.IsInRole(profileUser.Roles, model.ROLE_ADMIN) {
activeAdmins = activeAdmins + 1
}
}
if activeAdmins <= 0 {
c.Err = model.NewAppError("updateRoles", "There must be at least one active admin", "")
return nil
}
}
}
user.Roles = roles
var ruser *model.User
if result := <-Srv.Store.User().Update(user, true); result.Err != nil {
c.Err = result.Err
return nil
} else {
c.LogAuditWithUserId(user.Id, "roles="+roles)
ruser = result.Data.([2]*model.User)[0]
}
return ruser
}
func updateActive(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body)
@@ -1025,14 +1042,14 @@ func updateActive(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) && !c.IsSystemAdmin() {
if !model.IsInRole(c.Session.Roles, model.ROLE_ADMIN) && !c.IsSystemAdmin() {
c.Err = model.NewAppError("updateActive", "You do not have the appropriate permissions", "userId="+user_id)
c.Err.StatusCode = http.StatusForbidden
return
}
// make sure there is at least 1 other active admin
if !active && strings.Contains(user.Roles, model.ROLE_ADMIN) {
if !active && model.IsInRole(user.Roles, model.ROLE_ADMIN) {
if result := <-Srv.Store.User().GetProfiles(user.TeamId); result.Err != nil {
c.Err = result.Err
return
@@ -1040,7 +1057,7 @@ func updateActive(c *Context, w http.ResponseWriter, r *http.Request) {
activeAdmins := -1
profileUsers := result.Data.(map[string]*model.User)
for _, profileUser := range profileUsers {
if profileUser.DeleteAt == 0 && strings.Contains(profileUser.Roles, model.ROLE_ADMIN) {
if profileUser.DeleteAt == 0 && model.IsInRole(profileUser.Roles, model.ROLE_ADMIN) {
activeAdmins = activeAdmins + 1
}
}

View File

@@ -684,6 +684,7 @@ func TestUserUpdateRoles(t *testing.T) {
data["user_id"] = user2.Id
if result, err := Client.UpdateUserRoles(data); err != nil {
t.Log(data["new_roles"])
t.Fatal(err)
} else {
if result.Data.(*model.User).Roles != "admin" {

View File

@@ -6,46 +6,338 @@ package main
import (
"flag"
"fmt"
"github.com/mattermost/platform/api"
"github.com/mattermost/platform/manualtesting"
"github.com/mattermost/platform/utils"
"github.com/mattermost/platform/web"
"os"
"os/signal"
"strings"
"syscall"
"time"
l4g "code.google.com/p/log4go"
"github.com/mattermost/platform/api"
"github.com/mattermost/platform/manualtesting"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
"github.com/mattermost/platform/web"
)
var flagCmdCreateTeam bool
var flagCmdCreateUser bool
var flagCmdAssignRole bool
var flagCmdResetPassword bool
var flagConfigFile string
var flagEmail string
var flagPassword string
var flagTeamName string
var flagRole string
var flagRunCmds bool
func main() {
pwd, _ := os.Getwd()
fmt.Println("Current working directory is set to " + pwd)
parseCmds()
var config = flag.String("config", "config.json", "path to config file")
var action = flag.String("action", "none", "path to config file")
flag.Parse()
utils.LoadConfig(flagConfigFile)
fmt.Println(action)
if len(action) > 0 {
return
if flagRunCmds {
utils.ConfigureCmdLineLog()
}
utils.LoadConfig(*config)
pwd, _ := os.Getwd()
l4g.Info("Current working directory is %v", pwd)
l4g.Info("Loaded config file from %v", utils.FindConfigFile(flagConfigFile))
api.NewServer()
api.InitApi()
web.InitWeb()
api.StartServer()
// If we allow testing then listen for manual testing URL hits
if utils.Cfg.ServiceSettings.AllowTesting {
manualtesting.InitManualTesting()
if flagRunCmds {
runCmds()
} else {
api.StartServer()
// If we allow testing then listen for manual testing URL hits
if utils.Cfg.ServiceSettings.AllowTesting {
manualtesting.InitManualTesting()
}
// wait for kill signal before attempting to gracefully shutdown
// the running service
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
<-c
api.StopServer()
}
}
func parseCmds() {
flag.Usage = func() {
fmt.Fprintln(os.Stderr, usage)
}
// wait for kill signal before attempting to gracefully shutdown
// the running service
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
<-c
flag.StringVar(&flagConfigFile, "config", "config.json", "")
flag.StringVar(&flagEmail, "email", "", "")
flag.StringVar(&flagPassword, "password", "", "")
flag.StringVar(&flagTeamName, "team_name", "", "")
flag.StringVar(&flagRole, "role", "", "")
api.StopServer()
flag.BoolVar(&flagCmdCreateTeam, "create_team", false, "")
flag.BoolVar(&flagCmdCreateUser, "create_user", false, "")
flag.BoolVar(&flagCmdAssignRole, "assign_role", false, "")
flag.BoolVar(&flagCmdResetPassword, "reset_password", false, "")
flag.Parse()
flagRunCmds = flagCmdCreateTeam || flagCmdCreateUser || flagCmdAssignRole || flagCmdResetPassword
}
func runCmds() {
cmdCreateTeam()
cmdCreateUser()
cmdAssignRole()
cmdResetPassword()
}
func cmdCreateTeam() {
if flagCmdCreateTeam {
if len(flagTeamName) == 0 {
fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
flag.Usage()
os.Exit(1)
}
if len(flagEmail) == 0 {
fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
flag.Usage()
os.Exit(1)
}
c := &api.Context{}
c.RequestId = model.NewId()
c.IpAddress = "cmd_line"
team := &model.Team{}
team.DisplayName = flagTeamName
team.Name = flagTeamName
team.Email = flagEmail
team.Type = model.TEAM_INVITE
api.CreateTeam(c, team)
if c.Err != nil {
if c.Err.Message != "A team with that domain already exists" {
l4g.Error("%v", c.Err)
flushLogAndExit(1)
}
}
os.Exit(0)
}
}
func cmdCreateUser() {
if flagCmdCreateUser {
if len(flagTeamName) == 0 {
fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
flag.Usage()
os.Exit(1)
}
if len(flagEmail) == 0 {
fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
flag.Usage()
os.Exit(1)
}
if len(flagPassword) == 0 {
fmt.Fprintln(os.Stderr, "flag needs an argument: -password")
flag.Usage()
os.Exit(1)
}
c := &api.Context{}
c.RequestId = model.NewId()
c.IpAddress = "cmd_line"
var team *model.Team
user := &model.User{}
user.Email = flagEmail
user.Password = flagPassword
splits := strings.Split(strings.Replace(flagEmail, "@", " ", -1), " ")
user.Username = splits[0]
if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil {
l4g.Error("%v", result.Err)
flushLogAndExit(1)
} else {
team = result.Data.(*model.Team)
user.TeamId = team.Id
}
api.CreateUser(c, team, user)
if c.Err != nil {
if c.Err.Message != "An account with that email already exists." {
l4g.Error("%v", c.Err)
flushLogAndExit(1)
}
}
os.Exit(0)
}
}
func cmdAssignRole() {
if flagCmdAssignRole {
if len(flagTeamName) == 0 {
fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
flag.Usage()
os.Exit(1)
}
if len(flagEmail) == 0 {
fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
flag.Usage()
os.Exit(1)
}
if !model.IsValidRoles(flagRole) {
fmt.Fprintln(os.Stderr, "flag invalid argument: -role")
flag.Usage()
os.Exit(1)
}
c := &api.Context{}
c.RequestId = model.NewId()
c.IpAddress = "cmd_line"
var team *model.Team
if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil {
l4g.Error("%v", result.Err)
flushLogAndExit(1)
} else {
team = result.Data.(*model.Team)
}
var user *model.User
if result := <-api.Srv.Store.User().GetByEmail(team.Id, flagEmail); result.Err != nil {
l4g.Error("%v", result.Err)
flushLogAndExit(1)
} else {
user = result.Data.(*model.User)
}
if !user.IsInRole(flagRole) {
if flagRole == model.ROLE_SYSTEM_ADMIN && team.Name != "admin" {
l4g.Error("system_admin can only be added to a user in the admin team")
flushLogAndExit(1)
}
api.UpdateRoles(c, user, flagRole)
}
os.Exit(0)
}
}
func cmdResetPassword() {
if flagCmdResetPassword {
if len(flagTeamName) == 0 {
fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
flag.Usage()
os.Exit(1)
}
if len(flagEmail) == 0 {
fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
flag.Usage()
os.Exit(1)
}
if len(flagPassword) == 0 {
fmt.Fprintln(os.Stderr, "flag needs an argument: -password")
flag.Usage()
os.Exit(1)
}
c := &api.Context{}
c.RequestId = model.NewId()
c.IpAddress = "cmd_line"
var team *model.Team
if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil {
l4g.Error("%v", result.Err)
flushLogAndExit(1)
} else {
team = result.Data.(*model.Team)
}
var user *model.User
if result := <-api.Srv.Store.User().GetByEmail(team.Id, flagEmail); result.Err != nil {
l4g.Error("%v", result.Err)
flushLogAndExit(1)
} else {
user = result.Data.(*model.User)
}
if result := <-api.Srv.Store.User().UpdatePassword(user.Id, model.HashPassword(flagPassword)); result.Err != nil {
l4g.Error("%v", result.Err)
flushLogAndExit(1)
}
os.Exit(0)
}
}
func flushLogAndExit(code int) {
l4g.Close()
time.Sleep(time.Second)
os.Exit(code)
}
var usage = `Mattermost commands to help configure the system
Usage:
platform [options]
-config="config.json" Path to the config file
-email="user@example.com" Email address used in other commands
-password="mypassword" Password used in other commands
-team_name="name" The team name used in other commands
-role="admin" The role used in other commands
valid values are
"" - The empty role is basic user
permissions
"admin" - Represents a team admin and
is used to help adminsiter one team.
"system_admin" - Represents a system
admin who has access to all teams
and configuration settings. This
role can only be created on the
team named "admin"
-create_team Creates a team. It requres the -team_name
and -email flag to create a team.
Example:
platform -create_team -team_name="name" -email="user@example.com"
-create_user Creates a user. It requres the -team_name,
-email and -password flag to create a user.
Example:
platform -create_user -team_name="name" -email="user@example.com" -password="mypassword"
-assign_role Assigns role to a user. It requres the -team_name,
-email and -role flag. If you're assigning the
"system_admin" role it must be for a user on the
team_name="admin"
Example:
platform -assign_role -team_name="name" -email="user@example.com" -role="admin"
-reset_password Resets the password for a user. It requres the
-team_name, -email and -password flag.
Example:
platform -reset_password -team_name="name" -email="user@example.com" -paossword="newpassword"
`

View File

@@ -15,7 +15,6 @@ import (
const (
ROLE_ADMIN = "admin"
ROLE_SYSTEM_ADMIN = "system_admin"
ROLE_SYSTEM_SUPPORT = "system_support"
USER_AWAY_TIMEOUT = 5 * 60 * 1000 // 5 minutes
USER_OFFLINE_TIMEOUT = 1 * 60 * 1000 // 1 minute
USER_OFFLINE = "offline"
@@ -272,6 +271,52 @@ func (u *User) GetDisplayName() string {
}
}
func IsValidRoles(userRoles string) bool {
roles := strings.Split(userRoles, " ")
for _, r := range roles {
if !isValidRole(r) {
return false
}
}
return true
}
func isValidRole(role string) bool {
if role == "" {
return true
}
if role == ROLE_ADMIN {
return true
}
if role == ROLE_SYSTEM_ADMIN {
return true
}
return false
}
func (u *User) IsInRole(inRole string) bool {
return IsInRole(u.Roles, inRole)
}
func IsInRole(userRoles string, inRole string) bool {
roles := strings.Split(userRoles, " ")
for _, r := range roles {
if r == inRole {
return true
}
}
return false
}
// UserFromJson will decode the input and return a User
func UserFromJson(data io.Reader) *User {
decoder := json.NewDecoder(data)

View File

@@ -192,3 +192,13 @@ func TestCleanUsername(t *testing.T) {
t.Fatal("didn't clean name properly")
}
}
func TestRoles(t *testing.T) {
if !IsValidRoles("admin") {
t.Fatal()
}
//IsInRole
}

View File

@@ -35,6 +35,10 @@ func (s SqlTeamStore) CreateIndexesIfNotExists() {
}
func (s SqlTeamStore) Save(team *model.Team) StoreChannel {
return s.SaveWithValidate(team, true)
}
func (s SqlTeamStore) SaveWithValidate(team *model.Team, validate bool) StoreChannel {
storeChannel := make(StoreChannel)
go func() {
@@ -49,10 +53,13 @@ func (s SqlTeamStore) Save(team *model.Team) StoreChannel {
}
team.PreSave()
if result.Err = team.IsValid(); result.Err != nil {
storeChannel <- result
close(storeChannel)
return
if validate {
if result.Err = team.IsValid(); result.Err != nil {
storeChannel <- result
close(storeChannel)
return
}
}
if err := s.GetMaster().Insert(team); err != nil {

View File

@@ -39,6 +39,7 @@ type Store interface {
type TeamStore interface {
Save(team *model.Team) StoreChannel
SaveWithValidate(team *model.Team, validate bool) StoreChannel
Update(team *model.Team) StoreChannel
UpdateDisplayName(name string, teamId string) StoreChannel
Get(id string) StoreChannel

View File

@@ -6,7 +6,6 @@ package utils
import (
l4g "code.google.com/p/log4go"
"encoding/json"
"net/mail"
"os"
"path/filepath"
)
@@ -149,7 +148,7 @@ func (o *Config) ToJson() string {
var Cfg *Config = &Config{}
var SanitizeOptions map[string]bool = map[string]bool{}
func findConfigFile(fileName string) string {
func FindConfigFile(fileName string) string {
if _, err := os.Stat("/tmp/" + fileName); err == nil {
fileName, _ = filepath.Abs("/tmp/" + fileName)
} else if _, err := os.Stat("./config/" + fileName); err == nil {
@@ -176,6 +175,14 @@ func FindDir(dir string) string {
return fileName + "/"
}
func ConfigureCmdLineLog() {
ls := LogSettings{}
ls.ConsoleEnable = true
ls.ConsoleLevel = "ERROR"
ls.FileEnable = false
configureLog(ls)
}
func configureLog(s LogSettings) {
l4g.Close()
@@ -220,8 +227,7 @@ func configureLog(s LogSettings) {
// then ../config/fileName and last it will look at fileName
func LoadConfig(fileName string) {
fileName = findConfigFile(fileName)
l4g.Info("Loading config file at " + fileName)
fileName = FindConfigFile(fileName)
file, err := os.Open(fileName)
if err != nil {
@@ -232,24 +238,13 @@ func LoadConfig(fileName string) {
config := Config{}
err = decoder.Decode(&config)
if err != nil {
panic("Error decoding configuration " + err.Error())
}
// Check for a valid email for feedback, if not then do feedback@domain
if _, err := mail.ParseAddress(config.EmailSettings.FeedbackEmail); err != nil {
l4g.Error("Misconfigured feedback email setting: %s", config.EmailSettings.FeedbackEmail)
config.EmailSettings.FeedbackEmail = "feedback@localhost"
panic("Error decoding config file=" + fileName + ", err=" + err.Error())
}
configureLog(config.LogSettings)
Cfg = &config
SanitizeOptions = getSanitizeOptions()
// Validates our mail settings
if err := CheckMailSettings(); err != nil {
l4g.Error("Email settings are not valid err=%v", err)
}
}
func getSanitizeOptions() map[string]bool {