package main import ( //"context" "errors" "fmt" garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang" "github.com/go-ldap/ldap/v3" //"github.com/gorilla/mux" "log" "net/http" "strings" ) func checkLoginAPI(w http.ResponseWriter, r *http.Request) (*LoginStatus, error) { username, password, ok := r.BasicAuth() if !ok { w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`) http.Error(w, "Unauthorized", http.StatusUnauthorized) return nil, errors.New("Missing or invalid 'Authenticate: Basic' field") } user_dn := buildUserDN(username) login_info := &LoginInfo{ DN: user_dn, Username: username, Password: password, } l := ldapOpen(w) if l == nil { log.Println(l) http.Error(w, "Internal server error", http.StatusInternalServerError) return nil, errors.New("Unable to open LDAP connection") } err := l.Bind(login_info.DN, login_info.Password) if err != nil { w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`) http.Error(w, "Unauthorized", http.StatusUnauthorized) return nil, errors.New("Unable to bind this user+password combination on the LDAP server") } loginStatus := &LoginStatus{ Info: login_info, conn: l, } requestKind := "(objectClass=organizationalPerson)" if strings.EqualFold(login_info.DN, config.AdminAccount) { requestKind = "(objectclass=*)" } searchRequest := ldap.NewSearchRequest( login_info.DN, ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, requestKind, []string{ "dn", "displayname", "givenname", "sn", "mail", "memberof", "description", "garage_s3_access_key", FIELD_NAME_DIRECTORY_VISIBILITY, FIELD_NAME_PROFILE_PICTURE, }, nil) sr, err := l.Search(searchRequest) if err != nil { log.Println(err) http.Error(w, "Internal server error", http.StatusInternalServerError) return nil, errors.New("Unable to search essential information about the logged user on LDAP") } if len(sr.Entries) != 1 { log.Println(fmt.Sprintf("Unable to find entry for %s", login_info.DN)) http.Error(w, "Internal server error", http.StatusInternalServerError) return nil, errors.New("Not enough or too many entries for this user in the LDAP directory (expect a unique result)") } loginStatus.UserEntry = sr.Entries[0] loginStatus.CanAdmin = strings.EqualFold(loginStatus.Info.DN, config.AdminAccount) loginStatus.CanInvite = false for _, attr := range loginStatus.UserEntry.Attributes { if strings.EqualFold(attr.Name, "memberof") { for _, group := range attr.Values { if config.GroupCanInvite != "" && strings.EqualFold(group, config.GroupCanInvite) { loginStatus.CanInvite = true } if config.GroupCanAdmin != "" && strings.EqualFold(group, config.GroupCanAdmin) { loginStatus.CanAdmin = true } } } } return loginStatus, nil } func checkLoginAndS3API(w http.ResponseWriter, r *http.Request) (*LoginStatus, *garage.KeyInfo, error) { login, err := checkLoginAPI(w, r) if err != nil { return nil, nil, err } keyPair, err := checkS3(login) return login, keyPair, err } func handleAPIGarageBucket(w http.ResponseWriter, r *http.Request) { login, s3key, err := checkLoginAndS3API(w, r) if err != nil { log.Println(err) return } // CHECK PATCH REQUEST // READ BODY JSON // VALIDATE OBJECT // --- bucket query parameter --- // 1. bucket must be owned by the key with owner permission, otherwise throw "unauthorized" (401) // 2. must not end with deuxfleurs.fr or deuxfleurs.org, otherwise throw "forbidden" (403) // --- global --- // 1. can be true, false, or nil (use pointers) // 2. if nil do nothing // 3. if false, throw "not yet implemented" (501) // 4. if true, check that the bucket name does not exist yet in the global namespace, throw "forbidden" (403) // --- quota.size --- // 1. if no quota on the bucket + this field is none, set to 50MB // 2. if lower than 50MB, set to 50MB. If higher than 200MB, set to 200MB // --- quota.files --- // 1. if no quota on the bucket + this field is none, set to 10k // 2. if lower than 10k, set to 10k. If higher than 40k, set to 40k // IF BODY.GLOBAL is not NONE // Add an alias // IF BODY.QUOTA.SIZE is not NONE // Change quota // IF BODY.QUOTA.FILE is not NONE // Change quota log.Println(login, s3key) return }