2019-01-16 15:01:38 +00:00
package auth
import (
2019-02-18 01:46:34 +00:00
"encoding/json"
"io/ioutil"
2019-02-20 00:53:25 +00:00
"log"
2019-05-24 06:04:58 +00:00
"net/http"
2019-01-18 08:13:33 +00:00
"github.com/asaskevich/govalidator"
2019-01-16 15:01:38 +00:00
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
2019-03-21 01:20:14 +00:00
"github.com/portainer/portainer/api"
2019-01-16 15:01:38 +00:00
)
2019-01-18 08:13:33 +00:00
type oauthPayload struct {
Code string
}
func ( payload * oauthPayload ) Validate ( r * http . Request ) error {
if govalidator . IsNull ( payload . Code ) {
return portainer . Error ( "Invalid OAuth authorization code" )
}
return nil
}
2019-02-18 01:46:34 +00:00
func ( handler * Handler ) authenticateThroughExtension ( code , licenseKey string , settings * portainer . OAuthSettings ) ( string , error ) {
extensionURL := handler . ProxyManager . GetExtensionURL ( portainer . OAuthAuthenticationExtension )
encodedConfiguration , err := json . Marshal ( settings )
if err != nil {
return "" , nil
}
req , err := http . NewRequest ( "GET" , extensionURL + "/validate" , nil )
if err != nil {
return "" , err
}
client := & http . Client { }
req . Header . Set ( "X-OAuth-Config" , string ( encodedConfiguration ) )
req . Header . Set ( "X-OAuth-Code" , code )
req . Header . Set ( "X-PortainerExtension-License" , licenseKey )
resp , err := client . Do ( req )
if err != nil {
return "" , err
}
defer resp . Body . Close ( )
body , err := ioutil . ReadAll ( resp . Body )
if err != nil {
return "" , err
}
type extensionResponse struct {
Username string ` json:"Username,omitempty" `
Err string ` json:"err,omitempty" `
Details string ` json:"details,omitempty" `
}
var extResp extensionResponse
err = json . Unmarshal ( body , & extResp )
if err != nil {
return "" , err
}
if resp . StatusCode != http . StatusOK {
return "" , portainer . Error ( extResp . Err + ":" + extResp . Details )
}
return extResp . Username , nil
}
2019-01-18 08:15:02 +00:00
func ( handler * Handler ) validateOAuth ( w http . ResponseWriter , r * http . Request ) * httperror . HandlerError {
2019-01-16 15:01:38 +00:00
var payload oauthPayload
err := request . DecodeAndValidateJSONPayload ( r , & payload )
if err != nil {
return & httperror . HandlerError { http . StatusBadRequest , "Invalid request payload" , err }
}
settings , err := handler . SettingsService . Settings ( )
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to retrieve settings from the database" , err }
}
if settings . AuthenticationMethod != 3 {
2019-02-20 00:53:25 +00:00
return & httperror . HandlerError { http . StatusForbidden , "OAuth authentication is not enabled" , portainer . Error ( "OAuth authentication is not enabled" ) }
2019-01-16 15:01:38 +00:00
}
2019-02-18 01:46:34 +00:00
extension , err := handler . ExtensionService . Extension ( portainer . OAuthAuthenticationExtension )
if err == portainer . ErrObjectNotFound {
return & httperror . HandlerError { http . StatusNotFound , "Oauth authentication extension is not enabled" , err }
} else if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to find a extension with the specified identifier inside the database" , err }
2019-01-16 15:01:38 +00:00
}
2019-02-18 01:46:34 +00:00
username , err := handler . authenticateThroughExtension ( payload . Code , extension . License . LicenseKey , & settings . OAuthSettings )
2019-01-16 15:01:38 +00:00
if err != nil {
2019-02-20 00:53:25 +00:00
log . Printf ( "[DEBUG] - OAuth authentication error: %s" , err )
2019-02-18 01:46:34 +00:00
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to authenticate through OAuth" , portainer . ErrUnauthorized }
2019-01-16 15:01:38 +00:00
}
2019-01-18 08:56:16 +00:00
user , err := handler . UserService . UserByUsername ( username )
2019-01-16 15:01:38 +00:00
if err != nil && err != portainer . ErrObjectNotFound {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to retrieve a user with the specified username from the database" , err }
}
2019-01-18 08:56:16 +00:00
if user == nil && ! settings . OAuthSettings . OAuthAutoCreateUsers {
2019-02-14 02:58:45 +00:00
return & httperror . HandlerError { http . StatusForbidden , "Account not created beforehand in Portainer and automatic user provisioning not enabled" , portainer . ErrUnauthorized }
2019-01-16 15:01:38 +00:00
}
2019-01-18 08:56:16 +00:00
if user == nil {
user = & portainer . User {
2019-09-09 22:58:26 +00:00
Username : username ,
Role : portainer . StandardUserRole ,
PortainerAuthorizations : portainer . DefaultPortainerAuthorizations ( ) ,
2019-01-16 15:01:38 +00:00
}
err = handler . UserService . CreateUser ( user )
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to persist user inside the database" , err }
}
2019-02-17 06:01:42 +00:00
if settings . OAuthSettings . DefaultTeamID != 0 {
membership := & portainer . TeamMembership {
UserID : user . ID ,
TeamID : settings . OAuthSettings . DefaultTeamID ,
Role : portainer . TeamMember ,
}
err = handler . TeamMembershipService . CreateTeamMembership ( membership )
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to persist team membership inside the database" , err }
}
}
2019-12-04 02:32:55 +00:00
err = handler . AuthorizationService . UpdateUsersAuthorizations ( )
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to update user authorizations" , err }
}
2019-01-16 15:01:38 +00:00
}
2019-01-18 08:56:16 +00:00
return handler . writeToken ( w , user )
2019-01-16 15:01:38 +00:00
}