2017-04-09 18:24:16 -05:00
package api
import (
2020-11-19 06:34:28 -06:00
"errors"
2021-11-29 03:18:01 -06:00
"net/http"
2022-01-14 10:55:57 -06:00
"strconv"
2020-11-19 06:34:28 -06:00
2017-12-20 14:20:12 -06:00
"github.com/grafana/grafana/pkg/api/dtos"
2021-01-15 07:43:20 -06:00
"github.com/grafana/grafana/pkg/api/response"
2023-01-27 01:50:36 -06:00
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
2023-01-26 07:46:30 -06:00
"github.com/grafana/grafana/pkg/services/dashboards"
2022-08-10 04:56:48 -05:00
"github.com/grafana/grafana/pkg/services/org"
2023-01-11 07:20:09 -06:00
"github.com/grafana/grafana/pkg/services/team"
2017-04-09 18:24:16 -05:00
"github.com/grafana/grafana/pkg/util"
2021-11-29 03:18:01 -06:00
"github.com/grafana/grafana/pkg/web"
2017-04-09 18:24:16 -05:00
)
2022-07-27 08:54:37 -05:00
// swagger:route POST /teams teams createTeam
//
// Add Team.
//
// Responses:
// 200: createTeamResponse
// 401: unauthorisedError
// 403: forbiddenError
// 409: conflictError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) CreateTeam ( c * contextmodel . ReqContext ) response . Response {
2023-01-11 07:20:09 -06:00
cmd := team . CreateTeamCommand { }
2021-11-29 03:18:01 -06:00
if err := web . Bind ( c . Req , & cmd ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2022-04-25 03:42:09 -05:00
accessControlEnabled := ! hs . AccessControl . IsDisabled ( )
2022-08-10 04:56:48 -05:00
if ! accessControlEnabled && c . OrgRole == org . RoleViewer {
2021-01-15 07:43:20 -06:00
return response . Error ( 403 , "Not allowed to create team." , nil )
2019-03-13 04:38:09 -05:00
}
2023-01-11 07:20:09 -06:00
t , err := hs . teamService . CreateTeam ( cmd . Name , cmd . Email , c . OrgID )
2021-03-17 10:06:10 -05:00
if err != nil {
2023-01-11 07:20:09 -06:00
if errors . Is ( err , team . ErrTeamNameTaken ) {
2021-01-15 07:43:20 -06:00
return response . Error ( 409 , "Team name taken" , err )
2017-04-09 18:24:16 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to create Team" , err )
2017-04-09 18:24:16 -05:00
}
2022-11-24 08:38:55 -06:00
// Clear permission cache for the user who's created the team, so that new permissions are fetched for their next call
// Required for cases when caller wants to immediately interact with the newly created object
if ! hs . AccessControl . IsDisabled ( ) {
hs . accesscontrolService . ClearUserPermissionCache ( c . SignedInUser )
}
2022-08-10 04:56:48 -05:00
if accessControlEnabled || ( c . OrgRole == org . RoleEditor && hs . Cfg . EditorsCanAdmin ) {
2019-08-08 03:27:47 -05:00
// if the request is authenticated using API tokens
// the SignedInUser is an empty struct therefore
// an additional check whether it is an actual user is required
if c . SignedInUser . IsRealUser ( ) {
2023-01-26 07:46:30 -06:00
if err := addOrUpdateTeamMember ( c . Req . Context ( ) , hs . teamPermissionsService , c . SignedInUser . UserID , c . OrgID , t . ID , dashboards . PERMISSION_ADMIN . String ( ) ) ; err != nil {
2021-03-17 10:06:10 -05:00
c . Logger . Error ( "Could not add creator to team" , "error" , err )
2019-08-08 03:27:47 -05:00
}
} else {
2021-03-17 10:06:10 -05:00
c . Logger . Warn ( "Could not add creator to team because is not a real user" )
2019-03-08 04:56:48 -06:00
}
2019-03-06 04:47:18 -06:00
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , & util . DynMap {
2023-01-11 07:20:09 -06:00
"teamId" : t . ID ,
2017-12-08 09:25:45 -06:00
"message" : "Team created" ,
2017-04-09 18:24:16 -05:00
} )
}
2022-07-27 08:54:37 -05:00
// swagger:route PUT /teams/{team_id} teams updateTeam
//
// Update Team.
//
// Responses:
// 200: okResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 409: conflictError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) UpdateTeam ( c * contextmodel . ReqContext ) response . Response {
2023-01-11 07:20:09 -06:00
cmd := team . UpdateTeamCommand { }
2022-01-14 10:55:57 -06:00
var err error
2021-11-29 03:18:01 -06:00
if err := web . Bind ( c . Req , & cmd ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2023-01-11 07:20:09 -06:00
cmd . OrgID = c . OrgID
cmd . ID , err = strconv . ParseInt ( web . Params ( c . Req ) [ ":teamId" ] , 10 , 64 )
2022-01-14 10:55:57 -06:00
if err != nil {
return response . Error ( http . StatusBadRequest , "teamId is invalid" , err )
}
2019-03-11 05:26:01 -05:00
2022-04-25 03:42:09 -05:00
if hs . AccessControl . IsDisabled ( ) {
2023-01-11 07:20:09 -06:00
if err := hs . teamGuardian . CanAdmin ( c . Req . Context ( ) , cmd . OrgID , cmd . ID , c . SignedInUser ) ; err != nil {
2022-01-27 09:16:44 -06:00
return response . Error ( 403 , "Not allowed to update team" , err )
}
2019-03-11 05:26:01 -05:00
}
2022-09-20 11:58:04 -05:00
if err := hs . teamService . UpdateTeam ( c . Req . Context ( ) , & cmd ) ; err != nil {
2023-01-11 07:20:09 -06:00
if errors . Is ( err , team . ErrTeamNameTaken ) {
2021-01-15 07:43:20 -06:00
return response . Error ( 400 , "Team name taken" , err )
2017-04-18 08:01:05 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to update Team" , err )
2017-04-18 08:01:05 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Success ( "Team updated" )
2017-04-18 08:01:05 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route DELETE /teams/{team_id} teams deleteTeamByID
//
// Delete Team By ID.
//
// Responses:
// 200: okResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) DeleteTeamByID ( c * contextmodel . ReqContext ) response . Response {
2023-01-11 07:20:09 -06:00
orgID := c . OrgID
teamID , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":teamId" ] , 10 , 64 )
2022-01-14 10:55:57 -06:00
if err != nil {
return response . Error ( http . StatusBadRequest , "teamId is invalid" , err )
}
2019-03-11 06:03:15 -05:00
user := c . SignedInUser
2022-04-25 03:42:09 -05:00
if hs . AccessControl . IsDisabled ( ) {
2023-01-11 07:20:09 -06:00
if err := hs . teamGuardian . CanAdmin ( c . Req . Context ( ) , orgID , teamID , user ) ; err != nil {
2022-01-27 09:16:44 -06:00
return response . Error ( 403 , "Not allowed to delete team" , err )
}
2019-03-11 06:03:15 -05:00
}
2023-01-11 07:20:09 -06:00
if err := hs . teamService . DeleteTeam ( c . Req . Context ( ) , & team . DeleteTeamCommand { OrgID : orgID , ID : teamID } ) ; err != nil {
if errors . Is ( err , team . ErrTeamNotFound ) {
2021-01-15 07:43:20 -06:00
return response . Error ( 404 , "Failed to delete Team. ID not found" , nil )
2017-04-09 18:24:16 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to delete Team" , err )
2017-04-09 18:24:16 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Success ( "Team deleted" )
2017-04-09 18:24:16 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /teams/search teams searchTeams
//
// Team Search With Paging.
//
// Responses:
// 200: searchTeamsResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) SearchTeams ( c * contextmodel . ReqContext ) response . Response {
2017-04-09 18:24:16 -05:00
perPage := c . QueryInt ( "perpage" )
if perPage <= 0 {
perPage = 1000
}
page := c . QueryInt ( "page" )
if page < 1 {
page = 1
}
2022-02-09 09:17:31 -06:00
// Using accesscontrol the filtering is done based on user permissions
2023-01-11 07:20:09 -06:00
userIDFilter := team . FilterIgnoreUser
2022-04-25 03:42:09 -05:00
if hs . AccessControl . IsDisabled ( ) {
2023-01-11 07:20:09 -06:00
userIDFilter = userFilter ( c )
2022-02-09 09:17:31 -06:00
}
2023-01-11 07:20:09 -06:00
query := team . SearchTeamsQuery {
OrgID : c . OrgID ,
2019-03-11 08:40:57 -05:00
Query : c . Query ( "query" ) ,
Name : c . Query ( "name" ) ,
2023-01-11 07:20:09 -06:00
UserIDFilter : userIDFilter ,
2019-03-11 08:40:57 -05:00
Page : page ,
Limit : perPage ,
2020-11-24 05:10:32 -06:00
SignedInUser : c . SignedInUser ,
HiddenUsers : hs . Cfg . HiddenUsers ,
2017-04-09 18:24:16 -05:00
}
2023-01-11 07:20:09 -06:00
queryResult , err := hs . teamService . SearchTeams ( c . Req . Context ( ) , & query )
if err != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to search Teams" , err )
2017-04-09 18:24:16 -05:00
}
2022-02-03 10:49:39 -06:00
teamIDs := map [ string ] bool { }
2023-01-11 07:20:09 -06:00
for _ , team := range queryResult . Teams {
team . AvatarURL = dtos . GetGravatarUrlWithDefault ( team . Email , team . Name )
teamIDs [ strconv . FormatInt ( team . ID , 10 ) ] = true
2022-02-03 10:49:39 -06:00
}
2022-08-11 06:28:55 -05:00
metadata := hs . getMultiAccessControlMetadata ( c , c . OrgID , "teams:id:" , teamIDs )
2022-02-18 04:27:00 -06:00
if len ( metadata ) > 0 {
2023-01-11 07:20:09 -06:00
for _ , team := range queryResult . Teams {
team . AccessControl = metadata [ strconv . FormatInt ( team . ID , 10 ) ]
2022-02-03 10:49:39 -06:00
}
2017-12-20 14:20:12 -06:00
}
2023-01-11 07:20:09 -06:00
queryResult . Page = page
queryResult . PerPage = perPage
2017-04-09 18:24:16 -05:00
2023-01-11 07:20:09 -06:00
return response . JSON ( http . StatusOK , queryResult )
2017-04-09 18:24:16 -05:00
}
2017-04-18 08:01:05 -05:00
2022-02-09 06:44:38 -06:00
// UserFilter returns the user ID used in a filter when querying a team
// 1. If the user is a viewer or editor, this will return the user's ID.
2022-02-15 11:09:03 -06:00
// 2. If the user is an admin, this will return models.FilterIgnoreUser (0)
2023-01-27 01:50:36 -06:00
func userFilter ( c * contextmodel . ReqContext ) int64 {
2022-08-11 06:28:55 -05:00
userIdFilter := c . SignedInUser . UserID
2022-08-10 04:56:48 -05:00
if c . OrgRole == org . RoleAdmin {
2023-01-11 07:20:09 -06:00
userIdFilter = team . FilterIgnoreUser
2022-02-09 06:44:38 -06:00
}
return userIdFilter
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /teams/{team_id} teams getTeamByID
//
// Get Team By ID.
//
// Responses:
// 200: getTeamByIDResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetTeamByID ( c * contextmodel . ReqContext ) response . Response {
2022-01-14 10:55:57 -06:00
teamId , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":teamId" ] , 10 , 64 )
if err != nil {
return response . Error ( http . StatusBadRequest , "teamId is invalid" , err )
}
2022-02-09 06:44:38 -06:00
2022-02-09 09:17:31 -06:00
// Using accesscontrol the filtering has already been performed at middleware layer
2023-01-11 07:20:09 -06:00
userIdFilter := team . FilterIgnoreUser
2022-04-25 03:42:09 -05:00
if hs . AccessControl . IsDisabled ( ) {
2022-02-15 11:09:03 -06:00
userIdFilter = userFilter ( c )
2022-02-09 09:17:31 -06:00
}
2023-01-11 07:20:09 -06:00
query := team . GetTeamByIDQuery {
OrgID : c . OrgID ,
ID : teamId ,
2020-11-24 05:10:32 -06:00
SignedInUser : c . SignedInUser ,
HiddenUsers : hs . Cfg . HiddenUsers ,
2022-02-09 09:17:31 -06:00
UserIdFilter : userIdFilter ,
2020-11-24 05:10:32 -06:00
}
2017-04-18 08:01:05 -05:00
2023-01-11 07:20:09 -06:00
queryResult , err := hs . teamService . GetTeamByID ( c . Req . Context ( ) , & query )
if err != nil {
if errors . Is ( err , team . ErrTeamNotFound ) {
2021-01-15 07:43:20 -06:00
return response . Error ( 404 , "Team not found" , err )
2017-04-18 08:01:05 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to get Team" , err )
2017-04-18 08:01:05 -05:00
}
2022-02-18 04:27:00 -06:00
// Add accesscontrol metadata
2023-01-11 07:20:09 -06:00
queryResult . AccessControl = hs . getAccessControlMetadata ( c , c . OrgID , "teams:id:" , strconv . FormatInt ( queryResult . ID , 10 ) )
2022-02-03 10:49:39 -06:00
2023-01-11 07:20:09 -06:00
queryResult . AvatarURL = dtos . GetGravatarUrlWithDefault ( queryResult . Email , queryResult . Name )
return response . JSON ( http . StatusOK , & queryResult )
2017-04-18 08:01:05 -05:00
}
2018-11-12 13:01:53 -06:00
2022-07-27 08:54:37 -05:00
// swagger:route GET /teams/{team_id}/preferences teams getTeamPreferences
//
// Get Team Preferences.
//
// Responses:
// 200: getPreferencesResponse
// 401: unauthorisedError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetTeamPreferences ( c * contextmodel . ReqContext ) response . Response {
2022-01-14 10:55:57 -06:00
teamId , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":teamId" ] , 10 , 64 )
if err != nil {
return response . Error ( http . StatusBadRequest , "teamId is invalid" , err )
}
2022-01-28 05:17:54 -06:00
2022-08-11 06:28:55 -05:00
orgId := c . OrgID
2019-03-12 07:59:53 -05:00
2022-04-25 03:42:09 -05:00
if hs . AccessControl . IsDisabled ( ) {
2022-01-28 05:17:54 -06:00
if err := hs . teamGuardian . CanAdmin ( c . Req . Context ( ) , orgId , teamId , c . SignedInUser ) ; err != nil {
return response . Error ( 403 , "Not allowed to view team preferences." , err )
}
2019-03-12 07:59:53 -05:00
}
2021-11-02 07:41:45 -05:00
return hs . getPreferencesFor ( c . Req . Context ( ) , orgId , 0 , teamId )
2018-11-12 13:01:53 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:route PUT /teams/{team_id}/preferences teams updateTeamPreferences
//
// Update Team Preferences.
//
// Responses:
// 200: okResponse
// 400: badRequestError
// 401: unauthorisedError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) UpdateTeamPreferences ( c * contextmodel . ReqContext ) response . Response {
2021-11-29 03:18:01 -06:00
dtoCmd := dtos . UpdatePrefsCmd { }
if err := web . Bind ( c . Req , & dtoCmd ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2022-01-28 05:17:54 -06:00
2022-01-14 10:55:57 -06:00
teamId , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":teamId" ] , 10 , 64 )
if err != nil {
return response . Error ( http . StatusBadRequest , "teamId is invalid" , err )
}
2022-01-28 05:17:54 -06:00
2022-08-11 06:28:55 -05:00
orgId := c . OrgID
2019-03-11 07:14:06 -05:00
2022-04-25 03:42:09 -05:00
if hs . AccessControl . IsDisabled ( ) {
2022-01-28 05:17:54 -06:00
if err := hs . teamGuardian . CanAdmin ( c . Req . Context ( ) , orgId , teamId , c . SignedInUser ) ; err != nil {
return response . Error ( 403 , "Not allowed to update team preferences." , err )
}
2019-03-11 07:14:06 -05:00
}
2021-11-02 07:41:45 -05:00
return hs . updatePreferencesFor ( c . Req . Context ( ) , orgId , 0 , teamId , & dtoCmd )
2018-11-12 13:01:53 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:parameters updateTeamPreferences
type UpdateTeamPreferencesParams struct {
// in:path
// required:true
TeamID string ` json:"team_id" `
// in:body
// required:true
Body dtos . UpdatePrefsCmd ` json:"body" `
}
// swagger:parameters getTeamByID
type GetTeamByIDParams struct {
// in:path
// required:true
TeamID string ` json:"team_id" `
}
// swagger:parameters deleteTeamByID
type DeleteTeamByIDParams struct {
// in:path
// required:true
TeamID string ` json:"team_id" `
}
// swagger:parameters getTeamPreferences
type GetTeamPreferencesParams struct {
// in:path
// required:true
TeamID string ` json:"team_id" `
}
// swagger:parameters searchTeams
type SearchTeamsParams struct {
// in:query
// required:false
// default: 1
Page int ` json:"page" `
// Number of items per page
// The totalCount field in the response can be used for pagination list E.g. if totalCount is equal to 100 teams and the perpage parameter is set to 10 then there are 10 pages of teams.
// in:query
// required:false
// default: 1000
PerPage int ` json:"perpage" `
Name string ` json:"name" `
// If set it will return results where the query value is contained in the name field. Query values with spaces need to be URL encoded.
// required:false
Query string ` json:"query" `
}
// swagger:parameters createTeam
type CreateTeamParams struct {
// in:body
// required:true
2023-01-11 07:20:09 -06:00
Body team . CreateTeamCommand ` json:"body" `
2022-07-27 08:54:37 -05:00
}
// swagger:parameters updateTeam
type UpdateTeamParams struct {
// in:body
// required:true
2023-01-11 07:20:09 -06:00
Body team . UpdateTeamCommand ` json:"body" `
2022-07-27 08:54:37 -05:00
// in:path
// required:true
TeamID string ` json:"team_id" `
}
// swagger:response searchTeamsResponse
type SearchTeamsResponse struct {
// The response message
// in: body
2023-01-11 07:20:09 -06:00
Body team . SearchTeamQueryResult ` json:"body" `
2022-07-27 08:54:37 -05:00
}
// swagger:response getTeamByIDResponse
type GetTeamByIDResponse struct {
// The response message
// in: body
2023-01-11 07:20:09 -06:00
Body * team . TeamDTO ` json:"body" `
2022-07-27 08:54:37 -05:00
}
// swagger:response createTeamResponse
type CreateTeamResponse struct {
// The response message
// in: body
Body struct {
TeamId int64 ` json:"teamId" `
Message string ` json:"message" `
} ` json:"body" `
}