From f00702b51c8904f9fb42a90a6e22301ae8d4014a Mon Sep 17 00:00:00 2001 From: MrArmonius Date: Fri, 30 Jul 2021 16:30:15 +0200 Subject: Correction Review Alex Change json Identifiant to ID --- S3.go | 216 +++++++++++++++++++++++++++++++++++++++ directory.go | 21 ++-- main.go | 13 ++- mimioClient.go | 244 -------------------------------------------- profile.go | 8 +- static/javascript/search.js | 14 +-- 6 files changed, 241 insertions(+), 275 deletions(-) create mode 100644 S3.go delete mode 100644 mimioClient.go diff --git a/S3.go b/S3.go new file mode 100644 index 0000000..8dd87b4 --- /dev/null +++ b/S3.go @@ -0,0 +1,216 @@ +package main + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "strconv" + + "image" + "image/jpeg" + _ "image/png" + + "mime/multipart" + "net/http" + "strings" + + "github.com/go-ldap/ldap/v3" + "github.com/google/uuid" + "github.com/gorilla/mux" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + "github.com/nfnt/resize" +) + +const PROFILE_PICTURE_FIELD_NAME = "profilePicture" + +//Upload image through guichet server. +func uploadImage(w http.ResponseWriter, r *http.Request, login *LoginStatus) (string, error) { + file, _, err := r.FormFile("image") + + if err == http.ErrMissingFile { + return "", nil + } + if err != nil { + return "", err + } + defer file.Close() + err = checkImage(file) + if err != nil { + return "", err + } + + buff := bytes.NewBuffer([]byte{}) + buff_thumbnail := bytes.NewBuffer([]byte{}) + err = resizeThumb(file, buff, buff_thumbnail) + if err != nil { + return "", err + } + + mc, err := newMinioClient() + if err != nil || mc == nil { + return "", err + } + + var name, nameFull string + + if nameConsul := login.UserEntry.GetAttributeValue(PROFILE_PICTURE_FIELD_NAME); nameConsul != "" { + name = nameConsul + nameFull = "full_" + name + } else { + name = uuid.New().String() + ".jpeg" + nameFull = "full_" + name + } + + _, err = mc.PutObject(context.Background(), config.S3_Bucket, name, buff_thumbnail, int64(buff_thumbnail.Len()), minio.PutObjectOptions{ + ContentType: "image/jpeg", + }) + if err != nil { + return "", err + } + + _, err = mc.PutObject(context.Background(), config.S3_Bucket, nameFull, buff, int64(buff.Len()), minio.PutObjectOptions{ + ContentType: "image/jpeg", + }) + if err != nil { + return "", err + } + + return name, nil +} + +func newMinioClient() (*minio.Client, error) { + endpoint := config.S3_Endpoint + accessKeyID := config.S3_AccesKey + secretKeyID := config.S3_SecretKey + useSSL := true + + //Initialize Minio + minioCLient, err := minio.New(endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(accessKeyID, secretKeyID, ""), + Secure: useSSL, + Region: config.S3_Region, + }) + + if err != nil { + return nil, err + } + + return minioCLient, nil + +} + +func checkImage(file multipart.File) error { + buff := make([]byte, 512) //Detect read only the first 512 bytes + _, err := file.Read(buff) + if err != nil { + return err + } + file.Seek(0, 0) + + fileType := http.DetectContentType(buff) + fileType = strings.Split(fileType, "/")[0] + switch fileType { + case "image": + return nil + default: + return errors.New("bad type") + } + +} + +func resizeThumb(file multipart.File, buff, buff_thumbnail *bytes.Buffer) error { + file.Seek(0, 0) + images, _, err := image.Decode(file) + if err != nil { + return err + } + buff.Reset() + images = resize.Thumbnail(200, 200, images, resize.Lanczos3) + images_thumbnail := resize.Thumbnail(80, 80, images, resize.Lanczos3) + + err = jpeg.Encode(buff, images, &jpeg.Options{ + Quality: 95, + }) + if err != nil { + return err + } + + err = jpeg.Encode(buff_thumbnail, images_thumbnail, &jpeg.Options{ + Quality: 95, + }) + + return err +} + +func handleDownloadImage(w http.ResponseWriter, r *http.Request) { + //Get input value by user + dn := mux.Vars(r)["name"] + size := mux.Vars(r)["size"] + //Check login + login := checkLogin(w, r) + if login == nil { + return + } + var imageName string + if dn != "unknown_profile" { + //Search values with ldap and filter + + searchRequest := ldap.NewSearchRequest( + dn, + ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, + "(objectclass=*)", + []string{PROFILE_PICTURE_FIELD_NAME}, + nil) + + sr, err := login.conn.Search(searchRequest) + if err != nil { + http.Error(w, "Search: "+err.Error(), http.StatusInternalServerError) + return + } + if len(sr.Entries) != 1 { + http.Error(w, fmt.Sprintf("Not found user: %s cn: %s and numberEntries: %d", dn, strings.Split(dn, ",")[0], len(sr.Entries)), http.StatusInternalServerError) + return + } + imageName = sr.Entries[0].GetAttributeValue(PROFILE_PICTURE_FIELD_NAME) + if imageName == "" { + http.Error(w, "User doesn't have profile image", http.StatusNotFound) + return + } + } else { + imageName = "unknown_profile.jpg" + } + + if size == "full" { + imageName = "full_" + imageName + } + //Get the object after connect MC + mc, err := newMinioClient() + if err != nil { + http.Error(w, "MinioClient: "+err.Error(), http.StatusInternalServerError) + return + } + obj, err := mc.GetObject(context.Background(), "bottin-pictures", imageName, minio.GetObjectOptions{}) + if err != nil { + http.Error(w, "MinioClient: GetObject: "+err.Error(), http.StatusInternalServerError) + return + } + defer obj.Close() + objStat, err := obj.Stat() + if err != nil { + http.Error(w, "MiniObjet: "+err.Error(), http.StatusInternalServerError) + return + } + //Send JSON through xhttp + w.Header().Set("Content-Type", objStat.ContentType) + w.Header().Set("Content-Length", strconv.Itoa(int(objStat.Size))) + //Copy obj in w + writting, err := io.Copy(w, obj) + if writting != objStat.Size || err != nil { + http.Error(w, fmt.Sprintf("WriteBody: %s, bytes wrote %d on %d", err.Error(), writting, objStat.Size), http.StatusInternalServerError) + return + } + +} diff --git a/directory.go b/directory.go index a66ef28..c36bd41 100644 --- a/directory.go +++ b/directory.go @@ -22,8 +22,8 @@ func handleDirectory(w http.ResponseWriter, r *http.Request) { } type SearchResult struct { - Identifiant string `json:"identifiant"` - Name string `json:"name"` + Id string `json:"id"` + Displayname string `json:"displayname"` Email string `json:"email"` Description string `json:"description"` DN string `json:"dn"` @@ -61,15 +61,16 @@ func handleSearch(w http.ResponseWriter, r *http.Request) { return } - //Transform the researh's result in a correct struct to send HSON + //Transform the researh's result in a correct struct to send JSON var result Results for _, values := range sr.Entries { - if strings.Contains(values.GetAttributeValue("cn"), input) { + if strings.Contains(values.GetAttributeValue(config.UserNameAttr), input) || strings.Contains(values.GetAttributeValue("displayname"), input) || + (values.GetAttributeValue("email") != "" && strings.Contains(values.GetAttributeValue("email"), input)) { result = Results{ Search: append(result.Search, SearchResult{ - Identifiant: values.GetAttributeValue("cn"), - Name: values.GetAttributeValue("displayname"), + Id: values.GetAttributeValue(config.UserNameAttr), + Displayname: values.GetAttributeValue("displayname"), Email: values.GetAttributeValue("email"), Description: values.GetAttributeValue("description"), DN: values.DN, @@ -80,13 +81,7 @@ func handleSearch(w http.ResponseWriter, r *http.Request) { } if result.Search == nil { result = Results{ - Search: append(result.Search, SearchResult{ - Identifiant: "", - Name: "", - Email: "", - Description: "", - DN: "", - }), + Search: append(result.Search, SearchResult{}), } } diff --git a/main.go b/main.go index 7dc51df..e1b1e23 100644 --- a/main.go +++ b/main.go @@ -44,9 +44,11 @@ type ConfigFile struct { GroupCanInvite string `json:"group_can_invite"` GroupCanAdmin string `json:"group_can_admin"` - Endpoint string `json:"endpoint"` - AccesKey string `json:"acces_key"` - SecretKey string `json:"secret_key"` + S3_Endpoint string `json:"s3_endpoint"` + S3_AccesKey string `json:"s3_acces_key"` + S3_SecretKey string `json:"s3_secret_key"` + S3_Region string `json:"s3_region"` + S3_Bucket string `json:"s3_bucket"` } var configFlag = flag.String("config", "./config.json", "Configuration file path") @@ -250,7 +252,7 @@ func checkLogin(w http.ResponseWriter, r *http.Request) *LoginStatus { login_info.DN, ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, requestKind, - []string{"dn", "displayname", "givenname", "sn", "mail", "memberof", "visibility", "description", "profilImage"}, + []string{"dn", "displayname", "givenname", "sn", "mail", "memberof", "visibility", "description", PROFILE_PICTURE_FIELD_NAME}, nil) sr, err := l.Search(searchRequest) @@ -398,9 +400,6 @@ func handleLogin(w http.ResponseWriter, r *http.Request) *LoginInfo { session.Values["login_password"] = password session.Values["login_dn"] = user_dn - //Add Value MessageID - session.Values["MessageID"] = uint32(0) - err = session.Save(r, w) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/mimioClient.go b/mimioClient.go deleted file mode 100644 index a3cfdfc..0000000 --- a/mimioClient.go +++ /dev/null @@ -1,244 +0,0 @@ -package main - -import ( - "bytes" - "context" - "errors" - "fmt" - "io" - "strconv" - - "image" - "image/jpeg" - _ "image/png" - - "mime/multipart" - "net/http" - "strings" - - "github.com/go-ldap/ldap/v3" - "github.com/google/uuid" - "github.com/gorilla/mux" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" - "github.com/nfnt/resize" -) - -//Upload image through guichet server. -func uploadImage(w http.ResponseWriter, r *http.Request, login *LoginStatus) (bool, string, error) { - file, _, err := r.FormFile("image") - - if err == http.ErrMissingFile { - return false, "", nil - } - if err != nil { - return false, "", err - } - defer file.Close() - fileType, err := checkImage(file) - if err != nil { - return false, "", err - } - if fileType == "" { - return false, "", nil - } - - buff := bytes.NewBuffer([]byte{}) - buff_thumbnail := bytes.NewBuffer([]byte{}) - err = resizeThumb(file, buff, buff_thumbnail) - if err != nil { - return false, "", err - } - - mc, err := newMimioClient() - if err != nil { - return false, "", err - } - if mc == nil { - return false, "", err - } - - var name, nameFull string - - if nameConsul := login.UserEntry.GetAttributeValue("profilImage"); nameConsul != "" { - name = nameConsul - nameFull = "full_" + name - } else { - name = uuid.New().String() + ".jpeg" - nameFull = "full_" + name - } - - _, err = mc.PutObject(context.Background(), "bottin-pictures", name, buff_thumbnail, int64(buff_thumbnail.Len()), minio.PutObjectOptions{ - ContentType: "image/jpeg", - }) - if err != nil { - return false, "", err - } - - _, err = mc.PutObject(context.Background(), "bottin-pictures", nameFull, buff, int64(buff.Len()), minio.PutObjectOptions{ - ContentType: "image/jpeg", - }) - if err != nil { - return false, "", err - } - - return true, name, nil -} - -func newMimioClient() (*minio.Client, error) { - endpoint := config.Endpoint - accessKeyID := config.AccesKey - secretKeyID := config.SecretKey - useSSL := true - - //Initialize Minio - minioCLient, err := minio.New(endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(accessKeyID, secretKeyID, ""), - Secure: useSSL, - Region: "garage", - }) - - if err != nil { - return nil, err - } - - return minioCLient, nil - -} - -func checkImage(file multipart.File) (string, error) { - buff := make([]byte, 512) //Detect read only the first 512 bytes - _, err := file.Read(buff) - if err != nil { - return "", err - } - file.Seek(0, 0) - - fileType := http.DetectContentType(buff) - fileType = strings.Split(fileType, "/")[0] - switch fileType { - case "image": - return fileType, nil - default: - return "", errors.New("bad type") - } - -} - -func resizeThumb(file multipart.File, buff, buff_thumbnail *bytes.Buffer) error { - file.Seek(0, 0) - images, _, err := image.Decode(file) - if err != nil { - return errors.New("Decode: " + err.Error()) - } - //Encode image to jpeg a first time to eliminate all problems - err = jpeg.Encode(buff, images, &jpeg.Options{ - Quality: 100, //Between 1 to 100, higher is better - }) - if err != nil { - return err - } - images, _, err = image.Decode(buff) - if err != nil { - return err - } - buff.Reset() - images = resize.Thumbnail(200, 200, images, resize.Lanczos3) - images_thumbnail := resize.Thumbnail(80, 80, images, resize.Lanczos3) - - err = jpeg.Encode(buff, images, &jpeg.Options{ - Quality: 95, - }) - if err != nil { - return err - } - - err = jpeg.Encode(buff_thumbnail, images_thumbnail, &jpeg.Options{ - Quality: 95, - }) - - return err -} - -func handleDownloadImage(w http.ResponseWriter, r *http.Request) { - //Get input value by user - dn := mux.Vars(r)["name"] - size := mux.Vars(r)["size"] - - //Check login - login := checkLogin(w, r) - if login == nil { - return - } - var imageName string - if dn != "unknown_profile" { - //Search values with ldap and filter - searchRequest := ldap.NewSearchRequest( - dn, - ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, - "(objectclass=*)", - []string{"profilImage"}, - nil) - - sr, err := login.conn.Search(searchRequest) - if err != nil { - http.Error(w, "Search: "+err.Error(), http.StatusInternalServerError) - return - } - if len(sr.Entries) != 1 { - http.Error(w, fmt.Sprintf("Not found user: %s cn: %s and numberEntries: %d", dn, strings.Split(dn, ",")[0], len(sr.Entries)), http.StatusInternalServerError) - return - } - imageName = sr.Entries[0].GetAttributeValue("profilImage") - if imageName == "" { - http.Error(w, "User doesn't have profile image", http.StatusInternalServerError) - return - } - } else { - imageName = "unknown_profile.jpg" - } - - if size == "full" { - imageName = "full_" + imageName - } - - //Get the object after connect MC - mc, err := newMimioClient() - if err != nil { - http.Error(w, "MinioClient: "+err.Error(), http.StatusInternalServerError) - return - } - obj, err := mc.GetObject(context.Background(), "bottin-pictures", imageName, minio.GetObjectOptions{}) - if err != nil { - http.Error(w, "MinioClient: GetObject: "+err.Error(), http.StatusInternalServerError) - return - } - defer obj.Close() - objStat, err := obj.Stat() - if err != nil { - http.Error(w, "MinioObjet: "+err.Error(), http.StatusInternalServerError) - return - } - - //Send JSON through xhttp - w.Header().Set("Content-Type", objStat.ContentType) - w.Header().Set("Content-Length", strconv.Itoa(int(objStat.Size))) - //http.Error(w, fmt.Sprintf("Length buffer: %d", objStat.Size), http.StatusInternalServerError) - buff := make([]byte, objStat.Size) - - obj.Seek(0, 0) - n, err := obj.Read(buff) - if err != nil && err != io.EOF { - http.Error(w, fmt.Sprintf("Read Error: %s, bytes Read: %d, bytes in file: %d", err.Error(), n, objStat.Size), http.StatusInternalServerError) - return - } - if int64(n) != objStat.Size { - http.Error(w, fmt.Sprintf("Read %d bytes on %d bytes", n, objStat.Size), http.StatusInternalServerError) - return - } - - if _, err := w.Write(buff); err != nil { - http.Error(w, "WriteBody: "+err.Error(), http.StatusInternalServerError) - return - } -} diff --git a/profile.go b/profile.go index ab43922..21cfe0a 100644 --- a/profile.go +++ b/profile.go @@ -56,12 +56,12 @@ func handleProfile(w http.ResponseWriter, r *http.Request) { } data.Visibility = visible - ok, name, err := uploadImage(w, r, login) + name, err := uploadImage(w, r, login) if err != nil { data.ErrorMessage = err.Error() } - if ok { + if name != "" { data.NameImage = name } @@ -71,8 +71,8 @@ func handleProfile(w http.ResponseWriter, r *http.Request) { modify_request.Replace("sn", []string{data.Surname}) modify_request.Replace("description", []string{data.Description}) modify_request.Replace("visibility", []string{data.Visibility}) - if ok { - modify_request.Replace("profilImage", []string{data.NameImage}) + if name != "" { + modify_request.Replace(PROFILE_PICTURE_FIELD_NAME, []string{data.NameImage}) } err = login.conn.Modify(modify_request) diff --git a/static/javascript/search.js b/static/javascript/search.js index 212ed15..ea98e34 100644 --- a/static/javascript/search.js +++ b/static/javascript/search.js @@ -23,20 +23,20 @@ function searchDirectory() { var row = table.insertRow(0); var urlName = row.insertCell(0); var identifiant = row.insertCell(1); - var name = row.insertCell(2); + var displayname = row.insertCell(2); var email = row.insertCell(3); var description = row.insertCell(4); description.setAttribute("style", "word-break: break-all;"); if (jsonResponse.search[i].dn.localeCompare("")!=0) { - urlName.innerHTML = `` + urlName.innerText = `` }else { - urlName.innerHTML="" + urlName.innerText="" } - identifiant.innerHTML = `${jsonResponse.search[i].identifiant}` - name.innerHTML = jsonResponse.search[i].name - email.innerHTML = jsonResponse.search[i].email - description.innerHTML = jsonResponse.search[i].description + identifiant.innerText = `${jsonResponse.search[i].id}` + displayname.innerText = jsonResponse.search[i].displayname + email.innerText = jsonResponse.search[i].email + description.innerText = jsonResponse.search[i].description } old_table.parentNode.replaceChild(table, old_table) -- cgit v1.2.3