Add_Directory_and_ProfilePicture #9

Merged
lx merged 13 commits from Add_Directory into main 2021-08-16 14:44:53 +00:00
5 changed files with 61 additions and 95 deletions
Showing only changes of commit f00702b51c - Show all commits

View file

@ -24,43 +24,39 @@ import (
"github.com/nfnt/resize" "github.com/nfnt/resize"
) )
const PROFILE_PICTURE_FIELD_NAME = "profilePicture"
//Upload image through guichet server. //Upload image through guichet server.
func uploadImage(w http.ResponseWriter, r *http.Request, login *LoginStatus) (bool, string, error) { func uploadImage(w http.ResponseWriter, r *http.Request, login *LoginStatus) (string, error) {
file, _, err := r.FormFile("image") file, _, err := r.FormFile("image")
if err == http.ErrMissingFile { if err == http.ErrMissingFile {
return false, "", nil return "", nil
} }
if err != nil { if err != nil {
return false, "", err return "", err
} }
defer file.Close() defer file.Close()
fileType, err := checkImage(file) err = checkImage(file)
if err != nil { if err != nil {
return false, "", err return "", err
}
if fileType == "" {
return false, "", nil
} }
buff := bytes.NewBuffer([]byte{}) buff := bytes.NewBuffer([]byte{})
buff_thumbnail := bytes.NewBuffer([]byte{}) buff_thumbnail := bytes.NewBuffer([]byte{})
err = resizeThumb(file, buff, buff_thumbnail) err = resizeThumb(file, buff, buff_thumbnail)
if err != nil { if err != nil {
return false, "", err return "", err
} }
mc, err := newMimioClient() mc, err := newMinioClient()
if err != nil { if err != nil || mc == nil {
return false, "", err return "", err
}
if mc == nil {
return false, "", err
} }
var name, nameFull string var name, nameFull string
if nameConsul := login.UserEntry.GetAttributeValue("profilImage"); nameConsul != "" { if nameConsul := login.UserEntry.GetAttributeValue(PROFILE_PICTURE_FIELD_NAME); nameConsul != "" {
name = nameConsul name = nameConsul
nameFull = "full_" + name nameFull = "full_" + name
} else { } else {
@ -68,34 +64,34 @@ func uploadImage(w http.ResponseWriter, r *http.Request, login *LoginStatus) (bo
nameFull = "full_" + name nameFull = "full_" + name
} }
_, err = mc.PutObject(context.Background(), "bottin-pictures", name, buff_thumbnail, int64(buff_thumbnail.Len()), minio.PutObjectOptions{ _, err = mc.PutObject(context.Background(), config.S3_Bucket, name, buff_thumbnail, int64(buff_thumbnail.Len()), minio.PutObjectOptions{
ContentType: "image/jpeg", ContentType: "image/jpeg",
}) })
if err != nil { if err != nil {
return false, "", err return "", err
} }
_, err = mc.PutObject(context.Background(), "bottin-pictures", nameFull, buff, int64(buff.Len()), minio.PutObjectOptions{ _, err = mc.PutObject(context.Background(), config.S3_Bucket, nameFull, buff, int64(buff.Len()), minio.PutObjectOptions{
ContentType: "image/jpeg", ContentType: "image/jpeg",
}) })
if err != nil { if err != nil {
return false, "", err return "", err
} }
return true, name, nil return name, nil
} }
func newMimioClient() (*minio.Client, error) { func newMinioClient() (*minio.Client, error) {
endpoint := config.Endpoint endpoint := config.S3_Endpoint
accessKeyID := config.AccesKey accessKeyID := config.S3_AccesKey
secretKeyID := config.SecretKey secretKeyID := config.S3_SecretKey
useSSL := true useSSL := true
//Initialize Minio //Initialize Minio
minioCLient, err := minio.New(endpoint, &minio.Options{ minioCLient, err := minio.New(endpoint, &minio.Options{
Creds: credentials.NewStaticV4(accessKeyID, secretKeyID, ""), Creds: credentials.NewStaticV4(accessKeyID, secretKeyID, ""),
Secure: useSSL, Secure: useSSL,
Region: "garage", Region: config.S3_Region,
}) })
if err != nil { if err != nil {
@ -106,11 +102,11 @@ func newMimioClient() (*minio.Client, error) {
} }
func checkImage(file multipart.File) (string, error) { func checkImage(file multipart.File) error {
buff := make([]byte, 512) //Detect read only the first 512 bytes buff := make([]byte, 512) //Detect read only the first 512 bytes
_, err := file.Read(buff) _, err := file.Read(buff)
if err != nil { if err != nil {
return "", err return err
} }
file.Seek(0, 0) file.Seek(0, 0)
@ -118,9 +114,9 @@ func checkImage(file multipart.File) (string, error) {
fileType = strings.Split(fileType, "/")[0] fileType = strings.Split(fileType, "/")[0]
switch fileType { switch fileType {
case "image": case "image":
return fileType, nil return nil
default: default:
return "", errors.New("bad type") return errors.New("bad type")
} }
} }
@ -128,17 +124,6 @@ func checkImage(file multipart.File) (string, error) {
func resizeThumb(file multipart.File, buff, buff_thumbnail *bytes.Buffer) error { func resizeThumb(file multipart.File, buff, buff_thumbnail *bytes.Buffer) error {
file.Seek(0, 0) file.Seek(0, 0)
images, _, err := image.Decode(file) 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 { if err != nil {
return err return err
} }
@ -164,7 +149,6 @@ func handleDownloadImage(w http.ResponseWriter, r *http.Request) {
//Get input value by user //Get input value by user
dn := mux.Vars(r)["name"] dn := mux.Vars(r)["name"]
size := mux.Vars(r)["size"] size := mux.Vars(r)["size"]
//Check login //Check login
login := checkLogin(w, r) login := checkLogin(w, r)
if login == nil { if login == nil {
@ -173,11 +157,12 @@ func handleDownloadImage(w http.ResponseWriter, r *http.Request) {
var imageName string var imageName string
if dn != "unknown_profile" { if dn != "unknown_profile" {
//Search values with ldap and filter //Search values with ldap and filter
searchRequest := ldap.NewSearchRequest( searchRequest := ldap.NewSearchRequest(
dn, dn,
ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false,
"(objectclass=*)", "(objectclass=*)",
[]string{"profilImage"}, []string{PROFILE_PICTURE_FIELD_NAME},
nil) nil)
sr, err := login.conn.Search(searchRequest) sr, err := login.conn.Search(searchRequest)
@ -189,9 +174,9 @@ func handleDownloadImage(w http.ResponseWriter, r *http.Request) {
http.Error(w, fmt.Sprintf("Not found user: %s cn: %s and numberEntries: %d", dn, strings.Split(dn, ",")[0], len(sr.Entries)), http.StatusInternalServerError) 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 return
} }
imageName = sr.Entries[0].GetAttributeValue("profilImage") imageName = sr.Entries[0].GetAttributeValue(PROFILE_PICTURE_FIELD_NAME)
if imageName == "" { if imageName == "" {
http.Error(w, "User doesn't have profile image", http.StatusInternalServerError) http.Error(w, "User doesn't have profile image", http.StatusNotFound)
return return
} }
} else { } else {
@ -201,9 +186,8 @@ func handleDownloadImage(w http.ResponseWriter, r *http.Request) {
if size == "full" { if size == "full" {
imageName = "full_" + imageName imageName = "full_" + imageName
} }
//Get the object after connect MC //Get the object after connect MC
mc, err := newMimioClient() mc, err := newMinioClient()
if err != nil { if err != nil {
http.Error(w, "MinioClient: "+err.Error(), http.StatusInternalServerError) http.Error(w, "MinioClient: "+err.Error(), http.StatusInternalServerError)
return return
@ -216,29 +200,17 @@ func handleDownloadImage(w http.ResponseWriter, r *http.Request) {
defer obj.Close() defer obj.Close()
objStat, err := obj.Stat() objStat, err := obj.Stat()
if err != nil { if err != nil {
http.Error(w, "MinioObjet: "+err.Error(), http.StatusInternalServerError) http.Error(w, "MiniObjet: "+err.Error(), http.StatusInternalServerError)
return return
} }
//Send JSON through xhttp //Send JSON through xhttp
w.Header().Set("Content-Type", objStat.ContentType) w.Header().Set("Content-Type", objStat.ContentType)
w.Header().Set("Content-Length", strconv.Itoa(int(objStat.Size))) w.Header().Set("Content-Length", strconv.Itoa(int(objStat.Size)))
//http.Error(w, fmt.Sprintf("Length buffer: %d", objStat.Size), http.StatusInternalServerError) //Copy obj in w
buff := make([]byte, objStat.Size) writting, err := io.Copy(w, obj)
if writting != objStat.Size || err != nil {
obj.Seek(0, 0) http.Error(w, fmt.Sprintf("WriteBody: %s, bytes wrote %d on %d", err.Error(), writting, objStat.Size), http.StatusInternalServerError)
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 return
} }
if _, err := w.Write(buff); err != nil {
http.Error(w, "WriteBody: "+err.Error(), http.StatusInternalServerError)
return
}
} }

View file

@ -22,8 +22,8 @@ func handleDirectory(w http.ResponseWriter, r *http.Request) {
} }
type SearchResult struct { type SearchResult struct {
Identifiant string `json:"identifiant"` Id string `json:"id"`
erwan marked this conversation as resolved Outdated
Outdated
Review

L'identifiant, c'est le CN ? Dans tous les cas Identifiant c'est un mot français, il faudrait appeller ça plutôt juste Id.

L'identifiant, c'est le CN ? Dans tous les cas `Identifiant` c'est un mot français, il faudrait appeller ça plutôt juste `Id`.
Name string `json:"name"` Displayname string `json:"displayname"`
erwan marked this conversation as resolved Outdated
Outdated
Review

C'est le displayname que tu prends depuis le LDAP? Dans ce cas il faudrait appeller ça DisplayName

C'est le displayname que tu prends depuis le LDAP? Dans ce cas il faudrait appeller ça `DisplayName`
Email string `json:"email"` Email string `json:"email"`
Description string `json:"description"` Description string `json:"description"`
DN string `json:"dn"` DN string `json:"dn"`
@ -61,15 +61,16 @@ func handleSearch(w http.ResponseWriter, r *http.Request) {
return 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 var result Results
for _, values := range sr.Entries { 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) ||
erwan marked this conversation as resolved Outdated
Outdated
Review
  1. Ce n'est pas toujours cn l'attribut qui contient l'identifiant de la personne, c'est le paramètre config.UserNameAttr qui te donne le bon attribut à checker

  2. On aimerait aussi chercher dans le displayname, et peut-être aussi le mail

1. Ce n'est pas toujours `cn` l'attribut qui contient l'identifiant de la personne, c'est le paramètre `config.UserNameAttr` qui te donne le bon attribut à checker 2. On aimerait aussi chercher dans le displayname, et peut-être aussi le mail
(values.GetAttributeValue("email") != "" && strings.Contains(values.GetAttributeValue("email"), input)) {
result = Results{ result = Results{
Search: append(result.Search, SearchResult{ Search: append(result.Search, SearchResult{
erwan marked this conversation as resolved Outdated
Outdated
Review

Idem, c'est pas cn chez tout le monde

Idem, c'est pas `cn` chez tout le monde
Identifiant: values.GetAttributeValue("cn"), Id: values.GetAttributeValue(config.UserNameAttr),
Name: values.GetAttributeValue("displayname"), Displayname: values.GetAttributeValue("displayname"),
Email: values.GetAttributeValue("email"), Email: values.GetAttributeValue("email"),
Description: values.GetAttributeValue("description"), Description: values.GetAttributeValue("description"),
DN: values.DN, DN: values.DN,
@ -80,13 +81,7 @@ func handleSearch(w http.ResponseWriter, r *http.Request) {
} }
if result.Search == nil { if result.Search == nil {
result = Results{ result = Results{
Search: append(result.Search, SearchResult{ Search: append(result.Search, SearchResult{}),
Identifiant: "",
Name: "",
Email: "",
Description: "",
DN: "",
}),
} }
} }

13
main.go
View file

@ -44,9 +44,11 @@ type ConfigFile struct {
GroupCanInvite string `json:"group_can_invite"` GroupCanInvite string `json:"group_can_invite"`
GroupCanAdmin string `json:"group_can_admin"` GroupCanAdmin string `json:"group_can_admin"`
Endpoint string `json:"endpoint"` S3_Endpoint string `json:"s3_endpoint"`
erwan marked this conversation as resolved Outdated
Outdated
Review

Endpoint de quoi? Il faudrait appeller ça S3Endpoint pour être clair, et les autres les appeller S3AccessKey et S3SecretKey.

Endpoint de quoi? Il faudrait appeller ça `S3Endpoint` pour être clair, et les autres les appeller `S3AccessKey` et `S3SecretKey`.
AccesKey string `json:"acces_key"` S3_AccesKey string `json:"s3_acces_key"`
SecretKey string `json:"secret_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") 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, login_info.DN,
ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false,
erwan marked this conversation as resolved Outdated
Outdated
Review

pas de français ! appller ça profilePicture et non profilImage

pas de français ! appller ça `profilePicture` et non `profilImage`
requestKind, 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) nil)
sr, err := l.Search(searchRequest) 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_password"] = password
session.Values["login_dn"] = user_dn session.Values["login_dn"] = user_dn
erwan marked this conversation as resolved Outdated
Outdated
Review

je pense que tu peux enlever ça du coup

je pense que tu peux enlever ça du coup
//Add Value MessageID
session.Values["MessageID"] = uint32(0)
err = session.Save(r, w) err = session.Save(r, w)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)

View file

@ -56,12 +56,12 @@ func handleProfile(w http.ResponseWriter, r *http.Request) {
} }
data.Visibility = visible data.Visibility = visible
ok, name, err := uploadImage(w, r, login) name, err := uploadImage(w, r, login)
if err != nil { if err != nil {
data.ErrorMessage = err.Error() data.ErrorMessage = err.Error()
} }
if ok { if name != "" {
data.NameImage = 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("sn", []string{data.Surname})
modify_request.Replace("description", []string{data.Description}) modify_request.Replace("description", []string{data.Description})
modify_request.Replace("visibility", []string{data.Visibility}) modify_request.Replace("visibility", []string{data.Visibility})
if ok { if name != "" {
modify_request.Replace("profilImage", []string{data.NameImage}) modify_request.Replace(PROFILE_PICTURE_FIELD_NAME, []string{data.NameImage})
} }
err = login.conn.Modify(modify_request) err = login.conn.Modify(modify_request)

View file

@ -23,20 +23,20 @@ function searchDirectory() {
var row = table.insertRow(0); var row = table.insertRow(0);
var urlName = row.insertCell(0); var urlName = row.insertCell(0);
var identifiant = row.insertCell(1); var identifiant = row.insertCell(1);
var name = row.insertCell(2); var displayname = row.insertCell(2);
var email = row.insertCell(3); var email = row.insertCell(3);
var description = row.insertCell(4); var description = row.insertCell(4);
description.setAttribute("style", "word-break: break-all;"); description.setAttribute("style", "word-break: break-all;");
if (jsonResponse.search[i].dn.localeCompare("")!=0) { if (jsonResponse.search[i].dn.localeCompare("")!=0) {
urlName.innerHTML = `<object data="/image/${jsonResponse.search[i].dn}/little" class=".img-thumbnail"><image src="/image/unknown_profile/little" class=".img-thumbnail"></object>` urlName.innerText = `<object data="/image/${jsonResponse.search[i].dn}/little" class=".img-thumbnail"><image src="/image/unknown_profile/little" class=".img-thumbnail"></object>`
}else { }else {
urlName.innerHTML="" urlName.innerText=""
} }
identifiant.innerHTML = `<a href="/admin/ldap/${jsonResponse.search[i].dn}">${jsonResponse.search[i].identifiant}</a>` identifiant.innerText = `<a href="/admin/ldap/${jsonResponse.search[i].dn}">${jsonResponse.search[i].id}</a>`
name.innerHTML = jsonResponse.search[i].name displayname.innerText = jsonResponse.search[i].displayname
email.innerHTML = jsonResponse.search[i].email email.innerText = jsonResponse.search[i].email
description.innerHTML = jsonResponse.search[i].description description.innerText = jsonResponse.search[i].description
Outdated
Review

faudrait utiliser innerText plutôt que innerHTML, sinon on peut trivialement faire une injection de script

faudrait utiliser innerText plutôt que innerHTML, sinon on peut trivialement faire une injection de script
Outdated
Review

?? une injection de script dans un JS ? je ne comprends pas bien. J'ai quand-même appliqué tes modifications. Mais je ne serais pas contre plus de détails.

?? une injection de script dans un JS ? je ne comprends pas bien. J'ai quand-même appliqué tes modifications. Mais je ne serais pas contre plus de détails.
Outdated
Review

Imagine dans ma description je met <script>alert('coucou')</script>.

Avec innerText, ça apparait tel quel.

Avec innerHTML, quand quelqu'un affiche mon profil, ça lui fait une popup qui affiche coucou.

Imagine dans ma description je met `<script>alert('coucou')</script>`. Avec `innerText`, ça apparait tel quel. Avec `innerHTML`, quand quelqu'un affiche mon profil, ça lui fait une popup qui affiche coucou.
Outdated
Review

Donc si je comprends bien, si un utilisateur crée un compte qui se nomme <script>alert('coucou')</script> alors à chaque fois qu'on le cherchera sur l'annuaire ça produira cette pop-up ?

Donc si je comprends bien, si un utilisateur crée un compte qui se nomme `<script>alert('coucou')</script>` alors à chaque fois qu'on le cherchera sur l'annuaire ça produira cette pop-up ?
} }
old_table.parentNode.replaceChild(table, old_table) old_table.parentNode.replaceChild(table, old_table)