Skip to content
This repository was archived by the owner on May 30, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -921,7 +921,7 @@ func (a *App) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service
}

func (a *App) SwitchEmailToOAuth(w http.ResponseWriter, r *http.Request, email, password, code, service string) (string, *model.AppError) {
if a.Srv().License() != nil && !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer {
if !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer {
return "", model.NewAppError("emailToOAuth", "api.user.email_to_oauth.not_available.app_error", nil, "", http.StatusForbidden)
}

Expand Down Expand Up @@ -951,7 +951,7 @@ func (a *App) SwitchEmailToOAuth(w http.ResponseWriter, r *http.Request, email,
}

func (a *App) SwitchOAuthToEmail(email, password, requesterId string) (string, *model.AppError) {
if a.Srv().License() != nil && !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer {
if !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer {
return "", model.NewAppError("oauthToEmail", "api.user.oauth_to_email.not_available.app_error", nil, "", http.StatusForbidden)
}

Expand Down
5 changes: 5 additions & 0 deletions app/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -2017,6 +2017,11 @@ func (a *App) UpdateOAuthUserAttrs(userData io.Reader, user *model.User, provide
}
}

if oauthUser.Roles != user.Roles {
user.Roles = oauthUser.Roles
userAttrsChanged = true
}

if user.DeleteAt > 0 {
// Make sure they are not disabled
user.DeleteAt = 0
Expand Down
1 change: 1 addition & 0 deletions cmd/mattermost/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
_ "github.com/mattermost/mattermost-server/v5/app/slashcommands"
// Plugins
_ "github.com/mattermost/mattermost-server/v5/model/gitlab"
_ "github.com/mattermost/mattermost-server/v5/model/openid"
// Enterprise Imports
_ "github.com/mattermost/mattermost-server/v5/imports"
)
Expand Down
10 changes: 4 additions & 6 deletions config/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,10 @@ func GenerateLimitedClientConfig(c *model.Config, telemetryID string, license *m
props["EnableGuestAccounts"] = strconv.FormatBool(*c.GuestAccountsSettings.Enable)
props["GuestAccountsEnforceMultifactorAuthentication"] = strconv.FormatBool(*c.GuestAccountsSettings.EnforceMultifactorAuthentication)

props["EnableSignUpWithOpenId"] = strconv.FormatBool(*c.OpenIdSettings.Enable)
props["OpenIdButtonColor"] = *c.OpenIdSettings.ButtonColor
props["OpenIdButtonText"] = *c.OpenIdSettings.ButtonText

if license != nil {
if *license.Features.LDAP {
props["EnableLdap"] = strconv.FormatBool(*c.LdapSettings.Enable)
Expand All @@ -337,12 +341,6 @@ func GenerateLimitedClientConfig(c *model.Config, telemetryID string, license *m
props["EnableSignUpWithOffice365"] = strconv.FormatBool(*c.Office365Settings.Enable)
}

if *license.Features.OpenId {
props["EnableSignUpWithOpenId"] = strconv.FormatBool(*c.OpenIdSettings.Enable)
props["OpenIdButtonColor"] = *c.OpenIdSettings.ButtonColor
props["OpenIdButtonText"] = *c.OpenIdSettings.ButtonText
}

if *license.Features.CustomTermsOfService {
props["EnableCustomTermsOfService"] = strconv.FormatBool(*c.SupportSettings.CustomTermsOfServiceEnabled)
props["CustomTermsOfServiceReAcceptancePeriod"] = strconv.FormatInt(int64(*c.SupportSettings.CustomTermsOfServiceReAcceptancePeriod), 10)
Expand Down
151 changes: 151 additions & 0 deletions model/openid/openid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package oauthopenid

import (
"encoding/json"
"net/http"
"io"
"io/ioutil"
"strings"

"github.com/mattermost/mattermost-server/v5/einterfaces"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/services/httpservice"
"github.com/mattermost/mattermost-server/v5/shared/mlog"
"github.com/mattermost/mattermost-server/v5/utils/testutils"
)

type OpenIdProvider struct {
}

type OpenIdProviderUrls struct {
AuthEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
UserApiEndpoint string `json:"userinfo_endpoint"`
}

// Keycloak sepcific
type OpenIdUser struct {
Id string `json:"uniqueId"`
Username string `json:"preferred_username"`
Email string `json:"email"`
FirstName string `json:"given_name"`
LastName string `json:"family_name"`
Roles []string `json:"roles"`
}

func init() {
provider := &OpenIdProvider{}
einterfaces.RegisterOauthProvider(model.SERVICE_OPENID, provider)
}

func userFromOpenIdUser(oiu *OpenIdUser) *model.User {
user := &model.User{}
username := oiu.Username
user.Username = model.CleanUsername(username)
user.FirstName = oiu.FirstName
user.LastName = oiu.LastName
user.Email = oiu.Email
user.Email = strings.ToLower(user.Email)
user.AuthData = model.NewString(oiu.getAuthData())
user.AuthService = model.SERVICE_OPENID
var roles string
roles = model.SYSTEM_USER_ROLE_ID
for _, r := range oiu.Roles {
if r == "mattermost_admins" {
roles = model.SYSTEM_USER_ROLE_ID+" "+model.SYSTEM_ADMIN_ROLE_ID
} else {
mlog.Debug("Skipping unknown role when processing user: " + username + " role: " + r)
}
}
user.Roles = roles
mlog.Debug("Parsed user from openId as model user: " + user.ToJson())

return user
}

func openIdUserFromJson(data io.Reader) (*OpenIdUser, error) {
var oiu OpenIdUser
body, err1 := ioutil.ReadAll(data)
if err1 != nil {
return nil, err1
}
mlog.Debug("Received OpenID User data: " + string(body))
err2 := json.Unmarshal(body, &oiu)
if err2 != nil {
return nil, err2
}
return &oiu, nil
}

func (oiu *OpenIdUser) ToJson() string {
b, err := json.Marshal(oiu)
if err != nil {
return ""
}
return string(b)
}


func (oiu *OpenIdUser) getAuthData() string {
return oiu.Id
}

func (m *OpenIdProvider) GetUserFromJson(data io.Reader, tokenUser *model.User) (*model.User, error) {
oiu, err := openIdUserFromJson(data)
if err != nil {
return nil, err
}
return userFromOpenIdUser(oiu), nil
}

func (m *OpenIdProvider) GetSSOSettings(config *model.Config, service string) (*model.SSOSettings, error) {
// This is suuuper janky. But needed a good way to make the http client with just a config
h := httpservice.MakeHTTPService(&testutils.StaticConfigService{Cfg: config})
req, err := http.NewRequest("GET", *config.OpenIdSettings.DiscoveryEndpoint, nil)
if err != nil {
mlog.Warn("Error while making discovery request", mlog.Err(err))
return nil, err
}

req.Header.Set("Accept", "application/json")
resp, err2 := h.MakeClient(true).Do(req)
if err2 != nil {
mlog.Warn("Error while fetching discovery request", mlog.Err(err2))
return nil, err2
}

defer resp.Body.Close()

var oidcUrls OpenIdProviderUrls
err3 := json.NewDecoder(resp.Body).Decode(&oidcUrls)
if err3 != nil {
mlog.Warn("Error while deserializing discovery response", mlog.Err(err3))
return nil, err3
}

// Merge the 'discovered' endpoints with the original settings
newSettings := &model.SSOSettings{
Enable: config.OpenIdSettings.Enable,
Id: config.OpenIdSettings.Id,
Secret: config.OpenIdSettings.Secret,
Scope: config.OpenIdSettings.Scope,
AuthEndpoint: &oidcUrls.AuthEndpoint,
TokenEndpoint: &oidcUrls.TokenEndpoint,
UserApiEndpoint: &oidcUrls.UserApiEndpoint,
DiscoveryEndpoint: config.OpenIdSettings.DiscoveryEndpoint,
ButtonText: config.OpenIdSettings.ButtonText,
ButtonColor: config.OpenIdSettings.ButtonColor,
}
return newSettings, nil
}

func (m *OpenIdProvider) GetUserFromIdToken(idToken string) (*model.User, error) {
return nil, nil
}

func (m *OpenIdProvider) IsSameUser(dbUser, oauthUser *model.User) bool {
// PCTE converted from emails as unique to SSO ID as unique
// so check IDs first (which will be in authData), then check email
return *dbUser.AuthData == *oauthUser.AuthData ||
dbUser.Email == oauthUser.Email
}