2022-02-13 23:39:27 +08:00
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
2021-03-06 16:39:17 +08:00
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2021-02-11 22:56:08 +08:00
package object
2021-05-01 16:50:47 +08:00
import (
"fmt"
2022-04-16 15:10:03 +08:00
"strings"
2022-08-17 01:39:53 +08:00
"time"
2022-08-16 00:14:26 +08:00
"unicode"
2021-05-01 17:45:01 +08:00
2022-01-20 14:11:46 +08:00
"github.com/casdoor/casdoor/cred"
2023-04-25 23:05:53 +08:00
"github.com/casdoor/casdoor/form"
2022-10-23 15:16:24 +08:00
"github.com/casdoor/casdoor/i18n"
2022-01-20 14:11:46 +08:00
"github.com/casdoor/casdoor/util"
2021-12-10 22:45:01 +08:00
goldap "github.com/go-ldap/ldap/v3"
2021-05-01 16:50:47 +08:00
)
2022-08-17 01:39:53 +08:00
const (
SigninWrongTimesLimit = 5
LastSignWrongTimeDuration = time . Minute * 15
)
2023-04-25 23:05:53 +08:00
func CheckUserSignup ( application * Application , organization * Organization , form * form . AuthForm , lang string ) string {
2021-06-17 00:49:02 +08:00
if organization == nil {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:Organization does not exist" )
2021-06-17 00:49:02 +08:00
}
if application . IsSignupItemVisible ( "Username" ) {
2023-04-25 23:05:53 +08:00
if len ( form . Username ) <= 1 {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:Username must have at least 2 characters" )
2022-08-16 00:14:26 +08:00
}
2023-04-25 23:05:53 +08:00
if unicode . IsDigit ( rune ( form . Username [ 0 ] ) ) {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:Username cannot start with a digit" )
2022-08-16 00:14:26 +08:00
}
2023-04-25 23:05:53 +08:00
if util . IsEmailValid ( form . Username ) {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:Username cannot be an email address" )
2022-08-16 00:14:26 +08:00
}
2023-06-29 21:44:14 +08:00
if util . ReWhiteSpace . MatchString ( form . Username ) {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:Username cannot contain white spaces" )
2022-08-16 00:14:26 +08:00
}
2023-01-10 22:34:08 +08:00
2023-04-25 23:05:53 +08:00
if msg := CheckUsername ( form . Username , lang ) ; msg != "" {
2022-10-17 18:01:01 +08:00
return msg
}
2023-04-25 23:05:53 +08:00
if HasUserByField ( organization . Name , "name" , form . Username ) {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:Username already exists" )
2021-06-17 00:49:02 +08:00
}
2023-04-25 23:05:53 +08:00
if HasUserByField ( organization . Name , "email" , form . Email ) {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:Email already exists" )
2022-08-16 00:14:26 +08:00
}
2023-04-25 23:05:53 +08:00
if HasUserByField ( organization . Name , "phone" , form . Phone ) {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:Phone already exists" )
2022-08-16 00:14:26 +08:00
}
2021-06-17 00:49:02 +08:00
}
2021-05-15 13:54:23 +08:00
2023-04-25 23:05:53 +08:00
if len ( form . Password ) <= 5 {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:Password must have at least 6 characters" )
2021-02-11 22:56:08 +08:00
}
2021-06-17 00:49:02 +08:00
if application . IsSignupItemVisible ( "Email" ) {
2023-04-25 23:05:53 +08:00
if form . Email == "" {
2021-11-09 20:28:27 +08:00
if application . IsSignupItemRequired ( "Email" ) {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:Email cannot be empty" )
2021-11-09 20:28:27 +08:00
}
2023-03-15 23:44:38 +08:00
} else {
2023-04-25 23:05:53 +08:00
if HasUserByField ( organization . Name , "email" , form . Email ) {
2023-03-15 23:44:38 +08:00
return i18n . Translate ( lang , "check:Email already exists" )
2023-04-25 23:05:53 +08:00
} else if ! util . IsEmailValid ( form . Email ) {
2023-03-15 23:44:38 +08:00
return i18n . Translate ( lang , "check:Email is invalid" )
}
2021-06-17 00:49:02 +08:00
}
}
if application . IsSignupItemVisible ( "Phone" ) {
2023-04-25 23:05:53 +08:00
if form . Phone == "" {
2021-11-09 20:28:27 +08:00
if application . IsSignupItemRequired ( "Phone" ) {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:Phone cannot be empty" )
2021-11-09 20:28:27 +08:00
}
2023-03-15 23:44:38 +08:00
} else {
2023-04-25 23:05:53 +08:00
if HasUserByField ( organization . Name , "phone" , form . Phone ) {
2023-03-15 23:44:38 +08:00
return i18n . Translate ( lang , "check:Phone already exists" )
2023-04-25 23:05:53 +08:00
} else if ! util . IsPhoneAllowInRegin ( form . CountryCode , organization . CountryCodes ) {
2023-03-15 23:44:38 +08:00
return i18n . Translate ( lang , "check:Your region is not allow to signup by phone" )
2023-04-25 23:05:53 +08:00
} else if ! util . IsPhoneValid ( form . Phone , form . CountryCode ) {
2023-03-15 23:44:38 +08:00
return i18n . Translate ( lang , "check:Phone number is invalid" )
}
2021-06-17 00:49:02 +08:00
}
}
if application . IsSignupItemVisible ( "Display name" ) {
2023-04-25 23:05:53 +08:00
if application . GetSignupItemRule ( "Display name" ) == "First, last" && ( form . FirstName != "" || form . LastName != "" ) {
if form . FirstName == "" {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:FirstName cannot be blank" )
2023-04-25 23:05:53 +08:00
} else if form . LastName == "" {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:LastName cannot be blank" )
2022-02-27 14:02:52 +08:00
}
} else {
2023-04-25 23:05:53 +08:00
if form . Name == "" {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:DisplayName cannot be blank" )
2022-02-27 14:02:52 +08:00
} else if application . GetSignupItemRule ( "Display name" ) == "Real name" {
2023-04-25 23:05:53 +08:00
if ! isValidRealName ( form . Name ) {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:DisplayName is not valid real name" )
2022-02-27 14:02:52 +08:00
}
2021-06-17 11:55:06 +08:00
}
2021-06-17 00:49:02 +08:00
}
}
if application . IsSignupItemVisible ( "Affiliation" ) {
2023-04-25 23:05:53 +08:00
if form . Affiliation == "" {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:Affiliation cannot be blank" )
2021-06-17 00:49:02 +08:00
}
}
return ""
2021-02-11 22:56:08 +08:00
}
2022-10-23 15:16:24 +08:00
func checkSigninErrorTimes ( user * User , lang string ) string {
2022-08-17 01:39:53 +08:00
if user . SigninWrongTimes >= SigninWrongTimesLimit {
lastSignWrongTime , _ := time . Parse ( time . RFC3339 , user . LastSigninWrongTime )
passedTime := time . Now ( ) . UTC ( ) . Sub ( lastSignWrongTime )
2023-01-06 19:49:00 +08:00
minutes := int ( LastSignWrongTimeDuration . Minutes ( ) - passedTime . Minutes ( ) )
2022-08-17 01:39:53 +08:00
// deny the login if the error times is greater than the limit and the last login time is less than the duration
2023-01-06 19:49:00 +08:00
if minutes > 0 {
return fmt . Sprintf ( i18n . Translate ( lang , "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again" ) , minutes )
2022-08-17 01:39:53 +08:00
}
// reset the error times
user . SigninWrongTimes = 0
UpdateUser ( user . GetId ( ) , user , [ ] string { "signin_wrong_times" } , user . IsGlobalAdmin )
}
return ""
}
2023-04-22 16:16:25 +08:00
func CheckPassword ( user * User , password string , lang string , options ... bool ) string {
enableCaptcha := false
if len ( options ) > 0 {
enableCaptcha = options [ 0 ]
}
2022-08-17 01:39:53 +08:00
// check the login error times
2023-04-22 16:16:25 +08:00
if ! enableCaptcha {
if msg := checkSigninErrorTimes ( user , lang ) ; msg != "" {
return msg
}
2022-08-17 01:39:53 +08:00
}
2023-05-30 15:49:39 +08:00
organization , err := GetOrganizationByUser ( user )
if err != nil {
panic ( err )
}
2021-09-04 22:20:47 +08:00
if organization == nil {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:Organization does not exist" )
2021-09-04 22:20:47 +08:00
}
2021-11-04 21:08:43 +08:00
2023-05-16 20:14:05 +08:00
passwordType := user . PasswordType
if passwordType == "" {
passwordType = organization . PasswordType
}
credManager := cred . GetCredManager ( passwordType )
2021-11-04 21:08:43 +08:00
if credManager != nil {
2021-12-22 20:56:22 +08:00
if organization . MasterPassword != "" {
if credManager . IsPasswordCorrect ( password , organization . MasterPassword , "" , organization . PasswordSalt ) {
2022-08-17 01:39:53 +08:00
resetUserSigninErrorTimes ( user )
2021-12-22 20:56:22 +08:00
return ""
}
2021-11-06 21:14:53 +08:00
}
2021-12-22 13:56:32 +08:00
if credManager . IsPasswordCorrect ( password , user . Password , user . PasswordSalt , organization . PasswordSalt ) {
2022-08-17 01:39:53 +08:00
resetUserSigninErrorTimes ( user )
2021-05-03 10:13:32 +08:00
return ""
}
2022-08-17 01:39:53 +08:00
2023-04-22 16:16:25 +08:00
return recordSigninErrorInfo ( user , lang , enableCaptcha )
2021-05-03 10:13:32 +08:00
} else {
2022-12-07 13:13:23 +08:00
return fmt . Sprintf ( i18n . Translate ( lang , "check:unsupported password type: %s" ) , organization . PasswordType )
2021-05-03 10:13:32 +08:00
}
}
2023-06-17 00:01:20 +08:00
func CheckPasswordComplexityByOrg ( organization * Organization , password string ) string {
errorMsg := checkPasswordComplexity ( password , organization . PasswordOptions )
return errorMsg
}
func CheckPasswordComplexity ( user * User , password string ) string {
organization , _ := GetOrganizationByUser ( user )
return CheckPasswordComplexityByOrg ( organization , password )
}
2023-04-13 14:12:31 +08:00
func checkLdapUserPassword ( user * User , password string , lang string ) string {
2023-05-30 15:49:39 +08:00
ldaps , err := GetLdaps ( user . Owner )
if err != nil {
return err . Error ( )
}
2021-12-10 22:45:01 +08:00
ldapLoginSuccess := false
2023-04-13 14:12:31 +08:00
hit := false
2021-12-10 22:45:01 +08:00
for _ , ldapServer := range ldaps {
2023-03-15 11:12:31 +08:00
conn , err := ldapServer . GetLdapConn ( )
2021-12-10 22:45:01 +08:00
if err != nil {
continue
}
2023-04-13 14:12:31 +08:00
searchReq := goldap . NewSearchRequest ( ldapServer . BaseDn , goldap . ScopeWholeSubtree , goldap . NeverDerefAliases ,
2023-05-19 21:16:59 +08:00
0 , 0 , false , ldapServer . buildAuthFilterString ( user ) , [ ] string { } , nil )
2023-04-13 14:12:31 +08:00
2021-12-10 22:45:01 +08:00
searchResult , err := conn . Conn . Search ( searchReq )
if err != nil {
2023-04-13 14:12:31 +08:00
return err . Error ( )
2021-12-10 22:45:01 +08:00
}
if len ( searchResult . Entries ) == 0 {
continue
2023-04-13 14:12:31 +08:00
}
if len ( searchResult . Entries ) > 1 {
return i18n . Translate ( lang , "check:Multiple accounts with same uid, please check your ldap server" )
2021-12-10 22:45:01 +08:00
}
2023-04-13 14:12:31 +08:00
hit = true
2021-12-10 22:45:01 +08:00
dn := searchResult . Entries [ 0 ] . DN
if err := conn . Conn . Bind ( dn , password ) ; err == nil {
ldapLoginSuccess = true
break
}
}
if ! ldapLoginSuccess {
2023-04-13 14:12:31 +08:00
if ! hit {
return "user not exist"
}
return i18n . Translate ( lang , "check:LDAP user name or password incorrect" )
2021-12-10 22:45:01 +08:00
}
2023-04-13 14:12:31 +08:00
return ""
2021-12-10 22:45:01 +08:00
}
2023-04-22 16:16:25 +08:00
func CheckUserPassword ( organization string , username string , password string , lang string , options ... bool ) ( * User , string ) {
enableCaptcha := false
if len ( options ) > 0 {
enableCaptcha = options [ 0 ]
}
2023-05-30 15:49:39 +08:00
user , err := GetUserByFields ( organization , username )
if err != nil {
panic ( err )
}
2023-05-23 17:54:51 +08:00
if user == nil || user . IsDeleted {
2023-01-06 20:24:14 +08:00
return nil , fmt . Sprintf ( i18n . Translate ( lang , "general:The user: %s doesn't exist" ) , util . GetId ( organization , username ) )
2021-02-11 22:56:08 +08:00
}
2021-05-05 23:32:21 +08:00
if user . IsForbidden {
2022-12-07 13:13:23 +08:00
return nil , i18n . Translate ( lang , "check:The user is forbidden to sign in, please contact the administrator" )
2021-05-05 23:32:21 +08:00
}
2022-03-28 17:19:58 +08:00
if user . Ldap != "" {
2022-08-07 12:26:14 +08:00
// ONLY for ldap users
2023-04-13 14:12:31 +08:00
if msg := checkLdapUserPassword ( user , password , lang ) ; msg != "" {
if msg == "user not exist" {
return nil , fmt . Sprintf ( i18n . Translate ( lang , "check:The user: %s doesn't exist in LDAP server" ) , username )
}
return nil , msg
}
2022-03-28 17:19:58 +08:00
} else {
2023-04-22 16:16:25 +08:00
if msg := CheckPassword ( user , password , lang , enableCaptcha ) ; msg != "" {
2022-03-28 17:19:58 +08:00
return nil , msg
2022-03-12 21:06:38 +08:00
}
2021-02-11 22:56:08 +08:00
}
2021-05-01 19:45:40 +08:00
return user , ""
2021-02-11 22:56:08 +08:00
}
2022-01-26 19:36:36 +08:00
2023-04-06 23:06:18 +08:00
func CheckUserPermission ( requestUserId , userId string , strict bool , lang string ) ( bool , error ) {
2022-04-16 15:10:03 +08:00
if requestUserId == "" {
2023-01-06 20:12:32 +08:00
return false , fmt . Errorf ( i18n . Translate ( lang , "general:Please login first" ) )
2022-04-16 15:10:03 +08:00
}
2023-04-06 23:06:18 +08:00
userOwner := util . GetOwnerFromId ( userId )
2022-07-31 10:54:41 +08:00
if userId != "" {
2023-05-30 15:49:39 +08:00
targetUser , err := GetUser ( userId )
if err != nil {
panic ( err )
}
2022-07-31 10:54:41 +08:00
if targetUser == nil {
2023-05-12 13:03:43 +08:00
if strings . HasPrefix ( requestUserId , "built-in/" ) {
return true , nil
}
2023-01-06 20:12:32 +08:00
return false , fmt . Errorf ( i18n . Translate ( lang , "general:The user: %s doesn't exist" ) , userId )
2022-07-31 10:54:41 +08:00
}
userOwner = targetUser . Owner
2022-04-16 15:10:03 +08:00
}
hasPermission := false
if strings . HasPrefix ( requestUserId , "app/" ) {
hasPermission = true
} else {
2023-05-30 15:49:39 +08:00
requestUser , err := GetUser ( requestUserId )
if err != nil {
return false , err
}
2022-04-16 15:10:03 +08:00
if requestUser == nil {
2022-12-07 13:13:23 +08:00
return false , fmt . Errorf ( i18n . Translate ( lang , "check:Session outdated, please login again" ) )
2022-04-16 15:10:03 +08:00
}
if requestUser . IsGlobalAdmin {
hasPermission = true
} else if requestUserId == userId {
hasPermission = true
2022-07-31 10:54:41 +08:00
} else if userOwner == requestUser . Owner {
2022-04-16 15:10:03 +08:00
if strict {
hasPermission = requestUser . IsAdmin
} else {
hasPermission = true
}
}
}
2023-01-06 20:12:32 +08:00
return hasPermission , fmt . Errorf ( i18n . Translate ( lang , "auth:Unauthorized operation" ) )
2022-07-13 00:34:35 +08:00
}
2022-07-13 00:50:32 +08:00
func CheckAccessPermission ( userId string , application * Application ) ( bool , error ) {
2023-05-30 15:49:39 +08:00
var err error
2023-05-18 13:36:16 +08:00
if userId == "built-in/admin" {
return true , nil
}
2023-05-30 15:49:39 +08:00
permissions , err := GetPermissions ( application . Organization )
if err != nil {
return false , err
}
2022-07-13 00:50:32 +08:00
allowed := true
2022-07-13 00:34:35 +08:00
for _ , permission := range permissions {
2023-06-16 20:14:27 +08:00
if ! permission . IsEnabled {
2022-07-13 00:50:32 +08:00
continue
}
isHit := false
for _ , resource := range permission . Resources {
if application . Name == resource {
isHit = true
break
2022-07-13 00:34:35 +08:00
}
}
2022-07-13 00:50:32 +08:00
if isHit {
2022-09-02 12:03:13 +08:00
containsAsterisk := ContainsAsterisk ( userId , permission . Users )
if containsAsterisk {
return true , err
}
2023-07-25 17:17:59 +08:00
enforcer := getPermissionEnforcer ( permission )
2022-12-23 14:06:02 +08:00
if allowed , err = enforcer . Enforce ( userId , application . Name , "read" ) ; allowed {
return allowed , err
}
2022-07-13 00:50:32 +08:00
}
2022-07-13 00:34:35 +08:00
}
2022-07-13 00:50:32 +08:00
return allowed , err
}
2022-10-10 19:58:02 +08:00
2022-10-23 15:16:24 +08:00
func CheckUsername ( username string , lang string ) string {
2022-10-17 18:01:01 +08:00
if username == "" {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:Empty username." )
2022-10-17 18:01:01 +08:00
} else if len ( username ) > 39 {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:Username is too long (maximum is 39 characters)." )
2022-10-10 19:58:02 +08:00
}
// https://stackoverflow.com/questions/58726546/github-username-convention-using-regex
2023-06-29 21:44:14 +08:00
if ! util . ReUserName . MatchString ( username ) {
2022-12-07 13:13:23 +08:00
return i18n . Translate ( lang , "check:The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline." )
2022-10-10 19:58:02 +08:00
}
return ""
}
2022-10-28 13:38:14 +08:00
2023-04-09 10:35:30 +08:00
func CheckUpdateUser ( oldUser , user * User , lang string ) string {
2023-01-10 22:34:08 +08:00
if user . DisplayName == "" {
return i18n . Translate ( lang , "user:Display name cannot be empty" )
}
if oldUser . Name != user . Name {
2023-01-17 16:08:37 +07:00
if msg := CheckUsername ( user . Name , lang ) ; msg != "" {
return msg
}
2023-01-10 22:34:08 +08:00
if HasUserByField ( user . Owner , "name" , user . Name ) {
return i18n . Translate ( lang , "check:Username already exists" )
}
}
if oldUser . Email != user . Email {
if HasUserByField ( user . Name , "email" , user . Email ) {
return i18n . Translate ( lang , "check:Email already exists" )
}
}
if oldUser . Phone != user . Phone {
if HasUserByField ( user . Owner , "phone" , user . Phone ) {
return i18n . Translate ( lang , "check:Phone already exists" )
}
}
return ""
}
2023-05-30 15:49:39 +08:00
func CheckToEnableCaptcha ( application * Application , organization , username string ) ( bool , error ) {
2022-10-28 13:38:14 +08:00
if len ( application . Providers ) == 0 {
2023-05-30 15:49:39 +08:00
return false , nil
2022-10-28 13:38:14 +08:00
}
for _ , providerItem := range application . Providers {
if providerItem . Provider == nil {
continue
}
2023-03-05 20:31:46 +08:00
if providerItem . Provider . Category == "Captcha" {
2023-04-22 16:16:25 +08:00
if providerItem . Rule == "Dynamic" {
2023-05-30 15:49:39 +08:00
user , err := GetUserByFields ( organization , username )
if err != nil {
return false , err
}
return user != nil && user . SigninWrongTimes >= SigninWrongTimesLimit , nil
2023-04-22 16:16:25 +08:00
}
2023-05-30 15:49:39 +08:00
return providerItem . Rule == "Always" , nil
2022-10-28 13:38:14 +08:00
}
}
2023-05-30 15:49:39 +08:00
return false , nil
2022-10-28 13:38:14 +08:00
}