2015-07-17 02:51:34 -05:00
package api
import (
2021-11-04 05:17:07 -05:00
"context"
2020-08-13 07:38:54 -05:00
"errors"
2015-07-21 05:18:11 -05:00
"fmt"
2021-11-29 03:18:01 -06:00
"net/http"
2022-07-08 06:07:00 -05:00
"strconv"
2022-11-14 06:11:26 -06:00
"strings"
2015-07-21 05:18:11 -05:00
2015-07-17 02:51:34 -05:00
"github.com/grafana/grafana/pkg/api/dtos"
2021-01-15 07:43:20 -06:00
"github.com/grafana/grafana/pkg/api/response"
2015-07-20 10:46:48 -05:00
"github.com/grafana/grafana/pkg/events"
2019-02-23 16:35:26 -06:00
"github.com/grafana/grafana/pkg/infra/metrics"
2022-07-08 06:07:00 -05:00
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
2023-08-09 05:33:35 -05:00
"github.com/grafana/grafana/pkg/services/auth/identity"
2023-01-27 01:50:36 -06:00
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
2023-01-17 13:47:31 -06:00
"github.com/grafana/grafana/pkg/services/notifications"
2022-09-23 04:59:07 -05:00
"github.com/grafana/grafana/pkg/services/org"
2023-01-06 02:02:05 -06:00
tempuser "github.com/grafana/grafana/pkg/services/temp_user"
2022-06-28 07:32:25 -05:00
"github.com/grafana/grafana/pkg/services/user"
2015-07-18 10:39:12 -05:00
"github.com/grafana/grafana/pkg/setting"
2015-07-17 02:51:34 -05:00
"github.com/grafana/grafana/pkg/util"
2021-10-11 07:30:59 -05:00
"github.com/grafana/grafana/pkg/web"
2015-07-17 02:51:34 -05:00
)
2022-07-27 08:54:37 -05:00
// swagger:route GET /org/invites org_invites getPendingOrgInvites
//
// Get pending invites.
//
// Responses:
// 200: getPendingOrgInvitesResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetPendingOrgInvites ( c * contextmodel . ReqContext ) response . Response {
2023-08-09 05:33:35 -05:00
query := tempuser . GetTempUsersQuery { OrgID : c . SignedInUser . GetOrgID ( ) , Status : tempuser . TmpUserInvitePending }
2015-07-17 02:51:34 -05:00
2023-01-06 02:02:05 -06:00
queryResult , err := hs . tempUserService . GetTempUsersQuery ( c . Req . Context ( ) , & query )
if err != nil {
2023-08-09 05:33:35 -05:00
return response . Error ( http . StatusInternalServerError , "Failed to get invites from db" , err )
2015-07-17 02:51:34 -05:00
}
2023-01-06 02:02:05 -06:00
for _ , invite := range queryResult {
invite . URL = setting . ToAbsUrl ( "invite/" + invite . Code )
2015-07-20 07:26:49 -05:00
}
2023-01-06 02:02:05 -06:00
return response . JSON ( http . StatusOK , queryResult )
2015-07-17 02:51:34 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route POST /org/invites org_invites addOrgInvite
//
// Add invite.
//
// Responses:
// 200: okResponse
// 400: badRequestError
// 401: unauthorisedError
// 403: forbiddenError
// 412: SMTPNotEnabledError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) AddOrgInvite ( c * contextmodel . ReqContext ) response . Response {
2021-11-29 03:18:01 -06:00
inviteDto := dtos . AddInviteForm { }
if err := web . Bind ( c . Req , & inviteDto ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2015-07-17 02:51:34 -05:00
if ! inviteDto . Role . IsValid ( ) {
2023-08-09 05:33:35 -05:00
return response . Error ( http . StatusBadRequest , "Invalid role specified" , nil )
2015-07-17 02:51:34 -05:00
}
2023-08-09 05:33:35 -05:00
if ! c . SignedInUser . GetOrgRole ( ) . Includes ( inviteDto . Role ) && ! c . SignedInUser . GetIsGrafanaAdmin ( ) {
2022-06-27 11:40:12 -05:00
return response . Error ( http . StatusForbidden , "Cannot assign a role higher than user's role" , nil )
}
2015-07-17 02:51:34 -05:00
2015-07-18 04:43:34 -05:00
// first try get existing user
2022-08-04 06:22:43 -05:00
userQuery := user . GetUserByLoginQuery { LoginOrEmail : inviteDto . LoginOrEmail }
usr , err := hs . userService . GetByLogin ( c . Req . Context ( ) , & userQuery )
if err != nil {
2022-07-20 07:50:06 -05:00
if ! errors . Is ( err , user . ErrUserNotFound ) {
2023-08-09 05:33:35 -05:00
return response . Error ( http . StatusInternalServerError , "Failed to query db for existing user check" , err )
2015-07-18 04:43:34 -05:00
}
} else {
2022-07-08 06:07:00 -05:00
// Evaluate permissions for adding an existing user to the organization
2022-08-04 06:22:43 -05:00
userIDScope := ac . Scope ( "users" , "id" , strconv . Itoa ( int ( usr . ID ) ) )
2022-07-08 06:07:00 -05:00
hasAccess , err := hs . AccessControl . Evaluate ( c . Req . Context ( ) , c . SignedInUser , ac . EvalPermission ( ac . ActionOrgUsersAdd , userIDScope ) )
if err != nil {
return response . Error ( http . StatusInternalServerError , "Failed to evaluate permissions" , err )
}
if ! hasAccess {
return response . Error ( http . StatusForbidden , "Permission denied: not permitted to add an existing user to this organisation" , err )
}
2022-08-04 06:22:43 -05:00
return hs . inviteExistingUserToOrg ( c , usr , & inviteDto )
2015-07-18 04:43:34 -05:00
}
2023-02-27 08:28:49 -06:00
if hs . Cfg . DisableLoginForm {
2023-08-09 05:33:35 -05:00
return response . Error ( http . StatusBadRequest , "Cannot invite external user when login is disabled." , nil )
2019-09-12 11:45:50 -05:00
}
2023-01-06 02:02:05 -06:00
cmd := tempuser . CreateTempUserCommand { }
2023-08-09 05:33:35 -05:00
cmd . OrgID = c . SignedInUser . GetOrgID ( )
2015-08-10 07:03:08 -05:00
cmd . Email = inviteDto . LoginOrEmail
2015-07-17 02:51:34 -05:00
cmd . Name = inviteDto . Name
2023-01-06 02:02:05 -06:00
cmd . Status = tempuser . TmpUserInvitePending
2023-08-09 05:33:35 -05:00
namespace , identifier := c . SignedInUser . GetNamespacedID ( )
var userID int64
switch namespace {
case identity . NamespaceUser , identity . NamespaceServiceAccount :
var err error
userID , err = strconv . ParseInt ( identifier , 10 , 64 )
if err != nil {
return response . Error ( http . StatusInternalServerError , "Unrecognized user" , err )
}
}
cmd . InvitedByUserID = userID
2019-10-23 03:40:12 -05:00
cmd . Code , err = util . GetRandomString ( 30 )
if err != nil {
2023-08-09 05:33:35 -05:00
return response . Error ( http . StatusInternalServerError , "Could not generate random string" , err )
2019-10-23 03:40:12 -05:00
}
2015-07-17 07:42:49 -05:00
cmd . Role = inviteDto . Role
2022-12-13 08:33:05 -06:00
cmd . RemoteAddr = c . RemoteAddr ( )
2015-07-17 02:51:34 -05:00
2023-01-06 02:02:05 -06:00
cmdResult , err := hs . tempUserService . CreateTempUser ( c . Req . Context ( ) , & cmd )
if err != nil {
2023-08-09 05:33:35 -05:00
return response . Error ( http . StatusInternalServerError , "Failed to save invite to database" , err )
2015-07-17 02:51:34 -05:00
}
2015-07-18 10:39:12 -05:00
// send invite email
2017-12-13 06:16:44 -06:00
if inviteDto . SendEmail && util . IsEmail ( inviteDto . LoginOrEmail ) {
2023-01-17 13:47:31 -06:00
emailCmd := notifications . SendEmailCommand {
2015-08-10 07:03:08 -05:00
To : [ ] string { inviteDto . LoginOrEmail } ,
2021-07-19 05:31:51 -05:00
Template : "new_user_invite" ,
2023-08-30 10:46:47 -05:00
Data : map [ string ] any {
2015-08-11 03:35:10 -05:00
"Name" : util . StringsFallback2 ( cmd . Name , cmd . Email ) ,
2023-08-09 05:33:35 -05:00
"OrgName" : c . SignedInUser . GetOrgName ( ) ,
"Email" : c . SignedInUser . GetEmail ( ) ,
2015-08-11 03:35:10 -05:00
"LinkUrl" : setting . ToAbsUrl ( "invite/" + cmd . Code ) ,
2023-08-09 05:33:35 -05:00
"InvitedBy" : c . SignedInUser . GetDisplayName ( ) ,
2015-07-18 10:39:12 -05:00
} ,
}
2022-01-31 10:24:52 -06:00
if err := hs . AlertNG . NotificationService . SendEmailCommandHandler ( c . Req . Context ( ) , & emailCmd ) ; err != nil {
2023-01-17 13:47:31 -06:00
if errors . Is ( err , notifications . ErrSmtpNotEnabled ) {
2023-08-09 05:33:35 -05:00
return response . Error ( http . StatusPreconditionFailed , err . Error ( ) , err )
2018-05-28 12:49:31 -05:00
}
2020-08-13 07:38:54 -05:00
2023-08-09 05:33:35 -05:00
return response . Error ( http . StatusInternalServerError , "Failed to send email invite" , err )
2015-07-18 10:39:12 -05:00
}
2015-08-10 07:03:08 -05:00
2023-01-06 02:02:05 -06:00
emailSentCmd := tempuser . UpdateTempUserWithEmailSentCommand { Code : cmdResult . Code }
2022-09-20 04:29:17 -05:00
if err := hs . tempUserService . UpdateTempUserWithEmailSent ( c . Req . Context ( ) , & emailSentCmd ) ; err != nil {
2023-08-09 05:33:35 -05:00
return response . Error ( http . StatusInternalServerError , "Failed to update invite with email sent info" , err )
2017-06-30 13:21:05 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Success ( fmt . Sprintf ( "Sent invite to %s" , inviteDto . LoginOrEmail ) )
2015-07-18 10:39:12 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Success ( fmt . Sprintf ( "Created invite for %s" , inviteDto . LoginOrEmail ) )
2015-07-17 02:51:34 -05:00
}
2015-07-20 03:57:39 -05:00
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) inviteExistingUserToOrg ( c * contextmodel . ReqContext , user * user . User , inviteDto * dtos . AddInviteForm ) response . Response {
2015-08-11 03:35:10 -05:00
// user exists, add org role
2023-08-09 05:33:35 -05:00
createOrgUserCmd := org . AddOrgUserCommand { OrgID : c . SignedInUser . GetOrgID ( ) , UserID : user . ID , Role : inviteDto . Role }
2022-09-23 04:59:07 -05:00
if err := hs . orgService . AddOrgUser ( c . Req . Context ( ) , & createOrgUserCmd ) ; err != nil {
2023-01-09 07:39:53 -06:00
if errors . Is ( err , org . ErrOrgUserAlreadyAdded ) {
2023-08-09 05:33:35 -05:00
return response . Error ( http . StatusPreconditionFailed , fmt . Sprintf ( "User %s is already added to organization" , inviteDto . LoginOrEmail ) , err )
2015-08-11 03:35:10 -05:00
}
2023-08-09 05:33:35 -05:00
return response . Error ( http . StatusInternalServerError , "Error while trying to create org user" , err )
2018-03-22 06:37:35 -05:00
}
2015-08-11 03:35:10 -05:00
2018-03-22 06:37:35 -05:00
if inviteDto . SendEmail && util . IsEmail ( user . Email ) {
2023-01-17 13:47:31 -06:00
emailCmd := notifications . SendEmailCommand {
2018-03-22 06:37:35 -05:00
To : [ ] string { user . Email } ,
2021-07-19 05:31:51 -05:00
Template : "invited_to_org" ,
2023-08-30 10:46:47 -05:00
Data : map [ string ] any {
2018-03-22 06:37:35 -05:00
"Name" : user . NameOrFallback ( ) ,
2023-08-09 05:33:35 -05:00
"OrgName" : c . SignedInUser . GetOrgName ( ) ,
"InvitedBy" : c . SignedInUser . GetDisplayName ( ) ,
2018-03-22 06:37:35 -05:00
} ,
2015-08-11 03:35:10 -05:00
}
2022-01-31 10:24:52 -06:00
if err := hs . AlertNG . NotificationService . SendEmailCommandHandler ( c . Req . Context ( ) , & emailCmd ) ; err != nil {
2023-08-09 05:33:35 -05:00
return response . Error ( http . StatusInternalServerError , "Failed to send email invited_to_org" , err )
2018-03-22 06:37:35 -05:00
}
2015-08-11 03:35:10 -05:00
}
2018-03-22 06:37:35 -05:00
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , util . DynMap {
2023-08-09 05:33:35 -05:00
"message" : fmt . Sprintf ( "Existing Grafana user %s added to org %s" , user . NameOrFallback ( ) , c . SignedInUser . GetOrgName ( ) ) ,
2022-06-28 07:32:25 -05:00
"userId" : user . ID ,
2020-09-14 01:58:23 -05:00
} )
2015-08-11 03:35:10 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route DELETE /org/invites/{invitation_code}/revoke org_invites revokeInvite
//
// Revoke invite.
//
// Responses:
// 200: okResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) RevokeInvite ( c * contextmodel . ReqContext ) response . Response {
2023-01-06 02:02:05 -06:00
if ok , rsp := hs . updateTempUserStatus ( c . Req . Context ( ) , web . Params ( c . Req ) [ ":code" ] , tempuser . TmpUserRevoked ) ; ! ok {
2015-08-30 11:56:53 -05:00
return rsp
2015-07-20 03:57:39 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Success ( "Invite revoked" )
2015-07-20 03:57:39 -05:00
}
2015-07-20 08:52:49 -05:00
2019-12-04 06:01:37 -06:00
// GetInviteInfoByCode gets a pending user invite corresponding to a certain code.
// A response containing an InviteInfo object is returned if the invite is found.
// If a (pending) invite is not found, 404 is returned.
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetInviteInfoByCode ( c * contextmodel . ReqContext ) response . Response {
2023-01-06 02:02:05 -06:00
query := tempuser . GetTempUserByCodeQuery { Code : web . Params ( c . Req ) [ ":code" ] }
queryResult , err := hs . tempUserService . GetTempUserByCode ( c . Req . Context ( ) , & query )
if err != nil {
if errors . Is ( err , tempuser . ErrTempUserNotFound ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusNotFound , "Invite not found" , nil )
2015-07-20 08:52:49 -05:00
}
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to get invite" , err )
2015-07-20 08:52:49 -05:00
}
2023-01-06 02:02:05 -06:00
invite := queryResult
if invite . Status != tempuser . TmpUserInvitePending {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusNotFound , "Invite not found" , nil )
2019-12-04 06:01:37 -06:00
}
2015-07-20 08:52:49 -05:00
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , dtos . InviteInfo {
2015-08-10 06:46:59 -05:00
Email : invite . Email ,
Name : invite . Name ,
Username : invite . Email ,
InvitedBy : util . StringsFallback3 ( invite . InvitedByName , invite . InvitedByLogin , invite . InvitedByEmail ) ,
} )
2015-07-20 08:52:49 -05:00
}
2015-07-20 10:46:48 -05:00
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) CompleteInvite ( c * contextmodel . ReqContext ) response . Response {
2021-11-29 03:18:01 -06:00
completeInvite := dtos . CompleteInviteForm { }
2022-11-14 06:11:26 -06:00
var err error
if err = web . Bind ( c . Req , & completeInvite ) ; err != nil {
2021-11-29 03:18:01 -06:00
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2015-07-20 10:46:48 -05:00
2022-11-14 06:11:26 -06:00
completeInvite . Email , err = ValidateAndNormalizeEmail ( completeInvite . Email )
if err != nil {
return response . Error ( http . StatusBadRequest , "Invalid email address provided" , nil )
}
completeInvite . Username = strings . TrimSpace ( completeInvite . Username )
2023-01-06 02:02:05 -06:00
query := tempuser . GetTempUserByCodeQuery { Code : completeInvite . InviteCode }
queryResult , err := hs . tempUserService . GetTempUserByCode ( c . Req . Context ( ) , & query )
if err != nil {
if errors . Is ( err , tempuser . ErrTempUserNotFound ) {
2022-11-14 06:11:26 -06:00
return response . Error ( http . StatusNotFound , "Invite not found" , nil )
2015-07-20 10:46:48 -05:00
}
2022-11-14 06:11:26 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to get invite" , err )
2015-07-20 10:46:48 -05:00
}
2023-01-06 02:02:05 -06:00
invite := queryResult
if invite . Status != tempuser . TmpUserInvitePending {
2022-11-14 06:11:26 -06:00
return response . Error ( http . StatusPreconditionFailed , fmt . Sprintf ( "Invite cannot be used in status %s" , invite . Status ) , nil )
}
// In case the user is invited by email address
if inviteMail , err := ValidateAndNormalizeEmail ( invite . Email ) ; err == nil {
// Make sure that the email address is not amended
if completeInvite . Email != inviteMail {
return response . Error ( http . StatusBadRequest , "The provided email is different from the address that is found in the invite" , nil )
}
2015-07-29 02:30:23 -05:00
}
2015-07-20 10:46:48 -05:00
2024-02-16 04:58:05 -06:00
if err := completeInvite . Password . Validate ( hs . Cfg ) ; err != nil {
return response . Err ( err )
}
2022-06-28 07:32:25 -05:00
cmd := user . CreateUserCommand {
2015-09-01 05:35:06 -05:00
Email : completeInvite . Email ,
Name : completeInvite . Name ,
Login : completeInvite . Username ,
Password : completeInvite . Password ,
SkipOrgSetup : true ,
2015-07-20 10:46:48 -05:00
}
2022-11-29 03:20:44 -06:00
usr , err := hs . userService . Create ( c . Req . Context ( ) , & cmd )
2021-03-18 11:16:56 -05:00
if err != nil {
2022-07-20 07:50:06 -05:00
if errors . Is ( err , user . ErrUserAlreadyExists ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusPreconditionFailed , fmt . Sprintf ( "User with email '%s' or username '%s' already exists" , completeInvite . Email , completeInvite . Username ) , err )
2020-08-13 07:38:54 -05:00
}
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "failed to create user" , err )
2015-07-20 10:46:48 -05:00
}
2022-06-14 09:07:41 -05:00
if err := hs . bus . Publish ( c . Req . Context ( ) , & events . SignUpCompleted {
2022-06-28 07:32:25 -05:00
Name : usr . NameOrFallback ( ) ,
Email : usr . Email ,
2019-10-08 11:57:53 -05:00
} ) ; err != nil {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "failed to publish event" , err )
2019-10-08 11:57:53 -05:00
}
2015-07-20 10:46:48 -05:00
2022-06-28 07:32:25 -05:00
if ok , rsp := hs . applyUserInvite ( c . Req . Context ( ) , usr , invite , true ) ; ! ok {
2015-08-30 11:56:53 -05:00
return rsp
}
2022-06-28 07:32:25 -05:00
err = hs . loginUserWithUser ( usr , c )
2020-03-23 07:37:53 -05:00
if err != nil {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "failed to accept invite" , err )
2020-03-23 07:37:53 -05:00
}
2015-08-30 11:56:53 -05:00
2019-07-16 09:58:46 -05:00
metrics . MApiUserSignUpCompleted . Inc ( )
metrics . MApiUserSignUpInvite . Inc ( )
2015-08-30 11:56:53 -05:00
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , util . DynMap {
2020-09-07 10:06:11 -05:00
"message" : "User created and logged in" ,
2022-06-28 07:32:25 -05:00
"id" : usr . ID ,
2020-09-07 10:06:11 -05:00
} )
2015-08-30 11:56:53 -05:00
}
2023-01-06 02:02:05 -06:00
func ( hs * HTTPServer ) updateTempUserStatus ( ctx context . Context , code string , status tempuser . TempUserStatus ) ( bool , response . Response ) {
2015-08-30 11:56:53 -05:00
// update temp user status
2023-01-06 02:02:05 -06:00
updateTmpUserCmd := tempuser . UpdateTempUserStatusCommand { Code : code , Status : status }
2022-08-12 11:13:23 -05:00
if err := hs . tempUserService . UpdateTempUserStatus ( ctx , & updateTmpUserCmd ) ; err != nil {
2024-02-27 10:39:51 -06:00
return false , response . Error ( http . StatusInternalServerError , "Failed to update invite status" , err )
2015-08-30 11:56:53 -05:00
}
return true , nil
}
2023-01-06 02:02:05 -06:00
func ( hs * HTTPServer ) applyUserInvite ( ctx context . Context , usr * user . User , invite * tempuser . TempUserDTO , setActive bool ) ( bool , response . Response ) {
2015-08-11 03:45:03 -05:00
// add to org
2023-01-06 02:02:05 -06:00
addOrgUserCmd := org . AddOrgUserCommand { OrgID : invite . OrgID , UserID : usr . ID , Role : invite . Role }
2022-09-23 04:59:07 -05:00
if err := hs . orgService . AddOrgUser ( ctx , & addOrgUserCmd ) ; err != nil {
2023-01-09 07:39:53 -06:00
if ! errors . Is ( err , org . ErrOrgUserAlreadyAdded ) {
2024-02-27 10:39:51 -06:00
return false , response . Error ( http . StatusInternalServerError , "Error while trying to create org user" , err )
2015-08-17 03:55:52 -05:00
}
2015-08-11 03:45:03 -05:00
}
2015-07-20 10:46:48 -05:00
// update temp user status
2023-01-06 02:02:05 -06:00
if ok , rsp := hs . updateTempUserStatus ( ctx , invite . Code , tempuser . TmpUserCompleted ) ; ! ok {
2015-08-30 11:56:53 -05:00
return false , rsp
2015-07-20 10:46:48 -05:00
}
2015-08-30 11:56:53 -05:00
if setActive {
// set org to active
2023-01-06 02:02:05 -06:00
if err := hs . userService . SetUsingOrg ( ctx , & user . SetUsingOrgCommand { OrgID : invite . OrgID , UserID : usr . ID } ) ; err != nil {
2024-02-27 10:39:51 -06:00
return false , response . Error ( http . StatusInternalServerError , "Failed to set org as active" , err )
2015-08-30 11:56:53 -05:00
}
}
2015-07-20 10:46:48 -05:00
2015-08-30 11:56:53 -05:00
return true , nil
2015-07-20 10:46:48 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:parameters addOrgInvite
type AddInviteParams struct {
// in:body
// required:true
Body dtos . AddInviteForm ` json:"body" `
}
// swagger:parameters revokeInvite
type RevokeInviteParams struct {
// in:path
// required:true
Code string ` json:"invitation_code" `
}
// swagger:response getPendingOrgInvitesResponse
type GetPendingOrgInvitesResponse struct {
// The response message
// in: body
2023-01-06 02:02:05 -06:00
Body [ ] * tempuser . TempUserDTO ` json:"body" `
2022-07-27 08:54:37 -05:00
}