// Copyright 2019 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package password import ( "bytes" "crypto/rand" "math/big" "strings" "sync" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" ) // complexity contains information about a particular kind of password complexity type complexity struct { ValidChars string TrNameOne string } var ( matchComplexityOnce sync.Once validChars string requiredList []complexity charComplexities = map[string]complexity{ "lower": { `abcdefghijklmnopqrstuvwxyz`, "form.password_lowercase_one", }, "upper": { `ABCDEFGHIJKLMNOPQRSTUVWXYZ`, "form.password_uppercase_one", }, "digit": { `0123456789`, "form.password_digit_one", }, "spec": { ` !"#$%&'()*+,-./:;<=>?@[\]^_{|}~` + "`", "form.password_special_one", }, } ) // NewComplexity for preparation func NewComplexity() { matchComplexityOnce.Do(func() { setupComplexity(setting.PasswordComplexity) }) } func setupComplexity(values []string) { if len(values) != 1 || values[0] != "off" { for _, val := range values { if complex, ok := charComplexities[val]; ok { validChars += complex.ValidChars requiredList = append(requiredList, complex) } } if len(requiredList) == 0 { // No valid character classes found; use all classes as default for _, complex := range charComplexities { validChars += complex.ValidChars requiredList = append(requiredList, complex) } } } if validChars == "" { // No complexities to check; provide a sensible default for password generation validChars = charComplexities["lower"].ValidChars + charComplexities["upper"].ValidChars + charComplexities["digit"].ValidChars } } // IsComplexEnough return True if password meets complexity settings func IsComplexEnough(pwd string) bool { NewComplexity() if len(validChars) > 0 { for _, req := range requiredList { if !strings.ContainsAny(req.ValidChars, pwd) { return false } } } return true } // Generate a random password func Generate(n int) (string, error) { NewComplexity() buffer := make([]byte, n) max := big.NewInt(int64(len(validChars))) for { for j := 0; j < n; j++ { rnd, err := rand.Int(rand.Reader, max) if err != nil { return "", err } buffer[j] = validChars[rnd.Int64()] } if IsComplexEnough(string(buffer)) && string(buffer[0]) != " " && string(buffer[n-1]) != " " { return string(buffer), nil } } } // BuildComplexityError builds the error message when password complexity checks fail func BuildComplexityError(ctx *context.Context) string { var buffer bytes.Buffer buffer.WriteString(ctx.Tr("form.password_complexity")) buffer.WriteString("<ul>") for _, c := range requiredList { buffer.WriteString("<li>") buffer.WriteString(ctx.Tr(c.TrNameOne)) buffer.WriteString("</li>") } buffer.WriteString("</ul>") return buffer.String() } n='/gitea.git/log/routers/api/v1/swagger/notify.go'> <input type='hidden' name='h' value='v1.20.0-rc0'/><input type='hidden' name='id' value='d0a18a12704360c06cf48b586956148a17a4505a'/><select name='qt'> <option value='grep'>log msg</option> <option value='author'>author</option> <option value='committer'>committer</option> <option value='range'>range</option> </select> <input class='txt' type='search' size='10' name='q' value=''/> <input type='submit' value='search'/> </form> </td></tr></table> <div class='path'>path: <a href='/gitea.git/tree/?h=v1.20.0-rc0&id=d0a18a12704360c06cf48b586956148a17a4505a'>root</a>/<a href='/gitea.git/tree/routers?h=v1.20.0-rc0&id=d0a18a12704360c06cf48b586956148a17a4505a'>routers</a>/<a href='/gitea.git/tree/routers/api?h=v1.20.0-rc0&id=d0a18a12704360c06cf48b586956148a17a4505a'>api</a>/<a href='/gitea.git/tree/routers/api/v1?h=v1.20.0-rc0&id=d0a18a12704360c06cf48b586956148a17a4505a'>v1</a>/<a href='/gitea.git/tree/routers/api/v1/swagger?h=v1.20.0-rc0&id=d0a18a12704360c06cf48b586956148a17a4505a'>swagger</a>/<a href='/gitea.git/tree/routers/api/v1/swagger/notify.go?h=v1.20.0-rc0&id=d0a18a12704360c06cf48b586956148a17a4505a'>notify.go</a></div><div class='content'>blob: cd30d496e0dc1476525c1465e7076bbe0cda4fb8 (<a href='/gitea.git/plain/routers/api/v1/swagger/notify.go?h=v1.20.0-rc0&id=d0a18a12704360c06cf48b586956148a17a4505a'>plain</a>) <table summary='blob content' class='blob'> <tr><td class='linenumbers'><pre><a id='n1' href='#n1'>1</a> <a id='n2' href='#n2'>2</a> <a id='n3' href='#n3'>3</a> <a id='n4' href='#n4'>4</a> <a id='n5' href='#n5'>5</a> <a id='n6' href='#n6'>6</a> <a id='n7' href='#n7'>7</a> <a id='n8' href='#n8'>8</a> <a id='n9' href='#n9'>9</a> <a id='n10' href='#n10'>10</a> <a id='n11' href='#n11'>11</a> <a id='n12' href='#n12'>12</a> <a id='n13' href='#n13'>13</a> <a id='n14' href='#n14'>14</a> <a id='n15' href='#n15'>15</a> <a id='n16' href='#n16'>16</a> <a id='n17' href='#n17'>17</a> <a id='n18' href='#n18'>18</a> <a id='n19' href='#n19'>19</a> <a id='n20' href='#n20'>20</a> <a id='n21' href='#n21'>21</a> <a id='n22' href='#n22'>22</a> <a id='n23' href='#n23'>23</a> <a id='n24' href='#n24'>24</a> <a id='n25' href='#n25'>25</a> <a id='n26' href='#n26'>26</a> <a id='n27' href='#n27'>27</a> <a id='n28' href='#n28'>28</a> <a id='n29' href='#n29'>29</a> <a id='n30' href='#n30'>30</a> </pre></td> <td class='lines'><pre><code>