Uwierzytelniony widzi jedynie swoje dane (posty, newsy, userów)
#1
Posted 18 February 2012 - 05:38 AM
Od kilku tygodni przyglądam się Yii i bardzo mi się spodobał.
Przeszedłem przez lekturę 'Przewodnika' i 'zaimplementowałem' bloga ;-)
W ramach dalszej nauki chciałbym dać możliwość 'zakładania' bloga wielu
użytkownikom w ramach tej samej aplikacji, wiec np. każdy po pomyślnym
uwierzytelnieniu widziałby np. jedynie swoje posty. Jednak napotkałem na problem. Jak łatwo zdeterminować jakie
dane mogę danemu, uwierzytelnionemu użytkownikowi pokazać?
Sam gii generuje nam wiele plikow i faktycznie aplikacja 'dziala' niemal od
razu. Gdy wpisze url /post/1 otrzymam dane posta 1, jak 2 to dane posta 2 itp.
Gdy wpisze samo /post dostane liste postow - ale wszystkich!
W kontrolerze pobierającym dane moge ograniczyc dane do np. user_id===Yii::app()->user->id,
ale skolei tych akcji kontrolera też jest niemało, więc
zmiana tego w każdym miejscu jest niewygodna. Inna rzecz, że nawet jak
powyrzucam z widoku linki do tych akcji, ktoś znający budowę standardowej
aplikacji w yii, może zacząć 'preparować' linki - i co wtedy?
Więc jak uniemożliwić podejrzenie posta o ID=2 osobie, która nie jest jego
autorem?
Jak to ugryźć?
Najchętniej bym filtrował dane już na poziomie samego modelu tak żeby
odwołując się do niego z akcji kontrolera miał pewność, że ograniczy mi
serwowane dane do tych przypisanych danemu użytkownikowi.
Czytałem już trochę o 'scopes', 'filtrach' i 'behaviors', ale nie wiem jeszcze
jak się do nich zabrać i które rozwiązanie jest najlepsze.
A jakie Wy stosujecie?
Kolejny bardziej złożony przypadek (bardziej mnie interesuje) to np.
osoba przypisana do danego wydziału (department) np. na uczelni. Wydział oferuje nauke na
wielu kierunkach, zatrudnia wielu pracowników i zrzesza wielu studentów itd.
Załóżmy, że do 'panelu' loguje się taki pracownik. Na podstawie jego ID chcemy
odczytać do jakiego wydziału jest przypisany (department_id), chcemy mu wylistować pracowników
w tym wydziale itd.
Jak najlepiej zrealizować coś takiego? Po uwierzytelnieniu znamy ID
użytkownika. Po relacji (deparment_id) możemy odczytać do jakiego wydziału
jest przypisany itd. Jak realizując pobieranie danych zabezpieczyć się przed
'preparowaniem' linków - tak żeby mieć pewność, że zalogowany zobaczy jedynie
te dane, które powinien?
Trochę się rozpisałem.. ale chciałem mieć pewność, że dobrze opiszę istote
problemu. Za wszelkie sugestie, rozwiązania, liki z góry dziękuję.
#2
Posted 18 February 2012 - 08:37 AM
nie mam w tej chwili za dużo czasu (za 15 minut start 10km klasykiem
Koniecznie przeczytaj:
dział o autoryzacji w podręczniku Yii: http://www.yiiframew.../en/topics.auth a szczególnie od punktu 6.
wpis na wiki yii: http://www.yiiframew...cal-rbac-scheme.
wpis na wikipedii: http://en.wikipedia...._access_control
W podręczniku yii zwróć uwagę na reguły biznesowe, bo kilkoma prostymi testami logicznymi możesz odwalić kawał dobrej roboty i ułatwić sobie życie.
Uważam się za osobę o przeciętnej inteligencji, a czytać to wszystko musiałem kilka razy, żeby do mnie dotarło w 100% jak działa RBAC. W razie czego więc nie zniechęcaj się i czytaj raz jeszcze:).
"Never memorize what you can look up in books."
Albert Einstein
#3
Posted 18 February 2012 - 08:52 AM
belzi, on 18 February 2012 - 05:38 AM, said:
użytkownikom w ramach tej samej aplikacji, wiec np. każdy po pomyślnym
uwierzytelnieniu widziałby np. jedynie swoje posty. Jednak napotkałem na problem. Jak łatwo zdeterminować jakie
dane mogę danemu, uwierzytelnionemu użytkownikowi pokazać?
(...)
Więc jak uniemożliwić podejrzenie posta o ID=2 osobie, która nie jest jego
autorem?
Jak to ugryźć?
Najchętniej bym filtrował dane już na poziomie samego modelu tak żeby
odwołując się do niego z akcji kontrolera miał pewność, że ograniczy mi
serwowane dane do tych przypisanych danemu użytkownikowi.
Czytałem już trochę o 'scopes', 'filtrach' i 'behaviors', ale nie wiem jeszcze
jak się do nich zabrać i które rozwiązanie jest najlepsze.
A jakie Wy stosujecie?
Cześć,
jeśli blog jest prywatny i posty w nim ma widzieć tylko właściciel, to do każdego wybierania z bazy dodaj ograniczenie dla konkretnego author_id (czy jak tam się nazywa kolumna w bazie). Możesz to zrobić za pomocą 'scopes',np. tak:
class Post extends CActiveRecord
{
......
public function scopes()
{
return array(
'own'=>array(
'condition'=>'author_id='.Yii::app()->getUser()->getId(),
),
);
}
}
Post::model()->own()->findAll();
$model=Post::model()->findByPk(2); // zwróci post o pk=2, author_id nie ma znaczenia
$model=Post::model()->own()->findByPk(2); // zwróci null, jeśli author_id != Yii::app()->getUser()->getId()
jeśli każdy może przeglądać wszystkie blogi i chcesz wyświetlać tylko posty właścicieli, to Twój url musi zawierać informację o użytkowniku, którego blog jest aktualnie przeglądany (http://www.yiiframew...amed-parameters), np. jego username, albo id. Wtedy korzystasz z np. innego scope'a z parametrem:
class Post extends CActiveRecord
{
......
public function author($id)
{
$this->getDbCriteria()->mergeWith(array(
'condition'=>'author_id='.$id,
));
return $this;
}
}
Post::model()->author(2)->findAll(); // wszystkie posty autora o id=2
$model=Post::model()->author(2)->findByPk(2); // zwróci post o pk=2, jeśli autorem jest user o id=2, bądź null w przeciwnym wypadku
belzi, on 18 February 2012 - 05:38 AM, said:
osoba przypisana do danego wydziału (department) np. na uczelni. Wydział oferuje nauke na
wielu kierunkach, zatrudnia wielu pracowników i zrzesza wielu studentów itd.
Załóżmy, że do 'panelu' loguje się taki pracownik. Na podstawie jego ID chcemy
odczytać do jakiego wydziału jest przypisany (department_id), chcemy mu wylistować pracowników
w tym wydziale itd.
Jak najlepiej zrealizować coś takiego? Po uwierzytelnieniu znamy ID
użytkownika. Po relacji (deparment_id) możemy odczytać do jakiego wydziału
jest przypisany itd. Jak realizując pobieranie danych zabezpieczyć się przed
'preparowaniem' linków - tak żeby mieć pewność, że zalogowany zobaczy jedynie
te dane, które powinien?
To samo, co wyżej.
d
#4
Posted 18 February 2012 - 11:13 AM
Cieszę się tak szybkiego odzewu. Dziękuję za sugestie i rozwiązania. RBAC'em się zainteresuję. Czytałem o nim wcześniej. Implementowałem ACL'a w Zendzie, a to wydaje mi się podobne, więc nie powinno być problemu. Dzięki za sugestię!
Co do scopów to fajnie, że są i tak jak podejrzewałem łatwo je można zaadoptować jako rozwiązanie opisanego przeze mnie problemu, jednak, może to zabrzmi naiwnie, spodziewałem się (oczekiwałem) czegoś innego. Dlaczego? Dlatego, że aby użyć tych zdefiniowanych scopów i tak muszę we wszystkich miejscach w kontrolerach czy w klasach innych modeli wymusić użycie scope'a, a to już jest błędogenne (gii wygenerował mi ileś tam odwołań do postów - teraz muszę odszukiwać każde z nich i wpisywać czy author czy nie author?).
Spodziewam się, że bez mojej implementacji się nie obejdzie jednak zanim w yii zaczne pisać coś większego niż blog wolałbym znać odpowiedź na większość nasuwających się wątpliwości.
Dziękuję raz jeszcze za odpowiedzi i liczę na kolejne. ;-)
Pozdrawiam.
edit.
Teraz w sumie pomyślałem, że to bardziej akcje kontrolerów wygenerowane przez gii mnie tak stresują. Gdybym utworzyl na ich bazie swoje np. /user/profile zamiast /user/view/ID, a takie akcje jak search czy admin zaremował, to wiele by mi to pomogło.
This post has been edited by belzi: 18 February 2012 - 11:24 AM
#5
Posted 18 February 2012 - 02:37 PM
to tylko framework, a scaffolding nie zrobi za Ciebie gotowego projektu. Jednak jest jeszcze jeden rodzaj scope'ów: http://www.yiiframew...r#default-scope, więc może to rozwiąże Twój problem modyfikacji kodu w każdej akcji, która tego wymaga.
Jeśli chcesz, by Gii za każdym razem tworzyło szablon z jakiego chciałbyś korzystać rzuć okiem na ten temat: http://www.yiiframew...i#extending-gii
d
#6
Posted 19 February 2012 - 04:59 AM
drylko, on 18 February 2012 - 02:37 PM, said:
to tylko framework, a scaffolding nie zrobi za Ciebie gotowego projektu. Jednak jest jeszcze jeden rodzaj scope'ów: http://www.yiiframew...r#default-scope, więc może to rozwiąże Twój problem modyfikacji kodu w każdej akcji, która tego wymaga.
Jeśli chcesz, by Gii za każdym razem tworzyło szablon z jakiego chciałbyś korzystać rzuć okiem na ten temat: http://www.yiiframew...i#extending-gii
d
Witaj!
I to jest właściwa odpowiedź! Wczoraj już wyłączyłem komputer, ale niedługo po tym olśniło mnie, że przecież czytałem o czymś takim jak default scope's, i pewnie tego spróbuję!
Ważniejsze jest jednak to co napisałeś na samym początku - scaffolding nie zrobi za mnie projektu. Faktycznie zachłysnąłem się 'gotowością' wygenerowanej przez gii aplikacji. Pocieszające jest to, że niewielkim kosztem można to dostosować do swoich potrzeb.Dopiero po pewnym czasie zdałem sobie sprawę, że ja tak naprawdę wiem jak zrealizować to zadanie, a bardziej skupiłem się nad 'dostosowaniem' akcji wygenerowanych przez gii. Przy okazji natknąłem się na (krytyczny?) artykuł dotyczący gii:
weavora.com/blog/2012/02/11/scaffolding-in-yii-thoughts-about-gii
Jeszcze jedno pytanie pośrednio związane z moim problemem. Czy Waszym zdaniem bezpiecznie i wydajnie jest aby w klasie WebUser zapisać przez $this->setState np. wspomniany przeze mnie departmentId (zalogowany użytkownik przypisany jest do konkretnego wydziału)? W aplikacji będzie wiele rzeczy połączonych właśnie przez deparmentID. Czy lepiej jest zapamiętać jedynie user->id a później już w aplikacji normalnie User::model()->findByPk(ID)->department->id?
I co myślicie o zastosowaniu metody model w klasie WebUser?
public function getModel()
{
$user = $this->loadUser(Yii::app()->user->id);
return $user;
}
protected function loadUser($id=null)
{
if($this->_model===null)
{
if($id!==null)
$this->_model=User::model()->findByPk($id);
}
return $this->_model;
}
Dzięki czemu wywołujemy jedynie przez:
Yii::app()->user->model->department->id;
Pozdrawiam.
#7
Posted 20 February 2012 - 03:12 PM
belzi, on 19 February 2012 - 04:59 AM, said:
I co myślicie o zastosowaniu metody model w klasie WebUser?
public function getModel()
{
$user = $this->loadUser(Yii::app()->user->id);
return $user;
}
protected function loadUser($id=null)
{
if($this->_model===null)
{
if($id!==null)
$this->_model=User::model()->findByPk($id);
}
return $this->_model;
}
Dzięki czemu wywołujemy jedynie przez:
Yii::app()->user->model->department->id;
Cześć,
stosuje podobne rozwiązanie (getModel) do podanego przez Ciebie, z tą różnicą, że cache'uje cały model użytkownika.
Jeśli opierasz na jakiejś danej pobieranej z bazy działanie aplikacji to nie ma sensu pobierać jej przy każdym odświeżeniu strony. Jak najbardziej stosuj takie metody jak set- i getState, właśnie po to są.
d
#9
Posted 27 February 2013 - 05:23 AM
Jak napisał kolega wyżej, udało mi się wyświetlać zadania i projekty użytkownikowi o danym id, który jest przypisany do danego zadania .
Problem w tym , że gdy administrator jest zalogowany również widzi tylko zadania przypisane tylko jemu.
Co zrobić by widział wszystkie zadania, nie tylko te do których jest przypisany .
funkcja task.php:
public function defaultScope()
{
return array(
'condition'=>"user_id='".Yii::app()->user->id."'",
);
}
#10
Posted 28 February 2013 - 06:40 AM
public function defaultScope(){
if(Yii::app()->user->checkAccess('administrator')){
return array();
}else{
return array(
'condition'=>"user_id='".Yii::app()->user->id."'",
);
}
}
#11
Posted 01 March 2013 - 04:04 AM
#12
Posted 04 April 2013 - 07:30 AM
Sprawa wygląda tak, posiadam model Task i Project. W projektach wyświetlam od razu zadania. W kontrolerze Tasku posiadam właśnie funkcje :
public function defaultScope() {
if (Yii::app()->user->checkAccess('administrator')) {
return array();
} else {
return array(
'condition' => "user_id='" . Yii::app()->user->id . "'",
);
}
}W tabeli TASK istnieje user_id, a w projekcie nie , i gdy wyświetla się od razu zadanie w projektach to dostaje error z Joinem :
CDbCommand failed to execute the SQL statement: SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'author_id' in where clause is ambiguous. The SQL statement executed was: SELECT COUNT(DISTINCT `t`.`id`) FROM `project` `t` LEFT OUTER JOIN `user_project` `users_users` ON (`t`.`id`=`users_users`.`project_id`) LEFT OUTER JOIN `user` `users` ON (`users`.`id`=`users_users`.`user_id`) LEFT OUTER JOIN `user` `author` ON (`t`.`author_id`=`author`.`id`) LEFT OUTER JOIN `task` `tasks` ON (`tasks`.`project_id`=`t`.`id`) WHERE (author_id='79')
Dla tego pewnie ponieważ w projekcie nie posiadam user_id i tutaj problem. Jak to zrobić?
#13
Posted 05 April 2013 - 02:55 AM
#14
Posted 10 April 2013 - 04:51 AM
gadd33, on 05 April 2013 - 02:55 AM, said:
Tak dokładnie .
Ale tu jednak chodzi o "user_id" a nie "author_id" <-błąd występował gdy zamiast "user_id" było wpisane "user_id".
Więc teraz wyrzuca mi :
CDbCommand failed to execute the SQL statement: SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'user_id' in on clause is ambiguous. The SQL statement executed was: SELECT COUNT(DISTINCT `t`.`id`) FROM `project` `t` LEFT OUTER JOIN `user_project` `users_users` ON (`t`.`id`=`users_users`.`project_id`) LEFT OUTER JOIN `user` `users` ON (`users`.`id`=`users_users`.`user_id`) LEFT OUTER JOIN `user` `author` ON (`t`.`author_id`=`author`.`id`) LEFT OUTER JOIN `task` `tasks` ON (`tasks`.`project_id`=`t`.`id`) AND (user_id='49')
W tym przypadku user_id nie istnieje w "project" , ale istnieje w "task" więc dla czego kolumna "user_id" nie jest jednoznaczna?
#15
Posted 10 April 2013 - 04:56 AM
W default scope przy user_id brakuje aliasu. Poczytaj tutaj: http://www.yiiframew...n-named-scopes/
#16
Posted 10 April 2013 - 09:27 AM
public function defaultScope() {
if (Yii::app()->user->checkAccess('administrator')) {
return array();
} else {
return array(
'condition' => "$this.'.user_id='" . Yii::app()->user->id . "'",
);
}
}Wypluwa:
Object of class Project could not be converted to string
#17
Posted 10 April 2013 - 11:37 AM
'condition' => "{$this->tableAlias}.user_id = '" . Yii::app()->user->id . "'",
#18
Posted 11 April 2013 - 02:46 AM
ublic function defaultScope() {
if (Yii::app()->user->checkAccess('administrator')) {
return array();
} else {
return array(
'condition' => "{$this->users}.user_id = '" . Yii::app()->user->id . "'",
);
}
}Zrobiłem tak , jako alias dałem relacje z "projektu" i otrzymałem :
Array to string conversion
#19
Posted 11 April 2013 - 04:34 AM
Jak już, to coś w tym rodzaju:
public function defaultScope() {
if (Yii::app()->user->checkAccess('administrator')) {
return array();
} else {
$tabAlias = ... //Wyciągnij alias z właściwego modelu
/*
Coś w tym rodzaju:
$tabAlias = Task::model()->getTableAlias();
*/
return array(
'condition' => $tabAlias . ".user_id = '" . Yii::app()->user->id . "'",
);
}
}
#20
Posted 11 April 2013 - 05:24 AM
Column not found: 1054 Unknown column 't.user_id' in 'where clause'.

Help















