Add_Directory_and_ProfilePicture #9
|
@ -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
|
||||
}
|
||||
}
|
21
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"`
|
||||
erwan marked this conversation as resolved
Outdated
|
||||
Displayname string `json:"displayname"`
|
||||
erwan marked this conversation as resolved
Outdated
lx
commented
C'est le displayname que tu prends depuis le LDAP? Dans ce cas il faudrait appeller ça 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
lx
commented
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
lx
commented
Idem, c'est pas 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
|
@ -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
lx
commented
Endpoint de quoi? Il faudrait appeller ça 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
lx
commented
pas de français ! appller ça 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
lx
commented
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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
lx
commented
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
erwan
commented
?? 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.
lx
commented
Imagine dans ma description je met Avec Avec 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.
erwan
commented
Donc si je comprends bien, si un utilisateur crée un compte qui se nomme 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)
|
||||
|
|
L'identifiant, c'est le CN ? Dans tous les cas
Identifiant
c'est un mot français, il faudrait appeller ça plutôt justeId
.