Меню

Проверка на ошибки permissions

Для управления правами доступа в DRF есть встроенные классы, они называются permissions.

Права доступа проверяются при получении запроса, одновременно с аутентификацией: система определяет, достаточно ли у пользователя прав на выполнение запрошенных операций.

Настроить права доступа можно на уровне всего проекта или на уровне отдельных классов и функций. Устанавливаются глобальные ограничения на уровне проекта, а при необходимости ограничения ослабляются на уровне классов или функций.

Ссылка на документацию

Установка ограничений на уровне проекта

В словаре настроек REST_FRAMEWORK задают параметр DEFAULT_PERMISSION_CLASSES.

Уровни доступа:

  • AllowAny — всё запросы разрешены, для всех пользователей.
  • IsAuthenticated — только аутентифицированные пользователи могут выполнить любой запрос.
  • IsAuthenticatedOrReadOnly — аутентифицированные пользователи могут выполнить любой запрос. Анонимные могут делать запросы на чтение. Аутентифицированные могут делать запросы на создание, удаление или редактирование информации.
  • IsAdminUser — выполнение запросов запрещено всем, кроме администратора.
  • DjangoModelPermissions — авторизация будет предоставлена только в том случае, если пользователь прошел проверку подлинности и ему назначены соответствующие разрешения в модели.
  • DjangoModelPermissionsOrAnonReadOnly — Аналогичен
    DjangoModelPermissions, но также позволяет пользователям, не прошедшим проверку подлинности, иметь доступ к API только для чтения.
  • DjangoObjectPermissions — Этот класс разрешений связан со стандартной структурой разрешений объектов Django, которая описывает разрешения для отдельных объектов в моделях.

Настройки файла settings.py

REST_FRAMEWORK = {
    ...
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated', 
    ],

УСТАНОВКА ОГРАНИЧЕНИЙ НА УРОВНЕ запроса

Для указания ограничения на уровне класса необходимо указать атрибут permission_classes в теле. Его значение будет равно объекту из модуля permissions пакета rest_framework

Файл views.py

from rest_framework.permissions import IsAuthenticatedOrReadOnly

...

class PersonViewSet(viewsets.ModelViewSet):
    queryset = Person.objects.all()
    serializer_class = PersonSerializer
    permission_classes = (IsAuthenticatedOrReadOnly,) 

УСТАНОВКА ОГРАНИЧЕНИЙ НА УРОВНЕ объекта

Часто встречается ограничение доступа до запросов с объектами, созданными пользователями. Например, удалить пост может только автор поста.

В Django REST Framework все классы разрешений наследуются от базового класса BasePermission. В нем описаны два метода:

  • в методе has_permission определяются разрешения на уровне запроса;
  • в методе has_object_permission устанавливаются разрешения на уровне объекта.

Доступ будет разрешен, если методы вернут True. Для создания собственных разрешений — необходимо описать класс, расширяющий BasePermission и переопределить один или оба его метода. Создавать классы принято в отдельном файле permissions.py

Файл permissions.py

from rest_framework import permissions

class PersonOrReadOnly(permissions.BasePermission):

    def has_permission(self, request, view):
        return (request.method in permissions.SAFE_METHODS
                   or request.user.is_authenticated)

    def has_object_permission(self, request, view, obj):
        return obj.person == request.user 

  • Метод has_object_permission не выполняется для представлений, возвращающих коллекции объектов или создающих новый объект модели (поскольку объект ещё не существует).
  • Метод has_object_permission вызывается только в том случае, если метод has_permission вернул True.
  • По умолчанию оба метода возвращают значение True. Окончательное решение о доступе будет зависеть от результата работы того метода, который был переопределён.

Подключение класса к отображению

from .permissions import PersonOrReadOnly

class PersonViewSet(viewsets.ModelViewSet):
    queryset = Person.objects.all()
    serializer_class = PersonSerializer
    permission_classes = (PersonOrReadOnly,

I design a function that may get/set a resource from SD and if not found from sd then take it from Asset and if possible write the asset back to SD
This function may check by method invocation if SD is mounted and accessible…

boolean bSDisAvalaible = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);

My designed function may be used from one app(project) to another (with or without android.permission.WRITE_EXTERNAL_STORAGE)

Then I would like to check if the current application has this particular permission without playing with SecurityException.

Does it exist a «nice» way to consult current defined permissions at runtime ?

asked Aug 26, 2011 at 10:58

Emmanuel Devaux's user avatar

Emmanuel DevauxEmmanuel Devaux

3,2574 gold badges25 silver badges30 bronze badges

You can use Context.checkCallingorSelfPermission() function for this. Here is an example:

private boolean checkWriteExternalPermission()
{
    String permission = android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
    int res = getContext().checkCallingOrSelfPermission(permission);
    return (res == PackageManager.PERMISSION_GRANTED);            
}

Komal12's user avatar

Komal12

3,2674 gold badges15 silver badges25 bronze badges

answered Aug 26, 2011 at 11:07

inazaruk's user avatar

inazarukinazaruk

74k24 gold badges187 silver badges156 bronze badges

8

This is another solution as well

PackageManager pm = context.getPackageManager();
int hasPerm = pm.checkPermission(
    android.Manifest.permission.WRITE_EXTERNAL_STORAGE, 
    context.getPackageName());
if (hasPerm != PackageManager.PERMISSION_GRANTED) {
   // do stuff
}

answered Apr 13, 2013 at 0:33

user123321's user avatar

user123321user123321

12.5k11 gold badges50 silver badges63 bronze badges

3

You can also use this:

private boolean doesUserHavePermission()
{
    int result = context.checkCallingOrSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
    return result == PackageManager.PERMISSION_GRANTED;
}

answered Feb 12, 2014 at 16:37

MrAppa's user avatar

MrAppaMrAppa

2213 silver badges9 bronze badges

Like Google documentation:

// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE);

answered Feb 2, 2016 at 17:26

beni's user avatar

benibeni

2,9895 gold badges34 silver badges54 bronze badges

Sharing my methods in case someone needs them:

 /** Determines if the context calling has the required permission
 * @param context - the IPC context
 * @param permissions - The permissions to check
 * @return true if the IPC has the granted permission
 */
public static boolean hasPermission(Context context, String permission) {

    int res = context.checkCallingOrSelfPermission(permission);

    Log.v(TAG, "permission: " + permission + " = tt" + 
    (res == PackageManager.PERMISSION_GRANTED ? "GRANTED" : "DENIED"));

    return res == PackageManager.PERMISSION_GRANTED;

}

/** Determines if the context calling has the required permissions
 * @param context - the IPC context
 * @param permissions - The permissions to check
 * @return true if the IPC has the granted permission
 */
public static boolean hasPermissions(Context context, String... permissions) {

    boolean hasAllPermissions = true;

    for(String permission : permissions) {
        //you can return false instead of assigning, but by assigning you can log all permission values
        if (! hasPermission(context, permission)) {hasAllPermissions = false; }
    }

    return hasAllPermissions;

}

And to call it:

boolean hasAndroidPermissions = SystemUtils.hasPermissions(mContext, new String[] {
                android.Manifest.permission.ACCESS_WIFI_STATE,
                android.Manifest.permission.READ_PHONE_STATE,
                android.Manifest.permission.ACCESS_NETWORK_STATE,
                android.Manifest.permission.INTERNET,
        });

answered Feb 25, 2016 at 0:09

htafoya's user avatar

htafoyahtafoya

17.6k11 gold badges76 silver badges100 bronze badges

You should check for permissions in the following way (as described here Android permissions):

int result = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.READ_PHONE_STATE);

then, compare your result to either:

result == PackageManager.PERMISSION_DENIED

or:

result == PackageManager.PERMISSION_GRANTED

answered Aug 4, 2016 at 14:09

box's user avatar

boxbox

4,3402 gold badges36 silver badges38 bronze badges

1

Step 1 — add permission request

    String[] permissionArrays = new String[]{Manifest.permission.CAMERA, 
    Manifest.permission.WRITE_EXTERNAL_STORAGE};
    int REQUEST_CODE = 101;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(permissionArrays, REQUEST_CODE );
        } else {
             // if already permition granted
            // PUT YOUR ACTION (Like Open cemara etc..)
        }
    }

Step 2 — Handle Permission result

     @Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    boolean openActivityOnce = true;
    boolean openDialogOnce = true;
    if (requestCode == REQUEST_CODE ) {
        for (int i = 0; i < grantResults.length; i++) {
            String permission = permissions[i];

            isPermitted = grantResults[i] == PackageManager.PERMISSION_GRANTED;

            if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
                // user rejected the permission

            }else {
                //  user grant the permission
                // you can perfome your action 
            }
        }
    }
}

answered Feb 1, 2019 at 8:26

Jaydeep Dobariya's user avatar

The code which works fine for me is :-

  final int MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 102;
  if ((ContextCompat.checkSelfPermission(getActivity(),Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)) {

                requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                        MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
            } else {
        // user already provided permission
                // perform function for what you want to achieve
     }

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

    boolean canUseExternalStorage = false;

    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: {
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                canUseExternalStorage = true;
            }

            if (!canUseExternalStorage) {
                Toast.makeText(getActivity(), "Cannot use this feature without requested permission", Toast.LENGTH_SHORT).show();
            } else {
                // user now provided permission
                // perform function for what you want to achieve
            }
        }
      }
 }

answered Mar 16, 2016 at 10:48

Sanoop Surendran's user avatar

Sanoop SurendranSanoop Surendran

3,4464 gold badges29 silver badges49 bronze badges

if ((ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

        requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA},
                MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
    }
} 

Komal12's user avatar

Komal12

3,2674 gold badges15 silver badges25 bronze badges

answered Jun 18, 2018 at 9:47

dhaval malavia's user avatar

Check Permissions In KOTLIN (RunTime)

In Manifest: (android.permission.WRITE_EXTERNAL_STORAGE)

    fun checkPermissions(){

      var permission_array=arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
      if((ContextCompat.checkSelfPermission(this,permission_array[0]))==PackageManager.PERMI   SSION_DENIED){
        requestPermissions(permission_array,0)
      }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)

          if(requestCode==0 && grantResults[0]==PackageManager.PERMISSION_GRANTED){

       //Do Your Operations Here

        ---------->
         //


          }
    }

answered Jan 28, 2019 at 10:08

Ankush Shrivastava's user avatar

Enable GPS location Android Studio

  1. Add permission entry in AndroidManifest.Xml
  1. MapsActivity.java

    public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
    
    private GoogleMap mMap;
    private Context context;
    private static final int PERMISSION_REQUEST_CODE = 1;
    Activity activity;
    /**
     * ATTENTION: This was auto-generated to implement the App Indexing API.
     * See https://g.co/AppIndexing/AndroidStudio for more information.
     */
    private GoogleApiClient client;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        context = getApplicationContext();
        activity = this;
        super.onCreate(savedInstanceState);
        requestPermission();
        checkPermission();
        setContentView(R.layout.activity_maps);
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    
    }
    
    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
        LatLng location = new LatLng(0, 0);
        mMap.addMarker(new MarkerOptions().position(location).title("Marker in Bangalore"));
        mMap.moveCamera(CameraUpdateFactory.newLatLng(location));
        mMap.setMyLocationEnabled(true);
    }
    
    private void requestPermission() {
        if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.ACCESS_FINE_LOCATION)) {
            Toast.makeText(context, "GPS permission allows us to access location data. Please allow in App Settings for additional functionality.", Toast.LENGTH_LONG).show();
        } else {
            ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQUEST_CODE);
        }
    }
    
    private boolean checkPermission() {
        int result = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION);
        if (result == PackageManager.PERMISSION_GRANTED) {
            return true;
        } else {
            return false;
        }
    }
    

RominaV's user avatar

RominaV

3,3351 gold badge29 silver badges58 bronze badges

answered Aug 18, 2016 at 11:13

Naveen Sharma's user avatar

В тренажёр загружена облегчённая версия проекта Yatube; она реализована на дженериках.Настройте права доступа так, чтобы неаутентифицированные пользователи могли только читать публикации; все остальные операции должны быть доступны только пользователям с действующим токеном.

Подсказка:

В DEFAULT_PERMISSION_CLASSES словаря REST_FRAMEWORK установите уровень доступа на уровне проекта.

В описании вью-классов для публикаций добавьте атрибут permission_classes и укажите в нём необходимый уровень доступа для анонимов.

from rest_framework import generics, permissions

from posts.models import Post
from .serializers import PostSerializer


class PostList(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,) 

    def perform_create(self, serializer):
        serializer.save(author=self.request.user)


class PostDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

Permalink

Cannot retrieve contributors at this time


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters

Show hidden characters

#!/usr/bin/python
# License:
# wget -O — https://raw.github.com/avsm/openbsd-xen-sys/master/sys/timetc.h | head -n 8 | sed ‘s/Poul-Henning Kamp/GDR!/g’ | sed ‘s/phk@FreeBSD.ORG/gdr@go2.pl/g’
import grp
import os
import os.path
import pwd
from stat import *
import sys
dir = None
user = None
user_printable = «[no user]»
groupids = []
groupnames = []
if len(sys.argv) == 1:
dir = os.getcwd()
elif len(sys.argv) >= 2:
if sys.argv[1] in (‘-h’, ‘—help’):
print «»»
%s [path] [username]
Check is <path> is accessible by <user>
If not, highlight which permissions are conflicting
Path defaults to current working directory
User defaults to current user
By GDR! [http://gdr.geekhood.net/]
«»» % sys.argv[0]
sys.exit(0)
dir = os.path.abspath(sys.argv[1])
if len(sys.argv) >= 3:
user_printable = sys.argv[2]
user = sys.argv[2]
userid = pwd.getpwnam(user)[2]
else:
userid = os.stat(dir)[4]
user = pwd.getpwuid(userid)[0]
user_printable = user
if user:
user_printable = user
groupids.append(pwd.getpwuid(userid)[3])
groupnames.append(grp.getgrgid(groupids[0])[0])
for group in grp.getgrall():
if user in group[3]:
groupids.append(group[2])
groupnames.append(group[0])
class bcolors:
HEADER = 33[95m’
OKBLUE = 33[94m’
OKGREEN = 33[92m’
WARNING = 33[31;40m’
# WARNING = ’33[93m’
FAIL = 33[91m’
ENDC = 33[0m’
def disable(self):
self.HEADER = »
self.OKBLUE = »
self.OKGREEN = »
self.WARNING = »
self.FAIL = »
self.ENDC = »
def warn(text):
return bcolors.WARNING + text + bcolors.ENDC
def ok(text):
return bcolors.OKGREEN + text + bcolors.ENDC
def check_if_can_access(stat, path):
failed = False
mode = stat[0]
smode = »
# Owner
if stat[4] == userid:
if mode & S_IRUSR:
smode += ok(‘r’)
else:
smode += warn(‘-‘)
failed = True
else:
if mode & S_IRUSR:
smode += ‘r’
else:
smode += ‘-‘
if mode & S_IWUSR:
smode += ‘w’
else:
smode += ‘-‘
if stat[4] == userid and S_ISDIR(mode):
if mode & S_IXUSR:
smode += ok(‘x’)
else:
smode += warn(‘-‘)
failed = True
else:
if mode & S_IXUSR:
smode += ‘x’
else:
smode += ‘-‘
smode += » «
# Group
if stat[5] in groupids and stat[4] <> userid:
if mode & S_IRGRP:
smode += ok(‘r’)
else:
smode += warn(‘-‘)
failed = True
else:
if mode & S_IRGRP:
smode += ‘r’
else:
smode += ‘-‘
if mode & S_IWGRP:
smode += ‘w’
else:
smode += ‘-‘
if stat[5] in groupids and S_ISDIR(mode) and stat[4] <> userid:
if mode & S_IXGRP:
smode += ok(‘x’)
else:
smode += warn(‘-‘)
failed = True
else:
if mode & S_IXGRP:
smode += ‘x’
else:
smode += ‘-‘
smode += ‘ ‘
# others
if stat[5] not in groupids and stat[4] <> userid:
if mode & S_IROTH:
smode += ok(‘r’)
else:
smode += warn(‘-‘)
failed = True
else:
if mode & S_IROTH:
smode += ‘r’
else:
smode += ‘-‘
if mode & S_IWOTH:
smode += ‘w’
else:
smode += ‘-‘
if stat[5] not in groupids and stat[4] <> userid and S_ISDIR(mode):
if mode & S_IXOTH:
smode += ok(‘x’)
else:
smode += warn(‘-‘)
failed = True
else:
if mode & S_IXOTH:
smode += ‘x’
else:
smode += ‘-‘
return (smode, failed)
print «Checking access to file/directory %s for user %s» % (dir, user_printable)
if groupids:
print «User is member of the following groups: %s» % ‘, ‘.join(groupnames)
current_dir = dir
u_width = 15
g_width = 10
print » U G O t%st%stFile» % (‘Username’.ljust(u_width), ‘Group’.ljust(g_width))
something_failed = False
while True:
stat = os.stat(current_dir)
rights, failed = check_if_can_access(stat, current_dir)
something_failed = something_failed or failed
print «%st%st%st%s» % (rights,
pwd.getpwuid(stat[4])[0].ljust(u_width),
grp.getgrgid(stat[5])[0].ljust(g_width),
current_dir,
)
if current_dir == ‘/’:
break
current_dir = os.path.dirname(current_dir)

Проверка прав доступа

Когда пользователь пытается получить доступ к ресурсу, AggreGate Server просматривает его таблицу прав доступа построчно, начиная с самой первой (верхней).

Если путь контекста ресурса, к которому пользователь пытается получить доступ, совпадает или продолжает маску контекста, определенную в текущей строке таблицы прав, уровень прав доступа в этой строке будет эффективным уровнем прав доступа для этого пути. Как только совпадающая строка найдена, другие строки не проверяются на совпадение. Так, например, если вы начинаете таблицу с контекстом * (т.е. первая строка в таблице), другие строки не будут проверяться, потому что эта строка совпадает со всеми контекстами. Именно по этому контекст * всегда является последней строкой в таблице.

Требуемый уровень прав доступа, определенный на предыдущем шаге, сравнивается с уровнем прав доступа, запрашиваемым ресурсом, к которому осуществляется доступ. Если эффективный уровень включает уровень прав доступа источника, доступ будет разрешен. Иначе, в доступе будет отказано.

Если контекстный путь запрашиваемого ресурса не соответствует или не расширяет любую маску в таблице, эффективный уровень прав доступа запрашиваемого ресурса определяется последней строкой в таблице прав доступа, потому что она всегда определяет уровень прав доступа для всех контекстов (Маска контекста *).

Пример 1

Предположим, что таблица прав доступа пользователя john выглядит следующим образом:

Строка

Маска контекста

Права доступа

1

users.test

Менеджер

2

users.*

Не определен

3

*

Менеджер

1. John пытается получить доступ к user.abc.alerts. Необходимый уровень прав для данного ресурса является Менеджер. Первая строка таблицы, users.test, не совпадает и не продолжает маску users.abc.alerts, поэтому мы переходим ко второй строке users.*, и видим, что она продливает users.test. Данная вторая строка задает Не определен в качестве уровня прав доступа, и поэтому доступ будет запрещен.

2. John пытается получить доступ к event_filters.filter1, требующий уровень прав доступа Менеджер.  Эффективный уровень прав доступа для event_filters.filter1 будет определен третьей строкой в таблице, и, таким образом, доступ будет запрещен.

3. John пытается получить доступ к users.test.queries, требующий уровень прав доступа Администратор. Его эффективный уровень для этого контекста определяется первой строкой в таблице. Он будет уровнем Менеджер (ниже, чем Администратор), и, таким образом, доступ к users.test.queries будет запрещен.

Пример 2

Таблица прав доступа администратора по умолчанию:

Строка

Маска контекста

Права доступа

1

*

Admin

Эффективный уровень прав доступа администратора по умолчанию для любого контекста — Администратор, потому что все контекстные пути продливают маску *. Поэтому администратор может выполнять любую операцию в контексте.

Окно отказа в доступе

Если прав доступа пользователя недостаточно для осуществления требуемой операции, в доступе будет отказано, и появится сообщение об ошибке «Нет прав».

Временное отключение доступа к некоторым объектам

Когда таблица прав доступа пользователя настраивается так, чтобы запретить ему доступ к некоторым из ресурсов, никаким образом не влияя на сами ресурсы. Фактически, какой-либо другой пользователь может иметь доступ к этим ресурсам, даже если для их владельца доступ запрещен.

Пример

John, пользователь из последнего примера, имеет контекст Тревоги, users.john.alerts. Данный путь существует всегда. Изначально John может получить доступ к его Тревогам, потому что это позволяет Права доступа пользователя по умолчанию. Он входит на AggreGate Server и создает тревогу alert1. Затем администратор настраивает таблицу прав доступа John’а так, чтобы он больше не смог видеть его контекст Тревоги (например, добавив запись с Маской контекста users.john.alerts и Уровнем прав доступа Не определен в начало таблицы). Это не удалит alert1 и даже не прекратит ее работу. Некоторые пользователи AggreGate Server все еще имеют доступ к этой тревоге и смогут конфигурировать и контролировать ее. Как только администратор разрешает John’у получить доступ к его тревогам, alert1 снова появится в его контексте Тревоги.

Операционная система Android устроена таким образом, что для выполнения некоторых операций или доступа к определенным ресурсам, приложение должно иметь разрешение на это.

Разрешения могут быть двух типов: normal и dangerous. Отличие между ними в том, что dangerous разрешения опасны, т.к. могут быть использованы для получения ваших личных данных или информации о вас, или еще каким-то способом могут навредить вам. Примеры dangerous разрешений — это доступ к контактам или смс.

Полный список существующих разрешений можно посмотреть здесь. Характеристика Protection level подскажет насколько опасно это разрешение. А здесь можно сразу просмотреть весь список normal разрешений.

Если приложению необходимо получить какое-либо разрешение, то оно должно быть указано в AndroidManifest.xml, в корневом теге <manifest>. Тег разрешения — <uses-permission>.

Вот пример манифеста с разрешениями:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.test.someapplication">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.SEND_SMS" />

    <application
    ...
    ></application>

</manifest>

Здесь мы указываем, что приложению понадобятся разрешения на работу с интернет, контактами, bluetooth, локацией, камерой и смс. Пользователю необходимо будет подтвердить, что он предоставляет приложению эти разрешения.

В этом материале мы подробно рассмотрим, как происходит это подтверждение.

До Android 6

До выхода Android 6 все было просто и легко. Когда пользователь устанавливал приложение с манифестом, который мы рассмотрели чуть выше, то он видел такой экран:

Система показывает разрешения, которые были прописаны в манифесте. Сначала те, которые могут быть опасными с точки зрения приватности (отправка смс, доступ к камере/местоположению/контактам), а затем — обычные (интернет, bluetooth).

Таким образом пользователь видит, на что претендует приложение, и может примерно понять все ли в порядке. Если, например, приложение калькулятор при установке просит у вас доступ к контактам и смс, то скорее всего, что-то не так с этим приложением и оно может быть опасным для ваших данных.

Нажав кнопку Install, пользователь автоматически подтверждает свое согласие, что приложению будут предоставлены эти запрашиваемые разрешения. И далее, когда приложение, например, пытается в коде получить список контактов, то оно без проблем их получает.

Если же в манифесте не указать разрешение READ_CONTACTS, то его не будет и в списке тех разрешений, которые подтверждает пользователь. Соответственно, система не предоставит этому приложению доступ к контактам. И при попытке получить список контактов, будет ошибка:
java.lang.SecurityException: Permission Denial: opening provider com.android.providers.contacts.ContactsProvider2

Android 6

С выходом Android 6 механизм подтверждения поменялся. Теперь при установке приложения пользователь больше не видит списка запрашиваемых разрешений. Приложение автоматически получает все требуемые normal разрешения, а dangerous разрешения необходимо будет программно запрашивать в процессе работы приложения.

Т.е. теперь недостаточно просто указать в манифесте, что вам нужен, например, доступ к контактам. Когда вы в коде попытаетесь запросить список контактов, то получите ошибку SecurityException: Permission Denial. Потому что вы явно не запрашивали это разрешение, и пользователь его не подтверждал.

Перед выполнением операции, требующей разрешения, необходимо спросить у системы, есть ли у приложения разрешение на это. Т.е. подтверждал ли пользователь, что он дает приложению это разрешение. Если разрешение уже есть, то выполняем операцию. Если нет, то запрашиваем это разрешение у пользователя.

Давайте посмотрим, как это выглядит на практике.

Проверка текущего статуса разрешения выполняется методом checkSelfPermission

int permissionStatus = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS);

На вход метод требует Context и название разрешения. Он вернет константу PackageManager.PERMISSION_GRANTED (если разрешение есть) или PackageManager.PERMISSION_DENIED (если разрешения нет).

Если разрешение есть, значит мы ранее его уже запрашивали, и пользователь подтвердил его. Можем получать список контактов, система даст нам доступ.

Если разрешения нет, то нам надо его запросить. Это выполняется методом requestPermissions. Схема его работы похожа на метод startActivityForResult. Мы вызываем метод, передаем ему данные и request code, а ответ потом получаем в определенном onResult методе.

Добавим запрос разрешения к уже имеющейся проверке.

int permissionStatus = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS);

if (permissionStatus == PackageManager.PERMISSION_GRANTED) {
   readContacts();
} else {
   ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.READ_CONTACTS},
           REQUEST_CODE_PERMISSION_READ_CONTACTS);
}

Проверяем разрешение READ_CONTACTS. Если оно есть, то читаем контакты. Иначе запрашиваем разрешение READ_CONTACTS методом requestPermissions. На вход метод требует Activity, список требуемых разрешений, и request code. Обратите внимание, что для разрешений используется массив. Т.е. вы можете запросить сразу несколько разрешений.

После вызова метода requestPermissions система покажет следующий диалог

Здесь будет отображено разрешение, которое мы запросили методом requestPermissions. Пользователь может либо подтвердить его (ALLOW), либо отказать (DENY). Если будет запрошено сразу несколько разрешений, то на каждое из них будет показан отдельный диалог. И пользователь может какие-то разрешения подтвердить, а какие-то нет.

Решение пользователя мы получим в методе onRequestPermissionsResult

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
   switch (requestCode) {
       case REQUEST_CODE_PERMISSION_READ_CONTACTS:
           if (grantResults.length > 0
                   && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
               // permission granted
               readContacts();
           } else {
               // permission denied
           }
           return;
   }
}

Проверяем, что requestСode тот же, что мы указывали в requestPermissions. В массиве permissions придут название разрешений, которые мы запрашивали. В массиве grantResults придут ответы пользователя на запросы разрешений.

Мы проверяем, что массив ответов не пустой и берем первый результат из него (т.к. мы запрашивали всего одно разрешение). Если пользователь подтвердил разрешение, то выполняем операцию. Если же пользователь отказал, то дальнейшие действия зависят от логики вашего приложения.

В итоге схема получения разрешения состоит из трех действий:
— проверка текущего состояния разрешения
— запрос на получение разрешения, если оно еще не было получено
— обработка ответа на запрос

Далее поговорим про некоторые дополнительные возможности, нюансы и прочие мелочи.

Манифест

При использовании новой схемы разрешений вам все равно необходимо указывать разрешение в манифесте. Если его там не указать и сделать запрос на это разрешение, то вам просто сразу придет отказ без всякого диалога.

Всегда проверяйте разрешение

Каждый раз (а не только первый) перед выполнением операции, требующей определенного разрешения, необходимо проверять, что это разрешение есть. Потому что, даже если пользователь уже давал это разрешение, он всегда может зайти в настройки приложения и отменить его. И если вы после этого не выполните проверку, то получите ошибку при выполнении операции.

Don’t ask again

Когда вы первый раз делаете запрос на какое-либо разрешение, пользователь может отказать. При последующих запросах этого же разрешения, в диалоге появится чекбокс Don’t ask again

Если пользователь включит этот чекбокс, то при последующих ваших запросах диалог не будет отображаться, а в onRequestPermissionsResult сразу будет приходить отказ.

Объяснение для пользователя

Когда вы запрашиваете разрешение, пользователю должно быть очевидно, зачем приложению понадобилось это разрешение, и у него не должно возникать вопросов. Но случаи бывают разные, и вы можете решить, что вам надо явно объяснить пользователю, почему приложению понадобилось это разрешение.

Диалог, который показывается при запросе разрешения, — системный, вы не можете менять его содержимое и добавлять туда свой текст. Но вы можете сделать свой диалог или что-то подобное и показать его перед тем, как будете делать запрос разрешения.

Есть метод shouldShowRequestPermissionRationale, который может быть полезен в данной ситуации. Передаете ему название разрешения, а он вам в виде boolean ответит, надо ли показывать объяснение для пользователя.

Т.е. вы сначала проверяете наличие разрешения. Если его нет, то вызываете shouldShowRequestPermissionRationale, чтобы решить, надо ли показывать объяснение пользователю. Если не надо, то делаете запрос разрешения. А если надо, то показываете ваш диалог с объяснением, а после этого диалога делаете запрос разрешения.

Алгоритм работы метода shouldShowRequestPermissionRationale прост.

Если вы еще ни разу не запрашивали это разрешение, то он вернет false. Т.е. перед первым запросом разрешения ничего объяснять не надо.

Если вы ранее уже запрашивали это разрешение и пользователь отказал, то метод вернет true. Т.е. пользователь не понимает, почему он должен давать это разрешение, и надо ему это объяснить.

Если пользователь ставил галку Don’t ask again, то метод вернет false. Запрос полномочий все равно не будет выполнен. Объяснять что-то не имеет смысла.

Разумеется, вы можете показывать дополнительную информацию согласно вашим правилам и не использовать метод shouldShowRequestPermissionRationale.

Группы

Dangerous разрешения собраны в группы. Список групп можно посмотреть здесь. Если вы запросили одно разрешение из группы и пользователь предоставил вам его, то вы автоматически получаете все разрешения этой группы.

Например, разрешения READ_CONTACTS и WRITE_CONTACTS принадлежат группе CONTACTS. И если пользователь уже подтверждал разрешение на READ_CONTACTS, то при проверке WRITE_CONTACTS вы получите PERMISSION_GRANTED.

Android 6 и targetSdkVersion 23

Схема работы разрешений зависит от версии Android, на которой запущено приложение и от параметра targetSdkVersion приложения.

Новая схема будет работать, если версия Android >= 6 И targetSdkVersion >= 23.

В остальных случаях, т.е. когда targetSdkVersion < 23 ИЛИ версия Android < 6, разрешения будут работать по старому. Т.е. пользователь будет подтверждать их сразу все при установке. Если в приложении есть код, который проверяет разрешения, то он будет получать PERMISSION_GRANTED.

Но учитывайте, что в Android версии 6 и выше, пользователь может отменить разрешения в настройках приложения.

Intent

Не забывайте, что иногда для работы с контактами, камерой и т.п., вы можете использовать Intent и уже установленные приложения. В этом случае вам не придется писать лишний код и запрашивать разрешения для работы с этими ресурсами.


Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance 

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня


0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

А вот еще интересные материалы:

  • Яшка сломя голову остановился исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного где ошибка
  • Проверка ноутбука на ошибки windows 10
  • Проверка скрипта на ошибки php