func getEnv(w http.ResponseWriter, r *http.Request, t auth.Token) error { var variables []string if r.Body != nil { defer r.Body.Close() err := json.NewDecoder(r.Body).Decode(&variables) if err != nil && err != io.EOF { return err } } appName := r.URL.Query().Get(":app") var u *auth.User var err error a, err := getAppFromContext(appName, r) if err != nil { return err } if !t.IsAppToken() { u, err = t.User() if err != nil { return err } rec.Log(u.Email, "get-env", "app="+appName, fmt.Sprintf("envs=%s", variables)) allowed := permission.Check(t, permission.PermAppReadEnv, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )..., ) if !allowed { return permission.ErrUnauthorized } } return writeEnvVars(w, &a, variables...) }
func listContainersHandler(w http.ResponseWriter, r *http.Request, t auth.Token) error { address := r.URL.Query().Get(":address") if address != "" { node, err := mainDockerProvisioner.Cluster().GetNode(address) if err != nil { return err } hasAccess := permission.Check(t, permission.PermNodeRead, permission.Context(permission.CtxPool, node.Metadata["pool"])) if !hasAccess { return permission.ErrUnauthorized } containerList, err := mainDockerProvisioner.listContainersByHost(address) if err != nil { return err } return json.NewEncoder(w).Encode(containerList) } appName := r.URL.Query().Get(":appname") a, err := app.GetByName(appName) if err != nil { return err } hasAccess := permission.Check(t, permission.PermNodeRead, permission.Context(permission.CtxPool, a.Pool)) if !hasAccess { return permission.ErrUnauthorized } containerList, err := mainDockerProvisioner.listContainersByApp(appName) if err != nil { return err } return json.NewEncoder(w).Encode(containerList) }
func appDelete(w http.ResponseWriter, r *http.Request, t auth.Token) error { u, err := t.User() if err != nil { return err } a, err := getAppFromContext(r.URL.Query().Get(":app"), r) if err != nil { return err } canDelete := permission.Check(t, permission.PermAppDelete, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )..., ) if !canDelete { return permission.ErrUnauthorized } rec.Log(u.Email, "app-delete", "app="+a.Name) keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "") defer keepAliveWriter.Stop() writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)} err = app.Delete(&a, writer) if err != nil { writer.Encode(tsuruIo.SimpleJsonMessage{Error: err.Error()}) } return nil }
func (s *S) TestNewWithPermission(c *check.C) { evt, err := New(&Opts{ Target: Target{Type: "app", Value: "myapp"}, Kind: permission.PermAppUpdateEnvSet, Owner: s.token, Allowed: Allowed(permission.PermAppReadEvents, permission.Context(permission.CtxApp, "myapp"), permission.Context(permission.CtxTeam, "myteam")), }) c.Assert(err, check.IsNil) expected := &Event{eventData: eventData{ ID: eventID{Target: Target{Type: "app", Value: "myapp"}}, UniqueID: evt.UniqueID, Target: Target{Type: "app", Value: "myapp"}, Kind: Kind{Type: KindTypePermission, Name: "app.update.env.set"}, Owner: Owner{Type: OwnerTypeUser, Name: s.token.GetUserName()}, Running: true, StartTime: evt.StartTime, LockUpdateTime: evt.LockUpdateTime, Allowed: AllowedPermission{ Scheme: permission.PermAppReadEvents.FullName(), Contexts: []permission.PermissionContext{permission.Context(permission.CtxApp, "myapp"), permission.Context(permission.CtxTeam, "myteam")}, }, }} c.Assert(evt, check.DeepEquals, expected) evts, err := All() c.Assert(err, check.IsNil) c.Assert(evts, check.HasLen, 1) evts[0].StartTime = expected.StartTime evts[0].LockUpdateTime = expected.LockUpdateTime c.Assert(&evts[0], check.DeepEquals, expected) }
func (s *EventSuite) TestEventInfoPermission(c *check.C) { token := customUserWithPermission(c, "myuser", permission.Permission{ Scheme: permission.PermAppRead, Context: permission.Context(permission.CtxTeam, s.team.Name), }) evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeApp, Value: "aha"}, Owner: s.token, Kind: permission.PermAppDeploy, Allowed: event.Allowed(permission.PermAppReadEvents, permission.Context(permission.CtxTeam, s.team.Name)), }) c.Assert(err, check.IsNil) u := fmt.Sprintf("/events/%s", evt.UniqueID.Hex()) request, err := http.NewRequest("GET", u, nil) c.Assert(err, check.IsNil) request.Header.Set("Authorization", "bearer "+token.GetValue()) recorder := httptest.NewRecorder() server := RunServer(true) server.ServeHTTP(recorder, request) c.Assert(recorder.Code, check.Equals, http.StatusOK) var result event.Event err = json.Unmarshal(recorder.Body.Bytes(), &result) c.Assert(err, check.IsNil) c.Assert(result.Kind, check.DeepEquals, evt.Kind) c.Assert(result.Target, check.DeepEquals, evt.Target) }
func BaseTokenPermission(t Token) ([]permission.Permission, error) { if t.IsAppToken() { // TODO(cezarsa): Improve handling of app tokens. These permissions // listed here are the ones required by deploy-agent and legacy tsuru- // unit-agent. return []permission.Permission{ { Scheme: permission.PermAppUpdateUnitRegister, Context: permission.Context(permission.CtxApp, t.GetAppName()), }, { Scheme: permission.PermAppUpdateLog, Context: permission.Context(permission.CtxApp, t.GetAppName()), }, { Scheme: permission.PermAppUpdateUnitStatus, Context: permission.Context(permission.CtxApp, t.GetAppName()), }, { Scheme: permission.PermAppReadDeploy, Context: permission.Context(permission.CtxApp, t.GetAppName()), }, }, nil } user, err := t.User() if err != nil { return nil, err } return user.Permissions() }
func (s *S) TestListUsersWithPermissions(c *check.C) { u1 := User{Email: "*****@*****.**", Password: "******"} err := u1.Create() c.Assert(err, check.IsNil) u2 := User{Email: "*****@*****.**", Password: "******"} err = u2.Create() c.Assert(err, check.IsNil) r1, err := permission.NewRole("r1", "app", "") c.Assert(err, check.IsNil) err = r1.AddPermissions("app.update.env", "app.deploy") c.Assert(err, check.IsNil) err = u1.AddRole("r1", "myapp1") c.Assert(err, check.IsNil) err = u2.AddRole("r1", "myapp2") c.Assert(err, check.IsNil) users, err := ListUsersWithPermissions(permission.Permission{ Scheme: permission.PermAppDeploy, Context: permission.Context(permission.CtxApp, "myapp1"), }) c.Assert(err, check.IsNil) c.Assert(users, check.HasLen, 1) c.Assert(users[0].Email, check.Equals, u1.Email) users, err = ListUsersWithPermissions(permission.Permission{ Scheme: permission.PermAppDeploy, Context: permission.Context(permission.CtxApp, "myapp2"), }) c.Assert(err, check.IsNil) c.Assert(users, check.HasLen, 1) c.Assert(users[0].Email, check.Equals, u2.Email) }
func (s *S) TestDissociateRoleNotAuthorized(c *check.C) { role, err := permission.NewRole("test", "team") c.Assert(err, check.IsNil) err = role.AddPermissions("app.create") c.Assert(err, check.IsNil) otherToken := customUserWithPermission(c, "user2") otherUser, err := otherToken.User() c.Assert(err, check.IsNil) err = otherUser.AddRole(role.Name, "myteam") c.Assert(err, check.IsNil) url := fmt.Sprintf("/roles/test/user/%s?context=myteam", otherToken.GetUserName()) req, err := http.NewRequest("DELETE", url, nil) c.Assert(err, check.IsNil) token := customUserWithPermission(c, "user1", permission.Permission{ Scheme: permission.PermRoleUpdateDissociate, Context: permission.Context(permission.CtxGlobal, ""), }, permission.Permission{ Scheme: permission.PermAppCreate, Context: permission.Context(permission.CtxTeam, "otherteam"), }) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Authorization", "bearer "+token.GetValue()) recorder := httptest.NewRecorder() server := RunServer(true) server.ServeHTTP(recorder, req) c.Assert(err, check.IsNil) c.Assert(recorder.Code, check.Equals, http.StatusForbidden) c.Assert(recorder.Body.String(), check.Equals, "User not authorized to use permission app.create(team myteam)\n") otherUser, err = otherToken.User() c.Assert(err, check.IsNil) c.Assert(otherUser.Roles, check.HasLen, 1) }
func unsetCName(w http.ResponseWriter, r *http.Request, t auth.Token) error { cnames := r.URL.Query()["cname"] if len(cnames) == 0 { msg := "You must provide the cname." return &errors.HTTP{Code: http.StatusBadRequest, Message: msg} } u, err := t.User() if err != nil { return err } appName := r.URL.Query().Get(":app") a, err := getAppFromContext(appName, r) if err != nil { return err } allowed := permission.Check(t, permission.PermAppUpdateCnameRemove, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )..., ) if !allowed { return permission.ErrUnauthorized } rec.Log(u.Email, "remove-cname", "app="+appName, "cnames="+strings.Join(cnames, ", ")) if err = a.RemoveCName(cnames...); err == nil { return nil } if err.Error() == "Invalid cname" { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } return err }
func changePlan(w http.ResponseWriter, r *http.Request, t auth.Token) error { var plan app.Plan err := json.NewDecoder(r.Body).Decode(&plan) if err != nil { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "unable to parse request body", } } a, err := getAppFromContext(r.URL.Query().Get(":app"), r) if err != nil { return err } allowed := permission.Check(t, permission.PermAppUpdatePlan, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )..., ) if !allowed { return permission.ErrUnauthorized } keepAliveWriter := io.NewKeepAliveWriter(w, 30*time.Second, "") defer keepAliveWriter.Stop() writer := &io.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)} err = a.ChangePlan(plan.Name, writer) if err == app.ErrPlanNotFound { writer.Encode(io.SimpleJsonMessage{Error: err.Error()}) return err } return err }
func (s *S) TestPoolListHandlerWithPermissionToDefault(c *check.C) { team := auth.Team{Name: "angra"} err := s.conn.Teams().Insert(team) c.Assert(err, check.IsNil) perms := []permission.Permission{ { Scheme: permission.PermAppCreate, Context: permission.Context(permission.CtxGlobal, ""), }, { Scheme: permission.PermPoolUpdate, Context: permission.Context(permission.CtxGlobal, ""), }, } token := userWithPermission(c, perms...) pool := provision.Pool{Name: "pool1", Teams: []string{team.Name}} opts := provision.AddPoolOptions{Name: pool.Name, Default: pool.Default} err = provision.AddPool(opts) c.Assert(err, check.IsNil) err = provision.AddTeamsToPool(pool.Name, pool.Teams) c.Assert(err, check.IsNil) defer provision.RemovePool(pool.Name) req, err := http.NewRequest("GET", "/pools", nil) c.Assert(err, check.IsNil) rec := httptest.NewRecorder() err = poolList(rec, req, token) c.Assert(err, check.IsNil) var pools []provision.Pool err = json.NewDecoder(rec.Body).Decode(&pools) c.Assert(err, check.IsNil) c.Assert(pools, check.HasLen, 2) c.Assert(pools[0].Name, check.Equals, "test1") c.Assert(pools[1].Name, check.Equals, "pool1") }
func restart(w http.ResponseWriter, r *http.Request, t auth.Token) error { process := r.URL.Query().Get("process") w.Header().Set("Content-Type", "text") u, err := t.User() if err != nil { return err } appName := r.URL.Query().Get(":app") a, err := getAppFromContext(appName, r) if err != nil { return err } allowed := permission.Check(t, permission.PermAppUpdateRestart, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )..., ) if !allowed { return permission.ErrUnauthorized } rec.Log(u.Email, "restart", "app="+appName) keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "") defer keepAliveWriter.Stop() writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)} err = a.Restart(process, writer) if err != nil { writer.Encode(tsuruIo.SimpleJsonMessage{Error: err.Error()}) return err } return nil }
func appChangePool(w http.ResponseWriter, r *http.Request, t auth.Token) error { u, err := t.User() if err != nil { return err } a, err := getAppFromContext(r.URL.Query().Get(":app"), r) if err != nil { return err } allowed := permission.Check(t, permission.PermAppUpdatePool, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )..., ) if !allowed { return permission.ErrUnauthorized } defer r.Body.Close() data, err := ioutil.ReadAll(r.Body) if err != nil { return &errors.HTTP{ Code: http.StatusBadRequest, Message: fmt.Sprintf("Unable to decode body: %s", err.Error()), } } pool := string(data) rec.Log(u.Email, "app-change-pool", "app="+r.URL.Query().Get(":app"), "pool="+pool) return a.ChangePool(pool) }
func deployDataToEvent(data *DeployData) error { var evt event.Event evt.UniqueID = data.ID evt.Target = event.Target{Type: event.TargetTypeApp, Value: data.App} evt.Owner = event.Owner{Type: event.OwnerTypeUser, Name: data.User} evt.Kind = event.Kind{Type: event.KindTypePermission, Name: permission.PermAppDeploy.FullName()} evt.StartTime = data.Timestamp evt.EndTime = data.Timestamp.Add(data.Duration) evt.Error = data.Error evt.Log = data.Log evt.RemoveDate = data.RemoveDate a, err := GetByName(data.App) if err == nil { evt.Allowed = event.Allowed(permission.PermAppReadEvents, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )...) } else { evt.Allowed = event.Allowed(permission.PermAppReadEvents) } startOpts := DeployOptions{ Commit: data.Commit, Origin: data.Origin, } var otherData map[string]string if data.Diff != "" { otherData = map[string]string{"diff": data.Diff} } endData := map[string]string{"image": data.Image} err = evt.RawInsert(startOpts, otherData, endData) if mgo.IsDup(err) { return nil } return err }
// addNodeHandler can provide an machine and/or register a node address. // If register flag is true, it will just register a node. // It checks if node address is valid and accessible. func addNodeHandler(w http.ResponseWriter, r *http.Request, t auth.Token) error { params, err := unmarshal(r.Body) if err != nil { return err } if templateName, ok := params["template"]; ok { params, err = iaas.ExpandTemplate(templateName) if err != nil { w.WriteHeader(http.StatusBadRequest) return json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) } } pool := params["pool"] if pool == "" { w.WriteHeader(http.StatusBadRequest) return json.NewEncoder(w).Encode(map[string]string{"error": "pool is required"}) } if !permission.Check(t, permission.PermNodeCreate, permission.Context(permission.CtxPool, pool)) { return permission.ErrUnauthorized } isRegister, _ := strconv.ParseBool(r.URL.Query().Get("register")) if !isRegister { canCreateMachine := permission.Check(t, permission.PermMachineCreate, permission.Context(permission.CtxIaaS, params["iaas"])) if !canCreateMachine { return permission.ErrUnauthorized } } response, err := mainDockerProvisioner.addNodeForParams(params, isRegister) if err != nil { w.WriteHeader(http.StatusBadRequest) response["error"] = err.Error() } return json.NewEncoder(w).Encode(response) }
func (s *S) TestAssignRoleCheckGandalf(c *check.C) { role, err := permission.NewRole("test", "app", "") c.Assert(err, check.IsNil) err = role.AddPermissions("app.deploy") c.Assert(err, check.IsNil) emptyToken := customUserWithPermission(c, "user2") a := app.App{Name: "myapp", TeamOwner: s.team.Name} err = app.CreateApp(&a, s.user) c.Assert(err, check.IsNil) roleBody := bytes.NewBufferString(fmt.Sprintf("email=%s&context=myapp", emptyToken.GetUserName())) req, err := http.NewRequest("POST", "/roles/test/user", roleBody) c.Assert(err, check.IsNil) token := customUserWithPermission(c, "user1", permission.Permission{ Scheme: permission.PermRoleUpdateAssign, Context: permission.Context(permission.CtxGlobal, ""), }, permission.Permission{ Scheme: permission.PermAppDeploy, Context: permission.Context(permission.CtxApp, "myapp"), }) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Authorization", "bearer "+token.GetValue()) recorder := httptest.NewRecorder() server := RunServer(true) server.ServeHTTP(recorder, req) c.Assert(err, check.IsNil) c.Assert(recorder.Code, check.Equals, http.StatusOK) emptyUser, err := emptyToken.User() c.Assert(err, check.IsNil) users, err := repositorytest.Granted("myapp") c.Assert(err, check.IsNil) c.Assert(users, check.DeepEquals, []string{s.user.Email, emptyToken.GetUserName()}) c.Assert(emptyUser.Roles, check.HasLen, 1) }
// title: regenerate token // path: /users/api-key // method: POST // produce: application/json // responses: // 200: OK // 401: Unauthorized // 404: User not found func regenerateAPIToken(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { r.ParseForm() email := r.URL.Query().Get("user") if email == "" { email = t.GetUserName() } allowed := permission.Check(t, permission.PermUserUpdateToken, permission.Context(permission.CtxUser, email), ) if !allowed { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: userTarget(email), Kind: permission.PermUserUpdateToken, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermUserReadEvents, permission.Context(permission.CtxUser, email)), }) if err != nil { return err } defer func() { evt.Done(err) }() u, err := auth.GetUserByEmail(email) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } apiKey, err := u.RegenerateAPIKey() if err != nil { return err } w.Header().Add("Content-Type", "application/json") return json.NewEncoder(w).Encode(apiKey) }
// title: remove team // path: /teams/{name} // method: DELETE // responses: // 200: Team removed // 401: Unauthorized // 403: Forbidden // 404: Not found func removeTeam(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { r.ParseForm() name := r.URL.Query().Get(":name") allowed := permission.Check(t, permission.PermTeamDelete, permission.Context(permission.CtxTeam, name), ) if !allowed { return &errors.HTTP{Code: http.StatusNotFound, Message: fmt.Sprintf(`Team "%s" not found.`, name)} } evt, err := event.New(&event.Opts{ Target: teamTarget(name), Kind: permission.PermTeamDelete, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermTeamReadEvents, permission.Context(permission.CtxTeam, name)), }) if err != nil { return err } defer func() { evt.Done(err) }() err = auth.RemoveTeam(name) if err != nil { if _, ok := err.(*auth.ErrTeamStillUsed); ok { msg := fmt.Sprintf("This team cannot be removed because there are still references to it:\n%s", err) return &errors.HTTP{Code: http.StatusForbidden, Message: msg} } if err == auth.ErrTeamNotFound { return &errors.HTTP{Code: http.StatusNotFound, Message: fmt.Sprintf(`Team "%s" not found.`, name)} } return err } return nil }
func (s *EventSuite) TestEventCancelWithoutPermission(c *check.C) { token := customUserWithPermission(c, "myuser", permission.Permission{ Scheme: permission.PermAppRead, Context: permission.Context(permission.CtxTeam, s.team.Name), }) evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeApp, Value: "anything"}, Owner: s.token, Kind: permission.PermAppDeploy, Cancelable: true, Allowed: event.Allowed(permission.PermAppReadEvents, permission.Context(permission.CtxTeam, s.team.Name)), AllowedCancel: event.Allowed(permission.PermAppUpdateEvents, permission.Context(permission.CtxTeam, "other-team")), }) c.Assert(err, check.IsNil) body := strings.NewReader("reason=we ain't gonna take it") u := fmt.Sprintf("/events/%s/cancel", evt.UniqueID.Hex()) request, err := http.NewRequest("POST", u, body) c.Assert(err, check.IsNil) request.Header.Set("Authorization", "bearer "+token.GetValue()) request.Header.Set("Content-Type", "application/x-www-form-urlencoded") recorder := httptest.NewRecorder() server := RunServer(true) server.ServeHTTP(recorder, request) c.Assert(recorder.Code, check.Equals, http.StatusForbidden) }
func appRebuildRoutes(w http.ResponseWriter, r *http.Request, t auth.Token) error { u, err := t.User() if err != nil { return err } a, err := getAppFromContext(r.URL.Query().Get(":app"), r) if err != nil { return err } allowed := permission.Check(t, permission.PermAppAdminRoutes, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )..., ) if !allowed { return permission.ErrUnauthorized } rec.Log(u.Email, "app-rebuild-routes", "app="+r.URL.Query().Get(":app")) w.Header().Set("Content-Type", "application/json") result, err := a.RebuildRoutes() if err != nil { return err } return json.NewEncoder(w).Encode(&result) }
func (s *EventSuite) insertEvents(target string, c *check.C) ([]*event.Event, error) { t, err := event.GetTargetType(target) if err != nil { return nil, err } evts := make([]*event.Event, 10) for i := 0; i < 10; i++ { name := fmt.Sprintf("app-%d", i) opts := &event.Opts{ Target: event.Target{Type: t, Value: name}, Owner: s.token, Kind: permission.PermAppDeploy, Cancelable: i == 0, } if t == event.TargetTypeApp { opts.Allowed = event.Allowed(permission.PermAppReadEvents, permission.Context(permission.CtxTeam, s.team.Name)) opts.AllowedCancel = event.Allowed(permission.PermAppUpdateEvents, permission.Context(permission.CtxTeam, s.team.Name)) } else { opts.Allowed = event.Allowed(permission.PermApp) opts.AllowedCancel = event.Allowed(permission.PermApp) } evt, err := event.New(opts) c.Assert(err, check.IsNil) if i == 1 { err = evt.Done(nil) c.Assert(err, check.IsNil) } evts[i] = evt } return evts, nil }
func updateApp(w http.ResponseWriter, r *http.Request, t auth.Token) error { var updateData app.App defer r.Body.Close() body, err := ioutil.ReadAll(r.Body) if err != nil { return err } if err = json.Unmarshal(body, &updateData); err != nil { return err } appName := r.URL.Query().Get(":appname") a, err := getAppFromContext(appName, r) if err != nil { return err } if updateData.Description != "" { allowed := permission.Check(t, permission.PermAppUpdate, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )..., ) if !allowed { return permission.ErrUnauthorized } a.Description = updateData.Description } u, err := t.User() if err != nil { return err } rec.Log(u.Email, "update-app", "app="+a.Name, "description="+a.Description) return a.Update() }
func (s *ConsumptionSuite) SetUpTest(c *check.C) { repositorytest.Reset() config.Set("database:url", "127.0.0.1:27017") config.Set("database:name", "tsuru_api_consumption_test") config.Set("auth:hash-cost", 4) config.Set("repo-manager", "fake") var err error s.conn, err = db.Conn() c.Assert(err, check.IsNil) dbtest.ClearAllCollections(s.conn.Apps().Database) s.team = &auth.Team{Name: "tsuruteam"} err = s.conn.Teams().Insert(s.team) c.Assert(err, check.IsNil) s.token = customUserWithPermission(c, "consumption-master-user", permission.Permission{ Scheme: permission.PermServiceInstance, Context: permission.Context(permission.CtxTeam, s.team.Name), }, permission.Permission{ Scheme: permission.PermServiceRead, Context: permission.Context(permission.CtxTeam, s.team.Name), }) s.user, err = s.token.User() c.Assert(err, check.IsNil) app.AuthScheme = nativeScheme s.provisioner = provisiontest.NewFakeProvisioner() app.Provisioner = s.provisioner }
func sleep(w http.ResponseWriter, r *http.Request, t auth.Token) error { process := r.URL.Query().Get("process") w.Header().Set("Content-Type", "text") u, err := t.User() if err != nil { return err } appName := r.URL.Query().Get(":app") a, err := getAppFromContext(appName, r) if err != nil { return err } proxy := r.URL.Query().Get("proxy") if proxy == "" { return &errors.HTTP{Code: http.StatusBadRequest, Message: "Empty proxy URL"} } proxyURL, err := url.Parse(proxy) if err != nil { log.Errorf("Invalid url for proxy param: %v", proxy) return err } allowed := permission.Check(t, permission.PermAppUpdateSleep, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )..., ) if !allowed { return permission.ErrUnauthorized } rec.Log(u.Email, "sleep", "app="+appName) return a.Sleep(w, process, proxyURL) }
func (s *S) TestAssignRoleNotAuthorized(c *check.C) { role, err := permission.NewRole("test", "team") c.Assert(err, check.IsNil) err = role.AddPermissions("app.create") c.Assert(err, check.IsNil) emptyToken := customUserWithPermission(c, "user2") roleBody := bytes.NewBufferString(fmt.Sprintf("email=%s&context=myteam", emptyToken.GetUserName())) req, err := http.NewRequest("POST", "/roles/test/user", roleBody) c.Assert(err, check.IsNil) token := userWithPermission(c, permission.Permission{ Scheme: permission.PermRoleUpdateAssign, Context: permission.Context(permission.CtxGlobal, ""), }, permission.Permission{ Scheme: permission.PermAppCreate, Context: permission.Context(permission.CtxTeam, "otherteam"), }) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Authorization", "bearer "+token.GetValue()) recorder := httptest.NewRecorder() server := RunServer(true) server.ServeHTTP(recorder, req) c.Assert(err, check.IsNil) c.Assert(recorder.Code, check.Equals, http.StatusForbidden) c.Assert(recorder.Body.String(), check.Equals, "User not authorized to use permission app.create(team myteam)\n") emptyUser, err := emptyToken.User() c.Assert(err, check.IsNil) c.Assert(emptyUser.Roles, check.HasLen, 0) }
func (s *S) TestUserPermissionsWithRemovedRole(c *check.C) { role, err := permission.NewRole("test", "team") c.Assert(err, check.IsNil) u := User{Email: "*****@*****.**", Password: "******"} err = u.Create() c.Assert(err, check.IsNil) err = u.AddRole(role.Name, "team") c.Assert(err, check.IsNil) conn, err := db.Conn() c.Assert(err, check.IsNil) defer conn.Close() err = conn.Roles().RemoveId(role.Name) c.Assert(err, check.IsNil) perms, err := u.Permissions() c.Assert(err, check.IsNil) c.Assert(perms, check.IsNil) r1, err := permission.NewRole("r1", "app") c.Assert(err, check.IsNil) err = r1.AddPermissions("app.update.env", "app.deploy") c.Assert(err, check.IsNil) err = u.AddRole("r1", "myapp") c.Assert(err, check.IsNil) err = u.AddRole("r1", "myapp2") c.Assert(err, check.IsNil) perms, err = u.Permissions() c.Assert(err, check.IsNil) c.Assert(perms, check.DeepEquals, []permission.Permission{ {Scheme: permission.PermAppDeploy, Context: permission.Context(permission.CtxApp, "myapp")}, {Scheme: permission.PermAppUpdateEnv, Context: permission.Context(permission.CtxApp, "myapp")}, {Scheme: permission.PermAppDeploy, Context: permission.Context(permission.CtxApp, "myapp2")}, {Scheme: permission.PermAppUpdateEnv, Context: permission.Context(permission.CtxApp, "myapp2")}, }) }
// title: set envs // path: /apps/{app}/env // method: POST // consume: application/x-www-form-urlencoded // produce: application/x-json-stream // responses: // 200: Envs updated // 400: Invalid data // 401: Unauthorized // 404: App not found func setEnv(w http.ResponseWriter, r *http.Request, t auth.Token) error { err := r.ParseForm() if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } var e Envs dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err = dec.DecodeValues(&e, r.Form) if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } if len(e.Envs) == 0 { msg := "You must provide the list of environment variables" return &errors.HTTP{Code: http.StatusBadRequest, Message: msg} } u, err := t.User() if err != nil { return err } extra := fmt.Sprintf("private=%t", e.Private) appName := r.URL.Query().Get(":app") a, err := getAppFromContext(appName, r) if err != nil { return err } allowed := permission.Check(t, permission.PermAppUpdateEnvSet, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )..., ) if !allowed { return permission.ErrUnauthorized } envs := map[string]string{} variables := []bind.EnvVar{} for _, v := range e.Envs { envs[v.Name] = v.Value variables = append(variables, bind.EnvVar{Name: v.Name, Value: v.Value, Public: !e.Private}) } rec.Log(u.Email, "set-env", "app="+appName, envs, extra) w.Header().Set("Content-Type", "application/x-json-stream") keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "") defer keepAliveWriter.Stop() writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)} err = a.SetEnvs( bind.SetEnvApp{ Envs: variables, PublicOnly: true, ShouldRestart: !e.NoRestart, }, writer) if err != nil { writer.Encode(tsuruIo.SimpleJsonMessage{Error: err.Error()}) return nil } return nil }
// title: service instance create // path: /services/{service}/instances // method: POST // consume: application/x-www-form-urlencoded // responses: // 201: Service created // 400: Invalid data // 401: Unauthorized // 409: Service already exists func createServiceInstance(w http.ResponseWriter, r *http.Request, t auth.Token) error { serviceName := r.URL.Query().Get(":service") user, err := t.User() if err != nil { return err } srv, err := getService(serviceName) if err != nil { return err } instance := service.ServiceInstance{ Name: r.FormValue("name"), PlanName: r.FormValue("plan"), TeamOwner: r.FormValue("owner"), Description: r.FormValue("description"), } var teamOwner string if instance.TeamOwner == "" { teamOwner, err = permission.TeamForPermission(t, permission.PermServiceInstanceCreate) if err != nil { return err } instance.TeamOwner = teamOwner } allowed := permission.Check(t, permission.PermServiceInstanceCreate, permission.Context(permission.CtxTeam, instance.TeamOwner), ) if !allowed { return permission.ErrUnauthorized } if srv.IsRestricted { allowed := permission.Check(t, permission.PermServiceRead, append(permission.Contexts(permission.CtxTeam, srv.Teams), permission.Context(permission.CtxService, srv.Name))..., ) if !allowed { return permission.ErrUnauthorized } } rec.Log(user.Email, "create-service-instance", fmt.Sprintf("%#v", instance)) err = service.CreateServiceInstance(instance, &srv, user) if err == service.ErrInstanceNameAlreadyExists { return &errors.HTTP{ Code: http.StatusConflict, Message: err.Error(), } } if err == service.ErrInvalidInstanceName { return &errors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } if err == nil { w.WriteHeader(http.StatusCreated) } return err }
func updateNodeHandler(w http.ResponseWriter, r *http.Request, t auth.Token) error { params, err := unmarshal(r.Body) if err != nil { return err } address, _ := params["address"] if address == "" { return &errors.HTTP{Code: http.StatusBadRequest, Message: "address is required"} } nodes, err := mainDockerProvisioner.Cluster().UnfilteredNodes() if err != nil { return err } var oldNode *cluster.Node for i := range nodes { if nodes[i].Address == address { oldNode = &nodes[i] break } } oldPool, _ := oldNode.Metadata["pool"] allowedOldPool := permission.Check(t, permission.PermNodeUpdate, permission.Context(permission.CtxPool, oldPool), ) if !allowedOldPool { return permission.ErrUnauthorized } newPool, ok := params["pool"] if ok { allowedNewPool := permission.Check(t, permission.PermNodeUpdate, permission.Context(permission.CtxPool, newPool), ) if !allowedNewPool { return permission.ErrUnauthorized } } delete(params, "address") node := cluster.Node{Address: address, Metadata: params} disabled, _ := strconv.ParseBool(r.URL.Query().Get("disabled")) enabled, _ := strconv.ParseBool(r.URL.Query().Get("enabled")) if disabled && enabled { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "You can't make a node enable and disable at the same time.", } } if disabled { node.CreationStatus = cluster.NodeCreationStatusDisabled } if enabled { node.CreationStatus = cluster.NodeStatusReady } _, err = mainDockerProvisioner.Cluster().UpdateNode(node) return err }
// removeNodeHandler calls scheduler.Unregister to unregistering a node into it. func removeNodeHandler(w http.ResponseWriter, r *http.Request, t auth.Token) error { params, err := unmarshal(r.Body) if err != nil { return err } address, _ := params["address"] if address == "" { return fmt.Errorf("Node address is required.") } node, err := mainDockerProvisioner.Cluster().GetNode(address) if err != nil { return err } allowedNodeRemove := permission.Check(t, permission.PermNodeDelete, permission.Context(permission.CtxPool, node.Metadata["pool"]), ) if !allowedNodeRemove { return permission.ErrUnauthorized } removeIaaS, _ := strconv.ParseBool(params["remove_iaas"]) if removeIaaS { allowedIaasRemove := permission.Check(t, permission.PermMachineDelete, permission.Context(permission.CtxIaaS, node.Metadata["iaas"]), ) if !allowedIaasRemove { return permission.ErrUnauthorized } } node.CreationStatus = cluster.NodeCreationStatusDisabled _, err = mainDockerProvisioner.Cluster().UpdateNode(node) if err != nil { return err } noRebalance, err := strconv.ParseBool(r.URL.Query().Get("no-rebalance")) if !noRebalance { err = mainDockerProvisioner.rebalanceContainersByHost(urlToHost(address), w) if err != nil { return err } } err = mainDockerProvisioner.Cluster().Unregister(address) if err != nil { return err } if removeIaaS { var m iaas.Machine m, err = iaas.FindMachineByIdOrAddress(node.Metadata["iaas-id"], urlToHost(address)) if err != nil && err != mgo.ErrNotFound { return err } return m.Destroy() } return nil }