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"
)
const PROFILE_PICTURE_FIELD_NAME = "profilePicture"
//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")
if err == http.ErrMissingFile {
return false, "", nil
return "", nil
}
if err != nil {
return false, "", err
return "", err
}
defer file.Close()
fileType, err := checkImage(file)
err = checkImage(file)
if err != nil {
return false, "", err
}
if fileType == "" {
return false, "", nil
return "", err
}
buff := bytes.NewBuffer([]byte{})
buff_thumbnail := bytes.NewBuffer([]byte{})
err = resizeThumb(file, buff, buff_thumbnail)
if err != nil {
return false, "", err
return "", err
}
mc, err := newMimioClient()
if err != nil {
return false, "", err
}
if mc == nil {
return false, "", err
mc, err := newMinioClient()
if err != nil || mc == nil {
return "", err
}
var name, nameFull string
if nameConsul := login.UserEntry.GetAttributeValue("profilImage"); nameConsul != "" {
if nameConsul := login.UserEntry.GetAttributeValue(PROFILE_PICTURE_FIELD_NAME); nameConsul != "" {
name = nameConsul
nameFull = "full_" + name
} else {
@ -68,34 +64,34 @@ func uploadImage(w http.ResponseWriter, r *http.Request, login *LoginStatus) (bo
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",
})
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",
})
if err != nil {
return false, "", err
return "", err
}
return true, name, nil
return name, nil
}
func newMimioClient() (*minio.Client, error) {
endpoint := config.Endpoint
accessKeyID := config.AccesKey
secretKeyID := config.SecretKey
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: "garage",
Region: config.S3_Region,
})
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
_, err := file.Read(buff)
if err != nil {
return "", err
return err
}
file.Seek(0, 0)
@ -118,9 +114,9 @@ func checkImage(file multipart.File) (string, error) {
fileType = strings.Split(fileType, "/")[0]
switch fileType {
case "image":
return fileType, nil
return nil
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 {
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
}
@ -164,7 +149,6 @@ 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 {
@ -173,11 +157,12 @@ func handleDownloadImage(w http.ResponseWriter, r *http.Request) {
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"},
[]string{PROFILE_PICTURE_FIELD_NAME},
nil)
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)
return
}
imageName = sr.Entries[0].GetAttributeValue("profilImage")
imageName = sr.Entries[0].GetAttributeValue(PROFILE_PICTURE_FIELD_NAME)
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
}
} else {
@ -201,9 +186,8 @@ func handleDownloadImage(w http.ResponseWriter, r *http.Request) {
if size == "full" {
imageName = "full_" + imageName
}
//Get the object after connect MC
mc, err := newMimioClient()
mc, err := newMinioClient()
if err != nil {
http.Error(w, "MinioClient: "+err.Error(), http.StatusInternalServerError)
return
@ -216,29 +200,17 @@ func handleDownloadImage(w http.ResponseWriter, r *http.Request) {
defer obj.Close()
objStat, err := obj.Stat()
if err != nil {
http.Error(w, "MinioObjet: "+err.Error(), http.StatusInternalServerError)
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)))
//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)
//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
}
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 {
Identifiant string `json:"identifiant"`
Name string `json:"name"`
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`.
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"`
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) ||
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{
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"),
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{}),
}
}

13
main.go
View file

@ -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"`
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`.
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,
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,
[]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
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)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)

View file

@ -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)

View file

@ -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 = `<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 {
urlName.innerHTML=""
urlName.innerText=""
}
identifiant.innerHTML = `<a href="/admin/ldap/${jsonResponse.search[i].dn}">${jsonResponse.search[i].identifiant}</a>`
name.innerHTML = jsonResponse.search[i].name
email.innerHTML = jsonResponse.search[i].email
description.innerHTML = jsonResponse.search[i].description
identifiant.innerText = `<a href="/admin/ldap/${jsonResponse.search[i].dn}">${jsonResponse.search[i].id}</a>`
displayname.innerText = jsonResponse.search[i].displayname
email.innerText = jsonResponse.search[i].email
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)