Browse Source

login_source: add default authentication switch (#5338)

* Add default Authentication Switch.

* adjust the code accroding to reviews

* #1. Remove redudant logic.
#2, Fix a bug in "Edit" panel.

* Remove unused logic

* Fix local authentication files are not flushed.

* refactor according to review.
pull/5380/merge
haixunlu 6 years ago committed by 无闻
parent
commit
68a6579852
  1. 48
      models/login_source.go
  2. 1
      pkg/form/auth.go
  3. 4
      routes/admin/auths.go
  4. 19
      routes/user/auth.go
  5. 7
      templates/admin/auth/edit.tmpl
  6. 2
      templates/admin/auth/list.tmpl
  7. 8
      templates/admin/auth/new.tmpl
  8. 34
      templates/user/auth/login.tmpl

48
models/login_source.go

@ -133,6 +133,7 @@ type LoginSource struct {
Type LoginType Type LoginType
Name string `xorm:"UNIQUE"` Name string `xorm:"UNIQUE"`
IsActived bool `xorm:"NOT NULL DEFAULT false"` IsActived bool `xorm:"NOT NULL DEFAULT false"`
IsDefault bool `xorm:"DEFAULT false"`
Cfg core.Conversion `xorm:"TEXT"` Cfg core.Conversion `xorm:"TEXT"`
Created time.Time `xorm:"-" json:"-"` Created time.Time `xorm:"-" json:"-"`
@ -257,7 +258,12 @@ func CreateLoginSource(source *LoginSource) error {
} }
_, err = x.Insert(source) _, err = x.Insert(source)
return err if err != nil {
return err
} else if source.IsDefault {
return ResetNonDefaultLoginSources(source)
}
return nil
} }
// LoginSources returns all login sources defined. // LoginSources returns all login sources defined.
@ -291,23 +297,48 @@ func GetLoginSourceByID(id int64) (*LoginSource, error) {
return source, nil return source, nil
} }
// ResetNonDefaultLoginSources clean other default source flag
func ResetNonDefaultLoginSources(source *LoginSource) error {
// update changes to DB
if _, err := x.NotIn("id", []int64{source.ID}).Cols("is_default").Update(&LoginSource{IsDefault: false}); err != nil {
return err
}
// write changes to local authentications
for i := range localLoginSources.sources {
if localLoginSources.sources[i].LocalFile != nil && localLoginSources.sources[i].ID != source.ID {
localLoginSources.sources[i].LocalFile.SetGeneral("is_default", "false")
if err := localLoginSources.sources[i].LocalFile.SetConfig(source.Cfg); err != nil {
return fmt.Errorf("LocalFile.SetConfig: %v", err)
} else if err = localLoginSources.sources[i].LocalFile.Save(); err != nil {
return fmt.Errorf("LocalFile.Save: %v", err)
}
}
}
// flush memory so that web page can show the same behaviors
localLoginSources.UpdateLoginSource(source)
return nil
}
// UpdateLoginSource updates information of login source to database or local file. // UpdateLoginSource updates information of login source to database or local file.
func UpdateLoginSource(source *LoginSource) error { func UpdateLoginSource(source *LoginSource) error {
if source.LocalFile == nil { if source.LocalFile == nil {
_, err := x.Id(source.ID).AllCols().Update(source) if _, err := x.Id(source.ID).AllCols().Update(source); err != nil {
return err return err
} else {
return ResetNonDefaultLoginSources(source)
}
} }
source.LocalFile.SetGeneral("name", source.Name) source.LocalFile.SetGeneral("name", source.Name)
source.LocalFile.SetGeneral("is_activated", com.ToStr(source.IsActived)) source.LocalFile.SetGeneral("is_activated", com.ToStr(source.IsActived))
source.LocalFile.SetGeneral("is_default", com.ToStr(source.IsDefault))
if err := source.LocalFile.SetConfig(source.Cfg); err != nil { if err := source.LocalFile.SetConfig(source.Cfg); err != nil {
return fmt.Errorf("LocalFile.SetConfig: %v", err) return fmt.Errorf("LocalFile.SetConfig: %v", err)
} else if err = source.LocalFile.Save(); err != nil { } else if err = source.LocalFile.Save(); err != nil {
return fmt.Errorf("LocalFile.Save: %v", err) return fmt.Errorf("LocalFile.Save: %v", err)
} }
return ResetNonDefaultLoginSources(source)
localLoginSources.UpdateLoginSource(source)
return nil
} }
func DeleteSource(source *LoginSource) error { func DeleteSource(source *LoginSource) error {
@ -361,7 +392,6 @@ func (s *LocalLoginSources) ActivatedList() []*LoginSource {
if !s.sources[i].IsActived { if !s.sources[i].IsActived {
continue continue
} }
source := &LoginSource{} source := &LoginSource{}
*source = *s.sources[i] *source = *s.sources[i]
list = append(list, source) list = append(list, source)
@ -394,7 +424,8 @@ func (s *LocalLoginSources) UpdateLoginSource(source *LoginSource) {
for i := range s.sources { for i := range s.sources {
if s.sources[i].ID == source.ID { if s.sources[i].ID == source.ID {
*s.sources[i] = *source *s.sources[i] = *source
break } else if source.IsDefault {
s.sources[i].IsDefault = false
} }
} }
} }
@ -429,6 +460,7 @@ func LoadAuthSources() {
ID: s.Key("id").MustInt64(), ID: s.Key("id").MustInt64(),
Name: s.Key("name").String(), Name: s.Key("name").String(),
IsActived: s.Key("is_activated").MustBool(), IsActived: s.Key("is_activated").MustBool(),
IsDefault: s.Key("is_default").MustBool(),
LocalFile: &AuthSourceFile{ LocalFile: &AuthSourceFile{
abspath: fpath, abspath: fpath,
file: authSource, file: authSource,

1
pkg/form/auth.go

@ -32,6 +32,7 @@ type Authentication struct {
GroupMemberUID string GroupMemberUID string
UserUID string UserUID string
IsActive bool IsActive bool
IsDefault bool
SMTPAuth string SMTPAuth string
SMTPHost string SMTPHost string
SMTPPort int SMTPPort int

4
routes/admin/auths.go

@ -69,6 +69,7 @@ func NewAuthSource(c *context.Context) {
c.Data["CurrentSecurityProtocol"] = models.SecurityProtocolNames[ldap.SECURITY_PROTOCOL_UNENCRYPTED] c.Data["CurrentSecurityProtocol"] = models.SecurityProtocolNames[ldap.SECURITY_PROTOCOL_UNENCRYPTED]
c.Data["smtp_auth"] = "PLAIN" c.Data["smtp_auth"] = "PLAIN"
c.Data["is_active"] = true c.Data["is_active"] = true
c.Data["is_default"] = true
c.Data["AuthSources"] = authSources c.Data["AuthSources"] = authSources
c.Data["SecurityProtocols"] = securityProtocols c.Data["SecurityProtocols"] = securityProtocols
c.Data["SMTPAuths"] = models.SMTPAuths c.Data["SMTPAuths"] = models.SMTPAuths
@ -152,6 +153,7 @@ func NewAuthSourcePost(c *context.Context, f form.Authentication) {
Type: models.LoginType(f.Type), Type: models.LoginType(f.Type),
Name: f.Name, Name: f.Name,
IsActived: f.IsActive, IsActived: f.IsActive,
IsDefault: f.IsDefault,
Cfg: config, Cfg: config,
}); err != nil { }); err != nil {
if models.IsErrLoginSourceAlreadyExist(err) { if models.IsErrLoginSourceAlreadyExist(err) {
@ -225,11 +227,13 @@ func EditAuthSourcePost(c *context.Context, f form.Authentication) {
source.Name = f.Name source.Name = f.Name
source.IsActived = f.IsActive source.IsActived = f.IsActive
source.IsDefault = f.IsDefault
source.Cfg = config source.Cfg = config
if err := models.UpdateLoginSource(source); err != nil { if err := models.UpdateLoginSource(source); err != nil {
c.ServerError("UpdateLoginSource", err) c.ServerError("UpdateLoginSource", err)
return return
} }
log.Trace("Authentication changed by admin '%s': %d", c.User.Name, source.ID) log.Trace("Authentication changed by admin '%s': %d", c.User.Name, source.ID)
c.Flash.Success(c.Tr("admin.auths.update_success")) c.Flash.Success(c.Tr("admin.auths.update_success"))

19
routes/user/auth.go

@ -113,7 +113,15 @@ func Login(c *context.Context) {
return return
} }
c.Data["LoginSources"] = loginSources c.Data["LoginSources"] = loginSources
for i := range loginSources {
if loginSources[i].IsDefault {
c.Data["DefaultSource"] = *loginSources[i]
c.Data["login_source"] = loginSources[i].ID
newLoginSources := append(loginSources[:i], loginSources[i+1:]...)
c.Data["LoginSources"] = newLoginSources
break
}
}
c.Success(LOGIN) c.Success(LOGIN)
} }
@ -173,6 +181,15 @@ func LoginPost(c *context.Context, f form.SignIn) {
default: default:
c.ServerError("UserLogin", err) c.ServerError("UserLogin", err)
} }
for i := range loginSources {
if loginSources[i].IsDefault {
c.Data["DefaultSource"] = *loginSources[i]
c.Data["login_source"] = loginSources[i].ID
newLoginSources := append(loginSources[:i], loginSources[i+1:]...)
c.Data["LoginSources"] = newLoginSources
break
}
}
return return
} }

7
templates/admin/auth/edit.tmpl

@ -186,7 +186,12 @@
<input name="is_active" type="checkbox" {{if .Source.IsActived}}checked{{end}}> <input name="is_active" type="checkbox" {{if .Source.IsActived}}checked{{end}}>
</div> </div>
</div> </div>
<div class="inline field">
<div class="ui checkbox">
<label><strong>{{.i18n.Tr "admin.auths.default_auth"}}</strong></label>
<input name="is_default" type="checkbox" {{if .Source.IsDefault}}checked{{end}}>
</div>
</div>
<div class="field"> <div class="field">
<button class="ui green button">{{.i18n.Tr "admin.auths.update"}}</button> <button class="ui green button">{{.i18n.Tr "admin.auths.update"}}</button>
{{if not .Source.LocalFile}} {{if not .Source.LocalFile}}

2
templates/admin/auth/list.tmpl

@ -16,6 +16,7 @@
<th>{{.i18n.Tr "admin.auths.name"}}</th> <th>{{.i18n.Tr "admin.auths.name"}}</th>
<th>{{.i18n.Tr "admin.auths.type"}}</th> <th>{{.i18n.Tr "admin.auths.type"}}</th>
<th>{{.i18n.Tr "admin.auths.enabled"}}</th> <th>{{.i18n.Tr "admin.auths.enabled"}}</th>
<th>{{.i18n.Tr "admin.auths.default"}}</th>
<th>{{.i18n.Tr "admin.auths.updated"}}</th> <th>{{.i18n.Tr "admin.auths.updated"}}</th>
<th>{{.i18n.Tr "admin.users.created"}}</th> <th>{{.i18n.Tr "admin.users.created"}}</th>
<th>{{.i18n.Tr "admin.users.edit"}}</th> <th>{{.i18n.Tr "admin.users.edit"}}</th>
@ -28,6 +29,7 @@
<td><a href="{{AppSubURL}}/admin/auths/{{.ID}}">{{.Name}}</a></td> <td><a href="{{AppSubURL}}/admin/auths/{{.ID}}">{{.Name}}</a></td>
<td>{{.TypeName}}</td> <td>{{.TypeName}}</td>
<td><i class="fa fa{{if .IsActived}}-check{{end}}-square-o"></i></td> <td><i class="fa fa{{if .IsActived}}-check{{end}}-square-o"></i></td>
<td><i class="fa fa{{if .IsDefault}}-check{{end}}-square-o"></i></td>
<td><span class="poping up" data-content="{{DateFmtLong .Updated}}" data-variation="tiny">{{DateFmtShort .Updated}}</span></td> <td><span class="poping up" data-content="{{DateFmtLong .Updated}}" data-variation="tiny">{{DateFmtShort .Updated}}</span></td>
<td> <td>
{{if .Created.IsZero}} {{if .Created.IsZero}}

8
templates/admin/auth/new.tmpl

@ -176,6 +176,7 @@
<input name="skip_verify" type="checkbox" {{if .skip_verify}}checked{{end}}> <input name="skip_verify" type="checkbox" {{if .skip_verify}}checked{{end}}>
</div> </div>
</div> </div>
<div class="inline field"> <div class="inline field">
<div class="ui checkbox"> <div class="ui checkbox">
<label><strong>{{.i18n.Tr "admin.auths.activated"}}</strong></label> <label><strong>{{.i18n.Tr "admin.auths.activated"}}</strong></label>
@ -183,6 +184,13 @@
</div> </div>
</div> </div>
<div class="inline field">
<div class="ui checkbox">
<label><strong>{{.i18n.Tr "admin.auths.default_auth"}}</strong></label>
<input name="is_default" type="checkbox" {{if .is_default}}checked{{end}}>
</div>
</div>
<div class="field"> <div class="field">
<button class="ui green button">{{.i18n.Tr "admin.auths.new"}}</button> <button class="ui green button">{{.i18n.Tr "admin.auths.new"}}</button>
</div> </div>

34
templates/user/auth/login.tmpl

@ -22,16 +22,30 @@
<label for="password">{{.i18n.Tr "auth.auth_source"}}</label> <label for="password">{{.i18n.Tr "auth.auth_source"}}</label>
<div class="ui selection dropdown"> <div class="ui selection dropdown">
<input type="hidden" id="login_source" name="login_source" value="{{.login_source}}" required> <input type="hidden" id="login_source" name="login_source" value="{{.login_source}}" required>
<span class="text"> {{if .DefaultSource}}
{{.i18n.Tr "auth.local"}} <span class="text">
</span> {{.DefaultSource.Name}}
<i class="dropdown icon"></i> </span>
<div class="menu"> <i class="dropdown icon"></i>
<div class="item" data-value="0">{{.i18n.Tr "auth.local"}}</div> <div class="menu">
{{range .LoginSources}} <div class="item" data-value="{{.DefaultSource.ID}}">{{.DefaultSource.Name}}</div>
<div class="item" data-value="{{.ID}}">{{.Name}}</div> <div class="item" data-value="0">{{.i18n.Tr "auth.local"}}</div>
{{end}} {{range .LoginSources}}
</div> <div class="item" data-value="{{.ID}}">{{.Name}}</div>
{{end}}
</div>
{{else}}
<span class="text">
{{.i18n.Tr "auth.local"}}
</span>
<i class="dropdown icon"></i>
<div class="menu">
<div class="item" data-value="0">{{.i18n.Tr "auth.local"}}</div>
{{range .LoginSources}}
<div class="item" data-value="{{.ID}}">{{.Name}}</div>
{{end}}
</div>
{{end}}
</div> </div>
</div> </div>
{{end}} {{end}}

Loading…
Cancel
Save