Browse Source

ldap: minor fix for PR #4398

pull/4517/merge
Unknwon 8 years ago
parent
commit
a1d411a018
No known key found for this signature in database
GPG Key ID: 25B575AE3213B2B3
  1. 13
      conf/locale/locale_en-US.ini
  2. 59
      pkg/auth/ldap/ldap.go
  3. 4
      pkg/bindata/bindata.go
  4. 4
      pkg/form/auth.go
  5. 28
      public/js/gogs.js
  6. 10
      routers/admin/auths.go
  7. 37
      templates/admin/auth/edit.tmpl
  8. 38
      templates/admin/auth/new.tmpl
  9. 2
      templates/repo/settings/options.tmpl

13
conf/locale/locale_en-US.ini

@ -1098,11 +1098,16 @@ auths.bind_password = Bind Password
auths.bind_password_helper = Warning: This password is stored in plain text. Do not use a high privileged account. auths.bind_password_helper = Warning: This password is stored in plain text. Do not use a high privileged account.
auths.user_base = User Search Base auths.user_base = User Search Base
auths.user_dn = User DN auths.user_dn = User DN
auths.attribute_username = Username attribute auths.attribute_username = Username Attribute
auths.attribute_username_placeholder = Leave empty to use sign-in form field value for user name. auths.attribute_username_placeholder = Leave empty to use sign-in form field value for user name.
auths.attribute_name = First name attribute auths.attribute_name = First Name Attribute
auths.attribute_surname = Surname attribute auths.attribute_surname = Surname Attribute
auths.attribute_mail = Email attribute auths.attribute_mail = Email Attribute
auths.verify_group_membership = Verify group membership
auths.group_search_base_dn = Group Search Base DN
auths.group_filter = Group Filter
auths.group_attribute_contain_user_list = Group Attribute Containing List of Users
auths.user_attribute_listed_in_group = User Attribute Listed in Group
auths.attributes_in_bind = Fetch attributes in Bind DN context auths.attributes_in_bind = Fetch attributes in Bind DN context
auths.filter = User Filter auths.filter = User Filter
auths.admin_filter = Admin Filter auths.admin_filter = Admin Filter

59
pkg/auth/ldap/ldap.go

@ -42,10 +42,10 @@ type Source struct {
AttributesInBind bool // fetch attributes in bind context (not user) AttributesInBind bool // fetch attributes in bind context (not user)
Filter string // Query filter to validate entry Filter string // Query filter to validate entry
AdminFilter string // Query filter to check if user is admin AdminFilter string // Query filter to check if user is admin
GroupsEnabled bool // if the group checking is enabled GroupEnabled bool // if the group checking is enabled
GroupDN string // Group Search Base GroupDN string // Group Search Base
GroupFilter string // Group Name Filter GroupFilter string // Group Name Filter
GroupMemberUid string // Group Attribute containing array of UserUID GroupMemberUID string // Group Attribute containing array of UserUID
UserUID string // User Attribute listed in Group UserUID string // User Attribute listed in Group
Enabled bool // if this source is disabled Enabled bool // if this source is disabled
} }
@ -54,7 +54,7 @@ func (ls *Source) sanitizedUserQuery(username string) (string, bool) {
// See http://tools.ietf.org/search/rfc4515 // See http://tools.ietf.org/search/rfc4515
badCharacters := "\x00()*\\" badCharacters := "\x00()*\\"
if strings.ContainsAny(username, badCharacters) { if strings.ContainsAny(username, badCharacters) {
log.Trace("Username contains invalid query characters: %s", username) log.Trace("LDAP: Username contains invalid query characters: %s", username)
return "", false return "", false
} }
@ -65,7 +65,7 @@ func (ls *Source) sanitizedUserDN(username string) (string, bool) {
// See http://tools.ietf.org/search/rfc4514: "special characters" // See http://tools.ietf.org/search/rfc4514: "special characters"
badCharacters := "\x00()*\\,='\"#+;<>" badCharacters := "\x00()*\\,='\"#+;<>"
if strings.ContainsAny(username, badCharacters) || strings.HasPrefix(username, " ") || strings.HasSuffix(username, " ") { if strings.ContainsAny(username, badCharacters) || strings.HasPrefix(username, " ") || strings.HasSuffix(username, " ") {
log.Trace("Username contains invalid query characters: %s", username) log.Trace("LDAP: Username contains invalid query characters: %s", username)
return "", false return "", false
} }
@ -76,7 +76,7 @@ func (ls *Source) sanitizedGroupFilter(group string) (string, bool) {
// See http://tools.ietf.org/search/rfc4515 // See http://tools.ietf.org/search/rfc4515
badCharacters := "\x00*\\" badCharacters := "\x00*\\"
if strings.ContainsAny(group, badCharacters) { if strings.ContainsAny(group, badCharacters) {
log.Trace("Group filter invalid query characters: %s", group) log.Trace("LDAP: Group filter invalid query characters: %s", group)
return "", false return "", false
} }
@ -87,7 +87,7 @@ func (ls *Source) sanitizedGroupDN(groupDn string) (string, bool) {
// See http://tools.ietf.org/search/rfc4514: "special characters" // See http://tools.ietf.org/search/rfc4514: "special characters"
badCharacters := "\x00()*\\'\"#+;<>" badCharacters := "\x00()*\\'\"#+;<>"
if strings.ContainsAny(groupDn, badCharacters) || strings.HasPrefix(groupDn, " ") || strings.HasSuffix(groupDn, " ") { if strings.ContainsAny(groupDn, badCharacters) || strings.HasPrefix(groupDn, " ") || strings.HasSuffix(groupDn, " ") {
log.Trace("Group DN contains invalid query characters: %s", groupDn) log.Trace("LDAP: Group DN contains invalid query characters: %s", groupDn)
return "", false return "", false
} }
@ -99,12 +99,12 @@ func (ls *Source) findUserDN(l *ldap.Conn, name string) (string, bool) {
if ls.BindDN != "" && ls.BindPassword != "" { if ls.BindDN != "" && ls.BindPassword != "" {
err := l.Bind(ls.BindDN, ls.BindPassword) err := l.Bind(ls.BindDN, ls.BindPassword)
if err != nil { if err != nil {
log.Trace("Failed to bind as BindDN '%s': %v", ls.BindDN, err) log.Trace("LDAP: Failed to bind as BindDN '%s': %v", ls.BindDN, err)
return "", false return "", false
} }
log.Trace("Bound as BindDN: %s", ls.BindDN) log.Trace("LDAP: Bound as BindDN: %s", ls.BindDN)
} else { } else {
log.Trace("Proceeding with anonymous LDAP search") log.Trace("LDAP: Proceeding with anonymous LDAP search")
} }
// A search for the user. // A search for the user.
@ -113,7 +113,7 @@ func (ls *Source) findUserDN(l *ldap.Conn, name string) (string, bool) {
return "", false return "", false
} }
log.Trace("Searching for DN using filter '%s' and base '%s'", userFilter, ls.UserBase) log.Trace("LDAP: Searching for DN using filter '%s' and base '%s'", userFilter, ls.UserBase)
search := ldap.NewSearchRequest( search := ldap.NewSearchRequest(
ls.UserBase, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, ls.UserBase, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0,
false, userFilter, []string{}, nil) false, userFilter, []string{}, nil)
@ -121,16 +121,16 @@ func (ls *Source) findUserDN(l *ldap.Conn, name string) (string, bool) {
// Ensure we found a user // Ensure we found a user
sr, err := l.Search(search) sr, err := l.Search(search)
if err != nil || len(sr.Entries) < 1 { if err != nil || len(sr.Entries) < 1 {
log.Trace("Failed search using filter '%s': %v", userFilter, err) log.Trace("LDAP: Failed search using filter '%s': %v", userFilter, err)
return "", false return "", false
} else if len(sr.Entries) > 1 { } else if len(sr.Entries) > 1 {
log.Trace("Filter '%s' returned more than one user", userFilter) log.Trace("LDAP: Filter '%s' returned more than one user", userFilter)
return "", false return "", false
} }
userDN := sr.Entries[0].DN userDN := sr.Entries[0].DN
if userDN == "" { if userDN == "" {
log.Error(4, "LDAP search was successful, but found no DN!") log.Error(2, "LDAP: Search was successful, but found no DN!")
return "", false return "", false
} }
@ -138,7 +138,7 @@ func (ls *Source) findUserDN(l *ldap.Conn, name string) (string, bool) {
} }
func dial(ls *Source) (*ldap.Conn, error) { func dial(ls *Source) (*ldap.Conn, error) {
log.Trace("Dialing LDAP with security protocol '%v' without verifying: %v", ls.SecurityProtocol, ls.SkipVerify) log.Trace("LDAP: Dialing with security protocol '%v' without verifying: %v", ls.SecurityProtocol, ls.SkipVerify)
tlsCfg := &tls.Config{ tlsCfg := &tls.Config{
ServerName: ls.Host, ServerName: ls.Host,
@ -183,7 +183,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
} }
l, err := dial(ls) l, err := dial(ls)
if err != nil { if err != nil {
log.Error(4, "LDAP connect failed for '%s': %v", ls.Host, err) log.Error(2, "LDAP connect failed for '%s': %v", ls.Host, err)
ls.Enabled = false ls.Enabled = false
return "", "", "", "", false, false return "", "", "", "", false, false
} }
@ -221,7 +221,8 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
return "", "", "", "", false, false return "", "", "", "", false, false
} }
log.Trace("Fetching attributes '%v', '%v', '%v', '%v', '%v' with filter '%s' and base '%s'", ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, ls.UserUID, userFilter, userDN) log.Trace("Fetching attributes '%v', '%v', '%v', '%v', '%v' with filter '%s' and base '%s'",
ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, ls.UserUID, userFilter, userDN)
search := ldap.NewSearchRequest( search := ldap.NewSearchRequest(
userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter, userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter,
[]string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, ls.UserUID}, []string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, ls.UserUID},
@ -229,13 +230,13 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
sr, err := l.Search(search) sr, err := l.Search(search)
if err != nil { if err != nil {
log.Error(4, "LDAP user search failed: %v", err) log.Error(2, "LDAP: User search failed: %v", err)
return "", "", "", "", false, false return "", "", "", "", false, false
} else if len(sr.Entries) < 1 { } else if len(sr.Entries) < 1 {
if directBind { if directBind {
log.Error(4, "User filter inhibited user login") log.Trace("LDAP: User filter inhibited user login")
} else { } else {
log.Error(4, "LDAP search failed: 0 entries") log.Trace("LDAP: User search failed: 0 entries")
} }
return "", "", "", "", false, false return "", "", "", "", false, false
@ -248,7 +249,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
uid := sr.Entries[0].GetAttributeValue(ls.UserUID) uid := sr.Entries[0].GetAttributeValue(ls.UserUID)
// Check group membership // Check group membership
if ls.GroupsEnabled { if ls.GroupEnabled {
groupFilter, ok := ls.sanitizedGroupFilter(ls.GroupFilter) groupFilter, ok := ls.sanitizedGroupFilter(ls.GroupFilter)
if !ok { if !ok {
return "", "", "", "", false, false return "", "", "", "", false, false
@ -258,24 +259,24 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
return "", "", "", "", false, false return "", "", "", "", false, false
} }
log.Trace("Fetching groups '%v' with filter '%s' and base '%s'", ls.GroupMemberUid, groupFilter, groupDN) log.Trace("LDAP: Fetching groups '%v' with filter '%s' and base '%s'", ls.GroupMemberUID, groupFilter, groupDN)
groupSearch := ldap.NewSearchRequest( groupSearch := ldap.NewSearchRequest(
groupDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, groupFilter, groupDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, groupFilter,
[]string{ls.GroupMemberUid}, []string{ls.GroupMemberUID},
nil) nil)
srg, err := l.Search(groupSearch) srg, err := l.Search(groupSearch)
if err != nil { if err != nil {
log.Error(4, "LDAP group search failed: %v", err) log.Error(2, "LDAP: Group search failed: %v", err)
return "", "", "", "", false, false return "", "", "", "", false, false
} else if len(sr.Entries) < 1 { } else if len(sr.Entries) < 1 {
log.Error(4, "LDAP group search failed: 0 entries") log.Error(2, "LDAP: Group search failed: 0 entries")
return "", "", "", "", false, false return "", "", "", "", false, false
} }
isMember := false isMember := false
for _,group := range srg.Entries { for _, group := range srg.Entries {
for _,member := range group.GetAttributeValues(ls.GroupMemberUid) { for _, member := range group.GetAttributeValues(ls.GroupMemberUID) {
if member == uid { if member == uid {
isMember = true isMember = true
} }
@ -283,7 +284,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
} }
if !isMember { if !isMember {
log.Error(4, "LDAP group membership test failed") log.Trace("LDAP: Group membership test failed [username: %s, group_member_uid: %s, user_uid: %s", username, ls.GroupMemberUID, uid)
return "", "", "", "", false, false return "", "", "", "", false, false
} }
} }
@ -298,9 +299,9 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
sr, err = l.Search(search) sr, err = l.Search(search)
if err != nil { if err != nil {
log.Error(4, "LDAP admin search failed: %v", err) log.Error(2, "LDAP: Admin search failed: %v", err)
} else if len(sr.Entries) < 1 { } else if len(sr.Entries) < 1 {
log.Error(4, "LDAP admin search failed: 0 entries") log.Error(2, "LDAP: Admin search failed: 0 entries")
} else { } else {
isAdmin = true isAdmin = true
} }

4
pkg/bindata/bindata.go

File diff suppressed because one or more lines are too long

4
pkg/form/auth.go

@ -26,10 +26,10 @@ type Authentication struct {
AttributesInBind bool AttributesInBind bool
Filter string Filter string
AdminFilter string AdminFilter string
GroupsEnabled bool GroupEnabled bool
GroupDN string GroupDN string
GroupFilter string GroupFilter string
GroupMemberUid string GroupMemberUID string
UserUID string UserUID string
IsActive bool IsActive bool
SMTPAuth string SMTPAuth string

28
public/js/gogs.js

@ -254,19 +254,6 @@ function initRepository() {
$prompt.hide(); $prompt.hide();
} }
}); });
// Enable or select internal/external wiki system and issue tracker.
$('.enable-system').change(function () {
if (this.checked) {
$($(this).data('target')).removeClass('disabled');
} else {
$($(this).data('target')).addClass('disabled');
}
});
$('.enable-system-radio').change(function () {
$($(this).data('enable')).removeClass('disabled');
$($(this).data('disable')).addClass('disabled');
});
} }
// Branches // Branches
@ -1299,7 +1286,7 @@ $(document).ready(function () {
}); });
}); });
// Helpers. // Helpers
$('.delete-button').click(function () { $('.delete-button').click(function () {
var $this = $(this); var $this = $(this);
$('.delete.modal').modal({ $('.delete.modal').modal({
@ -1335,6 +1322,19 @@ $(document).ready(function () {
}); });
}); });
// Check or select on option to enable/disable target region
$('.enable-system').change(function () {
if (this.checked) {
$($(this).data('target')).removeClass('disabled');
} else {
$($(this).data('target')).addClass('disabled');
}
});
$('.enable-system-radio').change(function () {
$($(this).data('enable')).removeClass('disabled');
$($(this).data('disable')).addClass('disabled');
});
// Set anchor. // Set anchor.
$('.markdown').each(function () { $('.markdown').each(function () {
var headers = {}; var headers = {};

10
routers/admin/auths.go

@ -93,11 +93,11 @@ func parseLDAPConfig(f form.Authentication) *models.LDAPConfig {
AttributeMail: f.AttributeMail, AttributeMail: f.AttributeMail,
AttributesInBind: f.AttributesInBind, AttributesInBind: f.AttributesInBind,
Filter: f.Filter, Filter: f.Filter,
GroupsEnabled: f.GroupsEnabled, GroupEnabled: f.GroupEnabled,
GroupDN: f.GroupDN, GroupDN: f.GroupDN,
GroupFilter: f.GroupFilter, GroupFilter: f.GroupFilter,
GroupMemberUid: f.GroupMemberUid, GroupMemberUID: f.GroupMemberUID,
UserUID: f.UserUID, UserUID: f.UserUID,
AdminFilter: f.AdminFilter, AdminFilter: f.AdminFilter,
Enabled: true, Enabled: true,
}, },

37
templates/admin/auth/edit.tmpl

@ -92,27 +92,30 @@
<label for="attribute_mail">{{.i18n.Tr "admin.auths.attribute_mail"}}</label> <label for="attribute_mail">{{.i18n.Tr "admin.auths.attribute_mail"}}</label>
<input id="attribute_mail" name="attribute_mail" value="{{$cfg.AttributeMail}}" placeholder="e.g. mail" required> <input id="attribute_mail" name="attribute_mail" value="{{$cfg.AttributeMail}}" placeholder="e.g. mail" required>
</div> </div>
<div class="inline field"> <div class="inline field">
<div class="ui checkbox"> <div class="ui checkbox">
<label><strong>Verify group membership in LDAP</strong></label> <label><strong>{{.i18n.Tr "admin.auths.verify_group_membership"}}</strong></label>
<input name="groups_enabled" type="checkbox" {{if $cfg.GroupsEnabled}}checked{{end}}> <input class="enable-system" type="checkbox" name="group_enabled" data-target="#group_box" {{if $cfg.GroupEnabled}}checked{{end}}>
</div> </div>
</div> </div>
<div class="field"> <div class="ui segment field {{if not $cfg.GroupEnabled}}disabled{{end}}" id="group_box">
<label for="group_dn">Group search Base DN</label> <div class="field">
<input id="group_dn" name="group_dn" value="{{$cfg.GroupDN}}" placeholder="e.g. ou=group,dc=mydomain,dc=com"> <label for="group_dn">{{.i18n.Tr "admin.auths.group_search_base_dn"}}</label>
</div> <input id="group_dn" name="group_dn" value="{{$cfg.GroupDN}}" placeholder="e.g. ou=group,dc=mydomain,dc=com">
<div class="field"> </div>
<label for="group_filter">Valid groups filter</label> <div class="field">
<input id="group_filter" name="group_filter" value="{{$cfg.GroupFilter}}" placeholder="e.g. (|(cn=gogs_users)(cn=admins))"> <label for="group_filter">{{.i18n.Tr "admin.auths.group_filter"}}</label>
</div> <input id="group_filter" name="group_filter" value="{{$cfg.GroupFilter}}" placeholder="e.g. (|(cn=gogs_users)(cn=admins))">
<div class="field"> </div>
<label for="group_member_uid">Group attribute containing list of users</label> <div class="field">
<input id="group_member_uid" name="group_member_uid" value="{{$cfg.GroupMemberUid}}" placeholder="e.g. memberUid"> <label for="group_member_uid">{{.i18n.Tr "admin.auths.group_attribute_contain_user_list"}}</label>
</div> <input id="group_member_uid" name="group_member_uid" value="{{$cfg.GroupMemberUID}}" placeholder="e.g. memberUid">
<div class="field"> </div>
<label for="user_uid">User attribute listed in group</label> <div class="field">
<input id="user_uid" name="user_uid" value="{{$cfg.UserUID}}" placeholder="e.g. uid"> <label for="user_uid">{{.i18n.Tr "admin.auths.user_attribute_listed_in_group"}}</label>
<input id="user_uid" name="user_uid" value="{{$cfg.UserUID}}" placeholder="e.g. uid">
</div>
</div> </div>
{{if .Source.IsLDAP}} {{if .Source.IsLDAP}}
<div class="inline field"> <div class="inline field">

38
templates/admin/auth/new.tmpl

@ -57,7 +57,6 @@
<label for="bind_dn">{{.i18n.Tr "admin.auths.bind_dn"}}</label> <label for="bind_dn">{{.i18n.Tr "admin.auths.bind_dn"}}</label>
<input id="bind_dn" name="bind_dn" value="{{.bind_dn}}" placeholder="e.g. cn=Search,dc=mydomain,dc=com"> <input id="bind_dn" name="bind_dn" value="{{.bind_dn}}" placeholder="e.g. cn=Search,dc=mydomain,dc=com">
</div> </div>
<input class="fake" type="password">
<div class="ldap field {{if not (eq .type 2)}}hide{{end}}"> <div class="ldap field {{if not (eq .type 2)}}hide{{end}}">
<label for="bind_password">{{.i18n.Tr "admin.auths.bind_password"}}</label> <label for="bind_password">{{.i18n.Tr "admin.auths.bind_password"}}</label>
<input id="bind_password" name="bind_password" type="password" value="{{.bind_password}}"> <input id="bind_password" name="bind_password" type="password" value="{{.bind_password}}">
@ -95,27 +94,30 @@
<label for="attribute_mail">{{.i18n.Tr "admin.auths.attribute_mail"}}</label> <label for="attribute_mail">{{.i18n.Tr "admin.auths.attribute_mail"}}</label>
<input id="attribute_mail" name="attribute_mail" value="{{.attribute_mail}}" placeholder="e.g. mail"> <input id="attribute_mail" name="attribute_mail" value="{{.attribute_mail}}" placeholder="e.g. mail">
</div> </div>
<div class="inline field"> <div class="inline field">
<div class="ui checkbox"> <div class="ui checkbox">
<label><strong>Verify group membership in LDAP</strong></label> <label><strong>{{.i18n.Tr "admin.auths.verify_group_membership"}}</strong></label>
<input name="groups_enabled" type="checkbox"> <input class="enable-system" type="checkbox" name="group_enabled" data-target="#group_box" {{if .group_enabled}}checked{{end}}>
</div> </div>
</div> </div>
<div class="field"> <div class="ui segment field {{if not .group_enabled}}disabled{{end}}" id="group_box">
<label for="group_dn">Group search Base DN</label> <div class="field">
<input id="group_dn" name="group_dn" placeholder="e.g. ou=group,dc=mydomain,dc=com"> <label for="group_dn">{{.i18n.Tr "admin.auths.group_search_base_dn"}}</label>
</div> <input id="group_dn" name="group_dn" value="{{.group_dn}}" placeholder="e.g. ou=group,dc=mydomain,dc=com">
<div class="field"> </div>
<label for="group_filter">Valid groups filter</label> <div class="field">
<input id="group_filter" name="group_filter" placeholder="e.g. (|(cn=gogs_users)(cn=admins))"> <label for="group_filter">{{.i18n.Tr "admin.auths.group_filter"}}</label>
</div> <input id="group_filter" name="group_filter" value="{{.group_filter}}" placeholder="e.g. (|(cn=gogs_users)(cn=admins))">
<div class="field"> </div>
<label for="group_member_uid">Group attribute containing list of users</label> <div class="field">
<input id="group_member_uid" name="group_member_uid" placeholder="e.g. memberUid"> <label for="group_member_uid">{{.i18n.Tr "admin.auths.group_attribute_contain_user_list"}}</label>
</div> <input id="group_member_uid" name="group_member_uid" value="{{.group_member_uid}}" placeholder="e.g. memberUid">
<div class="field"> </div>
<label for="user_uid">User attribute listed in group</label> <div class="field">
<input id="user_uid" name="user_uid" placeholder="e.g. uid"> <label for="user_uid">{{.i18n.Tr "admin.auths.user_attribute_listed_in_group"}}</label>
<input id="user_uid" name="user_uid" value="{{.user_uid}}" placeholder="e.g. uid">
</div>
</div> </div>
</div> </div>

2
templates/repo/settings/options.tmpl

@ -100,7 +100,7 @@
<div class="inline field"> <div class="inline field">
<label>{{.i18n.Tr "repo.wiki"}}</label> <label>{{.i18n.Tr "repo.wiki"}}</label>
<div class="ui checkbox"> <div class="ui checkbox">
<input class="enable-system" name="enable_wiki" type="checkbox" data-target="#wiki_box" {{if .Repository.EnableWiki}}checked{{end}}> <input class="enable-system" type="checkbox" name="enable_wiki" data-target="#wiki_box" {{if .Repository.EnableWiki}}checked{{end}}>
<label>{{.i18n.Tr "repo.settings.wiki_desc"}}</label> <label>{{.i18n.Tr "repo.settings.wiki_desc"}}</label>
</div> </div>
</div> </div>

Loading…
Cancel
Save